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

HANSARDLIST   F

Complexity

Total Complexity 324

Size/Duplication

Total Lines 2531
Duplicated Lines 0 %

Test Coverage

Coverage 43.4%

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 2531
rs 0.6314
ccs 539
cts 1242
cp 0.434
wmc 324

35 Methods

Rating   Name   Duplication   Size   Complexity  
F _get_nextprev_items() 0 180 22
C _get_data_by_person() 0 89 9
B _get_data_by_recent() 0 51 6
A gid() 0 2 1
B _validate_date() 0 31 4
B _get_subsection() 0 35 5
A total_items() 0 7 1
F _get_data_by_search() 0 295 47
B _get_data_by_date() 0 82 4
B display() 0 54 8
A __construct() 0 2 1
C _get_item() 0 91 10
A htype() 0 2 1
B _get_section() 0 36 3
C _get_speaker_offices() 0 28 8
B _get_votes() 0 51 4
B _get_speaker_url() 0 12 5
B _get_comment_count_for_epobject() 0 33 5
C _get_listurl() 0 94 11
A _get_data_by_search_min() 0 2 1
F _get_data_by_calendar() 0 296 31
F _get_data_by_gid() 0 202 31
A _get_data_by_column() 0 20 1
A epobject_id() 0 2 1
B prepare_search_result_for_display() 0 26 2
C _get_speaker_alone() 0 22 10
A _get_mentions() 0 10 2
B most_recent_day() 0 48 4
B _get_comment() 0 60 4
A check_gid_change() 0 15 3
C _get_speaker() 0 67 13
A render() 0 11 2
F _get_hansard_data() 0 286 57
B _get_nextprev_dates() 0 68 6
A _get_data_by_search_video() 0 2 1

How to fix   Complexity   

Complex Class

Complex classes like HANSARDLIST often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use HANSARDLIST, and based on these observations, apply Extract Interface, too.

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 51 and the first side effect is on line 3.

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
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...
4
include_once INCLUDESPATH."easyparliament/searchlog.php";
5
6
/*
7
8
The HANSARDLIST class and its children, DEBATELIST and WRANSLIST, display data about
9
Hansard objects. You call display things by doing something like:
10
11
        $LIST = new DEBATELIST;
12
        $LIST->display('gid', array('gid'=>'2003-10-30.422.4') );
13
14
    The second line could be replaced with something like one of these:
15
16
        $LIST->display('date', array('date'=>'2003-12-31') );
17
        $LIST->display('recent');
18
        $LIST->display('member', array('id'=>37) );
19
20
21
Basic structure...
22
23
    The display() function calls a get function which returns all the data that
24
    is to be displayed. The function name depends on the $view (ie, 'gid', 'recent',
25
    etc).
26
    Once we have an array of data, the render() function is called, which includes
27
    a template. This cycles through the data array and outputs HTML.
28
29
    Most of the data is fetched from the database by the _get_hansard_data() function.
30
31
    The COMMENTSLIST class is simpler and works in a similar fashion - that might help
32
    you get your head round how this all works...
33
34
Future stuff...
35
36
    You could have multiple templates for different formats. Eg, to output stuff in
37
    XML, duplicate the HTML template and change what you need to create XML instead.
38
    Then call the display() function something like this:
39
        $LIST->display('gid', array('gid'=>'2003-10-30.422.4'), 'xml' );
40
    (You'll need to allow the 'xml' format in render() too).
41
42
    No support for pages of results yet. This would be passed in in the $args array
43
    and used in the LIMIT of the _get_hansard_data() function.
44
    The template could then display links to next/prev pages in the sequence.
45
46
47
48
49
*/
50
51
class RedirectException extends \Exception { }
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...
52
53
class HANSARDLIST {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
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...
54
    // This will be used to cache information about speakers on this page
55
    // so we don't have to keep fetching the same data from the DB.
56
    public $speakers = array ();
57
    /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% 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...
58
    $this->speakers[ $person_id ] = array (
59
        "name" => $name,
60
        "constituency"	=> $constituency,
61
        "party"			=> $party,
62
        "person_id"	    => $person_id,
63
        "url"			=> "/member/?p=$person_id"
64
    );
65
    */
66
67
    // This will be used to cache mappings from epobject_id to gid,
68
    // so we don't have to continually fetch the same data in get_hansard_data().
69
    public $epobjectid_to_gid = array ();
70
    /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% 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...
71
    $this->epobjectid_to_gid[ $epobject_id ] => $gid;
72
    */
73
74
    # Similarly, cache bill lookups
75
    public $bill_lookup = array();
76
77
    // This is so we can tell what type of thing we're displaying from outside
78
    // the object. eg, so we know if we should be able to post comments to the
79
    // item. It will have a value set if we are displaying by 'gid' (not 'date').
80
    // Use htype() to access it.
81
    public $htype;
82
83
84
    // Reset to the relevant major ID in DEBATELIST or WRANSLIST
85
    public $major;
86
87
88
    // When we view a particular item, we set these to the epobject_id and gid
89
    // of the item so we can attach Trackbacks etc to it from outside.
90
    public $epobject_id;
91
    public $gid;
92
93
94
    // This will be set if $this->most_recent_day() is called. Just so we
95
    // don't need to call it and it's lengthy query again.
96
    public $most_recent_day;
97
98
    // This prefix is used to pick out unique things by type
99
    public $gidprefix;
100
101
    // These are used to specify the pages for each subclass
102
    public $listpage;
103
    public $commentspage;
104
105
    # Only used by StandingCommittee subclass
106
    public $bill_title;
107
    public $url;
108
109
    public $db;
110
111 6
    public function __construct() {
112 6
        $this->db = new ParlDB;
113 6
    }
114
115
116
117
    public function display ($view, $args=array(), $format='html') {
118
119
        // $view is what we're viewing by:
120
        // 	'gid' is the gid of a hansard object,
121
        //	'date' is all items on a date,
122
        //	'person' is a person's recent debates/wrans,
123
        //	'recent' is a number of recent dates with items in.
124
        //  'recent_mostvotes' is the speeches with the most votes in the last x days.
125
        //	'search' is all debates/wrans that match a search term.
126
        //	'biggest_debates' is biggest recent debates (obviously only for DEBATESLIST).
127
        //  'recent_wrans' is some recent written answers (obv only for WRANSLIST).
128
129
        // $args is an associative array of stuff like
130
        //	'gid' => '2003-10-30.422.4'  or
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...
131
        //	'd' => '2003-12-31' or
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...
132
        //	's' => 'my search term'
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...
133
        //	'o' => Sort order: 'r' for relevance, 'd' for date
134
135
        // $format is the format the data should be rendered in,
136
        // using that set of templates (or 'none' for just returning
137
        // the data).
138
139
        global $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...
140
141
        if ($view == 'search' && (!defined('FRONT_END_SEARCH') || !FRONT_END_SEARCH))
1 ignored issue
show
Bug introduced by
The constant FRONT_END_SEARCH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
142
            return false;
143
144
        $validviews = array ('calendar', 'date', 'gid', 'person', 'search', 'search_min', 'search_video', 'recent', 'recent_mostvotes', 'biggest_debates', 'recent_wrans', 'recent_wms', 'column', 'mp', 'bill', 'session', 'recent_debates', 'recent_pbc_debates', 'featured_gid');
145
        if (in_array($view, $validviews)) {
146
147
            // What function do we call for this view?
148
            $function = '_get_data_by_'.$view;
149
            // Get all the data that's to be rendered.
150
            $data = $this->$function($args);
151
152
        } else {
153
            // Don't have a valid $view.
154
            $PAGE->error_message ("You haven't specified a view type.");
155
            return false;
156
        }
157
158
        // Set the values of this page's headings depending on the data we've fetched.
159
        if (isset($PAGE) && isset($data['info'])) {
160
            $PAGE->set_hansard_headings($data['info']);
161
        }
162
163
        // Glossary $view_override (to avoid too much code duplication...)
164
        if (isset($args['view_override'])) {
165
            $view = $args['view_override'];
166
        }
167
168
        $return = $this->render($view, $data, $format);
169
170
        return $return;
171
    }
172
173
174
175
    public function render($view, $data, $format='html') {
176
        // Once we have the data that's to be rendered,
177
        // include the template.
178
179
        // No format, so don't use the template sets.
180
        if ($format == 'none') {
181
            return $data;
182
        }
183
184
        include (INCLUDESPATH."easyparliament/templates/$format/hansard_$view" . ".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...
185
        return true;
186
187
    }
188
189
190
    public function total_items() {
191
        // Returns number of items in debates or wrans, depending on which class this is,
192
        // DEBATELIST or WRANSLIST.
193
194
        $q = $this->db->query("SELECT COUNT(*) AS count FROM hansard WHERE major = :major", array(':major' => $this->major));
195
196
        return $q->field(0, 'count');
197
    }
198
199
200
201 1
    public function most_recent_day() {
202
        // Very simple. Returns an array of stuff about the most recent data
203
        // for this major:
204
205
        // array (
206
        //		'hdate'		=> 'YYYY-MM-DD',
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% 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...
207
        //		'timestamp' => 124453679,
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% 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...
208
        //		'listurl'	=> '/foo/?id=bar'
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...
209
        // )
210
211
        // When we do this function the first time we cache the
212
        // results in this variable. As it's an expensive query.
213 1
        if (isset($this->most_recent_day)) {
214
            return $this->most_recent_day;
215
        }
216
217
        // What we return.
218 1
        $data = array();
219
220 1
        $q = $this->db->query("SELECT MAX(hdate) AS hdate
221
                        FROM 	hansard
222
                        WHERE	major = :major
223 1
                        ", array(':major' => $this->major));
224 1
        if ($q->rows() > 0) {
225
226 1
            $hdate = $q->field(0, 'hdate');
227 1
            if ($hdate) {
228 1
                $URL = new \MySociety\TheyWorkForYou\Url($this->listpage);
229 1
                $URL->insert( array('d'=>$hdate) );
230
231
                // Work out a timestamp which is handy for comparing to now.
232 1
                list($year, $month, $date) = explode('-', $hdate);
233 1
                $timestamp = gmmktime (0, 0, 0, $month, $date, $year);
0 ignored issues
show
Bug introduced by
$month of type string is incompatible with the type integer expected by parameter $month of gmmktime(). ( Ignorable by Annotation )

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

233
                $timestamp = gmmktime (0, 0, 0, /** @scrutinizer ignore-type */ $month, $date, $year);
Loading history...
Bug introduced by
$year of type string is incompatible with the type integer expected by parameter $year of gmmktime(). ( Ignorable by Annotation )

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

233
                $timestamp = gmmktime (0, 0, 0, $month, $date, /** @scrutinizer ignore-type */ $year);
Loading history...
Bug introduced by
$date of type string is incompatible with the type integer expected by parameter $day of gmmktime(). ( Ignorable by Annotation )

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

233
                $timestamp = gmmktime (0, 0, 0, $month, /** @scrutinizer ignore-type */ $date, $year);
Loading history...
234
235
                $data = array (
236 1
                    'hdate'		=> $hdate,
237 1
                    'timestamp'	=> $timestamp,
238 1
                    'listurl'	=> $URL->generate()
239 1
                );
240
241
                // This is just because it's an expensive query
242
                // and we really want to avoid doing it more than once.
243
                // So we're caching it.
244 1
                $this->most_recent_day = $data;
245 1
            }
246 1
        }
247
248 1
        return $data;
249
    }
250
251
252
    public function htype() {
253
        return $this->htype;
254
    }
255
256
    public function epobject_id() {
257
        return $this->epobject_id;
258
    }
259
260
    public function gid() {
261
        return $this->gid;
262
    }
263
264
265 2
    public function _get_section($itemdata) {
266
        // Pass it an array of data about an item and it will return an
267
        // array of data about the item's section heading.
268
269 2
        twfy_debug (get_class($this), "getting an item's section");
270
271 2
        if ($itemdata['htype'] != '10') {
272
273
            // This item is a subsection, speech or procedural,
274
            // or a wrans questions/answer,
275
            // so get the section info above this item.
276
277
            // For getting hansard data.
278
            $input = array (
279
                'amount' => array (
280
                    'body' => true
281
                ),
282
                'where' => array (
283
                    'hansard.epobject_id=' => $itemdata['section_id']
284
                )
285
            );
286
287
            $sectiondata = $this->_get_hansard_data($input);
288
289
            if (count($sectiondata) > 0) {
290
                $sectiondata = $sectiondata[0];
291
            }
292
293
        } else {
294
            // This item *is* a section, so just return that.
295
296 2
            $sectiondata = $itemdata;
297
298
        }
299
300 2
        return $sectiondata;
301
    }
302
303
304
305 1
    public function _get_subsection($itemdata) {
306
        // Pass it an array of data about an item and it will return an
307
        // array of data about the item's subsection heading.
308
309 1
        twfy_debug (get_class($this), "getting an item's subsection");
310
311
        // What we return.
312 1
        $subsectiondata = array ();
313
314 1
        if ($itemdata['htype'] == '12' || $itemdata['htype'] == '13') {
315
            // This item is a speech or procedural, so get the
316
            // subsection info above this item.
317
318
            // For getting hansard data.
319
            $input = array (
320
                'amount' => array (
321
                    'body' => true
322
                ),
323
                'where' => array (
324
                    'hansard.epobject_id=' => $itemdata['subsection_id']
325
                )
326
            );
327
328
            $subsectiondata = $this->_get_hansard_data($input);
329
            if (count($subsectiondata) == 0)
330
                $subsectiondata = null;
331
            else
332
                $subsectiondata = $subsectiondata[0];
333
334 1
        } elseif ($itemdata['htype'] == '11') {
335
            // It's a subsection, so use the item itself.
336
            $subsectiondata = $itemdata;
337
        }
338
339 1
        return $subsectiondata;
340
    }
341
342
343
344 1
    public function _get_nextprev_items($itemdata) {
345 1
        global $hansardmajors;
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...
346
347
        // Pass it an array of item info, of a section/subsection, and this will return
348
        // data for the next/prev items.
349
350 1
        twfy_debug (get_class($this), "getting next/prev items");
351
352
        // What we return.
353 1
        $nextprevdata = array();
354
355 1
        $prev_item_id = false;
356 1
        $next_item_id = false;
357
358 1
        if ($itemdata['htype'] == '10' || $itemdata['htype'] == '11') {
359
            // Debate subsection or section - get the next one.
360 1
            if ($hansardmajors[$itemdata['major']]['type'] == 'other' && $hansardmajors[$itemdata['major']]['location'] == 'UK') {
361
                $where = 'htype = 11';
362
            } else {
363 1
                $where = "(htype = 10 OR htype = 11)";
364
            }
365 1
        } else {
366
            // Anything else in debates - get the next element that isn't
367
            // a subsection or section, and is within THIS subsection.
368
            $where = "subsection_id = '" . $itemdata['subsection_id'] . "' AND (htype != 10 AND htype != 11)";
369
        }
370
371
        // Find if there are next/previous debate items of our
372
        // chosen type today.
373
374
        // For sections/subsections,
375
        // this will find headings with no content, but I failed to find
376
        // a vaguely simple way to do this. So this is it for now...
377
378
        // Find the epobject_id of the previous item (if any):
379 1
        $q = $this->db->query("SELECT epobject_id
380
                        FROM 	hansard
381 1
                        WHERE 	hdate = '" . $itemdata['hdate'] . "'
382 1
                        AND 	hpos < '" . $itemdata['hpos'] . "'
383 1
                        AND 	major = '" . $itemdata['major'] . "'
384
                        AND 	$where
385
                        ORDER BY hpos DESC
386 1
                        LIMIT 1");
387
388 1
        if ($q->rows() > 0) {
389 1
            $prev_item_id = $q->field(0, 'epobject_id');
390 1
        }
391
392
        // Find the epobject_id of the next item (if any):
393 1
        $q = $this->db->query("SELECT epobject_id
394
                        FROM 	hansard
395 1
                        WHERE 	hdate = '" . $itemdata['hdate'] . "'
396 1
                        AND 	hpos > '" . $itemdata['hpos'] . "'
397 1
                        AND 	major = '" . $itemdata['major'] . "'
398
                        AND 	$where
399
                        ORDER BY hpos ASC
400 1
                        LIMIT 1");
401
402 1
        if ($q->rows() > 0) {
403 1
            $next_item_id = $q->field(0, 'epobject_id');
404 1
        }
405
406
        // Now we're going to get the data for the next and prev items
407
        // that we will use to make the links on the page.
408
409
        // Previous item.
410 1
        if ($prev_item_id) {
411
            // We have a previous one to link to.
412 1
            $wherearr = array();
413 1
            $wherearr['hansard.epobject_id='] = $prev_item_id;
414
415
            // For getting hansard data.
416
            $input = array (
417
                'amount' => array (
418 1
                    'body' => true,
419
                    'speaker' => true
420 1
                ),
421 1
                'where' => $wherearr,
422 1
                'order' => 'hpos DESC',
423
                'limit' => 1
424 1
            );
425
426 1
            $prevdata = $this->_get_hansard_data($input);
427
428 1
            if (count($prevdata) > 0) {
429 1
                if ($itemdata['htype'] == '10' || $itemdata['htype'] == '11') {
430
                    // Linking to the prev (sub)section.
431 1
                    $thing = $hansardmajors[$this->major]['singular'];
432 1
                    $nextprevdata['prev'] = array (
433 1
                        'body'		=> "Previous $thing",
434 1
                        'url'		=> $prevdata[0]['listurl'],
435 1
                        'title'		=> $prevdata[0]['body']
436 1
                    );
437 1
                } else {
438
                    // Linking to the prev speaker.
439
440
                    if (isset($prevdata[0]['speaker']) && count($prevdata[0]['speaker']) > 0) {
441
                        $title = $prevdata[0]['speaker']['name'];
442
                    } else {
443
                        $title = '';
444
                    }
445
                    $nextprevdata['prev'] = array (
446
                        'body'		=> 'Previous speaker',
447
                        'url'		=> $prevdata[0]['commentsurl'],
448
                        'title'		=> $title
449
                    );
450
                }
451 1
            }
452 1
        }
453
454
        // Next item.
455 1
        if ($next_item_id) {
456
            // We have a next one to link to.
457 1
            $wherearr = array();
458 1
            $wherearr['hansard.epobject_id='] = $next_item_id;
459
460
            // For getting hansard data.
461
            $input = array (
462
                'amount' => array (
463 1
                    'body' => true,
464
                    'speaker' => true
465 1
                ),
466 1
                'where' => $wherearr,
467 1
                'order' => 'hpos ASC',
468
                'limit' => 1
469 1
            );
470 1
            $nextdata = $this->_get_hansard_data($input);
471
472 1
            if (count($nextdata) > 0) {
473 1
                if ($itemdata['htype'] == '10' || $itemdata['htype'] == '11') {
474
                    // Linking to the next (sub)section.
475 1
                    $thing = $hansardmajors[$this->major]['singular'];
476 1
                    $nextprevdata['next'] = array (
477 1
                        'body'		=> "Next $thing",
478 1
                        'url'		=> $nextdata[0]['listurl'],
479 1
                        'title'		=> $nextdata[0]['body']
480 1
                    );
481 1
                } else {
482
                    // Linking to the next speaker.
483
484
                    if (isset($nextdata[0]['speaker']) && count($nextdata[0]['speaker']) > 0) {
485
                        $title = $nextdata[0]['speaker']['name'];
486
                    } else {
487
                        $title = '';
488
                    }
489
                    $nextprevdata['next'] = array (
490
                        'body'		=> 'Next speaker',
491
                        'url'		=> $nextdata[0]['commentsurl'],
492
                        'title'		=> $title
493
                    );
494
                }
495 1
            }
496 1
        }
497
498 1
        if ($this->major == 6) {
499
            $URL = new \MySociety\TheyWorkForYou\Url('pbc_bill');
500
            $URL->remove(array('bill'));
501
            $nextprevdata['up'] = array(
502
                'body'	=> _htmlspecialchars($this->bill_title),
503
                'title'	=> '',
504
                'url'	=> $URL->generate() . $this->url,
505
            );
506 1
        } elseif ($itemdata['htype'] == '10' || $itemdata['htype'] == '11') {
507 1
            $URL = new \MySociety\TheyWorkForYou\Url($this->listpage);
508
            // Create URL for this (sub)section's date.
509 1
            $URL->insert(array('d' => $itemdata['hdate']));
510 1
            $URL->remove(array('id'));
511 1
            $things = $hansardmajors[$itemdata['major']]['title'];
512 1
            $nextprevdata['up'] = array(
513 1
                'body'	=> "All $things on " . format_date($itemdata['hdate'], SHORTDATEFORMAT),
514 1
                'title'	=> '',
515 1
                'url' 	=> $URL->generate()
516 1
            );
517 1
        } else {
518
            // We'll be setting $nextprevdata['up'] within $this->get_data_by_gid()
519
            // because we need to know the name and url of the parent item, which
520
            // we don't have here. Life sucks.
521
        }
522
523 1
        return $nextprevdata;
524
    }
525
526
527 1
    public function _get_nextprev_dates($date) {
528 1
        global $hansardmajors;
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...
529
        // Pass it a yyyy-mm-dd date and it'll return an array
530
        // containing the next/prev dates that contain items from
531
        // $this->major of hansard object.
532
533 1
        twfy_debug (get_class($this), "getting next/prev dates");
534
535
        // What we return.
536 1
        $nextprevdata = array ();
537
538 1
        $URL = new \MySociety\TheyWorkForYou\Url($this->listpage);
539
540 1
        $looper = array ("next", "prev");
541
542 1
        foreach ($looper as $n => $nextorprev) {
543
544 1
            $URL->reset();
545
546 1
            $params = array(':major' => $this->major,
547 1
                            ':date' => $date);
548 1
            if ($nextorprev == 'next') {
549 1
                $q = $this->db->query("SELECT MIN(hdate) AS hdate
550
                            FROM 	hansard
551
                            WHERE 	major = :major
552 1
                            AND		hdate > :date", $params);
553 1
            } else {
554 1
                $q = $this->db->query("SELECT MAX(hdate) AS hdate
555
                            FROM 	hansard
556
                            WHERE 	major = :major
557 1
                            AND		hdate < :date", $params);
558
            }
559
560
            // The '!= NULL' bit is needed otherwise I was getting errors
561
            // when displaying the first day of debates.
562 1
            if ($q->rows() > 0 && $q->field(0, 'hdate') != NULL) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $q->field(0, 'hdate') of type mixed|string against null; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
563
564 1
                $URL->insert( array( 'd'=>$q->field(0, 'hdate') ) );
565
566 1
                if ($nextorprev == 'next') {
567 1
                    $body = 'Next day';
568 1
                } else {
569
                    $body = 'Previous day';
570
                }
571
572 1
                $title = format_date($q->field(0, 'hdate'), SHORTDATEFORMAT);
573
574 1
                $nextprevdata[$nextorprev] = array (
575 1
                    'hdate'		=> $q->field(0, 'hdate'),
576 1
                    'url'	 	=> $URL->generate(),
577 1
                    'body'		=> $body,
578
                    'title'		=> $title
579 1
                );
580 1
            }
581 1
        }
582
583 1
        $year = substr($date, 0, 4);
584 1
        $URL = new \MySociety\TheyWorkForYou\Url($hansardmajors[$this->major]['page_year']);
585 1
        $thing = $hansardmajors[$this->major]['plural'];
586 1
        $URL->insert(array('y'=>$year));
587
588 1
        $nextprevdata['up'] = array (
589 1
            'body' 	=> "All of $year's $thing",
590 1
            'title'	=> '',
591 1
            'url' 	=> $URL->generate()
592 1
        );
593
594 1
        return $nextprevdata;
595
596
    }
597
598
599
600 1
    public function _validate_date($args) {
601
        // Used when we're viewing things by (_get_data_by_date() functions).
602
        // If $args['date'] is a valid yyyy-mm-dd date, it is returned.
603
        // Else false is returned.
604 1
        global $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...
605
606 1
        if (isset($args['date'])) {
607 1
            $date = $args['date'];
608 1
        } else {
609
            $PAGE->error_message ("Sorry, we don't have a date.");
610
            return false;
611
        }
612
613 1
        if (!preg_match("/^(\d\d\d\d)-(\d{1,2})-(\d{1,2})$/", $date, $matches)) {
614
            $PAGE->error_message ("Sorry, '" . _htmlentities($date) . "' isn't of the right format (YYYY-MM-DD).");
615
            return false;
616
        }
617
618 1
        list(, $year, $month, $day) = $matches;
619
620 1
        if (!checkdate($month, $day, $year)) {
621
            $PAGE->error_message ("Sorry, '" . _htmlentities($date) . "' isn't a valid date.");
622
            return false;
623
        }
624
625 1
        $day = substr("0$day", -2);
626 1
        $month = substr("0$month", -2);
627 1
        $date = "$year-$month-$day";
628
629
        // Valid date!
630 1
        return $date;
631
    }
632
633
634
635 2
    public function _get_item($args) {
636 2
        global $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...
637
638 2
        if (!isset($args['gid']) && $args['gid'] == '') {
639
            $PAGE->error_message ("Sorry, we don't have an item gid.");
640
            return false;
641
        }
642
643
644
        // Get all the data just for this epobject_id.
645
        $input = array (
646
            'amount' => array (
647 2
                'body' => true,
648 2
                'speaker' => true,
649 2
                'comment' => true,
650
                'votes' => true
651 2
            ),
652
            'where' => array (
653
                // Need to add the 'uk.org.publicwhip/debate/' or whatever on before
654
                // looking in the DB.
655 2
                'gid=' => $this->gidprefix . $args['gid']
656 2
            )
657 2
        );
658
659 2
        twfy_debug (get_class($this), "looking for redirected gid");
660 2
        $gid = $this->gidprefix . $args['gid'];
661 2
        $q = $this->db->query ("SELECT gid_to FROM gidredirect WHERE gid_from = :gid", array(':gid' => $gid));
662 2
        if ($q->rows() == 0) {
663 2
            $itemdata = $this->_get_hansard_data($input);
664 2
        } else {
665
            do {
666
                $gid = $q->field(0, 'gid_to');
667
                $q = $this->db->query("SELECT gid_to FROM gidredirect WHERE gid_from = :gid", array(':gid' => $gid));
668
            } while ($q->rows() > 0);
669
            twfy_debug (get_class($this), "found redirected gid $gid" );
670
            $input['where'] = array('gid=' => $gid);
671
            $itemdata = $this->_get_hansard_data($input);
672
            if (count($itemdata) > 0 ) {
673
                throw new RedirectException(fix_gid_from_db($gid));
674
            }
675
        }
676
677 2
        if (count($itemdata) > 0) {
678 2
            $itemdata = $itemdata[0];
679 2
        }
680
681 2
        if (count($itemdata) == 0) {
682
            /* Deal with old links to some Lords pages. Somewhere. I can't remember where */
683
            $this->check_gid_change($args['gid'], 'a', '');
684
685
            if (substr($args['gid'], -1) == 'L') {
686
                $letts = array('a','b','c','d','e');
687
                for ($i=0; $i<4; $i++) {
688
                    $this->check_gid_change($args['gid'], $letts[$i], $letts[$i+1]);
689
                }
690
            }
691
692
            /* A lot of written answers were moved from 10th to 11th May and 11th May to 12th May.
693
               Deal with the bots who have stored links to those now non-existant written answers. */
694
            /* 2007-05-31: And then they were moved BACK in the volume edition, ARGH */
695
            $this->check_gid_change($args['gid'], '2006-05-10a', '2006-05-10c');
696
            $this->check_gid_change($args['gid'], '2006-05-10a', '2006-05-11d');
697
            $this->check_gid_change($args['gid'], '2006-05-11b', '2006-05-11d');
698
            $this->check_gid_change($args['gid'], '2006-05-11b', '2006-05-12c');
699
            $this->check_gid_change($args['gid'], '2006-05-11c', '2006-05-10c');
700
            $this->check_gid_change($args['gid'], '2006-05-12b', '2006-05-11d');
701
702
            $this->check_gid_change($args['gid'], '2007-01-08', '2007-01-05');
703
            $this->check_gid_change($args['gid'], '2007-02-19', '2007-02-16');
704
705
            /* More movearounds... */
706
            $this->check_gid_change($args['gid'], '2005-10-10d', '2005-09-12a');
707
            $this->check_gid_change($args['gid'], '2005-10-14a', '2005-10-13b');
708
            $this->check_gid_change($args['gid'], '2005-10-18b', '2005-10-10e');
709
            $this->check_gid_change($args['gid'], '2005-11-17b', '2005-11-15c');
710
711
            $this->check_gid_change($args['gid'], '2007-01-08a', '2007-01-08e');
712
713
            /* Right back when Lords began, we sent out email alerts when they weren't on the site. So this was to work that. */
714
            #$lord_gid_like = 'uk.org.publicwhip/lords/' . $args['gid'] . '%';
0 ignored issues
show
Unused Code Comprehensibility introduced by
48% 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...
715
            #$q = $this->db->query('SELECT source_url FROM hansard WHERE gid LIKE :lord_gid_like', array(':lord_gid_like' => $lord_gid_like));
0 ignored issues
show
Unused Code Comprehensibility introduced by
66% 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...
716
            #$u = '';
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...
717
            #if ($q->rows()) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
73% 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...
718
            #	$u = $q->field(0, 'source_url');
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% 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...
719
            #	$u = '<br><a href="'. $u . '">' . $u . '</a>';
720
            #}
721
            $PAGE->error_message ("Sorry, there is no Hansard object with a gid of '" . _htmlentities($args['gid']) . "'.");
722
            return false;
723
        }
724
725 2
        return $itemdata;
726
727
    }
728
729
    private function check_gid_change($gid, $from, $to) {
730
        $input = array (
731
            'amount' => array (
732
                'body' => true,
733
                'speaker' => true,
734
                'comment' => true,
735
                'votes' => true
736
            )
737
        );
738
        if (strstr($gid, $from)) {
739
            $check_gid = str_replace($from, $to, $gid);
740
            $input['where'] = array('gid=' => $this->gidprefix . $check_gid);
741
            $itemdata = $this->_get_hansard_data($input);
742
            if (count($itemdata) > 0) {
743
                throw new RedirectException($check_gid);
744
            }
745
        }
746
    }
747
748
749 1
    public function _get_data_by_date($args) {
750
        // For displaying the section and subsection headings as
751
        // links for an entire day of debates/wrans.
752
753 1
        global $DATA, $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...
754
755 1
        twfy_debug (get_class($this), "getting data by date");
756
757
        // Where we'll put all the data we want to render.
758 1
        $data = array ();
759
760 1
        $date = $this->_validate_date($args);
761
762 1
        if ($date) {
763
764 1
            $nextprev = $this->_get_nextprev_dates($date);
765
766
            // We can then access this from $PAGE and the templates.
767 1
            $DATA->set_page_metadata($this_page, 'nextprev', $nextprev);
768
769
770
            // Get all the sections for this date.
771
            // Then for each of those we'll get the subsections and rows.
772
            $input = array (
773
                'amount' => array (
774 1
                    'body' => true,
775 1
                    'comment' => true,
776
                    'excerpt' => true
777 1
                ),
778
                'where' => array (
779 1
                    'hdate=' => "$date",
780 1
                    'htype=' => '10',
781 1
                    'major=' => $this->major
782 1
                ),
783
                'order' => 'hpos'
784 1
            );
785
786 1
            $sections = $this->_get_hansard_data($input);
787
788 1
            if (count($sections) > 0) {
789
790
                // Where we'll keep the full list of sections and subsections.
791 1
                $data['rows'] = array();
792
793 1
                $num_sections = count($sections);
794 1
                for ($n=0; $n<$num_sections; $n++) {
795
                    // For each section on this date, get the subsections within it.
796
797
                    // Get all the section data.
798 1
                    $sectionrow = $this->_get_section($sections[$n]);
799
800
                    // Get the subsections within the section.
801
                    $input = array (
802
                        'amount' => array (
803 1
                            'body' => true,
804 1
                            'comment' => true,
805
                            'excerpt' => true
806 1
                        ),
807
                        'where' => array (
808 1
                            'section_id='	=> $sections[$n]['epobject_id'],
809 1
                            'htype='		=> '11',
810 1
                            'major='		=> $this->major
811 1
                        ),
812
                        'order' => 'hpos'
813 1
                    );
814
815 1
                    $rows = $this->_get_hansard_data($input);
816
817
                    // Put the section at the top of the rows array.
818 1
                    array_unshift ($rows, $sectionrow);
819
820
                    // Add the section heading and the subsections to the full list.
821 1
                    $data['rows'] = array_merge ($data['rows'], $rows);
822 1
                }
823 1
            }
824
825
            // For page headings etc.
826 1
            $data['info']['date'] = $date;
827 1
            $data['info']['major'] = $this->major;
828 1
        }
829
830 1
        return $data;
831
    }
832
833
834
    public function _get_data_by_recent($args) {
835
        // Like _get_data_by_id() and _get_data_by_date()
0 ignored issues
show
Unused Code Comprehensibility introduced by
42% 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...
836
        // this returns a $data array suitable for sending to a template.
837
        // It lists recent dates with debates/wrans on them, with links.
838
839
        $params = array();
840
841
        if (isset($args['days']) && is_numeric($args['days'])) {
842
            $limit = 'LIMIT :limit';
843
            $params[':limit'] = $args['days'];
844
        } else {
845
            $limit = '';
846
        }
847
848
        if ($this->major != '') {
849
            // We must be in DEBATELIST or WRANSLIST.
850
851
            $major = 'WHERE major = :major';
852
            $params[':major'] = $this->major;
853
        } else {
854
            $major = '';
855
        }
856
857
        $data = array ();
858
859
        $q = $this->db->query ("SELECT DISTINCT(hdate)
860
                        FROM 	hansard
861
                        $major
862
                        ORDER BY hdate DESC
863
                        $limit
864
                        ", $params);
865
866
        if ($q->rows() > 0) {
867
868
            $URL = new \MySociety\TheyWorkForYou\Url($this->listpage);
869
870
            for ($n=0; $n<$q->rows(); $n++) {
871
                $rowdata = array();
872
873
                $rowdata['body'] = format_date($q->field($n, 'hdate'), SHORTDATEFORMAT);
874
                $URL->insert(array('d'=>$q->field($n, 'hdate')));
875
                $rowdata['listurl'] = $URL->generate();
876
877
                $data['rows'][] = $rowdata;
878
            }
879
        }
880
881
        $data['info']['text'] = 'Recent dates';
882
883
884
        return $data;
885
    }
886
887
    # Display a person's most recent debates.
888
    # Only used by MP RSS generator now, MP pages use Xapian search
889
    # XXX: Abolish this entirely?
890
891 1
    public function _get_data_by_person($args) {
892 1
        global $PAGE, $hansardmajors;
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...
893 1
        $items_to_list = isset($args['max']) ? $args['max'] : 20;
894
895
        // Where we'll put all the data we want to render.
896 1
        $data = array();
897
898 1
        if (!isset($args['person_id'])) {
899
            $PAGE->error_message ("Sorry, we need a valid person ID.");
900
            return $data;
901
        }
902
903 1
        $params = array();
904
905 1
        $where = 'hansard.person_id = :person_id';
906 1
        $params[':person_id'] = trim($args['person_id']);
907
908 1
        if (isset($this->major)) {
909
            $majorwhere = "AND hansard.major = :hansard_major ";
910
            $params[':hansard_major'] = $this->major;
911
        } else {
912
            // We're getting results for all debates/wrans/etc.
913 1
            $majorwhere = '';
914
        }
915
916 1
        $q = $this->db->query("SELECT hansard.subsection_id, hansard.section_id,
917
                    hansard.htype, hansard.gid, hansard.major, hansard.minor,
918
                    hansard.hdate, hansard.htime, hansard.person_id,
919
                    epobject.body, epobject_section.body AS body_section,
920
                    epobject_subsection.body AS body_subsection,
921
                                    hansard_subsection.gid AS gid_subsection
922
                FROM hansard
923
                JOIN epobject
924
                    ON hansard.epobject_id = epobject.epobject_id
925
                JOIN epobject AS epobject_section
926
                                    ON hansard.section_id = epobject_section.epobject_id
927
                JOIN epobject AS epobject_subsection
928
                                    ON hansard.subsection_id = epobject_subsection.epobject_id
929
                JOIN hansard AS hansard_subsection
930
                                    ON hansard.subsection_id = hansard_subsection.epobject_id
931 1
                        WHERE	$where $majorwhere
932
                        ORDER BY hansard.hdate DESC, hansard.hpos DESC
933 1
                        LIMIT	$items_to_list
934 1
                        ", $params);
935
936
937 1
        $speeches = array();
938 1
        if ($q->rows() > 0) {
939 1
            for ($n=0; $n<$q->rows(); $n++) {
940
                $speech = array (
941 1
                    'subsection_id'	=> $q->field($n, 'subsection_id'),
942 1
                    'section_id'	=> $q->field($n, 'section_id'),
943 1
                    'htype'		=> $q->field($n, 'htype'),
944 1
                    'major'		=> $q->field($n, 'major'),
945 1
                    'minor'		=> $q->field($n, 'minor'),
946 1
                    'hdate'		=> $q->field($n, 'hdate'),
947 1
                    'htime'		=> $q->field($n, 'htime'),
948 1
                    'person_id'	=> $q->field($n, 'person_id'),
949 1
                    'body'		=> $q->field($n, 'body'),
950 1
                    'body_section'  => $q->field($n, 'body_section'),
951 1
                    'body_subsection' => $q->field($n, 'body_subsection'),
952 1
                    'gid'		=> fix_gid_from_db($q->field($n, 'gid')),
953 1
                );
954
                // Cache parent id to speed up _get_listurl
955 1
                $this->epobjectid_to_gid[$q->field($n, 'subsection_id') ] = fix_gid_from_db( $q->field($n, 'gid_subsection') );
956
957 1
                $url_args = array ('p'=>$q->field($n, 'person_id'));
958 1
                $speech['listurl'] = $this->_get_listurl($speech, $url_args);
959 1
                $speeches[] = $speech;
960 1
            }
961 1
        }
962
963 1
        if (count($speeches) > 0) {
964
            // Get the subsection texts.
965 1
            $num_speeches = count($speeches);
966 1
            for ($n=0; $n<$num_speeches; $n++) {
967 1
                $thing = $hansardmajors[$speeches[$n]['major']]['title'];
968
                // Add the parent's body on...
969 1
                $speeches[$n]['parent']['body'] = $speeches[$n]['body_section'] . ' | ' . $thing;
970 1
                if ($speeches[$n]['subsection_id'] != $speeches[$n]['section_id']) {
971 1
                    $speeches[$n]['parent']['body'] = $speeches[$n]['body_subsection'] .
972 1
                        ' | ' . $speeches[$n]['parent']['body'];
973 1
                }
974 1
            }
975 1
            $data['rows'] = $speeches;
976 1
        } else {
977
            $data['rows'] = array();
978
        }
979 1
        return $data;
980
    }
981
982
983
    public function _get_data_by_search_min($args) {
984
        return $this->_get_data_by_search($args);
985
    }
986
    public function _get_data_by_search_video($args) {
987
        return $this->_get_data_by_search($args);
988
    }
989
    public function _get_data_by_search($args) {
990
991
        // Creates a fairly standard $data structure for the search function.
992
        // Will probably be rendered by the hansard_search.php template.
993
994
        // $args is an associative array with 's'=>'my search term' and
995
        // (optionally) 'p'=>1  (the page number of results to show) annd
996
        // (optionall) 'pop'=>1 (if "popular" search link, so don't log)
997
        global $PAGE, $hansardmajors;
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...
998
999
        if (isset($args['s'])) {
1000
            // $args['s'] should have been tidied up by the time we get here.
1001
            // eg, by doing filter_user_input($s, 'strict');
0 ignored issues
show
Unused Code Comprehensibility introduced by
44% 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...
1002
            $searchstring = $args['s'];
1003
        } else {
1004
            if ( isset($args['exceptions']) ) {
1005
                throw new \Exception('No search string provided.');
1006
            } else {
1007
                $PAGE->error_message("No search string");
1008
                return false;
1009
            }
1010
        }
1011
1012
        // What we'll return.
1013
        $data = array ();
1014
1015
        $data['info']['s'] = $args['s'];
1016
1017
        // Allows us to specify how many results we want
1018
        // Mainly for glossary term adding
1019
        if (isset($args['num']) && $args['num']) {
1020
            $results_per_page = $args['num']+0;
1021
        }
1022
        else {
1023
            $results_per_page = 20;
1024
        }
1025
        if ($results_per_page > 1000)
1026
            $results_per_page = 1000;
1027
1028
        $data['info']['results_per_page'] = $results_per_page;
1029
1030
        // What page are we on?
1031
        if (isset($args['p']) && is_numeric($args['p'])) {
1032
            $page = $args['p'];
1033
        } else {
1034
            $page = 1;
1035
        }
1036
        $data['info']['page'] = $page;
1037
1038
        if (isset($args['e'])) {
1039
            $encode = 'url';
1040
        } else {
1041
            $encode = 'html';
1042
        }
1043
1044
        // Fetch count of number of matches
1045
        global $SEARCHENGINE;
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...
1046
1047
        // For Xapian's equivalent of an SQL LIMIT clause.
1048
        $first_result = ($page-1) * $results_per_page;
1049
        $data['info']['first_result'] = $first_result + 1; // Take account of LIMIT's 0 base.
1050
1051
        // Get the gids from Xapian
1052
        $sort_order = 'date';
1053
        if (isset($args['o'])) {
1054
            if ($args['o']=='d') $sort_order = 'newest';
1055
            if ($args['o']=='o') $sort_order = 'oldest';
1056
            elseif ($args['o']=='c') $sort_order = 'created';
1057
            elseif ($args['o']=='r') $sort_order = 'relevance';
1058
        }
1059
1060
        $data['searchdescription'] = $SEARCHENGINE->query_description_long();
1061
        $count = $SEARCHENGINE->run_count($first_result, $results_per_page, $sort_order);
1062
        $data['info']['total_results'] = $count;
1063
        $data['info']['spelling_correction'] = $SEARCHENGINE->get_spelling_correction();
1064
1065
        // Log this query so we can improve them - if it wasn't a "popular
1066
        // query" link
1067
        if (! isset($args['pop']) or $args['pop'] != 1) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
1068
            global $SEARCHLOG;
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...
1069
            $SEARCHLOG->add(
1070
            array('query' => $searchstring,
1071
                'page' => $page,
1072
                'hits' => $count));
1073
        }
1074
        // No results.
1075
        if ($count <= 0) {
1076
            $data['rows'] = array();
1077
            return $data;
1078
        }
1079
1080
        $SEARCHENGINE->run_search($first_result, $results_per_page, $sort_order);
1081
        $gids = $SEARCHENGINE->get_gids();
1082
        if ($sort_order=='created') {
1083
            $createds = $SEARCHENGINE->get_createds();
1084
        }
1085
        $relevances = $SEARCHENGINE->get_relevances();
1086
        if (count($gids) <= 0) {
1087
            // No results.
1088
            $data['rows'] = array();
1089
            return $data;
1090
        }
1091
        #if ($sort_order=='created') { print_r($gids); }
0 ignored issues
show
Unused Code Comprehensibility introduced by
65% 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...
1092
1093
        // We'll put all the data in here before giving it to a template.
1094
        $rows = array();
1095
1096
        // Cycle through each result, munge the data, get more, and put it all in $data.
1097
        $num_gids = count($gids);
1098
        for ($n=0; $n<$num_gids; $n++) {
1099
            $gid = $gids[$n];
1100
            $relevancy = $relevances[$n];
1101
            $collapsed = $SEARCHENGINE->collapsed[$n];
1102
            if ($sort_order=='created') {
1103
                #$created = substr($createds[$n], 0, strpos($createds[$n], ':'));
0 ignored issues
show
Unused Code Comprehensibility introduced by
71% 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...
1104
                if ($createds[$n]<$args['threshold']) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $createds does not seem to be defined for all execution paths leading up to this point.
Loading history...
1105
                    $data['info']['total_results'] = $n;
1106
                    break;
1107
                }
1108
            }
1109
1110
            if (strstr($gid, 'calendar')) {
1111
                $id = fix_gid_from_db($gid);
1112
1113
                $q = $this->db->query("SELECT *, event_date as hdate, pos as hpos
1114
                    FROM future
1115
                    LEFT JOIN future_people ON id=calendar_id AND witness=0
1116
                    WHERE id = $id AND deleted=0");
1117
                if ($q->rows() == 0) continue;
1118
1119
                $itemdata = $q->row(0);
1120
1121
                # Ignore past events in places that we cover (we'll have the data from Hansard)
1122
                if ($itemdata['event_date'] < date('Y-m-d') &&
1123
                    in_array($itemdata['chamber'], array(
1124
                        'Commons: Main Chamber', 'Lords: Main Chamber',
1125
                        'Commons: Westminster Hall',
1126
                    )))
1127
                        continue;
1128
1129
                list($cal_item, $cal_meta) = \MySociety\TheyWorkForYou\Utility\Calendar::meta($itemdata);
1130
                $body = $this->prepare_search_result_for_display($cal_item) . '.';
1131
                if (!empty($cal_meta)) {
1132
                    $body .= ' <span class="future_meta">' . join('; ', $cal_meta) . '</span>';
1133
                }
1134
                if ($itemdata['witnesses']) {
1135
                    $body .= '<br><small>Witnesses: '
1136
                        . $this->prepare_search_result_for_display($itemdata['witnesses'])
1137
                        . '</small>';
1138
                }
1139
1140
                if ($itemdata['event_date'] >= date('Y-m-d')) {
1141
                    $title = 'Upcoming Business';
1142
                } else {
1143
                    $title = 'Previous Business';
1144
                }
1145
                $itemdata['gid']            = $id;
1146
                $itemdata['relevance']      = $relevancy;
1147
                $itemdata['parent']['body'] = $title . ' &#8211; ' . $itemdata['chamber'];
1148
                $itemdata['extract']        = $body;
1149
                $itemdata['listurl']        = '/calendar/?d=' . $itemdata['event_date'] . '#cal' . $itemdata['id'];
1150
                $itemdata['major']          = 'F';
1151
1152
            } else {
1153
1154
                // Get the data for the gid from the database
1155
                $q = $this->db->query("SELECT hansard.gid, hansard.hdate,
1156
                    hansard.htime, hansard.section_id, hansard.subsection_id,
1157
                    hansard.htype, hansard.major, hansard.minor,
1158
                    hansard.person_id, hansard.hpos, hansard.video_status,
1159
                    epobject.epobject_id, epobject.body
1160
                FROM hansard, epobject
1161
                WHERE hansard.gid = :gid
1162
                    AND hansard.epobject_id = epobject.epobject_id"
1163
                , array(':gid' => $gid));
1164
1165
                if ($q->rows() > 1) {
1166
                    if ( $isset($args['exceptions']) ) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $isset seems to be never defined.
Loading history...
1167
                        throw new \Exception("Got more than one row getting data for $gid.");
1168
                    } else {
1169
                        $PAGE->error_message("Got more than one row getting data for $gid");
1170
                    }
1171
                }
1172
                if ($q->rows() == 0) {
1173
                    # This error message is totally spurious, so don't show it
1174
                    # $PAGE->error_message("Unexpected missing gid $gid while searching");
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% 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...
1175
                    continue;
1176
                }
1177
1178
                $itemdata = $q->row(0);
1179
                $itemdata['collapsed']  = $collapsed;
1180
                $itemdata['gid']        = fix_gid_from_db( $q->field(0, 'gid') );
1181
                $itemdata['relevance']  = $relevancy;
1182
                $itemdata['extract']    = $this->prepare_search_result_for_display($q->field(0, 'body'));
1183
1184
                //////////////////////////
1185
                // 2. Create the URL to link to this bit of text.
1186
1187
                $id_data = array (
1188
                    'major'            => $itemdata['major'],
1189
                    'minor'            => $itemdata['minor'],
1190
                    'htype'         => $itemdata['htype'],
1191
                    'gid'             => $itemdata['gid'],
1192
                    'section_id'    => $itemdata['section_id'],
1193
                    'subsection_id'    => $itemdata['subsection_id']
1194
                );
1195
1196
                // We append the query onto the end of the URL as variable 's'
1197
                // so we can highlight them on the debate/wrans list page.
1198
                $url_args = array ('s' => $searchstring);
1199
1200
                $itemdata['listurl'] = $this->_get_listurl($id_data, $url_args, $encode);
1201
1202
                //////////////////////////
1203
                // 3. Get the speaker for this item, if applicable.
1204
                if ($itemdata['person_id'] != 0) {
1205
                    $itemdata['speaker'] = $this->_get_speaker($itemdata['person_id'], $itemdata['hdate'], $itemdata['htime'], $itemdata['major']);
1206
                }
1207
1208
                //////////////////////////
1209
                // 4. Get data about the parent (sub)section.
1210
                if ($itemdata['major'] && $hansardmajors[$itemdata['major']]['type'] == 'debate') {
1211
                    // Debate
1212
                    if ($itemdata['htype'] != 10) {
1213
                        $section = $this->_get_section($itemdata);
1214
                        $itemdata['parent']['body'] = $section['body'];
1215
#                        $itemdata['parent']['listurl'] = $section['listurl'];
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% 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...
1216
                        if ($itemdata['section_id'] != $itemdata['subsection_id']) {
1217
                            $subsection = $this->_get_subsection($itemdata);
1218
                            $itemdata['parent']['body'] .= ': ' . $subsection['body'];
1219
#                            $itemdata['parent']['listurl'] = $subsection['listurl'];
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% 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...
1220
                        }
1221
                        if ($itemdata['major'] == 5) {
1222
                            $itemdata['parent']['body'] = 'Northern Ireland Assembly: ' . $itemdata['parent']['body'];
1223
                        } elseif ($itemdata['major'] == 6) {
1224
                            $itemdata['parent']['body'] = 'Public Bill Committee: ' . $itemdata['parent']['body'];
1225
                        } elseif ($itemdata['major'] == 7) {
1226
                            $itemdata['parent']['body'] = 'Scottish Parliament: ' . $itemdata['parent']['body'];
1227
                        }
1228
1229
                    } else {
1230
                        // It's a section, so it will be its own title.
1231
                        $itemdata['parent']['body'] = $itemdata['body'];
1232
                        $itemdata['body'] = '';
1233
                    }
1234
1235
                } else {
1236
                    // Wrans or WMS
1237
                    $section = $this->_get_section($itemdata);
1238
                    $subsection = $this->_get_subsection($itemdata);
1239
                    $body = $hansardmajors[$itemdata['major']]['title'] . ' &#8212; ';
1240
                    if (isset($section['body'])) $body .= $section['body'];
1241
                    if (isset($subsection['body'])) $body .= ': ' . $subsection['body'];
1242
                    if (isset($subsection['listurl'])) $listurl = $subsection['listurl'];
1243
                    else $listurl = '';
1244
                    $itemdata['parent'] = array (
1245
                        'body' => $body,
1246
                        'listurl' => $listurl
1247
                    );
1248
                    if ($itemdata['htype'] == 11) {
1249
                        # Search result was a subsection heading; fetch the first entry
1250
                        # from the wrans/wms to show under the heading
1251
                        $input = array (
1252
                            'amount' => array(
1253
                                'body' => true,
1254
                                'speaker' => true
1255
                            ),
1256
                            'where' => array(
1257
                                'hansard.subsection_id=' => $itemdata['epobject_id']
1258
                            ),
1259
                            'order' => 'hpos ASC',
1260
                            'limit' => 1
1261
                        );
1262
                        $ddata = $this->_get_hansard_data($input);
1263
                        if (count($ddata)) {
1264
                            $itemdata['body'] = $ddata[0]['body'];
1265
                            $itemdata['extract'] = $this->prepare_search_result_for_display($ddata[0]['body']);
1266
                            $itemdata['person_id'] = $ddata[0]['person_id'];
1267
                            if ($itemdata['person_id']) {
1268
                                $itemdata['speaker'] = $this->_get_speaker($itemdata['person_id'], $itemdata['hdate'], $itemdata['htime'], $itemdata['major']);
1269
                            }
1270
                        }
1271
                    } elseif ($itemdata['htype'] == 10) {
1272
                        $itemdata['body'] = '';
1273
                        $itemdata['extract'] = '';
1274
                    }
1275
                }
1276
1277
            } // End of handling non-calendar search result
1278
1279
            $rows[] = $itemdata;
1280
        }
1281
1282
        $data['rows'] = $rows;
1283
        return $data;
1284
    }
1285
1286
    public function prepare_search_result_for_display($body) {
1287
        global $SEARCHENGINE;
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...
1288
        // We want to trim the body to an extract that is centered
1289
        // around the position of the first search word.
1290
1291
        // we don't use strip_tags as it doesn't replace tags with spaces,
1292
        // which means some words end up stuck together
1293
        $extract = strip_tags_tospaces($body);
1294
1295
        // $bestpos is the position of the first search word
1296
        $bestpos = $SEARCHENGINE->position_of_first_word($extract);
1297
1298
        // Where do we want to extract from the $body to start?
1299
        $length_of_extract = 400; // characters.
1300
        $startpos = $bestpos - ($length_of_extract / 2);
1301
        if ($startpos < 0) {
1302
            $startpos = 0;
1303
        }
1304
1305
        // Trim it to length and position, adding ellipses.
1306
        $extract = trim_characters ($extract, $startpos, $length_of_extract);
1307
1308
        // Highlight search words
1309
        $extract = $SEARCHENGINE->highlight($extract);
1310
1311
        return $extract;
1312
    }
1313
1314
    public function _get_data_by_calendar($args) {
1315
        // We should have come here via _get_data_by_calendar() in
1316
        // DEBATELIST or WRANLIST, so $this->major should now be set.
1317
1318
        // You can ask for:
1319
        // * The most recent n months - $args['months'] => n
1320
        // * All months from one year - $args['year'] => 2004
1321
        // * One month - $args['year'] => 2004, $args['month'] => 8
0 ignored issues
show
Unused Code Comprehensibility introduced by
49% 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...
1322
        // * The months from this year so far (no $args variables needed).
1323
1324
        // $args['onday'] may be like '2004-04-20' - if it appears in the
1325
        // calendar, this date will be highlighted and will have no link.
1326
1327
        // Returns a data structure of years, months and dates:
1328
        // $data = array(
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% 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...
1329
        // 		'info' => array (
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% 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...
1330
        //			'page' => 'debates',
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% 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...
1331
        //			'major'	=> 1
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...
1332
        //			'onpage' => '2004-02-01'
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...
1333
        //		),
1334
        // 		'years' => array (
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% 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...
1335
        //			'2004' => array (
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% 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...
1336
        //				'01' => array ('01', '02', '03' ... '31'),
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% 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...
1337
        //				'02' => etc...
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% 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...
1338
        //			)
1339
        //		)
1340
        // )
1341
        // It will just have entries for days for which we have relevant
1342
        // hansard data.
1343
        // But months that have no data will still have a month array (empty).
1344
1345
        // $data['info'] may have 'prevlink' => '/debates/?y=2003' or something
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% 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...
1346
        // if we're viewing recent months.
1347
1348
        global $DATA, $this_page, $PAGE, $hansardmajors;
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...
1349
1350
        // What we return.
1351
        $data = array(
1352
            'info' => array(
1353
                'page' => $this->listpage,
1354
                'major' => $this->major
1355
            )
1356
        );
1357
1358
        // Set a variable so we know what we're displaying...
1359
        if (isset($args['months']) && is_numeric($args['months'])) {
1360
1361
            // A number of recent months (may wrap around to previous year).
1362
            $action = 'recentmonths';
1363
1364
            // A check to prevent anyone requestion 500000 months.
1365
            if ($args['months'] > 12) {
1366
                $PAGE->error_message("Sorry, you can't view " . $args['months'] . " months.");
1367
                return $data;
1368
            }
1369
1370
        } elseif (isset($args['year']) && is_numeric($args['year'])) {
1371
1372
            if (isset($args['month']) && is_numeric($args['month'])) {
1373
                // A particular month.
1374
                $action = 'month';
1375
            } else {
1376
                // A single year.
1377
                $action = 'year';
1378
            }
1379
1380
        } else {
1381
            // The year to date so far.
1382
            $action = 'recentyear';
1383
        }
1384
1385
        if (isset($args['onday'])) {
1386
            // Will be highlighted.
1387
            $data['info']['onday'] = $args['onday'];
1388
        }
1389
1390
        // This first if/else section is simply to fill out these variables:
1391
1392
        if ($action == 'recentmonths' || $action == 'recentyear') {
1393
1394
            // We're either getting the most recent $args['months'] data
1395
            // Or the most recent year's data.
1396
            // (Not necessarily recent to *now* but compared to the most
1397
            // recent date for which we have relevant hansard data.)
1398
            // 'recentyear' will include all the months that haven't happened yet.
1399
1400
            // Find the most recent date we have data for.
1401
            $q = $this->db->query("SELECT MAX(hdate) AS hdate
1402
                            FROM	hansard
1403
                            WHERE	major = :major",
1404
                array(':major' => $this->major));
1405
1406
            if ($q->field(0, 'hdate') != NULL) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $q->field(0, 'hdate') of type mixed|string against null; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
1407
                $recentdate = $q->field(0, 'hdate');
1408
            } else {
1409
                $PAGE->error_message("Couldn't find the most recent date");
1410
                return $data;
1411
            }
1412
1413
            // What's the first date of data we need to fetch?
1414
            list($finalyear, $finalmonth, $day) = explode('-', $recentdate);
1415
1416
            $finalyear = intval($finalyear);
1417
            $finalmonth = intval($finalmonth);
1418
1419
            if ($action == 'recentmonths') {
1420
1421
                // We're getting this many recent months.
1422
                $months_to_fetch = $args['months'];
1423
1424
                // The month we need to start getting data.
1425
                $firstmonth = intval($finalmonth) - $months_to_fetch + 1;
1426
1427
                $firstyear = $finalyear;
1428
1429
                if ($firstmonth < 1) {
1430
                    // Wrap round to previous year.
1431
                    $firstyear--;
1432
                    // $firstmonth is negative, hence the '+'.
1433
                    $firstmonth = 12 + $firstmonth; // ()
1434
                };
1435
1436
            } else {
1437
                // $action == 'recentyear'
1438
1439
                // Get the most recent year's results.
1440
                $firstyear = $finalyear;
1441
                $firstmonth = 1;
1442
            }
1443
1444
1445
1446
        } else {
1447
            // $action == 'year' or 'month'.
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% 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...
1448
1449
            $firstyear = $args['year'];
1450
            $finalyear = $args['year'];
1451
1452
            if ($action == 'month') {
1453
                $firstmonth = intval($args['month']);
1454
                $finalmonth = intval($args['month']);
1455
            } else {
1456
                $firstmonth = 1;
1457
                $finalmonth = 12;
1458
            }
1459
1460
            $params = array(
1461
                ':firstdate' => $firstyear . '-' . $firstmonth . '-01',
1462
                ':finaldate' => $finalyear . '-' . $finalmonth . '-31');
1463
1464
            // Check there are some dates for this year/month.
1465
            $q = $this->db->query("SELECT epobject_id
1466
                            FROM	hansard
1467
                            WHERE	hdate >= :firstdate
1468
                            AND 	hdate <= :finaldate
1469
                            LIMIT 	1
1470
                            ", $params);
1471
1472
            if ($q->rows() == 0) {
1473
                // No data in db, so return empty array!
1474
                return $data;
1475
            }
1476
1477
        }
1478
1479
        // OK, Now we have $firstyear, $firstmonth, $finalyear, $finalmonth set up.
1480
1481
        // Get the data...
1482
1483
        $where = '';
1484
        $params = array();
1485
1486
        if ($finalyear > $firstyear || $finalmonth >= $firstmonth) {
1487
            $params[':finaldate'] = $finalyear . '-' . $finalmonth . '-31';
1488
            $where = 'AND hdate <= :finaldate';
1489
        }
1490
1491
        $params[':major'] = $this->major;
1492
        $params[':firstdate'] = $firstyear . '-' . $firstmonth . '-01';
1493
        $q =  $this->db->query("SELECT 	DISTINCT(hdate) AS hdate
1494
                        FROM		hansard
1495
                        WHERE		major = :major
1496
                        AND			hdate >= :firstdate
1497
                        $where
1498
                        ORDER BY	hdate ASC
1499
                        ", $params);
1500
1501
        if ($q->rows() > 0) {
1502
1503
            // We put the data in this array. See top of function for the structure.
1504
            $years = array();
1505
1506
            for ($row=0; $row<$q->rows(); $row++) {
1507
1508
                list($year, $month, $day) = explode('-', $q->field($row, 'hdate'));
1509
1510
                $month = intval($month);
1511
                $day = intval($day);
1512
1513
                // Add as a link.
1514
                $years[$year][$month][] = $day;
1515
            }
1516
1517
            // If nothing happened on one month we'll have fetched nothing for it.
1518
            // So now we need to fill in any gaps with blank months.
1519
1520
            // We cycle through every year and month we're supposed to have fetched.
1521
            // If it doesn't have an array in $years, we create an empty one for that
1522
            // month.
1523
            for ($y = $firstyear; $y <= $finalyear; $y++) {
1524
1525
                if (!isset($years[$y])) {
1526
                    $years[$y] = array(1=>array(), 2=>array(), 3=>array(), 4=>array(), 5=>array(), 6=>array(), 7=>array(), 8=>array(), 9=>array(), 10=>array(), 11=>array(), 12=>array());
1527
                } else {
1528
1529
                    // This year is set. Check it has all the months...
1530
1531
                    $minmonth = $y == $firstyear ? $firstmonth : 1;
1532
                    $maxmonth = $y == $finalyear ? $finalmonth : 12;
1533
1534
                    for ($m = $minmonth; $m <= $maxmonth; $m++) {
1535
                        if (!isset($years[$y][$m])) {
1536
                            $years[$y][$m] = array();
1537
                        }
1538
                    }
1539
                    ksort($years[$y]);
1540
1541
                }
1542
            }
1543
1544
            $data['years'] = $years;
1545
        }
1546
1547
        // Set the next/prev links.
1548
1549
        $YEARURL = new \MySociety\TheyWorkForYou\Url($hansardmajors[$this->major]['page_year']);
1550
1551
        if (substr($this_page, -4) == 'year') {
1552
            // Only need next/prev on these pages.
1553
            // Not sure this is the best place for this, but...
1554
1555
            $nextprev = array();
1556
1557
            if ($action == 'recentyear') {
1558
                // Assuming there will be a previous year!
1559
1560
                $YEARURL->insert(array('y'=> $firstyear-1));
1561
1562
                $nextprev['prev'] = array (
1563
                    'body' => 'Previous year',
1564
                    'title' => $firstyear - 1,
1565
                    'url' => $YEARURL->generate()
1566
                );
1567
1568
            } else { // action is 'year'.
1569
1570
                $nextprev['prev'] = array ('body' => 'Previous year');
1571
                $nextprev['next'] = array ('body' => 'Next year');
1572
1573
                $q = $this->db->query("SELECT DATE_FORMAT(hdate, '%Y') AS year
1574
                            FROM hansard WHERE major = :major
1575
                            AND year(hdate) < :firstyear
1576
                            ORDER BY hdate DESC
1577
                            LIMIT 1", array(
1578
                                ':major' => $this->major,
1579
                                ':firstyear' => $firstyear
1580
                            ));
1581
1582
                $prevyear = $q->field(0, 'year');
1583
                $q = $this->db->query("SELECT DATE_FORMAT(hdate, '%Y') AS year
1584
                            FROM hansard WHERE major = :major
1585
                            AND year(hdate) > :finalyear
1586
                            ORDER BY hdate
1587
                            LIMIT 1", array(
1588
                                ':major' => $this->major,
1589
                                ':finalyear' => $finalyear
1590
                            ));
1591
                $nextyear = $q->field(0, 'year');
1592
1593
                if ($action == 'year' && $prevyear) {
1594
                    $YEARURL->insert(array('y'=>$prevyear));
1595
                    $nextprev['prev']['title'] = $prevyear;
1596
                    $nextprev['prev']['url'] = $YEARURL->generate();
1597
                }
1598
                if ($nextyear) {
1599
                    $YEARURL->insert(array('y'=>$nextyear));
1600
                    $nextprev['next']['title'] = $nextyear;
1601
                    $nextprev['next']['url'] = $YEARURL->generate();
1602
                }
1603
            }
1604
1605
            // Will be used in $PAGE.
1606
            $DATA->set_page_metadata($this_page, 'nextprev', $nextprev);
1607
        }
1608
1609
        return $data;
1610
1611
    }
1612
1613
    public function _get_mentions($spid) {
1614
        $result = array();
1615
        $q = $this->db->query("select gid, type, date, url, mentioned_gid
1616
            from mentions where gid like 'uk.org.publicwhip/spq/$spid'
1617
            order by date, type");
1618
        $nrows = $q->rows();
1619
        for ($i=0; $i < $nrows; $i++) {
1620
            $result[$i] = $q->row($i);
1621
        }
1622
        return $result;
1623
    }
1624
1625 3
    protected function _get_hansard_data($input) {
1626 3
        global $hansardmajors;
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...
1627
        // Generic function for getting hansard data from the DB.
1628
        // It returns an empty array if no data was found.
1629
        // It returns an array of items if 1 or more were found.
1630
        // Each item is an array of key/value pairs.
1631
        // eg:
1632
        /*
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...
1633
            array (
1634
                0	=> array (
1635
                    'epobject_id'	=> '2',
1636
                    'htype'			=> '10',
1637
                    'section_id'		=> '0',
1638
                    etc...
1639
                ),
1640
                1	=> array (
1641
                    'epobject_id'	=> '3',
1642
                    etc...
1643
                )
1644
            );
1645
        */
1646
1647
        // $input['amount'] is an associative array indicating what data should be fetched.
1648
        // It has the structure
1649
        // 	'key' => true
1650
        // Where 'true' indicates the data of type 'key' should be fetched.
1651
        // Leaving a key/value pair out is the same as setting a key to false.
1652
1653
        // $input['amount'] can have any or all these keys:
1654
        //	'body' 		- Get the body text from the epobject table.
1655
        //	'comment' 	- Get the first comment (and totalcomments count) for this item.
1656
        //	'votes'		- Get the user votes for this item.
1657
        //	'speaker'	- Get the speaker for this item, where applicable.
1658
        //  'excerpt' 	- For sub/sections get the body text for the first item within them.
1659
1660
        // $input['wherearr'] is an associative array of stuff for the WHERE clause, eg:
1661
        // 	array ('id=' => '37', 'date>' => '2003-12-31');
0 ignored issues
show
Unused Code Comprehensibility introduced by
62% 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...
1662
        // $input['order'] is a string for the $order clause, eg 'hpos DESC'.
1663
        // $input['limit'] as a string for the $limit clause, eg '21,20'.
0 ignored issues
show
Unused Code Comprehensibility introduced by
36% 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...
1664
1665 3
        $amount 		= isset($input['amount']) ? $input['amount'] : array();
1666 3
        $wherearr 		= isset($input['where']) ? $input['where'] : array();
1667 3
        $order 			= isset($input['order']) ? $input['order'] : '';
1668 3
        $limit 			= isset($input['limit']) ? $input['limit'] : '';
1669
1670
1671
        // The fields to fetch from db. 'table' => array ('field1', 'field2').
1672
        $fieldsarr = array (
1673 3
            'hansard' => array ('epobject_id', 'htype', 'gid', 'hpos', 'section_id', 'subsection_id', 'hdate', 'htime', 'source_url', 'major', 'minor', 'video_status', 'colnum')
1674 3
        );
1675
1676 3
        $params = array();
1677
1678 3
        if (isset($amount['speaker']) && $amount['speaker'] == true) {
1679 2
            $fieldsarr['hansard'][] = 'person_id';
1680 2
        }
1681
1682 3
        if ((isset($amount['body']) && $amount['body'] == true) ||
1683
            (isset($amount['comment']) && $amount['comment'] == true)
1684 3
            ) {
1685 3
            $fieldsarr['epobject'] = array ('body');
1686 3
            $join = 'LEFT OUTER JOIN epobject ON hansard.epobject_id = epobject.epobject_id';
1687 3
        } else {
1688
            $join = '';
1689
        }
1690
1691
1692 3
        $fieldsarr2 = array ();
1693
        // Construct the $fields clause.
1694 3
        foreach ($fieldsarr as $table => $tablesfields) {
1695 3
            foreach ($tablesfields as $n => $field) {
1696 3
                $fieldsarr2[] = $table.'.'.$field;
1697 3
            }
1698 3
        }
1699 3
        $fields = implode(', ', $fieldsarr2);
1700
1701 3
        $wherearr2 = array ();
1702
        // Construct the $where clause.
1703 3
        $i = 0;
1704 3
        foreach ($wherearr as $key => $val) {
1705 3
            $params[":where$i"] = $val;
1706 3
            $wherearr2[] = "$key :where$i";
1707 3
            $i++;
1708 3
        }
1709 3
        $where = implode (" AND ", $wherearr2);
1710
1711
1712 3
        if ($order != '') {
1713
            # You can't use parameters for order by clauses
1714 2
            $order_by_clause = "ORDER BY $order";
1715 2
        } else {
1716 2
            $order_by_clause = '';
1717
        }
1718
1719 3
        if ($limit != '') {
1720 1
            $params[':limit'] = $limit;
1721 1
            $limit = "LIMIT :limit";
1722 1
        } else {
1723 3
            $limit = '';
1724
        }
1725
1726
        // Finally, do the query!
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% 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...
1727 3
        $q = $this->db->query ("SELECT $fields
1728
                        FROM 	hansard
1729 3
                        $join
1730 3
                        WHERE $where
1731 3
                        $order_by_clause
1732 3
                        $limit
1733 3
                        ", $params);
1734
1735
        // Format the data into an array for returning.
1736 3
        $data = array ();
1737
1738 3
        if ($q->rows() > 0) {
1739
1740 3
            for ($n=0; $n<$q->rows(); $n++) {
1741
1742
                // Where we'll store the data for this item before adding
1743
                // it to $data.
1744 3
                $item = array();
1745
1746
                // Put each row returned into its own array in $data.
1747 3
                foreach ($fieldsarr as $table => $tablesfields) {
1748 3
                    foreach ($tablesfields as $m => $field) {
1749 3
                        $item[$field] = $q->field($n, $field);
1750 3
                    }
1751 3
                }
1752
1753 3
                if (isset($item['gid'])) {
1754
                    // Remove the "uk.org.publicwhip/blah/" from the gid:
1755
                    // (In includes/utility.php)
1756 3
                    $item['gid'] = fix_gid_from_db( $item['gid'] );
1757 3
                }
1758
1759
                // Add mentions if (a) it's a question in the written
1760
                // answer section or (b) it's in the official reports
1761
                // and the body text ends in a bracketed SPID.
1762 3
                if (($this->major && $hansardmajors[$this->major]['page']=='spwrans') && ($item['htype'] == '12' && $item['minor'] == '1')) {
1763
                    // Get out the SPID:
1764
                    if ( preg_match('#\d{4}-\d\d-\d\d\.(.*?)\.q#', $item['gid'], $m) ) {
1765
                        $item['mentions'] = $this->_get_mentions($m[1]);
1766
                    }
1767
                }
1768
1769
                // The second case (b):
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% 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...
1770 3
                if (($this->major && $hansardmajors[$this->major]['page']=='spdebates') && isset($item['body'])) {
1771
                    $stripped_body = preg_replace('/<[^>]+>/ms','',$item['body']);
1772
                    if ( preg_match('/\((S\d+\w+-\d+)\)/ms',$stripped_body,$m) ) {
1773
                        $item['mentions'] = $this->_get_mentions($m[1]);
1774
                    }
1775
                }
1776
1777 3
                if ($item['epobject_id'] == 15674958 || $item['epobject_id'] == 15674959) {
1778
                    global $DATA, $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...
1779
                    $DATA->set_page_metadata($this_page, 'robots', 'noindex');
1780
                }
1781
1782
                // Get the number of items within a section or subsection.
1783
                // It could be that we can do this in the main query?
1784
                // Not sure.
1785 3
                if ( ($this->major && $hansardmajors[$this->major]['type']=='debate') && ($item['htype'] == '10' || $item['htype'] == '11') ) {
1786
1787 3
                    if ($item['htype'] == '10') {
1788
                        // Section - get a count of items within this section that
1789
                        // don't have a subsection heading.
1790 3
                        $where = "section_id = '" . $item['epobject_id'] . "'
1791 3
                            AND subsection_id = '" . $item['epobject_id'] . "'";
1792
1793 3
                    } else {
1794
                        // Subsection - get a count of items within this subsection.
1795 1
                        $where = "subsection_id = '" . $item['epobject_id'] . "'";
1796
                    }
1797
1798 3
                    $r = $this->db->query("SELECT COUNT(*) AS count
1799
                                    FROM 	hansard
1800
                                    WHERE 	$where
1801
                                    AND htype = 12
1802 3
                                    ");
1803
1804 3
                    if ($r->rows() > 0) {
1805 3
                        $item['contentcount'] = $r->field(0, 'count');
1806 3
                    } else {
1807
                        $item['contentcount'] = '0';
1808
                    }
1809 3
                }
1810
1811
                // Get the body of the first item with the section or
1812
                // subsection. This can then be printed as an excerpt
1813
                // on the daily list pages.
1814
1815 3
                if ((isset($amount['excerpt']) && $amount['excerpt'] == true) &&
1816 1
                    ($item['htype'] == '10' ||
1817
                    $item['htype'] == '11')
1818 3
                    ) {
1819 1
                    $params = array(':epobject_id' => $item['epobject_id']);
1820 1
                    if ($item['htype'] == '10') {
1821
                        $where = 'hansard.section_id = :epobject_id
1822 1
                            AND hansard.subsection_id = :epobject_id';
1823 1
                    } elseif ($item['htype'] == '11') {
1824
                        $where = 'hansard.subsection_id = :epobject_id';
1825
                    }
1826
1827 1
                    $r = $this->db->query("SELECT epobject.body
1828
                                    FROM 	hansard,
1829
                                            epobject
1830
                                    WHERE	$where
1831
                                    AND		hansard.epobject_id = epobject.epobject_id
1832
                                    ORDER BY hansard.hpos ASC
1833 1
                                    LIMIT	1", $params);
1834
1835 1
                    if ($r->rows() > 0) {
1836
                        $item['excerpt'] = $r->field(0, 'body');
1837
                    }
1838 1
                }
1839
1840
1841
                // We generate two permalinks for each item:
1842
                // 'listurl' is the URL of the item in the full list view.
1843
                // 'commentsurl' is the URL of the item on its own page, with comments.
1844
1845
                // All the things we need to work out a listurl!
1846
                $item_data = array (
1847 3
                    'major'			=> $this->major,
1848 3
                    'minor' 		=> $item['minor'],
1849 3
                    'htype' 		=> $item['htype'],
1850 3
                    'gid' 			=> $item['gid'],
1851 3
                    'section_id'	=> $item['section_id'],
1852 3
                    'subsection_id'	=> $item['subsection_id']
1853 3
                );
1854
1855
1856 3
                $item['listurl'] = $this->_get_listurl($item_data);
1857
1858
1859
                // Create a URL for where we can see all the comments for this item.
1860 3
                if (isset($this->commentspage)) {
1861
                    $COMMENTSURL = new \MySociety\TheyWorkForYou\Url($this->commentspage);
1862
                    if ($this->major == 6) {
1863
                        # Another hack...
1864
                        $COMMENTSURL->remove(array('id'));
1865
                        $id = preg_replace('#^.*?_.*?_#', '', $item['gid']);
1866
                        $fragment = $this->url . $id;
1867
                        $item['commentsurl'] = $COMMENTSURL->generate() . $fragment;
1868
                    } else {
1869
                        $COMMENTSURL->insert(array('id' => $item['gid']));
1870
                        $item['commentsurl'] = $COMMENTSURL->generate();
1871
                    }
1872
                }
1873
1874
                // Get the user/anon votes items that have them.
1875 3
                if (($this->major == 3 || $this->major == 8) && (isset($amount['votes']) && $amount['votes'] == true) &&
1876 3
                    $item['htype'] == '12') {
1877
                    // Debate speech or written answers (not questions).
1878
1879
                    $item['votes'] = $this->_get_votes( $item['epobject_id'] );
1880
                }
1881
1882
                // Get the speaker for this item, if applicable.
1883 3
                if ( (isset($amount['speaker']) && $amount['speaker'] == true) &&
1884 3
                    $item['person_id'] != '') {
1885
1886 2
                    $item['speaker'] = $this->_get_speaker($item['person_id'], $item['hdate'], $item['htime'], $item['major']);
1887 2
                }
1888
1889
1890
                // Get comment count and (if any) most recent comment for each item.
1891 3
                if (isset($amount['comment']) && $amount['comment'] == true) {
1892
1893
                    // All the things we need to get the comment data.
1894
                    $item_data = array (
1895 3
                        'htype' => $item['htype'],
1896 3
                        'epobject_id' => $item['epobject_id']
1897 3
                    );
1898
1899 3
                    $commentdata = $this->_get_comment($item_data);
1900 3
                    $item['totalcomments'] = $commentdata['totalcomments'];
1901 3
                    $item['comment'] = $commentdata['comment'];
1902 3
                }
1903
1904
1905
                // Add this item on to the array of items we're returning.
1906 3
                $data[$n] = $item;
1907 3
            }
1908 3
        }
1909
1910 3
        return $data;
1911
    }
1912
1913
1914
    public function _get_votes($epobject_id) {
1915
        // Called from _get_hansard_data().
1916
        // Separated out here just for clarity.
1917
        // Returns an array of user and anon yes/no votes for an epobject.
1918
1919
        $votes = array();
1920
1921
        // YES user votes.
1922
        $q = $this->db->query("SELECT COUNT(vote) as totalvotes
1923
                        FROM	uservotes
1924
                        WHERE	epobject_id = :epobject_id
1925
                        AND 	vote = '1'
1926
                        GROUP BY epobject_id", array(':epobject_id' => $epobject_id));
1927
1928
        if ($q->rows() > 0) {
1929
            $votes['user']['yes'] = $q->field(0, 'totalvotes');
1930
        } else {
1931
            $votes['user']['yes'] = '0';
1932
        }
1933
1934
        // NO user votes.
1935
        $q = $this->db->query("SELECT COUNT(vote) as totalvotes
1936
                        FROM	uservotes
1937
                        WHERE	epobject_id = :epobject_id
1938
                        AND 	vote = '0'
1939
                        GROUP BY epobject_id", array(':epobject_id' => $epobject_id));
1940
1941
        if ($q->rows() > 0) {
1942
            $votes['user']['no'] = $q->field(0, 'totalvotes');
1943
        } else {
1944
            $votes['user']['no'] = '0';
1945
        }
1946
1947
1948
        // Get the anon votes for each item.
1949
1950
        $q = $this->db->query("SELECT yes_votes,
1951
                                no_votes
1952
                        FROM	anonvotes
1953
                        WHERE	epobject_id = :epobject_id",
1954
            array(':epobject_id' => $epobject_id));
1955
1956
        if ($q->rows() > 0) {
1957
            $votes['anon']['yes'] = $q->field(0, 'yes_votes');
1958
            $votes['anon']['no'] = $q->field(0, 'no_votes');
1959
        } else {
1960
            $votes['anon']['yes'] = '0';
1961
            $votes['anon']['no'] = '0';
1962
        }
1963
1964
        return $votes;
1965
    }
1966
1967
1968 4
    public function _get_listurl ($id_data, $url_args=array(), $encode='html') {
1969 4
        global $hansardmajors;
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...
1970
        // Generates an item's listurl - this is the 'contextual' url
1971
        // for an item, in the full list view with an anchor (if appropriate).
1972
1973
        // $id_data is like this:
1974
        //		$id_data = array (
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% 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...
1975
        //		'major' 		=> 1,
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% 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...
1976
        //		'htype' 		=> 12,
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% 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...
1977
        //		'gid' 			=> 2003-10-30.421.4h2,
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% 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...
1978
        //		'section_id'	=> 345,
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% 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...
1979
        //		'subsection_id'	=> 346
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...
1980
        // );
1981
1982
        // $url_args is an array of other key/value pairs to be appended in the GET string.
1983 4
        if ($id_data['major'])
1984 4
            $LISTURL = new \MySociety\TheyWorkForYou\Url($hansardmajors[$id_data['major']]['page_all']);
1985
        else
1986
            $LISTURL = new \MySociety\TheyWorkForYou\Url('wrans');
1987
1988 4
        $fragment = '';
1989
1990 4
        if ($id_data['htype'] == '11' || $id_data['htype'] == '10') {
1991 4
            if ($this->major == 6) {
1992
                $id = preg_replace('#^.*?_.*?_#', '', $id_data['gid']);
1993
                global $DATA;
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...
1994
                $DATA->set_page_metadata('pbc_clause', 'url', 'pbc/' . $this->url . $id);
1995
                $LISTURL->remove(array('id'));
1996
            } else {
1997 4
                $LISTURL->insert( array( 'id' => $id_data['gid'] ) );
1998
            }
1999 4
        } else {
2000
            // A debate speech or question/answer, etc.
2001
            // We need to get the gid of the parent (sub)section for this item.
2002
            // We use this with the gid of the item itself as an #anchor.
2003
2004
            $parent_epobject_id = $id_data['subsection_id'];
2005
            $minor = $id_data['minor'];
2006
2007
            // Find the gid of this item's (sub)section.
2008
            $parent_gid = '';
2009
2010
            if (isset($this->epobjectid_to_gid[ $parent_epobject_id ])) {
2011
                // We've previously cached the gid for this epobject_id, so use that.
2012
2013
                $parent_gid = $this->epobjectid_to_gid[ $parent_epobject_id ];
2014
2015
            } else {
2016
                // We haven't cached the gid, so fetch from db.
2017
2018
                $r = $this->db->query("SELECT gid
2019
                                FROM 	hansard
2020
                                WHERE	epobject_id = :epobject_id",
2021
                    array(':epobject_id' => $parent_epobject_id));
2022
2023
                if ($r->rows() > 0) {
2024
                    // Remove the "uk.org.publicwhip/blah/" from the gid:
2025
                    // (In includes/utility.php)
2026
                    $parent_gid = fix_gid_from_db( $r->field(0, 'gid') );
2027
2028
                    // Cache it for if we need it again:
2029
                    $this->epobjectid_to_gid[ $parent_epobject_id ] = $parent_gid;
2030
                }
2031
            }
2032
2033
            if ($parent_gid != '') {
2034
                // We have a gid so add to the URL.
2035
                if ($id_data['major'] == 6) {
2036
                    if (isset($this->bill_lookup[$minor])) {
2037
                        list($title, $session) = $this->bill_lookup[$minor];
2038
                    } else {
2039
                        $qq = $this->db->query('select title, session from bills where id='.$minor);
2040
                        $title = $qq->field(0, 'title');
2041
                        $session = $qq->field(0, 'session');
2042
                        $this->bill_lookup[$minor] = array($title, $session);
2043
                    }
2044
                    $url = "$session/" . urlencode(str_replace(' ','_',$title));
2045
                    $parent_gid = preg_replace('#^.*?_.*?_#', '', $parent_gid);
2046
                    global $DATA;
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...
2047
                    $DATA->set_page_metadata('pbc_clause', 'url', "pbc/$url/$parent_gid");
2048
                    $LISTURL->remove(array('id'));
2049
                } else {
2050
                    $LISTURL->insert( array( 'id' => $parent_gid ) );
2051
                }
2052
                // Use a truncated form of this item's gid for the anchor.
2053
                $fragment = '#g' . gid_to_anchor($id_data['gid']);
2054
            }
2055
        }
2056
2057 4
        if (count($url_args) > 0) {
2058 1
            $LISTURL->insert( $url_args);
2059 1
        }
2060
2061 4
        return $LISTURL->generate($encode) . $fragment;
2062
    }
2063
2064
    private $major_to_house = array(
2065
        1 => array(1),
2066
        2 => array(1),
2067
        3 => array(1, 2),
2068
        4 => array(1, 2),
2069
        5 => array(3),
2070
        6 => array(1),
2071
        7 => array(4),
2072
        8 => array(4),
2073
        101 => array(2),
2074
    );
2075
2076 3
    public function _get_speaker($person_id, $hdate, $htime, $major) {
2077 3
        if ($person_id == 0) {
2078 1
            return array();
2079
        }
2080
2081
        # Special exemption below for when NI Speaker becomes Speaker
2082
        # mid-debate, so we don't want to cache
2083 3
        if (isset($this->speakers["$person_id-$hdate"]) && !($person_id == 13831 && $hdate == '2015-01-12')) {
2084 1
            return $this->speakers["$person_id-$hdate"];
2085
        }
2086
2087
        # Special exemptions for people 'speaking' after they have died
2088
        # Note identical code to this in search/index.pl
2089 3
        if ($person_id == 10170 && $hdate == '2014-09-08') {
2090
            $hdate = '2014-09-07';
2091 3
        } elseif ($person_id == 11068 && substr($hdate, 0, 7) == '2008-09') {
2092
            $hdate = '2008-08-13';
2093 3
        } elseif ($person_id == 25394 && $hdate == '2016-07-01') {
2094
            $hdate = '2016-06-16';
2095
        }
2096
2097
        # check for a person redirect
2098 3
        $q = $this->db->query("SELECT gid_to FROM gidredirect
2099 3
                WHERE gid_from = :gid_from",
2100 3
            array(':gid_from' => "uk.org.publicwhip/person/$person_id")
2101 3
        );
2102 3
        if ($q->rows > 0) {
2103
            $person_id = str_replace('uk.org.publicwhip/person/', '', $q->field(0, 'gid_to'));
2104
        }
2105
2106 3
        $q = $this->db->query("SELECT title, given_name, family_name, lordofname,
2107
                                house,
2108
                                constituency,
2109
                                party,
2110
                                member_id
2111
                        FROM    member m, person_names p
2112
                        WHERE	m.person_id = :person_id
2113
                            AND entered_house <= :hdate AND :hdate <= left_house
2114
                            AND p.person_id = m.person_id AND p.type = 'name'
2115
                            AND p.start_date <= :hdate AND :hdate <= p.end_date
2116 3
                        ORDER BY entered_house",
2117 3
            array(':person_id' => $person_id, ':hdate' => $hdate));
2118 3
        $member = $this->_get_speaker_alone($q->data, $person_id, $hdate, $htime, $major);
2119
2120 3
        $URL = $this->_get_speaker_url($member['house']);
2121 3
        $URL->insert( array ('p' => $person_id) );
2122
2123 3
        $name = member_full_name($member['house'], $member['title'], $member['given_name'], $member['family_name'], $member['lordofname']);
2124
        $speaker = array (
2125 3
            'member_id' => $member['member_id'],
2126 3
            "name" => $name,
2127 3
            'house' => $member['house'],
2128 3
            "constituency" => $member["constituency"],
2129 3
            "party" => $member["party"],
2130 3
            "person_id" => $person_id,
2131 3
            "url" => $URL->generate(),
2132 3
        );
2133
2134 3
        global $parties;
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...
2135
        // Manual fix for Speakers.
2136 3
        if (isset($parties[$speaker['party']])) {
2137
            $speaker['party'] = $parties[$speaker['party']];
2138
        }
2139
2140 3
        $speaker['office'] = $this->_get_speaker_offices($speaker, $hdate);
2141 3
        $this->speakers["$person_id-$hdate"] = $speaker;
2142 3
        return $speaker;
2143
    }
2144
2145 3
    private function _get_speaker_alone($members, $person_id, $hdate, $htime, $major) {
2146 3
        if (count($members) > 1) {
2147 2
            $members = array_filter($members, function($m) use ($major) {
2148 2
                return in_array($m['house'], $this->major_to_house[$major]);
2149 2
            });
2150
            # Of course, remember PHP treats lists as dictionaries
2151 2
            $members = array_values($members);
2152 2
        }
2153
        # Note identical code to this in search/index.pl
2154 3
        if (count($members) > 1) {
2155
            # Couple of special cases for the election of the NI Speaker
2156
            if ($person_id == 13799 && $hdate == '2007-05-08') {
2157
                $members = array($members[$htime < '11:00' ? 0 : 1]);
2158
            } elseif ($person_id == 13831 && $hdate == '2015-01-12') {
2159
                $members = array($members[$htime < '13:00' ? 0 : 1]);
2160
            }
2161
        }
2162 3
        if (count($members) != 1) {
2163
            throw new \Exception('Wanted one result, but got ' . count($members) . " for $person_id, $hdate, $major.");
2164
        }
2165
2166 3
        return $members[0];
2167
    }
2168
2169 3
    private function _get_speaker_url($house) {
2170 3
        $URL = new \MySociety\TheyWorkForYou\Url('mp'); # Default, house=1
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% 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...
2171 3
        if ($house == HOUSE_TYPE_LORDS) {
2172
            $URL = new \MySociety\TheyWorkForYou\Url('peer');
2173 3
        } elseif ($house == HOUSE_TYPE_NI) {
2174
            $URL = new \MySociety\TheyWorkForYou\Url('mla');
2175 3
        } elseif ($house == HOUSE_TYPE_SCOTLAND) {
2176
            $URL = new \MySociety\TheyWorkForYou\Url('msp');
2177 3
        } elseif ($house == HOUSE_TYPE_ROYAL) {
2178
            $URL = new \MySociety\TheyWorkForYou\Url('royal');
2179
        }
2180 3
        return $URL;
2181
    }
2182
2183 3
    private function _get_speaker_offices($speaker, $hdate) {
2184 3
        $offices = array();
2185 3
        $q = $this->db->query("SELECT dept, position, source FROM moffice
2186
            WHERE person=:person_id
2187 3
            AND from_date <= :hdate and :hdate <= to_date",
2188 3
            array(':person_id' => $speaker['person_id'], ':hdate' => $hdate));
2189 3
        $rows = $q->rows();
2190 3
        if (!$rows) {
2191 3
            return $offices;
2192
        }
2193
        for ($row=0; $row<$rows; $row++) {
2194
            $dept = $q->field($row, 'dept');
2195
            $pos = $q->field($row, 'position');
2196
            $source = $q->field($row, 'source');
2197
            if ($source == 'chgpages/libdem' && $hdate > '2009-01-15') {
2198
                continue;
2199
            }
2200
            if (!$pos || $pos == 'Chairman' || $pos == 'Member') {
2201
                continue;
2202
            }
2203
            $offices[] = array(
2204
                'dept' => $dept,
2205
                'position' => $pos,
2206
                'source' => $source,
2207
                'pretty' => prettify_office($pos, $dept)
2208
            );
2209
        }
2210
        return $offices;
2211
    }
2212
2213 3
    public function _get_comment($item_data) {
2214
        // Pass it some variables belonging to an item and the function
2215
        // returns an array containing:
2216
        // 1) A count of the comments within this item.
2217
        // 2) The details of the most recent comment posted to this item.
2218
2219
        // Sections/subsections have (1) (the sum of the comments
2220
        // of all contained items), but not (2).
2221
2222
        // What we return.
2223 3
        $totalcomments = $this->_get_comment_count_for_epobject($item_data);
2224 3
        $comment = array();
2225
2226 3
        if ($item_data['htype'] == '12' || $item_data['htype'] == '13') {
2227
2228
            // Things which can have comments posted directly to them.
2229
2230
            if ($totalcomments > 0) {
2231
2232
                // Get the first comment.
2233
2234
                // Not doing this for Wrans sections because we don't
2235
                // need it anywhere. Arbitrary but it'll save us MySQL time!
2236
2237
                $q = $this->db->query("SELECT c.comment_id,
2238
                                    c.user_id,
2239
                                    c.body,
2240
                                    c.posted,
2241
                                    u.firstname,
2242
                                    u.lastname
2243
                            FROM	comments c, users u
2244
                            WHERE	c.epobject_id = :epobject_id
2245
                            AND		c.user_id = u.user_id
2246
                            AND		c.visible = 1
2247
                            ORDER BY c.posted ASC
2248
                            LIMIT	1",
2249
                    array(':epobject_id' => $item_data['epobject_id']));
2250
2251
                // Add this comment to the data structure.
2252
                $comment = array (
2253
                    'comment_id' => $q->field(0, 'comment_id'),
2254
                    'user_id'	=> $q->field(0, 'user_id'),
2255
                    'body'		=> $q->field(0, 'body'),
2256
                    'posted'	=> $q->field(0, 'posted'),
2257
                    'username'	=> $q->field(0, 'firstname') .' '. $q->field(0, 'lastname')
2258
                );
2259
            }
2260
2261
        }
2262
2263
        // We don't currently allow people to post comments to a section
2264
        // or subsection itself, only the items within them. So
2265
        // we don't get the most recent comment. Because there isn't one.
2266
2267
        $return = array (
2268 3
            'totalcomments' => $totalcomments,
2269
            'comment' => $comment
2270 3
        );
2271
2272 3
        return $return;
2273
    }
2274
2275
2276 3
    public function _get_comment_count_for_epobject($item_data) {
2277 3
        global $hansardmajors;
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...
2278
        // What it says on the tin.
2279
        // $item_data must have 'htype' and 'epobject_id' elements. TODO: Check for major==4
2280
2281 3
        if (($hansardmajors[$this->major]['type']=='debate') &&
2282 3
            ($item_data['htype'] == '10' || $item_data['htype'] == '11')
2283 3
            ) {
2284
            // We'll be getting a count of the comments on all items
2285
            // within this (sub)section.
2286 3
            $from = "comments, hansard";
2287
            $where = "comments.epobject_id = hansard.epobject_id
2288 3
                    AND subsection_id = :epobject_id";
2289
2290 3
            if ($item_data['htype'] == '10') {
2291
                // Section - get a count of comments within this section that
2292
                // don't have a subsection heading.
2293 3
                $where .= " AND section_id = :epobject_id";
2294 3
            }
2295
2296 3
        } else {
2297
            // Just getting a count of the comments on this item.
2298
            $from = "comments";
2299
            $where = 'epobject_id = :epobject_id';
2300
        }
2301
2302 3
        $q = $this->db->query("SELECT COUNT(*) AS count
2303
                        FROM 	$from
2304 3
                        WHERE	$where
2305 3
                        AND		visible = 1",
2306 3
            array(':epobject_id' => $item_data['epobject_id']));
2307
2308 3
        return $q->field(0, 'count');
2309
    }
2310
2311
2312
/*
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% 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...
2313
    public function _get_trackback_data($itemdata) {
2314
        // Returns a array of data we need to create the Trackback Auto-discovery
2315
        // RDF on a page.
2316
2317
        // We're assuming that we're only using this on a page where there's only
2318
        // one 'thing' to be trackbacked to. ie, we don't add #anchor data onto the
2319
        // end of the URL so we can include this RDF in full lists of debate speeches.
2320
2321
        $trackback = array();
2322
2323
        $title = '';
2324
2325
        if (isset($itemdata['speaker']) && isset($itemdata['speaker']['name'])) {
2326
            // This is probably a debate speech.
2327
            $title .= $itemdata['speaker']['name'] . ': ';
2328
        }
2329
2330
        $trackback['title'] = $title . trim_characters($itemdata['body'], 0, 200);
2331
2332
        // We're just saying this was in GMT...
2333
        $trackback['date'] = $itemdata['hdate'] . 'T' . $itemdata['htime'] . '+00:00';
2334
2335
        // The URL of this particular item.
2336
        // For (sub)sections we link to their listing page.
2337
        // For everything else, to their individual pages.
2338
        if ($itemdata['htype'] == '10' ||
2339
            $itemdata['htype'] == '11'
2340
            ) {
2341
            $url = $itemdata['listurl'];
2342
        } else {
2343
            $url = $itemdata['commentsurl'];
2344
        }
2345
        $trackback['itemurl'] = 'https://' . DOMAIN . $url;
2346
2347
        // Getting the URL the user needs to ping for this item.
2348
        $URL = new \MySociety\TheyWorkForYou\Url('trackback');
2349
        $URL->insert(array('e'=>$itemdata['epobject_id']));
2350
2351
        $trackback['pingurl'] = 'https://' . DOMAIN . $URL->generate('html');
2352
2353
2354
        return $trackback;
2355
2356
    }
2357
*/
2358
2359 1
    public function _get_data_by_gid($args) {
2360
2361
        // We need to get the data for this gid.
2362
        // Then depending on what htype it is, we get the data for other items too.
2363 1
        global $DATA, $this_page, $hansardmajors;
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...
2364
2365 1
        twfy_debug (get_class($this), "getting data by gid");
2366
2367
        // Get the information about the item this URL refers to.
2368 1
        $itemdata = $this->_get_item($args);
2369 1
        if (!$itemdata) {
2370
            return array();
2371
        }
2372
2373
        // If part of a Written Answer (just question or just answer), select the whole thing
2374 1
        if (isset($itemdata['major']) && $hansardmajors[$itemdata['major']]['type']=='other' and ($itemdata['htype'] == '12' or $itemdata['htype'] == '13')) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
2375
            // find the gid of the subheading which holds this part
2376
            $input = array (
2377
                'amount' => array('gid' => true),
2378
                'where' => array (
2379
                    'epobject_id=' => $itemdata['subsection_id'],
2380
                ),
2381
            );
2382
            $parent = $this->_get_hansard_data($input);
2383
            // display that item, i.e. the whole of the Written Answer
2384
            twfy_debug (get_class($this), "instead of " . $args['gid'] . " selecting subheading gid " . $parent[0]['gid'] . " to get whole wrans");
2385
            $args['gid'] = $parent[0]['gid'];
2386
            $this->_get_item($args);
2387
            throw new RedirectException($args['gid']);
2388
        }
2389
2390
        # If a WMS main heading, go to next gid
2391 1
        if (isset($itemdata['major']) && $itemdata['major']==4 && $itemdata['htype'] == '10') {
2392
            $input = array (
2393
                'amount' => array('gid' => true),
2394
                'where' => array(
2395
                    'section_id=' => $itemdata['epobject_id'],
2396
                ),
2397
                'order' => 'hpos ASC',
2398
                'limit' => 1
2399
            );
2400
            $next = $this->_get_hansard_data($input);
2401
            if (!empty($next)) {
2402
                twfy_debug (get_class($this), 'instead of ' . $args['gid'] . ' moving to ' . $next[0]['gid']);
2403
                $args['gid'] = $next[0]['gid'];
2404
                $this->_get_item($args);
2405
                throw new RedirectException($args['gid']);
2406
            }
2407
        }
2408
2409
        // Where we'll put all the data we want to render.
2410 1
        $data = array();
2411
2412 1
        if (isset($itemdata['htype'])) {
2413 1
            $this->htype = $itemdata['htype'];
2414 1
            if ($this->htype >= 12) {
2415
                $this_page = $this->commentspage;
2416
            } else {
2417 1
                $this_page = $this->listpage;
2418
            }
2419 1
        }
2420 1
        if (isset($itemdata['epobject_id'])) {
2421 1
            $this->epobject_id = $itemdata['epobject_id'];
2422 1
        }
2423 1
        if (isset($itemdata['gid'])) {
2424 1
            $this->gid = $itemdata['gid'];
2425 1
        }
2426
2427
        // We'll use these for page headings/titles:
2428 1
        $data['info']['date'] = $itemdata['hdate'];
2429 1
        $data['info']['text'] = $itemdata['body'];
2430 1
        $data['info']['major'] = $this->major;
2431
2432
        // If we have a member id we'll pass it on to the template so it
2433
        // can highlight all their speeches.
2434 1
        if (isset($args['member_id'])) {
2435
            $data['info']['member_id'] = $args['member_id'];
2436
        }
2437 1
        if (isset($args['person_id'])) {
2438
            $data['info']['person_id'] = $args['person_id'];
2439
        }
2440
2441 1
        if (isset($args['s']) && $args['s'] != '') {
2442
            // We have some search term words that we could highlight
2443
            // when rendering.
2444
            $data['info']['searchstring'] = $args['s'];
2445
        }
2446
2447
        // Get the section and subsection headings for this item.
2448 1
        $sectionrow = $this->_get_section($itemdata);
2449 1
        $subsectionrow = $this->_get_subsection($itemdata);
2450
2451
        // Get the nextprev links for this item, to link to next/prev pages.
2452
        // Duh.
2453 1
        if ($itemdata['htype'] == '10') {
2454 1
            $nextprev = $this->_get_nextprev_items( $sectionrow );
2455 1
            $data['info']['text_heading'] = $itemdata['body'];
2456
2457 1
        } elseif ($itemdata['htype'] == '11') {
2458
            $nextprev = $this->_get_nextprev_items( $subsectionrow );
2459
            $data['info']['text_heading'] = $itemdata['body'];
2460
2461
        } else {
2462
            // Ordinary lowly item.
2463
            $nextprev = $this->_get_nextprev_items( $itemdata );
2464
2465
            if (isset($subsectionrow['gid'])) {
2466
                $nextprev['up']['url'] 		= $subsectionrow['listurl'];
2467
                $nextprev['up']['title'] 	= $subsectionrow['body'];
2468
            } else {
2469
                $nextprev['up']['url'] 		= $sectionrow['listurl'];
2470
                $nextprev['up']['title'] 	= $sectionrow['body'];
2471
            }
2472
            $nextprev['up']['body']		= 'See the whole debate';
2473
        }
2474
2475
        // We can then access this from $PAGE and the templates.
2476 1
        $DATA->set_page_metadata($this_page, 'nextprev', $nextprev);
2477
2478
        // Now get all the non-heading rows.
2479
2480
        // What data do we want for each item?
2481
        $amount = array (
2482 1
            'body' => true,
2483 1
            'speaker' => true,
2484 1
            'comment' => true,
2485
            'votes' => true
2486 1
        );
2487
2488 1
        if ($itemdata['htype'] == '10') {
2489
            // This item is a section, so we're displaying all the items within
2490
            // it that aren't within a subsection.
2491
2492
            # $sectionrow['trackback'] = $this->_get_trackback_data($sectionrow);
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% 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...
2493
2494
            $input = array (
2495 1
                'amount' => $amount,
2496
                'where' => array (
2497 1
                    'section_id=' => $itemdata['epobject_id'],
2498 1
                    'subsection_id=' => $itemdata['epobject_id']
2499 1
                ),
2500
                'order' => 'hpos ASC'
2501 1
            );
2502
2503 1
            $data['rows'] = $this->_get_hansard_data($input);
2504 1
            if (!count($data['rows']) || (count($data['rows'])==1 && strstr($data['rows'][0]['body'], 'was asked'))) {
2505
2506
                $input = array (
2507
                    'amount' => array (
2508 1
                        'body' => true,
2509 1
                        'comment' => true,
2510
                        'excerpt' => true
2511 1
                    ),
2512
                    'where' => array (
2513 1
                        'section_id='	=> $sectionrow['epobject_id'],
2514 1
                        'htype='		=> '11',
2515 1
                        'major='		=> $this->major
2516 1
                    ),
2517
                    'order' => 'hpos'
2518 1
                );
2519 1
                $data['subrows'] = $this->_get_hansard_data($input);
2520
                # If there's only one subheading, and nothing in the heading, redirect to it immediaetly
2521 1
                if (count($data['subrows']) == 1) {
2522
                    throw new RedirectException($data['subrows'][0]['gid']);
2523
                }
2524 1
            }
2525 1
        } elseif ($itemdata['htype'] == '11') {
2526
            // This item is a subsection, so we're displaying everything within it.
2527
2528
            # $subsectionrow['trackback'] = $this->_get_trackback_data($subsectionrow);
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% 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...
2529
2530
            $input = array (
2531
                'amount' => $amount,
2532
                'where' => array (
2533
                    'subsection_id=' => $itemdata['epobject_id']
2534
                ),
2535
                'order' => 'hpos ASC'
2536
            );
2537
2538
            $data['rows'] = $this->_get_hansard_data($input);
2539
2540
2541
        } elseif ($itemdata['htype'] == '12' || $itemdata['htype'] == '13') {
2542
            // Debate speech or procedural, so we're just displaying this one item.
2543
2544
            # $itemdata['trackback'] = $this->_get_trackback_data($itemdata);
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% 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...
2545
2546
            $data['rows'][] = $itemdata;
2547
2548
        }
2549
2550
        // Put the section and subsection at the top of the rows array.
2551 1
        if (count($subsectionrow) > 0 &&
2552 1
            $subsectionrow['gid'] != $sectionrow['gid']) {
2553
            // If we're looking at a section, there may not be a subsection.
2554
            // And if the subsectionrow and sectionrow aren't the same.
2555
            array_unshift ($data['rows'], $subsectionrow);
2556
        }
2557
2558 1
        array_unshift ($data['rows'], $sectionrow);
2559
2560 1
        return $data;
2561
2562
    }
2563
2564
    public function _get_data_by_column($args) {
2565
        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...
2566
2567
        twfy_debug (get_class($this), "getting data by column");
2568
2569
        $input = array( 'amount' => array('body'=>true, 'comment'=>true, 'speaker'=>true),
2570
        'where' => array( 'hdate='=>$args['date'], 'major=' => $this->major, 'gid LIKE ' =>'%.'.$args['column'].'.%' ),
2571
        'order' => 'hpos'
2572
        );
2573
        $data = $this->_get_hansard_data($input);
2574
        #		$data = array();
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% 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...
2575
2576
        #		$itemdata = $this->_get_item($args);
0 ignored issues
show
Unused Code Comprehensibility introduced by
59% 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...
2577
2578
        #		if ($itemdata) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% 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...
2579
            #	$data['info']['date'] = $itemdata['hdate'];
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% 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...
2580
            #			$data['info']['text'] = $itemdata['body'];
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% 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...
2581
            #			$data['info']['major'] = $this->major;
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% 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...
2582
            #		}
2583
        return $data;
2584
    }
2585
2586
}
2587
2588
class WMSLIST extends WRANSLIST {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
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...
2589
    public $major = 4;
2590
    public $listpage = 'wms';
2591
    public $commentspage = 'wms';
2592
    public $gidprefix = 'uk.org.publicwhip/wms/';
2593
2594
    public function _get_data_by_recent_wms($args = array()) {
2595
        return $this->_get_data_by_recent_wrans($args);
2596
    }
2597
}
2598
2599
class WHALLLIST extends DEBATELIST {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
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...
2600
    public $major = 2;
2601
    public $listpage = 'whalls';
2602
    public $commentspage = 'whall';
2603
    public $gidprefix = 'uk.org.publicwhip/westminhall/';
2604
}
2605
2606
class NILIST extends DEBATELIST {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
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...
2607
    public $major = 5;
2608
    public $listpage = 'nidebates';
2609
    public $commentspage = 'nidebate';
2610
    public $gidprefix = 'uk.org.publicwhip/ni/';
2611
}
2612
2613
class SPLIST extends DEBATELIST {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
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...
2614
    public $major = 7;
2615
    public $listpage = 'spdebates';
2616
    public $commentspage = 'spdebate';
2617
    public $gidprefix = 'uk.org.publicwhip/spor/';
2618
}
2619
2620
class SPWRANSLIST extends WRANSLIST {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
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...
2621
    public $major = 8;
2622
    public $listpage = 'spwrans';
2623
    public $commentspage = 'spwrans';
2624
    public $gidprefix = 'uk.org.publicwhip/spwa/';
2625
2626
    public function get_gid_from_spid($spid) {
2627
        // Fix the common errors of S.0 instead of S.O and leading
2628
        // zeros in the numbers:
2629
        $fixed_spid = preg_replace('/(S[0-9]+)0-([0-9]+)/','${1}O-${2}',$spid);
2630
        $fixed_spid = preg_replace('/(S[0-9]+\w+)-0*([0-9]+)/','${1}-${2}',$fixed_spid);
2631
        $q = $this->db->query(
2632
            "select mentioned_gid from mentions where gid = :gid_from_spid and (type = 4 or type = 6)",
2633
            array(':gid_from_spid' => 'uk.org.publicwhip/spq/' . $fixed_spid)
2634
        );
2635
        $gid = $q->field(0, 'mentioned_gid');
2636
        if ($gid) return $gid;
2637
        return null;
2638
    }
2639
    public function old_get_gid_from_spid($spid) {
2640
        $q = $this->db->query(
2641
            "select gid from hansard where gid like :gid_like",
2642
            array(':gid_like' => 'uk.org.publicwhip/spwa/%.' . $spid . '.h')
2643
        );
2644
        $gid = $q->field(0, 'gid');
2645
        if ($gid) return str_replace('uk.org.publicwhip/spwa/', '', $gid);
2646
        return null;
2647
    }
2648
}
2649
2650
class LORDSDEBATELIST extends DEBATELIST {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
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...
2651
    public $major = 101;
2652
    public $listpage = 'lordsdebates';
2653
    public $commentspage = 'lordsdebate';
2654
    public $gidprefix = 'uk.org.publicwhip/lords/';
2655
}
2656
2657
class DEBATELIST extends HANSARDLIST {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
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...
2658
    public $major = 1;
2659
    public $listpage = 'debates';
2660
    public $commentspage = 'debate';
2661
    public $gidprefix = 'uk.org.publicwhip/debate/';
2662
2663
    public function _get_data_by_recent_mostvotes($args) {
2664
        // Get the most highly voted recent speeches.
2665
        // $args may have 'days'=>7 and/or 'num'=>5
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...
2666
        // or something like that.
2667
2668
        // The most voted on things during how many recent days?
2669
        if (isset($args['days']) && is_numeric($args['days'])) {
2670
            $days = $args['days'];
2671
        } else {
2672
            $days = 7;
2673
        }
2674
2675
        // How many results?
2676
        if (isset($args['num']) && is_numeric($args['num'])) {
2677
            $items_to_list = $args['num'];
2678
        } else {
2679
            $items_to_list = 5;
2680
        }
2681
2682
        $q = $this->db->query("SELECT subsection_id,
2683
                                section_id,
2684
                                htype,
2685
                                gid,
2686
                                major, minor,
2687
                                hdate, htime,
2688
                                person_id,
2689
                                epobject.body,
2690
                                SUM(uservotes.vote) + anonvotes.yes_votes AS total_vote
2691
                        FROM	hansard,
2692
                                epobject
2693
                                LEFT OUTER JOIN uservotes ON epobject.epobject_id = uservotes.epobject_id
2694
                                LEFT OUTER JOIN anonvotes ON epobject.epobject_id = anonvotes.epobject_id
2695
                        WHERE		major = :major
2696
                        AND		hansard.epobject_id = epobject.epobject_id
2697
                        AND		hdate >= DATE_SUB(CURDATE(), INTERVAL $days DAY)
2698
                        GROUP BY epobject.epobject_id
2699
                        HAVING 	total_vote > 0
2700
                        ORDER BY total_vote DESC
2701
                        LIMIT	$items_to_list
2702
                        ", array(':major' => $this->major));
2703
2704
        // What we return.
2705
        $data = array ();
2706
        $speeches = array();
2707
2708
        if ($q->rows() > 0) {
2709
2710
            for ($n=0; $n<$q->rows(); $n++) {
2711
2712
                $speech = array (
2713
                    'subsection_id'	=> $q->field($n, 'subsection_id'),
2714
                    'section_id'	=> $q->field($n, 'section_id'),
2715
                    'htype'			=> $q->field($n, 'htype'),
2716
                    'major'			=> $q->field($n, 'major'),
2717
                    'minor'			=> $q->field($n, 'minor'),
2718
                    'hdate'			=> $q->field($n, 'hdate'),
2719
                    'body'			=> $q->field($n, 'body'),
2720
                    'votes'			=> $q->field($n, 'total_vote')
2721
                );
2722
2723
                // Remove the "uk.org.publicwhip/blah/" from the gid:
2724
                // (In includes/utility.php)
2725
                $speech['gid'] = fix_gid_from_db( $q->field($n, 'gid') );
2726
2727
                $speech['listurl'] = $this->_get_listurl($speech);
2728
2729
                $speech['speaker'] = $this->_get_speaker($q->field($n, 'person_id'), $q->field($n, 'hdate'), $q->field($n, 'htime'), $this->major );
2730
2731
                $speeches[] = $speech;
2732
            }
2733
        }
2734
2735
        if (count($speeches) > 0) {
2736
            // Get the subsection texts.
2737
2738
            $num_speeches = count($speeches);
2739
            for ($n=0; $n<$num_speeches; $n++) {
2740
                //if ($this->major == 1) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% 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...
2741
                    // Debate.
2742
                    $parent = $this->_get_subsection ($speeches[$n]);
2743
2744
                //} elseif ($this->major == 3) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% 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...
2745
                    // Wrans.
2746
                //	$parent = $this->_get_section ($speeches[$n]);
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% 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...
2747
                //}
2748
                // Add the parent's body on...
2749
                //if (isset($parent['body'])) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
85% 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...
2750
                    $speeches[$n]['parent']['body'] = $parent['body'];
2751
                //} else {
2752
                //	$parent = $this->_get_section ($speeches[$n]);
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% 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...
2753
                //	$speeches[$n]['parent']['body'] = $parent['body'];
0 ignored issues
show
Unused Code Comprehensibility introduced by
79% 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...
2754
                //}
2755
2756
            }
2757
2758
            $data['rows'] = $speeches;
2759
2760
        } else {
2761
            $data['rows'] = array ();
2762
        }
2763
2764
        $data['info']['days'] = $days;
2765
2766
        return $data;
2767
    }
2768
2769
2770
    public function total_speeches() {
2771
2772
        $q = $this->db->query("SELECT COUNT(*) AS count FROM hansard WHERE major = :major AND htype = 12", array(':major' => $this->major));
2773
2774
        return $q->field(0, 'count');
2775
    }
2776
2777
2778
    public function biggest_debates($args=array()) {
2779
        // So we can just get the data back for special formatting
2780
        // on the front page, without doing the whole display() thing.
2781
        return $this->_get_data_by_biggest_debates($args);
2782
    }
2783
2784
    public function _get_data_by_featured_gid($args=array()) {
2785
        $params = array();
2786
        $data = array();
2787
2788
        $params[':gid'] = $args['gid'];
2789
        $params[':major'] = $this->major;
2790
2791
        $query = "SELECT
2792
                    body,
2793
                    title,
2794
                    h.hdate,
2795
                    h.htime,
2796
                    h.htype,
2797
                    h.minor,
2798
                    h.gid,
2799
                    h.person_id,
2800
                    h.subsection_id,
2801
                    h.section_id,
2802
                    h.epobject_id
2803
            FROM    hansard h, epobject e
2804
            WHERE   h.major = :major
2805
            AND     h.gid = :gid
2806
            AND     h.epobject_id = e.epobject_id";
2807
2808
        $q = $this->db->query($query, $params);
2809
2810
        if ( $q->rows ) {
2811
2812
            // This array just used for getting further data about this debate.
2813
            $item_data = array (
2814
                'major'         => $this->major,
2815
                'minor'         => $q->field(0, 'minor'),
2816
                'gid'           => fix_gid_from_db( $q->field(0, 'gid') ),
2817
                'htype'         => $q->field(0, 'htype'),
2818
                'section_id'    => $q->field(0, 'section_id'),
2819
                'subsection_id' => $q->field(0, 'subsection_id'),
2820
                'epobject_id'   => $q->field(0, 'epobject_id')
2821
            );
2822
2823
            $list_url      = $this->_get_listurl( $item_data );
2824
            $totalcomments = $this->_get_comment_count_for_epobject( $item_data );
2825
2826
            $body          = $q->field(0, 'body');
2827
            $hdate         = $q->field(0, 'hdate');
2828
            $htime         = $q->field(0, 'htime');
2829
2830
            // If this is a subsection, we're going to prepend the title
2831
            // of the parent section, so let's get that.
2832
            $parentbody = '';
2833
            if ($item_data['htype'] == 11 || $item_data['htype'] == 12) {
2834
                $r = $this->db->query("SELECT sec.body as sec_body, sec.title as sec_title,
2835
                                              sub.body as sub_body, sub.title as sub_title
2836
                                FROM    epobject sec, epobject sub
2837
                                WHERE   sec.epobject_id = :section_id
2838
                                AND     sub.epobject_id = :subsection_id",
2839
                                array(
2840
                                    ':section_id' => $item_data['section_id'],
2841
                                    ':subsection_id' => $item_data['subsection_id'],
2842
                                )
2843
                            );
2844
                $section_body  = $r->field(0, 'sec_body');
2845
                $subsection_body = $r->field(0, 'sub_body');
2846
                if ( $section_body && $subsection_body ) {
2847
                    $parentbody = "$section_body : $subsection_body";
2848
                } else {
2849
                    $parentbody = "$section_body$subsection_body";
2850
                }
2851
            } else if ( $item_data['htype'] == 10 ) {
2852
                $parentbody = $body;
2853
            }
2854
2855
            // Get the question for this item.
2856
            if ( $item_data['htype'] == 12 ) {
2857
                $childbody = $body;
2858
                $speaker = $this->_get_speaker($q->field(0, 'person_id'), $q->field(0, 'hdate'), $q->field(0, 'htime'), $this->major );
2859
            } else {
2860
                $r = $this->db->query("SELECT e.body, e.title,
2861
                                        h.person_id, h.hdate, h.htime
2862
                                FROM    hansard h, epobject e
2863
                                WHERE   h.epobject_id = e.epobject_id
2864
                                AND     h.subsection_id = :object_id
2865
                                ORDER BY hpos
2866
                                LIMIT 1
2867
                                ",
2868
                                array( ':object_id' => $item_data['epobject_id'] )
2869
                );
2870
                $childbody = $r->field(0, 'body');
2871
                $speaker = $this->_get_speaker($r->field(0, 'person_id'), $r->field(0, 'hdate'), $r->field(0, 'htime'), $this->major );
2872
            }
2873
2874
            $contentcount = 0;
2875
            $r = $this->db->query("SELECT COUNT(*) AS count
2876
                            FROM hansard
2877
                            WHERE subsection_id = :object_id
2878
                            AND htype = 12",
2879
                            array(':object_id' => $item_data['epobject_id'])
2880
                );
2881
2882
            if ($r->rows() > 0) {
2883
                $contentcount = $r->field(0, 'count');
2884
            }
2885
2886
            global $hansardmajors;
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...
2887
            $more_url = new \MySociety\TheyWorkForYou\Url( $hansardmajors[$this->major]['page_all'] );
2888
            $details = array(
2889
                'body'          => $body,
2890
                'contentcount'  => $contentcount,
2891
                'hdate'         => $hdate,
2892
                'htime'         => $htime,
2893
                'list_url'      => $list_url,
2894
                'totalcomments' => $totalcomments,
2895
                'child'         => array(
2896
                    'body'      => $childbody,
2897
                    'speaker'   => $speaker
2898
                ),
2899
                'parent'        => array(
2900
                    'body'      => $parentbody
2901
                ),
2902
                'desc' => $hansardmajors[$this->major]['title'],
2903
                'more_url' => $more_url->generate()
2904
            );
2905
2906
            $data = array (
2907
                'gid' => $args['gid'],
2908
                'major' => $this->major,
2909
                'info' => array(),
2910
                'data' => $details,
2911
            );
2912
        }
2913
2914
        return $data;
2915
2916
    }
2917
    public function _get_data_by_recent_debates($args=array()) {
2918
        // Returns an array of some random recent debates from a set number of
2919
        // recent days (that's recent days starting from the most recent day
2920
        // that had any debates on).
2921
2922
        // $args['days'] is the number of days back to look for biggest debates (1 by default).
2923
        // $args['num'] is the number of links to return (1 by default).
2924
2925
        $data = array();
2926
2927
        $params = array();
2928
2929
        // Get the most recent day on which we have a debate.
2930
        $recentday = $this->most_recent_day();
2931
        if (!count($recentday)) return $data;
2932
2933
        if (!isset($args['days']) || !is_numeric($args['days'])) {
2934
            $args['days'] = 1;
2935
        }
2936
        if (!isset($args['num']) || !is_numeric($args['num'])) {
2937
            $args['num'] = 1;
2938
        }
2939
2940
        if ($args['num'] == 1) {
2941
            $datewhere = "h.hdate = :hdate";
2942
            $params[':hdate'] = $recentday['hdate'];
2943
        } else {
2944
            $firstdate = gmdate('Y-m-d', $recentday['timestamp'] - (86400 * $args['days']));
2945
            $datewhere = "h.hdate >= :firstdate
2946
                        AND h.hdate <= :hdate";
2947
            $params[':firstdate'] = $firstdate;
2948
            $params[':hdate'] = $recentday['hdate'];
2949
        }
2950
2951
        $params[':limit'] = $args['num'];
2952
        $params[':major'] = $this->major;
2953
2954
        $query = "SELECT COUNT(*) AS count,
2955
                    body,
2956
                    h.hdate,
2957
                    sech.htype,
2958
                    sech.htime,
2959
                    sech.gid,
2960
                    sech.subsection_id,
2961
                    sech.section_id,
2962
                    sech.epobject_id
2963
            FROM    hansard h, epobject e, hansard sech
2964
            WHERE   h.major = :major
2965
            AND     $datewhere
2966
            AND     h.subsection_id = e.epobject_id
2967
            AND     sech.epobject_id = h.subsection_id
2968
            GROUP BY h.subsection_id
2969
            HAVING  count >= 5
2970
            ORDER BY RAND()
2971
            LIMIT   :limit";
2972
2973
        $q = $this->db->query($query, $params);
2974
2975
        for ($row=0; $row<$q->rows; $row++) {
2976
2977
            // This array just used for getting further data about this debate.
2978
            $item_data = array (
2979
                'major'         => $this->major,
2980
                'gid'           => fix_gid_from_db( $q->field($row, 'gid') ),
2981
                'htype'         => $q->field($row, 'htype'),
2982
                'section_id'    => $q->field($row, 'section_id'),
2983
                'subsection_id' => $q->field($row, 'subsection_id'),
2984
                'epobject_id'   => $q->field($row, 'epobject_id')
2985
            );
2986
2987
            $list_url      = $this->_get_listurl( $item_data );
2988
            $totalcomments = $this->_get_comment_count_for_epobject( $item_data );
2989
2990
            $contentcount  = $q->field($row, 'count');
2991
            $body          = $q->field($row, 'body');
2992
            $hdate         = $q->field($row, 'hdate');
2993
            $htime         = $q->field($row, 'htime');
2994
2995
            // If this is a subsection, we're going to prepend the title
2996
            // of the parent section, so let's get that.
2997
            $parentbody = '';
2998
            if ($item_data['htype'] == 11) {
2999
                $r = $this->db->query("SELECT body
3000
                                FROM    epobject
3001
                                WHERE   epobject_id = :epobject_id",
3002
                    array(':epobject_id' => $item_data['section_id']));
3003
                $parentbody = $r->field(0, 'body');
3004
            }
3005
3006
            // Get the question for this item.
3007
            $r = $this->db->query("SELECT e.body,
3008
                                    h.person_id, h.hdate, h.htime
3009
                            FROM    hansard h, epobject e
3010
                            WHERE   h.epobject_id = e.epobject_id
3011
                            AND     h.subsection_id = '" . $item_data['epobject_id'] . "'
3012
                            ORDER BY hpos
3013
                            LIMIT 1
3014
                            ");
3015
            $childbody = $r->field(0, 'body');
3016
            $speaker = $this->_get_speaker($r->field(0, 'person_id'), $r->field(0, 'hdate'), $r->field(0, 'htime'), $this->major );
3017
3018
            $data[] = array(
3019
                'contentcount'  => $contentcount,
3020
                'body'          => $body,
3021
                'hdate'         => $hdate,
3022
                'htime'         => $htime,
3023
                'list_url'      => $list_url,
3024
                'totalcomments' => $totalcomments,
3025
                'child'         => array(
3026
                    'body'      => $childbody,
3027
                    'speaker'   => $speaker
3028
                ),
3029
                'parent'        => array(
3030
                    'body'      => $parentbody
3031
                )
3032
            );
3033
3034
        }
3035
3036
        $data = array (
3037
            'info' => array(),
3038
            'data' => $data
3039
        );
3040
3041
        return $data;
3042
3043
    }
3044
3045
    public function _get_data_by_biggest_debates($args=array()) {
3046
        // Returns an array of the debates with most speeches in from
3047
        // a set number of recent days (that's recent days starting from the
3048
        // most recent day that had any debates on).
3049
3050
        // $args['days'] is the number of days back to look for biggest debates.
3051
        // (1 by default)
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...
3052
        // $args['num'] is the number of links to return (1 by default).
3053
3054
        $data = array();
3055
3056
        // Get the most recent day on which we have a debate.
3057
        $recentday = $this->most_recent_day();
3058
        if (!count($recentday))
3059
            return array();
3060
3061
        if (!isset($args['days']) || !is_numeric($args['days'])) {
3062
            $args['days'] = 1;
3063
        }
3064
        if (!isset($args['num']) || !is_numeric($args['num'])) {
3065
            $args['num'] = 1;
3066
        }
3067
3068
        $params = array(':recentdate' => $recentday['hdate']);
3069
        if ($args['num'] == 1) {
3070
            $datewhere = "h.hdate = :recentdate";
3071
        } else {
3072
            $params[':firstdate'] = gmdate('Y-m-d', $recentday['timestamp'] - (86400 * $args['days']));
3073
            $datewhere = "h.hdate >= :firstdate AND	h.hdate <= :recentdate";
3074
        }
3075
3076
        $params[':limit'] = $args['num'];
3077
        $params[':major'] = $this->major;
3078
3079
        $q = $this->db->query("SELECT COUNT(*) AS count,
3080
                                body,
3081
                                h.hdate,
3082
                                sech.htype,
3083
                                sech.gid,
3084
                                sech.subsection_id,
3085
                                sech.section_id,
3086
                                sech.epobject_id
3087
                        FROM 	hansard h, epobject e, hansard sech
3088
                        WHERE 	h.major = :major
3089
                        AND 	$datewhere
3090
                        AND  	h.subsection_id = e.epobject_id
3091
                        AND 	sech.epobject_id = h.subsection_id
3092
                        GROUP BY h.subsection_id
3093
                        ORDER BY count DESC
3094
                        LIMIT :limit", $params);
3095
3096
3097
        for ($row=0; $row<$q->rows; $row++) {
3098
3099
            // This array just used for getting further data about this debate.
3100
            $item_data = array (
3101
                'major'			=> $this->major,
3102
                'gid'			=> fix_gid_from_db( $q->field($row, 'gid') ),
3103
                'htype'			=> $q->field($row, 'htype'),
3104
                'section_id'	=> $q->field($row, 'section_id'),
3105
                'subsection_id'	=> $q->field($row, 'subsection_id'),
3106
                'epobject_id'	=> $q->field($row, 'epobject_id')
3107
            );
3108
3109
            $list_url 		= $this->_get_listurl( $item_data );
3110
            $totalcomments	= $this->_get_comment_count_for_epobject( $item_data );
3111
3112
            $contentcount	= $q->field($row, 'count');
3113
            $body 			= $q->field($row, 'body');
3114
            $hdate			= $q->field($row, 'hdate');
3115
3116
3117
            // This array will be added to $data, which is what gets returned.
3118
            $debate = array (
3119
                'contentcount'	=> $contentcount,
3120
                'body'			=> $body,
3121
                'hdate'			=> $hdate,
3122
                'list_url'		=> $list_url,
3123
                'totalcomments'	=> $totalcomments
3124
            );
3125
3126
            // If this is a subsection, we're going to prepend the title
3127
            // of the parent section, so let's get that.
3128
            if ($item_data['htype'] == 11) {
3129
3130
                $r = $this->db->query("SELECT body
3131
                                FROM	epobject
3132
                                WHERE	epobject_id = :epobject_id",
3133
                    array(':epobject_id' => $item_data['section_id']));
3134
                $debate['parent']['body'] = $r->field(0, 'body');
3135
            }
3136
3137
            $r = $this->db->query("SELECT e.body,
3138
                                    h.person_id, h.hdate, h.htime
3139
                            FROM    hansard h, epobject e
3140
                            WHERE   h.epobject_id = e.epobject_id
3141
                            AND     h.subsection_id = '" . $item_data['epobject_id'] . "'
3142
                            ORDER BY hpos
3143
                            LIMIT 1
3144
                            ");
3145
            $childbody = $r->field(0, 'body');
3146
            $speaker = $this->_get_speaker($r->field(0, 'person_id'), $r->field(0, 'hdate'), $r->field(0, 'htime'), $this->major );
3147
3148
            $debate['child'] = array(
3149
                'body' => $childbody,
3150
                'speaker' => $speaker
3151
            );
3152
3153
            $data[] = $debate;
3154
        }
3155
3156
        $data = array (
3157
            'info' => array(),
3158
            'data' => $data
3159
        );
3160
3161
        return $data;
3162
3163
    }
3164
3165
}
3166
3167
3168
class WRANSLIST extends HANSARDLIST {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
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...
3169
    public $major = 3;
3170
    public $listpage = 'wrans';
3171
    public $commentspage = 'wrans'; // We don't have a separate page for wrans comments.
3172
    public $gidprefix = 'uk.org.publicwhip/wrans/';
3173
3174
    public function total_questions() {
3175
        $q = $this->db->query("SELECT COUNT(*) AS count FROM hansard WHERE major = :major AND minor = 1", array(':major' => $this->major));
3176
        return $q->field(0, 'count');
3177
    }
3178
3179
    public function _get_data_by_recent_wrans ($args=array()) {
3180
        global $hansardmajors;
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...
3181
3182
        // $args['days'] is the number of days back to look for biggest debates.
3183
        // (1 by default)
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...
3184
        // $args['num'] is the number of links to return (1 by default).
3185
3186
        $data = array();
3187
3188
        $params = array();
3189
3190
        // Get the most recent day on which we have wrans.
3191
        $recentday = $this->most_recent_day();
3192
        if (!count($recentday))
3193
            return $data;
3194
3195
        if (!isset($args['days']) || !is_numeric($args['days'])) {
3196
            $args['days'] = 1;
3197
        }
3198
        if (!isset($args['num']) || !is_numeric($args['num'])) {
3199
            $args['num'] = 1;
3200
        }
3201
3202
        if ($args['num'] == 1) {
3203
            $datewhere = "h.hdate = :datewhere";
3204
            $params[':datewhere'] = $recentday['hdate'];
3205
        } else {
3206
            $firstdate = gmdate('Y-m-d', $recentday['timestamp'] - (86400 * $args['days']));
3207
            $datewhere = "h.hdate >= :firstdate AND h.hdate <= :hdate";
3208
            $params[':firstdate'] = $firstdate;
3209
            $params[':hdate'] = $recentday['hdate'];
3210
        }
3211
3212
3213
        // Get a random selection of subsections in wrans.
3214
        if ($hansardmajors[$this->major]['location'] == 'Scotland') {
3215
            $htype = 'htype = 10 and section_id = 0';
3216
        } else {
3217
            $htype = 'htype = 11 and section_id != 0';
3218
        }
3219
3220
        $params[':limit'] = $args['num'];
3221
        $params[':major'] = $this->major;
3222
3223
        $query = "SELECT e.body,
3224
                    h.hdate,
3225
                    h.htype,
3226
                    h.gid,
3227
                    h.subsection_id,
3228
                    h.section_id,
3229
                    h.epobject_id
3230
            FROM    hansard h, epobject e
3231
            WHERE   h.major = :major
3232
            AND     $htype
3233
            AND     subsection_id = 0
3234
            AND     $datewhere
3235
            AND     h.epobject_id = e.epobject_id
3236
            ORDER BY RAND()
3237
            LIMIT   :limit";
3238
3239
        $q = $this->db->query($query, $params);
3240
3241
        for ($row=0; $row<$q->rows; $row++) {
3242
            // This array just used for getting further data about this debate.
3243
            $item_data = array (
3244
                'major'			=> $this->major,
3245
                'gid'			=> fix_gid_from_db( $q->field($row, 'gid') ),
3246
                'htype'			=> $q->field($row, 'htype'),
3247
                'section_id'	=> $q->field($row, 'section_id'),
3248
                'subsection_id'	=> $q->field($row, 'subsection_id'),
3249
                'epobject_id'	=> $q->field($row, 'epobject_id')
3250
            );
3251
3252
            $list_url 		= $this->_get_listurl( $item_data );
3253
            $totalcomments	= $this->_get_comment_count_for_epobject( $item_data );
3254
3255
            $body 			= $q->field($row, 'body');
3256
            $hdate			= $q->field($row, 'hdate');
3257
3258
            // Get the parent section for this item.
3259
            $parentbody = '';
3260
            if ($q->field($row, 'section_id')) {
3261
                $r = $this->db->query("SELECT e.body
3262
                            FROM	hansard h, epobject e
3263
                            WHERE	h.epobject_id = e.epobject_id
3264
                            AND		h.epobject_id = '" . $q->field($row, 'section_id') . "'
3265
                            ");
3266
                $parentbody = $r->field(0, 'body');
3267
            }
3268
3269
            // Get the question for this item.
3270
            $r = $this->db->query("SELECT e.body,
3271
                                    h.person_id, h.hdate, h.htime
3272
                            FROM	hansard h, epobject e
3273
                            WHERE	h.epobject_id = e.epobject_id
3274
                            AND 	h.subsection_id = '" . $q->field($row, 'epobject_id') . "'
3275
                            ORDER BY hpos
3276
                            LIMIT 1
3277
                            ");
3278
            $childbody = $r->field(0, 'body');
3279
            $speaker = $this->_get_speaker($r->field(0, 'person_id'), $r->field(0, 'hdate'), $r->field(0, 'htime'), $this->major );
3280
3281
            $data[] = array (
3282
                'body'			=> $body,
3283
                'hdate'			=> $hdate,
3284
                'list_url'		=> $list_url,
3285
                'totalcomments'	=> $totalcomments,
3286
                'child'			=> array (
3287
                    'body'		=> $childbody,
3288
                    'speaker'	=> $speaker
3289
                ),
3290
                'parent'		=> array (
3291
                    'body'		=> $parentbody
3292
                )
3293
            );
3294
3295
        }
3296
3297
        $data = array (
3298
            'info' => array(),
3299
            'data' => $data
3300
        );
3301
3302
        return $data;
3303
3304
    }
3305
3306
}
3307
3308
class StandingCommittee extends DEBATELIST {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
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...
3309
    public $major = 6;
3310
    public $listpage = 'pbc_clause';
3311
    public $commentspage = 'pbc_speech';
3312
    public $gidprefix = 'uk.org.publicwhip/standing/';
3313
3314
    public function __construct($session='', $title='') {
3315
        parent::__construct();
3316
        $this->bill_title = $title;
3317
        $title = str_replace(' ', '_', $title);
3318
        $this->url = urlencode($session) . '/' . urlencode($title) . '/';
3319
    }
3320
3321
    public function _get_committee($bill_id) {
3322
        include_once INCLUDESPATH."easyparliament/member.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...
3323
        $q = $this->db->query(
3324
            'select count(*) as c from hansard
3325
                where major=6 and minor=:bill_id and htype=10',
3326
            array(':bill_id' => $bill_id)
3327
        );
3328
        $sittings = $q->field(0, 'c');
3329
        $q = $this->db->query(
3330
            'select person_id,sum(attending) as attending, sum(chairman) as chairman
3331
                from pbc_members
3332
                where bill_id = :bill_id group by person_id',
3333
            array(':bill_id' => $bill_id));
3334
        $comm = array('sittings' => $sittings, 'chairmen' => array(), 'members' => array());
3335
        for ($i=0; $i<$q->rows(); $i++) {
3336
            $person_id = $q->field($i, 'person_id');
3337
            $mp = new MEMBER(array('person_id'=>$person_id));
3338
            $attending = $q->field($i, 'attending');
3339
            $chairman = $q->field($i, 'chairman');
3340
            $arr = array(
3341
                'name' => $mp->full_name(),
3342
                'attending' => $attending,
3343
            );
3344
            if ($chairman) {
3345
                $comm['chairmen'][$person_id] = $arr;
3346
            } else {
3347
                $comm['members'][$person_id] = $arr;
3348
            }
3349
        }
3350
        return $comm;
3351
    }
3352
3353
    public function _get_data_by_bill($args) {
3354
        global $DATA, $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...
3355
        $data = array();
3356
        $input = array (
3357
            'amount' => array (
3358
                'body' => true,
3359
                'comment' => true,
3360
                'excerpt' => true
3361
            ),
3362
            'where' => array (
3363
                'htype=' => '10',
3364
                'major=' => $this->major,
3365
                'minor=' => $args['id'],
3366
            ),
3367
            'order' => 'hdate,hpos'
3368
        );
3369
        $sections = $this->_get_hansard_data($input);
3370
        if (count($sections) > 0) {
3371
            $data['rows'] = array();
3372
            $num_sections = count($sections);
3373
            for ($n=0; $n<$num_sections; $n++) {
3374
                $sectionrow = $sections[$n];
3375
                list($sitting, $part) = $this->_get_sitting($sectionrow['gid']);
3376
                $sectionrow['sitting'] = $sitting;
3377
                $sectionrow['part'] = $part;
3378
                $input = array (
3379
                    'amount' => array (
3380
                        'body' => true,
3381
                        'comment' => true,
3382
                        'excerpt' => true
3383
                    ),
3384
                    'where' => array (
3385
                        'section_id='	=> $sectionrow['epobject_id'],
3386
                        'htype='	=> '11',
3387
                        'major='	=> $this->major
3388
                    ),
3389
                    'order' => 'hpos'
3390
                );
3391
                $rows = $this->_get_hansard_data($input);
3392
                array_unshift ($rows, $sectionrow);
3393
                $data['rows'] = array_merge ($data['rows'], $rows);
3394
            }
3395
        }
3396
        $data['info']['bill'] = $args['title'];
3397
        $data['info']['major'] = $this->major;
3398
        $data['info']['committee'] = $this->_get_committee($args['id']);
3399
        $DATA->set_page_metadata($this_page, 'title', $args['title']);
3400
        return $data;
3401
    }
3402
3403
    public function _get_data_by_session($args) {
3404
        global $DATA, $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...
3405
        $session = $args['session'];
3406
        $q = $this->db->query(
3407
            'select id, title from bills where session = :session order by title',
3408
            array(':session' => $session)
3409
        );
3410
        $bills = array();
3411
        for ($i=0; $i<$q->rows(); $i++) {
3412
            $bills[$q->field($i, 'id')] = $q->field($i, 'title');
3413
        }
3414
        if (!count($bills)) {
3415
            return array();
3416
        }
3417
        $q = $this->db->query('select minor,count(*) as c from hansard where major=6 and htype=12
3418
            and minor in (' . join(',', array_keys($bills)) . ')
3419
            group by minor');
3420
        $counts = array();
3421
        # $comments = array();
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% 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...
3422
        for ($i=0; $i<$q->rows(); $i++) {
3423
            $minor = $q->field($i, 'minor');
3424
            $counts[$minor] = $q->field($i, 'c');
3425
            # $comments[$minor] = 0;
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% 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...
3426
        }
3427
        /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
62% 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...
3428
        $q = $this->db->query('select minor,epobject_id from hansard where major=6 and htype=10
3429
            and minor in (' . join(',', array_keys($bills)) . ')');
3430
        for ($i=0; $i<$q->rows(); $i++) {
3431
            $comments[$q->field($i, 'minor')] += $this->_get_comment_count_for_epobject(array(
3432
                'epobject_id' => $q->field($i, 'epobject_id'),
3433
                'htype' => 10,
3434
            ));
3435
        }
3436
        */
3437
        $data = array();
3438
        foreach ($bills as $id => $title) {
3439
            $data[] = array(
3440
                'title' => $title,
3441
                'url' => "/pbc/" . urlencode($session) . '/' . urlencode(str_replace(' ', '_', $title)) . '/',
3442
                'contentcount' => isset($counts[$id]) ? $counts[$id] : '???',
3443
                # 'totalcomments' => isset($comments[$id]) ? $comments[$id] : '???',
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% 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...
3444
            );
3445
        }
3446
3447
        $YEARURL = new \MySociety\TheyWorkForYou\Url('pbc_session');
3448
        $nextprev = array();
3449
        $nextprev['prev'] = array ('body' => 'Previous session', 'title'=>'');
3450
        $nextprev['next'] = array ('body' => 'Next session', 'title'=>'');
3451
        $q = $this->db->query(
3452
            "SELECT session FROM bills WHERE session < :session ORDER BY session DESC LIMIT 1",
3453
            array(':session' => $session)
3454
        );
3455
        $prevyear = $q->field(0, 'session');
3456
        $q = $this->db->query(
3457
            "SELECT session FROM bills WHERE session > :session ORDER BY session ASC LIMIT 1",
3458
            array(':session' => $session)
3459
        );
3460
        $nextyear = $q->field(0, 'session');
3461
        if ($prevyear) {
3462
            $nextprev['prev']['url'] = $YEARURL->generate() . $prevyear . '/';
3463
        }
3464
        if ($nextyear) {
3465
            $nextprev['next']['url'] = $YEARURL->generate() . $nextyear . '/';
3466
        }
3467
        $DATA->set_page_metadata($this_page, 'nextprev', $nextprev);
3468
3469
        return $data;
3470
    }
3471
3472
    public function _get_data_by_recent_pbc_debates($args) {
3473
        if (!isset($args['num'])) $args['num'] = 20;
3474
        $q = $this->db->query('select gid, minor, hdate from hansard
3475
            where htype=10 and major=6
3476
            order by hdate desc limit ' . $args['num']);
3477
        $data = array();
3478
        for ($i=0; $i<$q->rows(); $i++) {
3479
            $minor = $q->field($i, 'minor');
3480
            $gid = $q->field($i, 'gid');
3481
            $hdate = format_date($q->field($i, 'hdate'), LONGDATEFORMAT);
3482
            $qq = $this->db->query('select title, session from bills where id='.$minor);
3483
            $title = $qq->field(0, 'title');
3484
            $session = $qq->field(0, 'session');
3485
            list($sitting, $part) = $this->_get_sitting($gid);
3486
            $sitting_txt = make_ranking($sitting) . ' sitting';
3487
            if ($part>0) $sitting .= ", part $part";
3488
            $data[$hdate][] = array(
3489
                'bill'=> $title,
3490
                'sitting' => $sitting_txt,
3491
                'url' => "/pbc/$session/" . urlencode(str_replace(' ','_',$title)) . '/#sitting' . $sitting,
3492
            );
3493
        }
3494
        return $data;
3495
    }
3496
3497
    # Given a GID, parse out the sitting number and optional part from it
3498
    public function _get_sitting($gid) {
3499
        if (preg_match('#_(\d\d)-(\d)_#', $gid, $m))
3500
            return array($m[1]+0, $m[2]);
3501
        return array(0, 0);
3502
    }
3503
}
3504