Passed
Pull Request — master (#1700)
by Struan
04:10
created

HANSARDLIST::_get_listurl()   B

Complexity

Conditions 11
Paths 56

Size

Total Lines 95
Code Lines 46

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 58.4006

Importance

Changes 0
Metric Value
cc 11
eloc 46
nc 56
nop 3
dl 0
loc 95
rs 7.3166
c 0
b 0
f 0
ccs 11
cts 41
cp 0.2683
crap 58.4006

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
include_once INCLUDESPATH."easyparliament/searchengine.php";
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 { }
52
53
class HANSARDLIST {
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
    /*
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
    /*
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 things 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
131
        //	'd' => '2003-12-31' or
132
        //	's' => 'my search term'
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;
140
141
        if ($view == 'search' && (!defined('FRONT_END_SEARCH') || !FRONT_END_SEARCH)) {
142
            return false;
143
        }
144
145
        $validviews = array ('calendar', 'date', 'gid', 'person', 'search', 'recent', 'recent_mostvotes', 'biggest_debates', 'recent_wrans', 'recent_wms', 'column', 'mp', 'bill', 'session', 'recent_debates', 'recent_pbc_debates', 'featured_gid');
146
        if (in_array($view, $validviews)) {
147
148
            // What function do we call for this view?
149
            $function = '_get_data_by_'.$view;
150
            // Get all the data that's to be rendered.
151
            $data = $this->$function($args);
152
153
        } else {
154
            // Don't have a valid $view.
155
            $PAGE->error_message ("You haven't specified a view type.");
156
            return false;
157
        }
158
159
        // Set the values of this page's headings depending on the data we've fetched.
160
        if (isset($PAGE) && isset($data['info'])) {
161
            $PAGE->set_hansard_headings($data['info']);
162
        }
163
164
        // Glossary $view_override (to avoid too much code duplication...)
165
        if (isset($args['view_override'])) {
166
            $view = $args['view_override'];
167
        }
168
169
        $return = $this->render($view, $data, $format);
170
171
        return $return;
172
    }
173
174
175
176
    public function render($view, $data, $format='html') {
177
        // Once we have the data that's to be rendered,
178
        // include the template.
179
180
        // No format, so don't use the template sets.
181
        if ($format == 'none') {
182
            return $data;
183
        }
184
185
        include (INCLUDESPATH."easyparliament/templates/$format/hansard_$view" . ".php");
186
        return true;
187
188
    }
189
190
191
    public function total_items() {
192
        // Returns number of items in debates or wrans, depending on which class this is,
193
        // DEBATELIST or WRANSLIST.
194
195
        $q = $this->db->query("SELECT COUNT(*) AS count FROM hansard WHERE major = :major", array(':major' => $this->major));
196
197
        return $q->first()['count'];
198
    }
199
200
201
202 1
    public function most_recent_day() {
203
        // Very simple. Returns an array of stuff about the most recent data
204
        // for this major:
205
206
        // array (
207
        //		'hdate'		=> 'YYYY-MM-DD',
208
        //		'timestamp' => 124453679,
209
        //		'listurl'	=> '/foo/?id=bar'
210
        // )
211
212
        // When we do this function the first time we cache the
213
        // results in this variable. As it's an expensive query.
214 1
        if (isset($this->most_recent_day)) {
215
            return $this->most_recent_day;
216
        }
217
218
        // What we return.
219 1
        $data = array();
220
221 1
        $q = $this->db->query("SELECT MAX(hdate) AS hdate
222
                        FROM 	hansard
223
                        WHERE	major = :major
224 1
                        ", array(':major' => $this->major))->first();
225 1
        if ($q) {
226
227 1
            $hdate = $q['hdate'];
228 1
            if ($hdate) {
229 1
                $URL = new \MySociety\TheyWorkForYou\Url($this->listpage);
230 1
                $URL->insert( array('d'=>$hdate) );
231
232
                // Work out a timestamp which is handy for comparing to now.
233 1
                list($year, $month, $date) = explode('-', $hdate);
234 1
                $timestamp = gmmktime (0, 0, 0, $month, $date, $year);
0 ignored issues
show
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

234
                $timestamp = gmmktime (0, 0, 0, $month, /** @scrutinizer ignore-type */ $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

234
                $timestamp = gmmktime (0, 0, 0, $month, $date, /** @scrutinizer ignore-type */ $year);
Loading history...
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

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

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1107
                    continue;
1108
                }
1109
                $itemdata = array_pop($itemdata); # day
1110
                $itemdata = array_pop($itemdata); # chamber
1111
                $itemdata = array_pop($itemdata); # event
1112
1113
                # Ignore past events in places that we cover (we'll have the data from Hansard)
1114
                if ($itemdata['event_date'] < date('Y-m-d') &&
1115
                    in_array($itemdata['chamber'], array(
1116
                        'Commons: Main Chamber', 'Lords: Main Chamber',
1117
                        'Commons: Westminster Hall',
1118
                    ))) {
1119
                    continue;
1120
                }
1121
1122
                list($cal_item, $cal_meta) = \MySociety\TheyWorkForYou\Utility\Calendar::meta($itemdata);
1123
                $body = $this->prepare_search_result_for_display($cal_item) . '.';
1124
                if (!empty($cal_meta)) {
1125
                    $body .= ' <span class="future_meta">' . join('; ', $cal_meta) . '</span>';
1126
                }
1127
                if ($itemdata['witnesses']) {
1128
                    $body .= '<br><small>Witnesses: '
1129
                        . $this->prepare_search_result_for_display($itemdata['witnesses'])
1130
                        . '</small>';
1131
                }
1132
1133
                if ($itemdata['event_date'] >= date('Y-m-d')) {
1134
                    $title = 'Upcoming Business';
1135
                } else {
1136
                    $title = 'Previous Business';
1137
                }
1138
                $itemdata['gid']            = $id;
1139
                $itemdata['relevance']      = $relevancy;
1140
                $itemdata['parent']['body'] = $title . ' &#8211; ' . $itemdata['chamber'];
1141
                $itemdata['extract']        = $body;
1142
                $itemdata['listurl']        = '/calendar/?d=' . $itemdata['event_date'] . '#cal' . $itemdata['id'];
1143
                $itemdata['major']          = 'F';
1144
1145
            } else {
1146
1147
                $q = array('gid_to' => $gid);
1148
                do {
1149
                    $gid = $q['gid_to'];
1150
                    $q = $this->db->query("SELECT gid_to FROM gidredirect WHERE gid_from = :gid", array(':gid' => $gid))->first();
1151
                } while ($q);
1152
1153
                // Get the data for the gid from the database
1154
                $q = $this->db->query("SELECT hansard.gid, hansard.hdate,
1155
                    hansard.htime, hansard.section_id, hansard.subsection_id,
1156
                    hansard.htype, hansard.major, hansard.minor,
1157
                    hansard.person_id, hansard.hpos,
1158
                    epobject.epobject_id, epobject.body
1159
                FROM hansard, epobject
1160
                WHERE hansard.gid = :gid
1161
                    AND hansard.epobject_id = epobject.epobject_id"
1162
                , array(':gid' => $gid));
1163
1164
                if ($q->rows() > 1) {
1165
                    if ( $isset($args['exceptions']) ) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $isset seems to be never defined.
Loading history...
1166
                        throw new \Exception("Got more than one row getting data for $gid.");
1167
                    } else {
1168
                        $PAGE->error_message("Got more than one row getting data for $gid");
1169
                    }
1170
                }
1171
                if ($q->rows() == 0) {
1172
                    # This error message is totally spurious, so don't show it
1173
                    # $PAGE->error_message("Unexpected missing gid $gid while searching");
1174
                    continue;
1175
                }
1176
1177
                $itemdata = $q->first();
1178
                $itemdata['collapsed']  = $collapsed;
1179
                $itemdata['gid']        = fix_gid_from_db($itemdata['gid']);
1180
                $itemdata['relevance']  = $relevancy;
1181
                $itemdata['extract']    = $this->prepare_search_result_for_display($itemdata['body']);
1182
1183
                //////////////////////////
1184
                // 2. Create the URL to link to this bit of text.
1185
1186
                $id_data = array (
1187
                    'major'            => $itemdata['major'],
1188
                    'minor'            => $itemdata['minor'],
1189
                    'htype'         => $itemdata['htype'],
1190
                    'gid'             => $itemdata['gid'],
1191
                    'section_id'    => $itemdata['section_id'],
1192
                    'subsection_id'    => $itemdata['subsection_id']
1193
                );
1194
1195
                // We append the query onto the end of the URL as variable 's'
1196
                // so we can highlight them on the debate/wrans list page.
1197
                $url_args = array ('s' => $searchstring);
1198
1199
                $itemdata['listurl'] = $this->_get_listurl($id_data, $url_args, $encode);
1200
1201
                //////////////////////////
1202
                // 3. Get the speaker for this item, if applicable.
1203
                if ($itemdata['person_id'] != 0) {
1204
                    $itemdata['speaker'] = $this->_get_speaker($itemdata['person_id'], $itemdata['hdate'], $itemdata['htime'], $itemdata['major']);
1205
                }
1206
1207
                //////////////////////////
1208
                // 4. Get data about the parent (sub)section.
1209
                if ($itemdata['major'] && $hansardmajors[$itemdata['major']]['type'] == 'debate') {
1210
                    // Debate
1211
                    if ($itemdata['htype'] != 10) {
1212
                        $section = $this->_get_section($itemdata);
1213
                        $itemdata['parent']['body'] = $section['body'];
1214
#                        $itemdata['parent']['listurl'] = $section['listurl'];
1215
                        if ($itemdata['section_id'] != $itemdata['subsection_id']) {
1216
                            $subsection = $this->_get_subsection($itemdata);
1217
                            $itemdata['parent']['body'] .= ': ' . $subsection['body'];
1218
#                            $itemdata['parent']['listurl'] = $subsection['listurl'];
1219
                        }
1220
                        if ($itemdata['major'] == 5) {
1221
                            $itemdata['parent']['body'] = gettext('Northern Ireland Assembly') . ': ' . $itemdata['parent']['body'];
1222
                        } elseif ($itemdata['major'] == 6) {
1223
                            $itemdata['parent']['body'] = gettext('Public Bill Committee') . ': ' . $itemdata['parent']['body'];
1224
                        } elseif ($itemdata['major'] == 7) {
1225
                            $itemdata['parent']['body'] = gettext('Scottish Parliament') . ': ' . $itemdata['parent']['body'];
1226
                        }
1227
1228
                    } else {
1229
                        // It's a section, so it will be its own title.
1230
                        $itemdata['parent']['body'] = $itemdata['body'];
1231
                        $itemdata['body'] = '';
1232
                    }
1233
1234
                } else {
1235
                    // Wrans or WMS
1236
                    $section = $this->_get_section($itemdata);
1237
                    $subsection = $this->_get_subsection($itemdata);
1238
                    $body = $hansardmajors[$itemdata['major']]['title'] . ' &#8212; ';
1239
                    if (isset($section['body'])) {
1240
                        $body .= $section['body'];
1241
                    }
1242
                    if (isset($subsection['body'])) {
1243
                        $body .= ': ' . $subsection['body'];
1244
                    }
1245
                    if (isset($subsection['listurl'])) {
1246
                        $listurl = $subsection['listurl'];
1247
                    } else {
1248
                        $listurl = '';
1249
                    }
1250
                    $itemdata['parent'] = array (
1251
                        'body' => $body,
1252
                        'listurl' => $listurl
1253
                    );
1254
                    if ($itemdata['htype'] == 11) {
1255
                        # Search result was a subsection heading; fetch the first entry
1256
                        # from the wrans/wms to show under the heading
1257
                        $input = array (
1258
                            'amount' => array(
1259
                                'body' => true,
1260
                                'speaker' => true
1261
                            ),
1262
                            'where' => array(
1263
                                'hansard.subsection_id=' => $itemdata['epobject_id']
1264
                            ),
1265
                            'order' => 'hpos ASC',
1266
                            'limit' => 1
1267
                        );
1268
                        $ddata = $this->_get_hansard_data($input);
1269
                        if (count($ddata)) {
1270
                            $itemdata['body'] = $ddata[0]['body'];
1271
                            $itemdata['extract'] = $this->prepare_search_result_for_display($ddata[0]['body']);
1272
                            $itemdata['person_id'] = $ddata[0]['person_id'];
1273
                            if ($itemdata['person_id']) {
1274
                                $itemdata['speaker'] = $this->_get_speaker($itemdata['person_id'], $itemdata['hdate'], $itemdata['htime'], $itemdata['major']);
1275
                            }
1276
                        }
1277
                    } elseif ($itemdata['htype'] == 10) {
1278
                        $itemdata['body'] = '';
1279
                        $itemdata['extract'] = '';
1280
                    }
1281
                }
1282
1283
            } // End of handling non-calendar search result
1284
1285
            $rows[] = $itemdata;
1286
        }
1287
1288
        $data['rows'] = $rows;
1289
        return $data;
1290
    }
1291
1292
    public function prepare_search_result_for_display($body) {
1293
        global $SEARCHENGINE;
1294
        // We want to trim the body to an extract that is centered
1295
        // around the position of the first search word.
1296
1297
        // we don't use strip_tags as it doesn't replace tags with spaces,
1298
        // which means some words end up stuck together
1299
        $extract = strip_tags_tospaces($body);
1300
1301
        // $bestpos is the position of the first search word
1302
        $bestpos = $SEARCHENGINE->position_of_first_word($extract);
1303
1304
        // Where do we want to extract from the $body to start?
1305
        $length_of_extract = 400; // characters.
1306
        $startpos = $bestpos - ($length_of_extract / 2);
1307
        if ($startpos < 0) {
1308
            $startpos = 0;
1309
        }
1310
1311
        // Trim it to length and position, adding ellipses.
1312
        // Decode HTML entities so position matches up.
1313
        $extract = trim_characters(html_entity_decode($extract), $startpos, $length_of_extract);
1314
1315
        // Highlight search words
1316
        $extract = mb_encode_numericentity( htmlentities($extract, ENT_QUOTES, 'UTF-8'), [0x80, 0x10FFFF, 0, ~0], 'UTF-8');
1317
        $extract = $SEARCHENGINE->highlight($extract);
1318
1319
        return $extract;
1320
    }
1321
1322
    public function _get_data_by_calendar($args) {
1323
        // We should have come here via _get_data_by_calendar() in
1324
        // DEBATELIST or WRANLIST, so $this->major should now be set.
1325
1326
        // You can ask for:
1327
        // * The most recent n months - $args['months'] => n
1328
        // * All months from one year - $args['year'] => 2004
1329
        // * One month - $args['year'] => 2004, $args['month'] => 8
1330
        // * The months from this year so far (no $args variables needed).
1331
1332
        // $args['onday'] may be like '2004-04-20' - if it appears in the
1333
        // calendar, this date will be highlighted and will have no link.
1334
1335
        // Returns a data structure of years, months and dates:
1336
        // $data = array(
1337
        // 		'info' => array (
1338
        //			'page' => 'debates',
1339
        //			'major'	=> 1
1340
        //			'onpage' => '2004-02-01'
1341
        //		),
1342
        // 		'years' => array (
1343
        //			'2004' => array (
1344
        //				'01' => array ('01', '02', '03' ... '31'),
1345
        //				'02' => etc...
1346
        //			)
1347
        //		)
1348
        // )
1349
        // It will just have entries for days for which we have relevant
1350
        // hansard data.
1351
        // But months that have no data will still have a month array (empty).
1352
1353
        // $data['info'] may have 'prevlink' => '/debates/?y=2003' or something
1354
        // if we're viewing recent months.
1355
1356
        global $DATA, $this_page, $PAGE, $hansardmajors;
1357
1358
        // What we return.
1359
        $data = array(
1360
            'info' => array(
1361
                'page' => $this->listpage,
1362
                'major' => $this->major
1363
            )
1364
        );
1365
1366
        // Set a variable so we know what we're displaying...
1367
        if (isset($args['months']) && is_numeric($args['months'])) {
1368
1369
            // A number of recent months (may wrap around to previous year).
1370
            $action = 'recentmonths';
1371
1372
            // A check to prevent anyone requestion 500000 months.
1373
            if ($args['months'] > 12) {
1374
                $PAGE->error_message("Sorry, you can't view " . $args['months'] . " months.");
1375
                return $data;
1376
            }
1377
1378
        } elseif (isset($args['year']) && is_numeric($args['year'])) {
1379
1380
            if (isset($args['month']) && is_numeric($args['month'])) {
1381
                // A particular month.
1382
                $action = 'month';
1383
            } else {
1384
                // A single year.
1385
                $action = 'year';
1386
            }
1387
1388
        } else {
1389
            // The year to date so far.
1390
            $action = 'recentyear';
1391
        }
1392
1393
        if (isset($args['onday'])) {
1394
            // Will be highlighted.
1395
            $data['info']['onday'] = $args['onday'];
1396
        }
1397
1398
        // This first if/else section is simply to fill out these variables:
1399
1400
        if ($action == 'recentmonths' || $action == 'recentyear') {
1401
1402
            // We're either getting the most recent $args['months'] data
1403
            // Or the most recent year's data.
1404
            // (Not necessarily recent to *now* but compared to the most
1405
            // recent date for which we have relevant hansard data.)
1406
            // 'recentyear' will include all the months that haven't happened yet.
1407
1408
            // Find the most recent date we have data for.
1409
            $q = $this->db->query("SELECT MAX(hdate) AS hdate
1410
                            FROM	hansard
1411
                            WHERE	major = :major",
1412
                array(':major' => $this->major))->first();
1413
1414
            if ($q && $q['hdate'] != NULL) {
1415
                $recentdate = $q['hdate'];
1416
            } else {
1417
                $PAGE->error_message("Couldn't find the most recent date");
1418
                return $data;
1419
            }
1420
1421
            // What's the first date of data we need to fetch?
1422
            list($finalyear, $finalmonth, $day) = explode('-', $recentdate);
1423
1424
            $finalyear = intval($finalyear);
1425
            $finalmonth = intval($finalmonth);
1426
1427
            if ($action == 'recentmonths') {
1428
1429
                // We're getting this many recent months.
1430
                $months_to_fetch = $args['months'];
1431
1432
                // The month we need to start getting data.
1433
                $firstmonth = intval($finalmonth) - $months_to_fetch + 1;
1434
1435
                $firstyear = $finalyear;
1436
1437
                if ($firstmonth < 1) {
1438
                    // Wrap round to previous year.
1439
                    $firstyear--;
1440
                    // $firstmonth is negative, hence the '+'.
1441
                    $firstmonth = 12 + $firstmonth; // ()
1442
                };
1443
1444
            } else {
1445
                // $action == 'recentyear'
1446
1447
                // Get the most recent year's results.
1448
                $firstyear = $finalyear;
1449
                $firstmonth = 1;
1450
            }
1451
1452
1453
1454
        } else {
1455
            // $action == 'year' or 'month'.
1456
1457
            $firstyear = $args['year'];
1458
            $finalyear = $args['year'];
1459
1460
            if ($action == 'month') {
1461
                $firstmonth = intval($args['month']);
1462
                $finalmonth = intval($args['month']);
1463
            } else {
1464
                $firstmonth = 1;
1465
                $finalmonth = 12;
1466
            }
1467
1468
            $params = array(
1469
                ':firstdate' => $firstyear . '-' . $firstmonth . '-01',
1470
                ':finaldate' => $finalyear . '-' . $finalmonth . '-31');
1471
1472
            // Check there are some dates for this year/month.
1473
            $q = $this->db->query("SELECT epobject_id
1474
                            FROM	hansard
1475
                            WHERE	hdate >= :firstdate
1476
                            AND 	hdate <= :finaldate
1477
                            LIMIT 	1
1478
                            ", $params);
1479
1480
            if ($q->rows() == 0) {
1481
                // No data in db, so return empty array!
1482
                return $data;
1483
            }
1484
1485
        }
1486
1487
        // OK, Now we have $firstyear, $firstmonth, $finalyear, $finalmonth set up.
1488
1489
        // Get the data...
1490
1491
        $where = '';
1492
        $params = array();
1493
1494
        if ($finalyear > $firstyear || $finalmonth >= $firstmonth) {
1495
            $params[':finaldate'] = $finalyear . '-' . $finalmonth . '-31';
1496
            $where = 'AND hdate <= :finaldate';
1497
        }
1498
1499
        $params[':major'] = $this->major;
1500
        $params[':firstdate'] = $firstyear . '-' . $firstmonth . '-01';
1501
        $q =  $this->db->query("SELECT 	DISTINCT(hdate) AS hdate
1502
                        FROM		hansard
1503
                        WHERE		major = :major
1504
                        AND			hdate >= :firstdate
1505
                        $where
1506
                        ORDER BY	hdate ASC
1507
                        ", $params);
1508
1509
        if ($q->rows() > 0) {
1510
1511
            // We put the data in this array. See top of function for the structure.
1512
            $years = array();
1513
1514
            foreach ($q as $row) {
1515
                list($year, $month, $day) = explode('-', $row['hdate']);
1516
1517
                $month = intval($month);
1518
                $day = intval($day);
1519
1520
                // Add as a link.
1521
                $years[$year][$month][] = $day;
1522
            }
1523
1524
            // If nothing happened on one month we'll have fetched nothing for it.
1525
            // So now we need to fill in any gaps with blank months.
1526
1527
            // We cycle through every year and month we're supposed to have fetched.
1528
            // If it doesn't have an array in $years, we create an empty one for that
1529
            // month.
1530
            for ($y = $firstyear; $y <= $finalyear; $y++) {
1531
1532
                if (!isset($years[$y])) {
1533
                    $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());
1534
                } else {
1535
1536
                    // This year is set. Check it has all the months...
1537
1538
                    $minmonth = $y == $firstyear ? $firstmonth : 1;
1539
                    $maxmonth = $y == $finalyear ? $finalmonth : 12;
1540
1541
                    for ($m = $minmonth; $m <= $maxmonth; $m++) {
1542
                        if (!isset($years[$y][$m])) {
1543
                            $years[$y][$m] = array();
1544
                        }
1545
                    }
1546
                    ksort($years[$y]);
1547
1548
                }
1549
            }
1550
1551
            $data['years'] = $years;
1552
        }
1553
1554
        // Set the next/prev links.
1555
1556
        $YEARURL = new \MySociety\TheyWorkForYou\Url($hansardmajors[$this->major]['page_year']);
1557
1558
        if (substr($this_page, -4) == 'year') {
1559
            // Only need next/prev on these pages.
1560
            // Not sure this is the best place for this, but...
1561
1562
            $nextprev = array();
1563
1564
            if ($action == 'recentyear') {
1565
                // Assuming there will be a previous year!
1566
1567
                $YEARURL->insert(array('y'=> $firstyear-1));
1568
1569
                $nextprev['prev'] = array (
1570
                    'body' => 'Previous year',
1571
                    'title' => $firstyear - 1,
1572
                    'url' => $YEARURL->generate()
1573
                );
1574
1575
            } else {
1576
                // action is 'year'.
1577
1578
                $nextprev['prev'] = array ('body' => 'Previous year');
1579
                $nextprev['next'] = array ('body' => 'Next year');
1580
1581
                $q = $this->db->query("SELECT DATE_FORMAT(hdate, '%Y') AS year
1582
                            FROM hansard WHERE major = :major
1583
                            AND year(hdate) < :firstyear
1584
                            ORDER BY hdate DESC
1585
                            LIMIT 1", array(
1586
                                ':major' => $this->major,
1587
                                ':firstyear' => $firstyear
1588
                            ))->first();
1589
                if ($action == 'year' && $q) {
1590
                    $YEARURL->insert(array('y' => $q['year']));
1591
                    $nextprev['prev']['title'] = $q['year'];
1592
                    $nextprev['prev']['url'] = $YEARURL->generate();
1593
                }
1594
1595
                $q = $this->db->query("SELECT DATE_FORMAT(hdate, '%Y') AS year
1596
                            FROM hansard WHERE major = :major
1597
                            AND year(hdate) > :finalyear
1598
                            ORDER BY hdate
1599
                            LIMIT 1", array(
1600
                                ':major' => $this->major,
1601
                                ':finalyear' => $finalyear
1602
                            ))->first();
1603
                if ($q) {
1604
                    $YEARURL->insert(array('y' => $q['year']));
1605
                    $nextprev['next']['title'] = $q['year'];
1606
                    $nextprev['next']['url'] = $YEARURL->generate();
1607
                }
1608
            }
1609
1610
            // Will be used in $PAGE.
1611
            $DATA->set_page_metadata($this_page, 'nextprev', $nextprev);
1612
        }
1613
1614
        return $data;
1615
1616
    }
1617
1618
    public function _get_mentions($spid) {
1619
        $q = $this->db->query("select gid, type, date, url, mentioned_gid
1620
            from mentions where gid like 'uk.org.publicwhip/spq/$spid'
1621
            order by date, type");
1622
        $result = $q->fetchAll();
1623
        return $result;
1624
    }
1625
1626 3
    protected function _get_hansard_data($input) {
1627 3
        global $hansardmajors, $MEMBER;
1628
1629
        // Generic function for getting hansard data from the DB.
1630
        // It returns an empty array if no data was found.
1631
        // It returns an array of items if 1 or more were found.
1632
        // Each item is an array of key/value pairs.
1633
        // eg:
1634
        /*
1635
            array (
1636
                0	=> array (
1637
                    'epobject_id'	=> '2',
1638
                    'htype'			=> '10',
1639
                    'section_id'		=> '0',
1640
                    etc...
1641
                ),
1642
                1	=> array (
1643
                    'epobject_id'	=> '3',
1644
                    etc...
1645
                )
1646
            );
1647
        */
1648
1649
        // $input['amount'] is an associative array indicating what data should be fetched.
1650
        // It has the structure
1651
        // 	'key' => true
1652
        // Where 'true' indicates the data of type 'key' should be fetched.
1653
        // Leaving a key/value pair out is the same as setting a key to false.
1654
1655
        // $input['amount'] can have any or all these keys:
1656
        //	'body' 		- Get the body text from the epobject table.
1657
        //	'comment' 	- Get the first comment (and totalcomments count) for this item.
1658
        //	'votes'		- Get the user votes for this item.
1659
        //	'speaker'	- Get the speaker for this item, where applicable.
1660
        //  'excerpt' 	- For sub/sections get the body text for the first item within them.
1661
1662
        // $input['wherearr'] is an associative array of stuff for the WHERE clause, eg:
1663
        // 	array ('id=' => '37', 'date>' => '2003-12-31');
1664
        // $input['order'] is a string for the $order clause, eg 'hpos DESC'.
1665
        // $input['limit'] as a string for the $limit clause, eg '21,20'.
1666
1667 3
        $amount 		= isset($input['amount']) ? $input['amount'] : array();
1668 3
        $wherearr 		= isset($input['where']) ? $input['where'] : array();
1669 3
        $order 			= isset($input['order']) ? $input['order'] : '';
1670 3
        $limit 			= isset($input['limit']) ? $input['limit'] : '';
1671
1672
1673
        // The fields to fetch from db. 'table' => array ('field1', 'field2').
1674
        $fieldsarr = array (
1675 3
            'hansard' => array ('epobject_id', 'htype', 'gid', 'hpos', 'section_id', 'subsection_id', 'hdate', 'htime', 'source_url', 'major', 'minor', 'colnum')
1676
        );
1677
1678 3
        $params = array();
1679
1680 3
        if (isset($amount['speaker']) && $amount['speaker'] == true) {
1681 2
            $fieldsarr['hansard'][] = 'person_id';
1682
        }
1683
1684 3
        if ((isset($amount['body']) && $amount['body'] == true) ||
1685 3
            (isset($amount['comment']) && $amount['comment'] == true)
1686
            ) {
1687 3
            $fieldsarr['epobject'] = array ('body');
1688 3
            $join = 'LEFT OUTER JOIN epobject ON hansard.epobject_id = epobject.epobject_id';
1689
        } else {
1690
            $join = '';
1691
        }
1692
1693
1694 3
        $fieldsarr2 = array ();
1695
        // Construct the $fields clause.
1696 3
        foreach ($fieldsarr as $table => $tablesfields) {
1697 3
            foreach ($tablesfields as $n => $field) {
1698 3
                $fieldsarr2[] = $table.'.'.$field;
1699
            }
1700
        }
1701 3
        $fields = implode(', ', $fieldsarr2);
1702
1703 3
        $wherearr2 = array ();
1704
        // Construct the $where clause.
1705 3
        $i = 0;
1706 3
        foreach ($wherearr as $key => $val) {
1707 3
            $params[":where$i"] = $val;
1708 3
            $wherearr2[] = "$key :where$i";
1709 3
            $i++;
1710
        }
1711 3
        $where = implode (" AND ", $wherearr2);
1712
1713
1714 3
        if ($order != '') {
1715
            # You can't use parameters for order by clauses
1716 2
            $order_by_clause = "ORDER BY $order";
1717
        } else {
1718 2
            $order_by_clause = '';
1719
        }
1720
1721 3
        if ($limit != '') {
1722 1
            $params[':limit'] = $limit;
1723 1
            $limit = "LIMIT :limit";
1724
        } else {
1725 3
            $limit = '';
1726
        }
1727
1728
        // Finally, do the query!
1729 3
        $q = $this->db->query ("SELECT $fields
1730
                        FROM 	hansard
1731 3
                        $join
1732 3
                        WHERE $where
1733 3
                        $order_by_clause
1734 3
                        $limit
1735
                        ", $params);
1736
1737
        // Format the data into an array for returning.
1738 3
        $data = array();
1739 3
        foreach ($q as $row) {
1740
            // Where we'll store the data for this item before adding
1741
            // it to $data.
1742 3
            $item = array();
1743
1744
            // Put each row returned into its own array in $data.
1745 3
            foreach ($fieldsarr as $table => $tablesfields) {
1746 3
                foreach ($tablesfields as $m => $field) {
1747 3
                    $item[$field] = $row[$field];
1748
                }
1749
            }
1750
1751 3
            if (isset($item['gid'])) {
1752
                // Remove the "uk.org.publicwhip/blah/" from the gid:
1753
                // (In includes/utility.php)
1754 3
                $item['gid'] = fix_gid_from_db( $item['gid'] );
1755
            }
1756
1757
            // Add mentions if (a) it's a question in the written
1758
            // answer section or (b) it's in the official reports
1759
            // and the body text ends in a bracketed SPID.
1760 3
            if (($this->major && $hansardmajors[$this->major]['page']=='spwrans') && ($item['htype'] == '12' && $item['minor'] == '1')) {
1761
                // Get out the SPID:
1762
                if ( preg_match('#\d{4}-\d\d-\d\d\.(.*?)\.q#', $item['gid'], $m) ) {
1763
                    $item['mentions'] = $this->_get_mentions($m[1]);
1764
                }
1765
            }
1766
1767
            // The second case (b):
1768 3
            if (($this->major && $hansardmajors[$this->major]['page']=='spdebates') && isset($item['body'])) {
1769
                $stripped_body = preg_replace('/<[^>]+>/ms','',$item['body']);
1770
                if ( preg_match('/\((S\d+\w+-\d+)\)/ms',$stripped_body,$m) ) {
1771
                    $item['mentions'] = $this->_get_mentions($m[1]);
1772
                }
1773
            }
1774
1775 3
            if (in_array($item['epobject_id'], [15674958, 15674959, 12822764, 12822765])) {
1776
                global $DATA, $this_page;
1777
                $DATA->set_page_metadata($this_page, 'robots', 'noindex');
1778
            }
1779
1780
            // Get the number of items within a section or subsection.
1781
            // It could be that we can do this in the main query?
1782
            // Not sure.
1783 3
            if ( ($this->major && $hansardmajors[$this->major]['type']=='debate') && ($item['htype'] == '10' || $item['htype'] == '11') ) {
1784
1785 3
                if ($item['htype'] == '10') {
1786
                    // Section - get a count of items within this section that
1787
                    // don't have a subsection heading.
1788 3
                    $where = "section_id = '" . $item['epobject_id'] . "'
1789 3
                        AND subsection_id = '" . $item['epobject_id'] . "'";
1790
1791
                } else {
1792
                    // Subsection - get a count of items within this subsection.
1793 1
                    $where = "subsection_id = '" . $item['epobject_id'] . "'";
1794
                }
1795
1796 3
                $r = $this->db->query("SELECT COUNT(*) AS count
1797
                                FROM 	hansard
1798 3
                                WHERE 	$where
1799
                                AND htype = 12
1800 3
                                ")->first();
1801
1802 3
                if ($r) {
1803 3
                    $item['contentcount'] = $r['count'];
1804
                } else {
1805
                    $item['contentcount'] = '0';
1806
                }
1807
            }
1808
1809
            // Get the body of the first item with the section or
1810
            // subsection. This can then be printed as an excerpt
1811
            // on the daily list pages.
1812
1813 3
            if ((isset($amount['excerpt']) && $amount['excerpt'] == true) &&
1814 1
                ($item['htype'] == '10' ||
1815 3
                $item['htype'] == '11')
1816
                ) {
1817 1
                $params = array(':epobject_id' => $item['epobject_id']);
1818 1
                if ($item['htype'] == '10') {
1819 1
                    $where = 'hansard.section_id = :epobject_id
1820
                        AND hansard.subsection_id = :epobject_id';
1821
                } elseif ($item['htype'] == '11') {
1822
                    $where = 'hansard.subsection_id = :epobject_id';
1823
                }
1824
1825 1
                $r = $this->db->query("SELECT epobject.body
1826
                                FROM 	hansard,
1827
                                        epobject
1828 1
                                WHERE	$where
1829
                                AND		hansard.epobject_id = epobject.epobject_id
1830
                                ORDER BY hansard.hpos ASC
1831 1
                                LIMIT	1", $params)->first();
1832
1833 1
                if ($r) {
1834
                    $item['excerpt'] = $r['body'];
1835
                }
1836
            }
1837
1838 3
            if ($item['htype'] == 14) {
1839
                $divisions = new MySociety\TheyWorkForYou\Divisions();
1840
                $division_votes = $divisions->getDivisionByGid($this->gidprefix . $item['gid']);
1841
                $item['division'] = $division_votes;
1842
                # Don't want MP vote on PBC pages
1843
                if (isset($MEMBER) && $this->major != 6) {
1844
                    $item['mp_vote'] = $divisions->getDivisionResultsForMember($division_votes['division_id'], $MEMBER->person_id());
1845
                    if (!$item['mp_vote']) {
1846
                        if ($division_votes['date'] < $MEMBER->entered_house($division_votes['house_number'])['date']) {
1847
                            $item['before_mp'] = true;
1848
                        } else if ($division_votes['date'] > $MEMBER->left_house($division_votes['house_number'])['date']) {
1849
                            $item['after_mp'] = true;
1850
                        }
1851
                    }
1852
                }
1853
            }
1854
1855
1856
            // We generate two permalinks for each item:
1857
            // 'listurl' is the URL of the item in the full list view.
1858
            // 'commentsurl' is the URL of the item on its own page, with comments.
1859
1860
            // All the things we need to work out a listurl!
1861
            $item_data = array (
1862 3
                'major'			=> $this->major,
1863 3
                'minor' 		=> $item['minor'],
1864 3
                'htype' 		=> $item['htype'],
1865 3
                'gid' 			=> $item['gid'],
1866 3
                'section_id'	=> $item['section_id'],
1867 3
                'subsection_id'	=> $item['subsection_id']
1868
            );
1869
1870
1871 3
            $item['listurl'] = $this->_get_listurl($item_data);
1872
1873
1874
            // Create a URL for where we can see all the comments for this item.
1875 3
            if (isset($this->commentspage)) {
1876
                $COMMENTSURL = new \MySociety\TheyWorkForYou\Url($this->commentspage);
1877
                if ($this->major == 6) {
1878
                    # Another hack...
1879
                    $COMMENTSURL->remove(array('id'));
1880
                    $id = preg_replace('#^.*?_.*?_#', '', $item['gid']);
1881
                    $fragment = $this->url . $id;
1882
                    $item['commentsurl'] = $COMMENTSURL->generate() . $fragment;
1883
                } else {
1884
                    $COMMENTSURL->insert(array('id' => $item['gid']));
1885
                    $item['commentsurl'] = $COMMENTSURL->generate();
1886
                }
1887
            }
1888
1889
            // Get the user/anon votes items that have them.
1890 3
            if (($this->major == 3 || $this->major == 8) && (isset($amount['votes']) && $amount['votes'] == true) &&
1891 3
                $item['htype'] == '12') {
1892
                // Debate speech or written answers (not questions).
1893
1894
                $item['votes'] = $this->_get_votes( $item['epobject_id'] );
1895
            }
1896
1897
            // Get the speaker for this item, if applicable.
1898 3
            if ( (isset($amount['speaker']) && $amount['speaker'] == true) &&
1899 3
                $item['person_id'] != '') {
1900
1901 2
                $item['speaker'] = $this->_get_speaker($item['person_id'], $item['hdate'], $item['htime'], $item['major']);
1902
            }
1903
1904
1905
            // Get comment count and (if any) most recent comment for each item.
1906 3
            if (isset($amount['comment']) && $amount['comment'] == true) {
1907
1908
                // All the things we need to get the comment data.
1909
                $item_data = array (
1910 3
                    'htype' => $item['htype'],
1911 3
                    'epobject_id' => $item['epobject_id']
1912
                );
1913
1914 3
                $commentdata = $this->_get_comment($item_data);
1915 3
                $item['totalcomments'] = $commentdata['totalcomments'];
1916 3
                $item['comment'] = $commentdata['comment'];
1917
            }
1918
1919
1920
            // Add this item on to the array of items we're returning.
1921 3
            $data[] = $item;
1922
        }
1923
1924 3
        return $data;
1925
    }
1926
1927
1928
    public function _get_votes($epobject_id) {
1929
        // Called from _get_hansard_data().
1930
        // Separated out here just for clarity.
1931
        // Returns an array of user and anon yes/no votes for an epobject.
1932
1933
        $votes = array();
1934
1935
        // YES user votes.
1936
        $q = $this->db->query("SELECT COUNT(vote) as totalvotes
1937
                        FROM	uservotes
1938
                        WHERE	epobject_id = :epobject_id
1939
                        AND 	vote = '1'
1940
                        GROUP BY epobject_id", array(':epobject_id' => $epobject_id))->first();
1941
1942
        if ($q) {
1943
            $votes['user']['yes'] = $q['totalvotes'];
1944
        } else {
1945
            $votes['user']['yes'] = '0';
1946
        }
1947
1948
        // NO user votes.
1949
        $q = $this->db->query("SELECT COUNT(vote) as totalvotes
1950
                        FROM	uservotes
1951
                        WHERE	epobject_id = :epobject_id
1952
                        AND 	vote = '0'
1953
                        GROUP BY epobject_id", array(':epobject_id' => $epobject_id))->first();
1954
1955
        if ($q) {
1956
            $votes['user']['no'] = $q['totalvotes'];
1957
        } else {
1958
            $votes['user']['no'] = '0';
1959
        }
1960
1961
1962
        // Get the anon votes for each item.
1963
1964
        $q = $this->db->query("SELECT yes_votes,
1965
                                no_votes
1966
                        FROM	anonvotes
1967
                        WHERE	epobject_id = :epobject_id",
1968
            array(':epobject_id' => $epobject_id))->first();
1969
1970
        if ($q) {
1971
            $votes['anon']['yes'] = $q['yes_votes'];
1972
            $votes['anon']['no'] = $q['no_votes'];
1973
        } else {
1974
            $votes['anon']['yes'] = '0';
1975
            $votes['anon']['no'] = '0';
1976
        }
1977
1978
        return $votes;
1979
    }
1980
1981
1982 4
    public function _get_listurl ($id_data, $url_args=array(), $encode='html') {
1983 4
        global $hansardmajors;
1984
        // Generates an item's listurl - this is the 'contextual' url
1985
        // for an item, in the full list view with an anchor (if appropriate).
1986
1987
        // $id_data is like this:
1988
        //		$id_data = array (
1989
        //		'major' 		=> 1,
1990
        //		'htype' 		=> 12,
1991
        //		'gid' 			=> 2003-10-30.421.4h2,
1992
        //		'section_id'	=> 345,
1993
        //		'subsection_id'	=> 346
1994
        // );
1995
1996
        // $url_args is an array of other key/value pairs to be appended in the GET string.
1997 4
        if ($id_data['major']) {
1998 4
            $LISTURL = new \MySociety\TheyWorkForYou\Url($hansardmajors[$id_data['major']]['page_all']);
1999
        } else {
2000
            $LISTURL = new \MySociety\TheyWorkForYou\Url('wrans');
2001
        }
2002
2003 4
        $fragment = '';
2004
2005 4
        if ($id_data['htype'] == '11' || $id_data['htype'] == '10') {
2006 4
            if ($this->major == 6) {
2007
                $id = preg_replace('#^.*?_.*?_#', '', $id_data['gid']);
2008
                global $DATA;
2009
                $DATA->set_page_metadata('pbc_clause', 'url', 'pbc/' . $this->url . $id);
2010
                $LISTURL->remove(array('id'));
2011
            } else {
2012 4
                $LISTURL->insert( array( 'id' => $id_data['gid'] ) );
2013
            }
2014
        } else {
2015
            // A debate speech or question/answer, etc.
2016
            // We need to get the gid of the parent (sub)section for this item.
2017
            // We use this with the gid of the item itself as an #anchor.
2018
2019
            $parent_epobject_id = $id_data['subsection_id'];
2020
            $minor = $id_data['minor'];
2021
2022
            // Find the gid of this item's (sub)section.
2023
            $parent_gid = '';
2024
2025
            if (isset($this->epobjectid_to_gid[ $parent_epobject_id ])) {
2026
                // We've previously cached the gid for this epobject_id, so use that.
2027
2028
                $parent_gid = $this->epobjectid_to_gid[ $parent_epobject_id ];
2029
2030
            } else {
2031
                // We haven't cached the gid, so fetch from db.
2032
2033
                $r = $this->db->query("SELECT gid
2034
                                FROM 	hansard
2035
                                WHERE	epobject_id = :epobject_id",
2036
                    array(':epobject_id' => $parent_epobject_id))->first();
2037
2038
                if ($r) {
2039
                    // Remove the "uk.org.publicwhip/blah/" from the gid:
2040
                    // (In includes/utility.php)
2041
                    $parent_gid = fix_gid_from_db($r['gid']);
2042
2043
                    // Cache it for if we need it again:
2044
                    $this->epobjectid_to_gid[ $parent_epobject_id ] = $parent_gid;
2045
                }
2046
            }
2047
2048
            if ($parent_gid != '') {
2049
                // We have a gid so add to the URL.
2050
                if ($id_data['major'] == 6) {
2051
                    if (isset($this->bill_lookup[$minor])) {
2052
                        list($title, $session) = $this->bill_lookup[$minor];
2053
                    } else {
2054
                        $qq = $this->db->query('select title, session from bills where id=' . $minor)->first();
2055
                        $title = $qq['title'];
2056
                        $session = $qq['session'];
2057
                        $this->bill_lookup[$minor] = array($title, $session);
2058
                    }
2059
                    $url = "$session/" . urlencode(str_replace(' ','_',$title));
2060
                    $parent_gid = preg_replace('#^.*?_.*?_#', '', $parent_gid);
2061
                    global $DATA;
2062
                    $DATA->set_page_metadata('pbc_clause', 'url', "pbc/$url/$parent_gid");
2063
                    $LISTURL->remove(array('id'));
2064
                } else {
2065
                    $LISTURL->insert( array( 'id' => $parent_gid ) );
2066
                }
2067
                // Use a truncated form of this item's gid for the anchor.
2068
                $fragment = '#g' . gid_to_anchor($id_data['gid']);
2069
            }
2070
        }
2071
2072 4
        if (count($url_args) > 0) {
2073 1
            $LISTURL->insert( $url_args);
2074
        }
2075
2076 4
        return $LISTURL->generate($encode) . $fragment;
2077
    }
2078
2079 3
    public function _get_speaker($person_id, $hdate, $htime, $major) {
2080 3
        if ($person_id == 0) {
2081 1
            return array();
2082
        }
2083
2084
        # Special exemption below for when NI Speaker becomes Speaker
2085
        # mid-debate, so we don't want to cache
2086 3
        if (isset($this->speakers["$person_id-$hdate"]) && !($person_id == 13831 && $hdate == '2015-01-12')) {
2087 1
            return $this->speakers["$person_id-$hdate"];
2088
        }
2089
2090
        # Special exemptions for people 'speaking' after they have left
2091
        # Note identical code to this in search/index.pl
2092 3
        if ($person_id == 10170 && $hdate == '2014-09-08') {
2093
            $hdate = '2014-09-07';
2094 3
        } elseif ($person_id == 11068 && substr($hdate, 0, 7) == '2008-09') {
2095
            $hdate = '2008-08-13';
2096 3
        } elseif ($person_id == 25394 && $hdate == '2016-07-01') {
2097
            $hdate = '2016-06-16';
2098 3
        } elseif ($person_id == 10599 && substr($hdate, 0, 7) == '2021-02') {
2099
            $hdate = '2021-02-19';
2100 3
        } elseif ($person_id == 11667 && substr($hdate, 0, 7) == '2022-12') {
2101
            $hdate = '2022-11-30';
2102
        }
2103
2104
        # London questions answered after election
2105 3
        if ($major == 9 && ($hdate == '2021-05-11' || $hdate == '2021-05-10')) {
2106
            $hdate = '2021-05-07';
2107
        }
2108 3
        if ($person_id == 25942 && $major == 9 && $hdate == '2021-09-17') {
2109
            $hdate = '2021-05-07';
2110
        }
2111
2112
        # check for a person redirect
2113 3
        $q = $this->db->query("SELECT gid_to FROM gidredirect
2114
                WHERE gid_from = :gid_from",
2115 3
            array(':gid_from' => "uk.org.publicwhip/person/$person_id")
2116 3
        )->first();
2117 3
        if ($q) {
2118
            $person_id = str_replace('uk.org.publicwhip/person/', '', $q['gid_to']);
2119
        }
2120
2121 3
        $q = $this->db->query("SELECT title, given_name, family_name, lordofname,
2122
                                house,
2123
                                constituency,
2124
                                party,
2125
                                member_id
2126
                        FROM    member m, person_names p
2127
                        WHERE	m.person_id = :person_id
2128
                            AND entered_house <= :hdate AND :hdate <= left_house
2129
                            AND p.person_id = m.person_id AND p.type = 'name'
2130
                            AND p.start_date <= :hdate AND :hdate <= p.end_date
2131
                        ORDER BY entered_house",
2132 3
            array(':person_id' => $person_id, ':hdate' => $hdate));
2133 3
        $member = $this->_get_speaker_alone($q, $person_id, $hdate, $htime, $major);
2134
2135 3
        $URL = $this->_get_speaker_url($member['house']);
2136 3
        $URL->insert( array ('p' => $person_id) );
2137
2138 3
        $name = member_full_name($member['house'], $member['title'], $member['given_name'], $member['family_name'], $member['lordofname']);
2139
        $speaker = array (
2140 3
            'member_id' => $member['member_id'],
2141 3
            "name" => $name,
2142 3
            'house' => $member['house'],
2143 3
            "constituency" => gettext($member["constituency"]),
2144 3
            "party" => $member["party"],
2145 3
            "person_id" => $person_id,
2146 3
            "url" => $URL->generate(),
2147
        );
2148
2149 3
        global $parties;
2150
        // Manual fix for Speakers.
2151 3
        if (isset($parties[$speaker['party']])) {
2152
            $speaker['party'] = $parties[$speaker['party']];
2153
        }
2154
2155 3
        $speaker['office'] = $this->_get_speaker_offices($speaker, $hdate);
2156 3
        $this->speakers["$person_id-$hdate"] = $speaker;
2157 3
        return $speaker;
2158
    }
2159
2160 3
    private function _get_speaker_alone($q, $person_id, $hdate, $htime, $major) {
2161 3
        $members = $q->fetchAll();
2162 3
        if (count($members) > 1) {
2163 2
            $members = array_filter($members, function($m) use ($major) {
2164 2
                $houses = \MySociety\TheyWorkForYou\Utility\House::majorToHouse($major);
2165 2
                return in_array($m['house'], $houses);
2166 2
            });
2167
            # Of course, remember PHP treats lists as dictionaries
2168 2
            $members = array_values($members);
2169
        }
2170
        # Note identical code to this in search/index.pl
2171 3
        if (count($members) > 1) {
2172
            # Couple of special cases for the election of the NI Speaker
2173
            if ($person_id == 13799 && $hdate == '2007-05-08') {
2174
                $members = array($members[$htime < '11:00' ? 0 : 1]);
2175
            } elseif ($person_id == 13831 && $hdate == '2015-01-12') {
2176
                $members = array($members[$htime < '13:00' ? 0 : 1]);
2177
            }
2178
        }
2179 3
        if (count($members) != 1) {
2180
            throw new \Exception('Wanted one result, but got ' . count($members) . " for $person_id, $hdate, $major.");
2181
        }
2182
2183 3
        return $members[0];
2184
    }
2185
2186 3
    private function _get_speaker_url($house) {
2187 3
        $URL = new \MySociety\TheyWorkForYou\Url('mp'); # Default, house=1
2188 3
        if ($house == HOUSE_TYPE_LORDS) {
2189
            $URL = new \MySociety\TheyWorkForYou\Url('peer');
2190 3
        } elseif ($house == HOUSE_TYPE_NI) {
2191
            $URL = new \MySociety\TheyWorkForYou\Url('mla');
2192 3
        } elseif ($house == HOUSE_TYPE_SCOTLAND) {
2193
            $URL = new \MySociety\TheyWorkForYou\Url('msp');
2194 3
        } elseif ($house == HOUSE_TYPE_WALES) {
2195
            $URL = new \MySociety\TheyWorkForYou\Url('ms');
2196 3
        } elseif ($house == HOUSE_TYPE_ROYAL) {
2197
            $URL = new \MySociety\TheyWorkForYou\Url('royal');
2198
        }
2199 3
        return $URL;
2200
    }
2201
2202 3
    private function _get_speaker_offices($speaker, $hdate) {
2203 3
        $offices = array();
2204 3
        $q = $this->db->query("SELECT dept, position, source FROM moffice
2205
            WHERE person=:person_id
2206
            AND from_date <= :hdate and :hdate <= to_date",
2207 3
            array(':person_id' => $speaker['person_id'], ':hdate' => $hdate));
2208 3
        foreach ($q as $row) {
2209
            $dept = $row['dept'];
2210
            $pos = $row['position'];
2211
            $source = $row['source'];
2212
            if ($source == 'chgpages/libdem' && $hdate > '2009-01-15') {
2213
                continue;
2214
            }
2215
            if (!$pos || $pos == 'Chairman' || $pos == 'Member') {
2216
                continue;
2217
            }
2218
            $offices[] = array(
2219
                'dept' => $dept,
2220
                'position' => $pos,
2221
                'source' => $source,
2222
                'pretty' => prettify_office($pos, $dept)
2223
            );
2224
        }
2225 3
        return $offices;
2226
    }
2227
2228 3
    public function _get_comment($item_data) {
2229
        // Pass it some variables belonging to an item and the function
2230
        // returns an array containing:
2231
        // 1) A count of the comments within this item.
2232
        // 2) The details of the most recent comment posted to this item.
2233
2234
        // Sections/subsections have (1) (the sum of the comments
2235
        // of all contained items), but not (2).
2236
2237
        // What we return.
2238 3
        $totalcomments = $this->_get_comment_count_for_epobject($item_data);
2239 3
        $comment = array();
2240
2241 3
        if ($item_data['htype'] == '12' || $item_data['htype'] == '13' || $item_data['htype'] == '14') {
2242
2243
            // Things which can have comments posted directly to them.
2244
2245
            if ($totalcomments > 0) {
2246
2247
                // Get the first comment.
2248
2249
                // Not doing this for Wrans sections because we don't
2250
                // need it anywhere. Arbitrary but it'll save us MySQL time!
2251
2252
                $q = $this->db->query("SELECT c.comment_id,
2253
                                    c.user_id,
2254
                                    c.body,
2255
                                    c.posted,
2256
                                    u.firstname,
2257
                                    u.lastname
2258
                            FROM	comments c, users u
2259
                            WHERE	c.epobject_id = :epobject_id
2260
                            AND		c.user_id = u.user_id
2261
                            AND		c.visible = 1
2262
                            ORDER BY c.posted ASC
2263
                            LIMIT	1",
2264
                    array(':epobject_id' => $item_data['epobject_id']))->first();
2265
2266
                // Add this comment to the data structure.
2267
                $comment = array (
2268
                    'comment_id' => $q['comment_id'],
2269
                    'user_id'	=> $q['user_id'],
2270
                    'body'		=> $q['body'],
2271
                    'posted'	=> $q['posted'],
2272
                    'username'	=> $q['firstname'] .' '. $q['lastname']
2273
                );
2274
            }
2275
2276
        }
2277
2278
        // We don't currently allow people to post comments to a section
2279
        // or subsection itself, only the items within them. So
2280
        // we don't get the most recent comment. Because there isn't one.
2281
2282
        $return = array (
2283 3
            'totalcomments' => $totalcomments,
2284 3
            'comment' => $comment
2285
        );
2286
2287 3
        return $return;
2288
    }
2289
2290
2291 3
    public function _get_comment_count_for_epobject($item_data) {
2292 3
        global $hansardmajors;
2293
        // What it says on the tin.
2294
        // $item_data must have 'htype' and 'epobject_id' elements. TODO: Check for major==4
2295
2296 3
        if (($hansardmajors[$this->major]['type']=='debate') &&
2297 3
            ($item_data['htype'] == '10' || $item_data['htype'] == '11')
2298
            ) {
2299
            // We'll be getting a count of the comments on all items
2300
            // within this (sub)section.
2301 3
            $from = "comments, hansard";
2302 3
            $where = "comments.epobject_id = hansard.epobject_id
2303
                    AND subsection_id = :epobject_id";
2304
2305 3
            if ($item_data['htype'] == '10') {
2306
                // Section - get a count of comments within this section that
2307
                // don't have a subsection heading.
2308 3
                $where .= " AND section_id = :epobject_id";
2309
            }
2310
2311
        } else {
2312
            // Just getting a count of the comments on this item.
2313
            $from = "comments";
2314
            $where = 'epobject_id = :epobject_id';
2315
        }
2316
2317 3
        $q = $this->db->query("SELECT COUNT(*) AS count
2318 3
                        FROM 	$from
2319 3
                        WHERE	$where
2320
                        AND		visible = 1",
2321 3
            array(':epobject_id' => $item_data['epobject_id']))->first();
2322
2323 3
        return $q['count'];
2324
    }
2325
2326 1
    public function _get_data_by_gid($args) {
2327
2328
        // We need to get the data for this gid.
2329
        // Then depending on what htype it is, we get the data for other items too.
2330 1
        global $DATA, $this_page, $hansardmajors;
2331
2332 1
        twfy_debug (get_class($this), "getting data by gid");
2333
2334
        // Get the information about the item this URL refers to.
2335 1
        $itemdata = $this->_get_item($args);
2336 1
        if (!$itemdata) {
2337
            return array();
2338
        }
2339
2340
        // If part of a Written Answer (just question or just answer), select the whole thing
2341 1
        if (isset($itemdata['major']) && $hansardmajors[$itemdata['major']]['type']=='other' and ($itemdata['htype'] == '12' or $itemdata['htype'] == '13' or $itemdata['htype'] == '14')) {
2342
            // find the gid of the subheading which holds this part
2343
            $input = array (
2344
                'amount' => array('gid' => true),
2345
                'where' => array (
2346
                    'epobject_id=' => $itemdata['subsection_id'],
2347
                ),
2348
            );
2349
            $parent = $this->_get_hansard_data($input);
2350
            // display that item, i.e. the whole of the Written Answer
2351
            twfy_debug (get_class($this), "instead of " . $args['gid'] . " selecting subheading gid " . $parent[0]['gid'] . " to get whole wrans");
2352
            $args['gid'] = $parent[0]['gid'];
2353
            $this->_get_item($args);
2354
            throw new RedirectException($args['gid']);
2355
        }
2356
2357
        # If a WMS main heading, go to next gid
2358 1
        if (isset($itemdata['major']) && $itemdata['major']==4 && $itemdata['htype'] == '10') {
2359
            $input = array (
2360
                'amount' => array('gid' => true),
2361
                'where' => array(
2362
                    'section_id=' => $itemdata['epobject_id'],
2363
                ),
2364
                'order' => 'hpos ASC',
2365
                'limit' => 1
2366
            );
2367
            $next = $this->_get_hansard_data($input);
2368
            if (!empty($next)) {
2369
                twfy_debug (get_class($this), 'instead of ' . $args['gid'] . ' moving to ' . $next[0]['gid']);
2370
                $args['gid'] = $next[0]['gid'];
2371
                $this->_get_item($args);
2372
                throw new RedirectException($args['gid']);
2373
            }
2374
        }
2375
2376
        // Where we'll put all the data we want to render.
2377 1
        $data = array();
2378
2379 1
        if (isset($itemdata['htype'])) {
2380 1
            $this->htype = $itemdata['htype'];
2381 1
            if ($this->htype >= 12) {
2382
                $this_page = $this->commentspage;
2383
            } else {
2384 1
                $this_page = $this->listpage;
2385
            }
2386
        }
2387 1
        if (isset($itemdata['epobject_id'])) {
2388 1
            $this->epobject_id = $itemdata['epobject_id'];
2389
        }
2390 1
        if (isset($itemdata['gid'])) {
2391 1
            $this->gid = $itemdata['gid'];
2392
        }
2393
2394
        // We'll use these for page headings/titles:
2395 1
        $data['info']['date'] = $itemdata['hdate'];
2396 1
        $data['info']['text'] = $itemdata['body'];
2397 1
        $data['info']['major'] = $this->major;
2398
2399
        // If we have a member id we'll pass it on to the template so it
2400
        // can highlight all their speeches.
2401 1
        if (isset($args['member_id'])) {
2402
            $data['info']['member_id'] = $args['member_id'];
2403
        }
2404 1
        if (isset($args['person_id'])) {
2405
            $data['info']['person_id'] = $args['person_id'];
2406
        }
2407
2408 1
        if (isset($args['s']) && $args['s'] != '') {
2409
            // We have some search term words that we could highlight
2410
            // when rendering.
2411
            $data['info']['searchstring'] = $args['s'];
2412
        }
2413
2414
        // Get the section and subsection headings for this item.
2415 1
        $sectionrow = $this->_get_section($itemdata);
2416 1
        $subsectionrow = $this->_get_subsection($itemdata);
2417
2418
        // Get the nextprev links for this item, to link to next/prev pages.
2419
        // Duh.
2420 1
        if ($itemdata['htype'] == '10') {
2421 1
            $nextprev = $this->_get_nextprev_items( $sectionrow );
2422 1
            $data['info']['text_heading'] = $itemdata['body'];
2423
2424
        } elseif ($itemdata['htype'] == '11') {
2425
            $nextprev = $this->_get_nextprev_items( $subsectionrow );
2426
            $data['info']['text_heading'] = $itemdata['body'];
2427
2428
        } else {
2429
            // Ordinary lowly item.
2430
            $nextprev = $this->_get_nextprev_items( $itemdata );
2431
2432
            if (isset($subsectionrow['gid'])) {
2433
                $nextprev['up']['url'] 		= $subsectionrow['listurl'];
2434
                $nextprev['up']['title'] 	= $subsectionrow['body'];
2435
            } else {
2436
                $nextprev['up']['url'] 		= $sectionrow['listurl'];
2437
                $nextprev['up']['title'] 	= $sectionrow['body'];
2438
            }
2439
            $nextprev['up']['body']		= gettext('See the whole debate');
2440
        }
2441
2442
        // We can then access this from $PAGE and the templates.
2443 1
        $DATA->set_page_metadata($this_page, 'nextprev', $nextprev);
2444
2445
        // Now get all the non-heading rows.
2446
2447
        // What data do we want for each item?
2448
        $amount = array (
2449 1
            'body' => true,
2450
            'speaker' => true,
2451
            'comment' => true,
2452
            'votes' => true
2453
        );
2454
2455 1
        if ($itemdata['htype'] == '10') {
2456
            // This item is a section, so we're displaying all the items within
2457
            // it that aren't within a subsection.
2458
2459
            $input = array (
2460 1
                'amount' => $amount,
2461
                'where' => array (
2462 1
                    'section_id=' => $itemdata['epobject_id'],
2463 1
                    'subsection_id=' => $itemdata['epobject_id']
2464
                ),
2465 1
                'order' => 'hpos ASC'
2466
            );
2467
2468 1
            $data['rows'] = $this->_get_hansard_data($input);
2469 1
            if (!count($data['rows']) || (count($data['rows'])==1 && strstr($data['rows'][0]['body'], 'was asked'))) {
2470
2471
                $input = array (
2472
                    'amount' => array (
2473 1
                        'body' => true,
2474
                        'comment' => true,
2475
                        'excerpt' => true
2476
                    ),
2477
                    'where' => array (
2478 1
                        'section_id='	=> $sectionrow['epobject_id'],
2479 1
                        'htype='		=> '11',
2480 1
                        'major='		=> $this->major
2481
                    ),
2482 1
                    'order' => 'hpos'
2483
                );
2484 1
                $data['subrows'] = $this->_get_hansard_data($input);
2485
                # If there's only one subheading, and nothing in the heading, redirect to it immediaetly
2486 1
                if (count($data['subrows']) == 1) {
2487 1
                    throw new RedirectException($data['subrows'][0]['gid']);
2488
                }
2489
            }
2490
        } elseif ($itemdata['htype'] == '11') {
2491
            // This item is a subsection, so we're displaying everything within it.
2492
2493
            $input = array (
2494
                'amount' => $amount,
2495
                'where' => array (
2496
                    'subsection_id=' => $itemdata['epobject_id']
2497
                ),
2498
                'order' => 'hpos ASC'
2499
            );
2500
2501
            $data['rows'] = $this->_get_hansard_data($input);
2502
2503
2504
        } elseif ($itemdata['htype'] == '12' || $itemdata['htype'] == '13' || $itemdata['htype'] == '14') {
2505
            // Debate speech or procedural, so we're just displaying this one item.
2506
2507
            $data['rows'][] = $itemdata;
2508
2509
        }
2510
2511
        // Put the section and subsection at the top of the rows array.
2512 1
        if (count($subsectionrow) > 0 &&
2513 1
            $subsectionrow['gid'] != $sectionrow['gid']) {
2514
            // If we're looking at a section, there may not be a subsection.
2515
            // And if the subsectionrow and sectionrow aren't the same.
2516
            array_unshift ($data['rows'], $subsectionrow);
2517
        }
2518
2519 1
        array_unshift ($data['rows'], $sectionrow);
2520
2521 1
        return $data;
2522
2523
    }
2524
2525
    public function _get_data_by_column($args) {
2526
        global $this_page;
2527
2528
        twfy_debug (get_class($this), "getting data by column");
2529
2530
        $input = array( 'amount' => array('body'=>true, 'comment'=>true, 'speaker'=>true),
2531
        'where' => array( 'hdate='=>$args['date'], 'major=' => $this->major, 'gid LIKE ' =>'%.'.$args['column'].'.%' ),
2532
        'order' => 'hpos'
2533
        );
2534
        $data = $this->_get_hansard_data($input);
2535
        #		$data = array();
2536
2537
        #		$itemdata = $this->_get_item($args);
2538
2539
        #		if ($itemdata) {
2540
            #	$data['info']['date'] = $itemdata['hdate'];
2541
            #			$data['info']['text'] = $itemdata['body'];
2542
            #			$data['info']['major'] = $this->major;
2543
            #		}
2544
        return $data;
2545
    }
2546
2547
}
2548
2549
class WMSLIST extends WRANSLIST {
2550
    public $major = 4;
2551
    public $listpage = 'wms';
2552
    public $commentspage = 'wms';
2553
    public $gidprefix = 'uk.org.publicwhip/wms/';
2554
2555
    public function _get_data_by_recent_wms($args = array()) {
2556
        return $this->_get_data_by_recent_wrans($args);
2557
    }
2558
}
2559
2560
class WHALLLIST extends DEBATELIST {
2561
    public $major = 2;
2562
    public $listpage = 'whalls';
2563
    public $commentspage = 'whall';
2564
    public $gidprefix = 'uk.org.publicwhip/westminhall/';
2565
}
2566
2567
class NILIST extends DEBATELIST {
2568
    public $major = 5;
2569
    public $listpage = 'nidebates';
2570
    public $commentspage = 'nidebate';
2571
    public $gidprefix = 'uk.org.publicwhip/ni/';
2572
}
2573
2574
class SENEDDENLIST extends DEBATELIST {
2575
    public $major = 10;
2576
    public $listpage = 'senedddebates';
2577
    public $commentspage = 'senedddebate';
2578
    public $gidprefix = 'uk.org.publicwhip/senedd/en/';
2579
}
2580
2581
class SENEDDCYLIST extends DEBATELIST {
2582
    public $major = 11;
2583
    public $listpage = 'senedddebates';
2584
    public $commentspage = 'senedddebate';
2585
    public $gidprefix = 'uk.org.publicwhip/senedd/cy/';
2586
}
2587
2588
class LMQLIST extends WRANSLIST {
2589
    public $major = 9;
2590
    public $listpage = 'lmqs';
2591
    public $commentspage = 'lmqs';
2592
    public $gidprefix = 'uk.org.publicwhip/london-mayors-questions/';
2593
}
2594
2595
class SPLIST extends DEBATELIST {
2596
    public $major = 7;
2597
    public $listpage = 'spdebates';
2598
    public $commentspage = 'spdebate';
2599
    public $gidprefix = 'uk.org.publicwhip/spor/';
2600
}
2601
2602
class SPWRANSLIST extends WRANSLIST {
2603
    public $major = 8;
2604
    public $listpage = 'spwrans';
2605
    public $commentspage = 'spwrans';
2606
    public $gidprefix = 'uk.org.publicwhip/spwa/';
2607
2608
    public function get_gid_from_spid($spid) {
2609
        // Fix the common errors of S.0 instead of S.O and leading
2610
        // zeros in the numbers:
2611
        $fixed_spid = preg_replace('/(S[0-9]+)0-([0-9]+)/','${1}O-${2}',$spid);
2612
        $fixed_spid = preg_replace('/(S[0-9]+\w+)-0*([0-9]+)/','${1}-${2}',$fixed_spid);
2613
        $q = $this->db->query(
2614
            "select mentioned_gid from mentions where gid = :gid_from_spid and (type = 4 or type = 6)",
2615
            array(':gid_from_spid' => 'uk.org.publicwhip/spq/' . $fixed_spid)
2616
        )->first();
2617
        $gid = $q['mentioned_gid'];
2618
        if ($gid) {
2619
            return $gid;
2620
        }
2621
        return null;
2622
    }
2623
    public function old_get_gid_from_spid($spid) {
2624
        $q = $this->db->query(
2625
            "select gid from hansard where gid like :gid_like",
2626
            array(':gid_like' => 'uk.org.publicwhip/spwa/%.' . $spid . '.h')
2627
        )->first();
2628
        $gid = $q['gid'];
2629
        if ($gid) {
2630
            return str_replace('uk.org.publicwhip/spwa/', '', $gid);
2631
        }
2632
        return null;
2633
    }
2634
}
2635
2636
class LORDSDEBATELIST extends DEBATELIST {
2637
    public $major = 101;
2638
    public $listpage = 'lordsdebates';
2639
    public $commentspage = 'lordsdebate';
2640
    public $gidprefix = 'uk.org.publicwhip/lords/';
2641
}
2642
2643
class DEBATELIST extends HANSARDLIST {
2644
    public $major = 1;
2645
    public $listpage = 'debates';
2646
    public $commentspage = 'debate';
2647
    public $gidprefix = 'uk.org.publicwhip/debate/';
2648
2649
    public function _get_data_by_recent_mostvotes($args) {
2650
        // Get the most highly voted recent speeches.
2651
        // $args may have 'days'=>7 and/or 'num'=>5
2652
        // or something like that.
2653
2654
        // The most voted on things during how many recent days?
2655
        if (isset($args['days']) && is_numeric($args['days'])) {
2656
            $days = $args['days'];
2657
        } else {
2658
            $days = 7;
2659
        }
2660
2661
        // How many results?
2662
        if (isset($args['num']) && is_numeric($args['num'])) {
2663
            $items_to_list = $args['num'];
2664
        } else {
2665
            $items_to_list = 5;
2666
        }
2667
2668
        $q = $this->db->query("SELECT min(subsection_id) AS subsection_id,
2669
                                min(section_id) AS section_id,
2670
                                min(htype) AS htype,
2671
                                min(gid) AS gid,
2672
                                min(major) AS major, min(minor) AS minor,
2673
                                min(hdate) AS hdate, min(htime) AS htime,
2674
                                min(person_id) AS person_id,
2675
                                min(epobject.body) AS body,
2676
                                SUM(uservotes.vote) + anonvotes.yes_votes AS total_vote
2677
                        FROM	hansard,
2678
                                epobject
2679
                                LEFT OUTER JOIN uservotes ON epobject.epobject_id = uservotes.epobject_id
2680
                                LEFT OUTER JOIN anonvotes ON epobject.epobject_id = anonvotes.epobject_id
2681
                        WHERE		major = :major
2682
                        AND		hansard.epobject_id = epobject.epobject_id
2683
                        AND		hdate >= DATE_SUB(CURDATE(), INTERVAL $days DAY)
2684
                        GROUP BY epobject.epobject_id
2685
                        HAVING 	total_vote > 0
2686
                        ORDER BY total_vote DESC
2687
                        LIMIT	$items_to_list
2688
                        ", array(':major' => $this->major));
2689
2690
        // What we return.
2691
        $data = array ();
2692
        $speeches = array();
2693
        foreach ($q as $row) {
2694
            $speech = array (
2695
                'subsection_id' => $row['subsection_id'],
2696
                'section_id' => $row['section_id'],
2697
                'htype' => $row['htype'],
2698
                'major' => $row['major'],
2699
                'minor' => $row['minor'],
2700
                'hdate' => $row['hdate'],
2701
                'body' => $row['body'],
2702
                'votes' => $row['total_vote']
2703
            );
2704
2705
            // Remove the "uk.org.publicwhip/blah/" from the gid:
2706
            // (In includes/utility.php)
2707
            $speech['gid'] = fix_gid_from_db( $row['gid'] );
2708
            $speech['listurl'] = $this->_get_listurl($speech);
2709
            $speech['speaker'] = $this->_get_speaker($row['person_id'], $row['hdate'], $row['htime'], $this->major );
2710
            $speeches[] = $speech;
2711
        }
2712
2713
        if (count($speeches) > 0) {
2714
            // Get the subsection texts.
2715
2716
            $num_speeches = count($speeches);
2717
            for ($n=0; $n<$num_speeches; $n++) {
2718
                //if ($this->major == 1) {
2719
                    // Debate.
2720
                    $parent = $this->_get_subsection ($speeches[$n]);
2721
2722
                //} elseif ($this->major == 3) {
2723
                    // Wrans.
2724
                //	$parent = $this->_get_section ($speeches[$n]);
2725
                //}
2726
                // Add the parent's body on...
2727
                //if (isset($parent['body'])) {
2728
                    $speeches[$n]['parent']['body'] = $parent['body'];
2729
                //} else {
2730
                //	$parent = $this->_get_section ($speeches[$n]);
2731
                //	$speeches[$n]['parent']['body'] = $parent['body'];
2732
                //}
2733
2734
            }
2735
2736
            $data['rows'] = $speeches;
2737
2738
        } else {
2739
            $data['rows'] = array ();
2740
        }
2741
2742
        $data['info']['days'] = $days;
2743
2744
        return $data;
2745
    }
2746
2747
2748
    public function total_speeches() {
2749
2750
        $q = $this->db->query("SELECT COUNT(*) AS count FROM hansard WHERE major = :major AND htype = 12", array(':major' => $this->major));
2751
2752
        return $q->first()['count'];
2753
    }
2754
2755
2756
    public function biggest_debates($args=array()) {
2757
        // So we can just get the data back for special formatting
2758
        // on the front page, without doing the whole display() thing.
2759
        return $this->_get_data_by_biggest_debates($args);
2760
    }
2761
2762
    public function _get_data_by_featured_gid($args=array()) {
2763
        $params = array();
2764
        $data = array();
2765
2766
        $params[':gid'] = $args['gid'];
2767
        $params[':major'] = $this->major;
2768
2769
        $query = "SELECT
2770
                    body,
2771
                    title,
2772
                    h.hdate,
2773
                    h.htime,
2774
                    h.htype,
2775
                    h.minor,
2776
                    h.gid,
2777
                    h.person_id,
2778
                    h.subsection_id,
2779
                    h.section_id,
2780
                    h.epobject_id
2781
            FROM    hansard h, epobject e
2782
            WHERE   h.major = :major
2783
            AND     h.gid = :gid
2784
            AND     h.epobject_id = e.epobject_id";
2785
2786
        $q = $this->db->query($query, $params)->first();
2787
2788
        if ($q) {
2789
2790
            // This array just used for getting further data about this debate.
2791
            $item_data = array (
2792
                'major'         => $this->major,
2793
                'minor'         => $q['minor'],
2794
                'gid'           => fix_gid_from_db( $q['gid'] ),
2795
                'htype'         => $q['htype'],
2796
                'section_id'    => $q['section_id'],
2797
                'subsection_id' => $q['subsection_id'],
2798
                'epobject_id'   => $q['epobject_id']
2799
            );
2800
2801
            $list_url      = $this->_get_listurl( $item_data );
2802
            $totalcomments = $this->_get_comment_count_for_epobject( $item_data );
2803
2804
            $body          = $q['body'];
2805
            $hdate         = $q['hdate'];
2806
            $htime         = $q['htime'];
2807
2808
            // If this is a subsection, we're going to prepend the title
2809
            // of the parent section, so let's get that.
2810
            $parentbody = '';
2811
            if ($item_data['htype'] == 11 || $item_data['htype'] == 12) {
2812
                $r = $this->db->query("SELECT sec.body as sec_body, sec.title as sec_title,
2813
                                              sub.body as sub_body, sub.title as sub_title
2814
                                FROM    epobject sec, epobject sub
2815
                                WHERE   sec.epobject_id = :section_id
2816
                                AND     sub.epobject_id = :subsection_id",
2817
                                array(
2818
                                    ':section_id' => $item_data['section_id'],
2819
                                    ':subsection_id' => $item_data['subsection_id'],
2820
                                )
2821
                            )->first();
2822
                $section_body  = $r['sec_body'];
2823
                $subsection_body = $r['sub_body'];
2824
                if ( $section_body && $subsection_body ) {
2825
                    $parentbody = "$section_body : $subsection_body";
2826
                } else {
2827
                    $parentbody = "$section_body$subsection_body";
2828
                }
2829
            } else if ( $item_data['htype'] == 10 ) {
2830
                $parentbody = $body;
2831
            }
2832
2833
            // Get the question for this item.
2834
            if ( $item_data['htype'] == 12 ) {
2835
                $childbody = $body;
2836
                $speaker = $this->_get_speaker($q['person_id'], $q['hdate'], $q['htime'], $this->major );
2837
            } else {
2838
                $r = $this->db->query("SELECT e.body, e.title,
2839
                                        h.person_id, h.hdate, h.htime
2840
                                FROM    hansard h, epobject e
2841
                                WHERE   h.epobject_id = e.epobject_id
2842
                                AND     h.subsection_id = :object_id
2843
                                ORDER BY hpos
2844
                                LIMIT 1
2845
                                ",
2846
                                array( ':object_id' => $item_data['epobject_id'] )
2847
                )->first();
2848
                $childbody = $r['body'];
2849
                $speaker = $this->_get_speaker($r['person_id'], $r['hdate'], $r['htime'], $this->major );
2850
            }
2851
2852
            $contentcount = 0;
2853
            $r = $this->db->query("SELECT COUNT(*) AS count
2854
                            FROM hansard
2855
                            WHERE subsection_id = :object_id
2856
                            AND htype = 12",
2857
                            array(':object_id' => $item_data['epobject_id'])
2858
                )->first();
2859
2860
            if ($r) {
2861
                $contentcount = $r['count'];
2862
            }
2863
2864
            global $hansardmajors;
2865
            $more_url = new \MySociety\TheyWorkForYou\Url( $hansardmajors[$this->major]['page_all'] );
2866
            $details = array(
2867
                'body'          => $body,
2868
                'contentcount'  => $contentcount,
2869
                'hdate'         => $hdate,
2870
                'htime'         => $htime,
2871
                'list_url'      => $list_url,
2872
                'totalcomments' => $totalcomments,
2873
                'child'         => array(
2874
                    'body'      => $childbody,
2875
                    'speaker'   => $speaker
2876
                ),
2877
                'parent'        => array(
2878
                    'body'      => $parentbody
2879
                ),
2880
                'desc' => $hansardmajors[$this->major]['title'],
2881
                'more_url' => $more_url->generate()
2882
            );
2883
2884
            $data = array (
2885
                'gid' => $args['gid'],
2886
                'major' => $this->major,
2887
                'info' => array(),
2888
                'data' => $details,
2889
            );
2890
        }
2891
2892
        return $data;
2893
2894
    }
2895
    public function _get_data_by_recent_debates($args=array()) {
2896
        // Returns an array of some random recent debates from a set number of
2897
        // recent days (that's recent days starting from the most recent day
2898
        // that had any debates on).
2899
2900
        // $args['days'] is the number of days back to look for biggest debates (1 by default).
2901
        // $args['num'] is the number of links to return (1 by default).
2902
2903
        $data = array();
2904
2905
        $params = array();
2906
2907
        // Get the most recent day on which we have a debate.
2908
        $recentday = $this->most_recent_day();
2909
        if (!count($recentday)) {
2910
            return $data;
2911
        }
2912
2913
        if (!isset($args['days']) || !is_numeric($args['days'])) {
2914
            $args['days'] = 1;
2915
        }
2916
        if (!isset($args['num']) || !is_numeric($args['num'])) {
2917
            $args['num'] = 1;
2918
        }
2919
2920
        if ($args['num'] == 1) {
2921
            $datewhere = "h.hdate = :hdate";
2922
            $params[':hdate'] = $recentday['hdate'];
2923
        } else {
2924
            $firstdate = gmdate('Y-m-d', $recentday['timestamp'] - (86400 * $args['days']));
2925
            $datewhere = "h.hdate >= :firstdate
2926
                        AND h.hdate <= :hdate";
2927
            $params[':firstdate'] = $firstdate;
2928
            $params[':hdate'] = $recentday['hdate'];
2929
        }
2930
2931
        $params[':limit'] = $args['num'];
2932
        $params[':major'] = $this->major;
2933
2934
        $query = "SELECT COUNT(*) AS count,
2935
                    min(body) AS body,
2936
                    min(h.hdate) AS hdate,
2937
                    min(sech.htype) AS htype,
2938
                    min(sech.htime) AS htime,
2939
                    min(sech.gid) AS gid,
2940
                    min(sech.subsection_id) AS subsection_id,
2941
                    min(sech.section_id) AS section_id,
2942
                    min(sech.epobject_id) AS epobject_id
2943
            FROM    hansard h, epobject e, hansard sech
2944
            WHERE   h.major = :major
2945
            AND     $datewhere
2946
            AND     h.subsection_id = e.epobject_id
2947
            AND     sech.epobject_id = h.subsection_id
2948
            GROUP BY h.subsection_id
2949
            HAVING  count >= 5
2950
            ORDER BY RAND()
2951
            LIMIT   :limit";
2952
2953
        $q = $this->db->query($query, $params);
2954
        foreach ($q as $row) {
2955
2956
            // This array just used for getting further data about this debate.
2957
            $item_data = array (
2958
                'major' => $this->major,
2959
                'gid' => fix_gid_from_db( $row['gid'] ),
2960
                'htype' => $row['htype'],
2961
                'section_id' => $row['section_id'],
2962
                'subsection_id' => $row['subsection_id'],
2963
                'epobject_id' => $row['epobject_id']
2964
            );
2965
2966
            $list_url      = $this->_get_listurl( $item_data );
2967
            $totalcomments = $this->_get_comment_count_for_epobject( $item_data );
2968
2969
            $contentcount  = $row['count'];
2970
            $body          = $row['body'];
2971
            $hdate         = $row['hdate'];
2972
            $htime         = $row['htime'];
2973
2974
            // If this is a subsection, we're going to prepend the title
2975
            // of the parent section, so let's get that.
2976
            $parentbody = '';
2977
            if ($item_data['htype'] == 11) {
2978
                $r = $this->db->query("SELECT body
2979
                                FROM    epobject
2980
                                WHERE   epobject_id = :epobject_id",
2981
                    array(':epobject_id' => $item_data['section_id']))->first();
2982
                $parentbody = $r['body'];
2983
            }
2984
2985
            // Get the question for this item.
2986
            $r = $this->db->query("SELECT e.body,
2987
                                    h.person_id, h.hdate, h.htime
2988
                            FROM    hansard h, epobject e
2989
                            WHERE   h.epobject_id = e.epobject_id
2990
                            AND     h.subsection_id = '" . $item_data['epobject_id'] . "'
2991
                            ORDER BY hpos
2992
                            LIMIT 1
2993
                            ")->first();
2994
            $childbody = $r['body'];
2995
            $speaker = $this->_get_speaker($r['person_id'], $r['hdate'], $r['htime'], $this->major );
2996
2997
            $data[] = array(
2998
                'contentcount'  => $contentcount,
2999
                'body'          => $body,
3000
                'hdate'         => $hdate,
3001
                'htime'         => $htime,
3002
                'list_url'      => $list_url,
3003
                'totalcomments' => $totalcomments,
3004
                'child'         => array(
3005
                    'body'      => $childbody,
3006
                    'speaker'   => $speaker
3007
                ),
3008
                'parent'        => array(
3009
                    'body'      => $parentbody
3010
                )
3011
            );
3012
3013
        }
3014
3015
        $data = array (
3016
            'info' => array(),
3017
            'data' => $data
3018
        );
3019
3020
        return $data;
3021
3022
    }
3023
3024
    public function _get_data_by_biggest_debates($args=array()) {
3025
        // Returns an array of the debates with most speeches in from
3026
        // a set number of recent days (that's recent days starting from the
3027
        // most recent day that had any debates on).
3028
3029
        // $args['days'] is the number of days back to look for biggest debates.
3030
        // (1 by default)
3031
        // $args['num'] is the number of links to return (1 by default).
3032
3033
        $data = array();
3034
3035
        // Get the most recent day on which we have a debate.
3036
        $recentday = $this->most_recent_day();
3037
        if (!count($recentday)) {
3038
            return array();
3039
        }
3040
3041
        if (!isset($args['days']) || !is_numeric($args['days'])) {
3042
            $args['days'] = 1;
3043
        }
3044
        if (!isset($args['num']) || !is_numeric($args['num'])) {
3045
            $args['num'] = 1;
3046
        }
3047
3048
        $params = array(':recentdate' => $recentday['hdate']);
3049
        if ($args['num'] == 1) {
3050
            $datewhere = "h.hdate = :recentdate";
3051
        } else {
3052
            $params[':firstdate'] = gmdate('Y-m-d', $recentday['timestamp'] - (86400 * $args['days']));
3053
            $datewhere = "h.hdate >= :firstdate AND	h.hdate <= :recentdate";
3054
        }
3055
3056
        $params[':limit'] = $args['num'];
3057
        $params[':major'] = $this->major;
3058
3059
        $q = $this->db->query("SELECT COUNT(*) AS count,
3060
                                min(body) AS body,
3061
                                min(h.hdate) AS hdate,
3062
                                min(sech.htype) AS htype,
3063
                                min(sech.gid) AS gid,
3064
                                min(sech.subsection_id) AS subsection_id,
3065
                                min(sech.section_id) AS section_id,
3066
                                min(sech.epobject_id) AS epobject_id
3067
                        FROM 	hansard h, epobject e, hansard sech
3068
                        WHERE 	h.major = :major
3069
                        AND 	$datewhere
3070
                        AND  	h.subsection_id = e.epobject_id
3071
                        AND 	sech.epobject_id = h.subsection_id
3072
                        GROUP BY h.subsection_id
3073
                        ORDER BY count DESC
3074
                        LIMIT :limit", $params);
3075
3076
        foreach ($q as $row) {
3077
3078
            // This array just used for getting further data about this debate.
3079
            $item_data = array (
3080
                'major' => $this->major,
3081
                'gid' => fix_gid_from_db( $row['gid'] ),
3082
                'htype' => $row['htype'],
3083
                'section_id' => $row['section_id'],
3084
                'subsection_id' => $row['subsection_id'],
3085
                'epobject_id' => $row['epobject_id']
3086
            );
3087
3088
            $list_url 		= $this->_get_listurl( $item_data );
3089
            $totalcomments	= $this->_get_comment_count_for_epobject( $item_data );
3090
3091
            $contentcount = $row['count'];
3092
            $body = $row['body'];
3093
            $hdate = $row['hdate'];
3094
3095
3096
            // This array will be added to $data, which is what gets returned.
3097
            $debate = array (
3098
                'contentcount'	=> $contentcount,
3099
                'body'			=> $body,
3100
                'hdate'			=> $hdate,
3101
                'list_url'		=> $list_url,
3102
                'totalcomments'	=> $totalcomments
3103
            );
3104
3105
            // If this is a subsection, we're going to prepend the title
3106
            // of the parent section, so let's get that.
3107
            if ($item_data['htype'] == 11) {
3108
3109
                $r = $this->db->query("SELECT body
3110
                                FROM	epobject
3111
                                WHERE	epobject_id = :epobject_id",
3112
                    array(':epobject_id' => $item_data['section_id']))->first();
3113
                $debate['parent']['body'] = $r['body'];
3114
            }
3115
3116
            $r = $this->db->query("SELECT e.body,
3117
                                    h.person_id, h.hdate, h.htime
3118
                            FROM    hansard h, epobject e
3119
                            WHERE   h.epobject_id = e.epobject_id
3120
                            AND     h.subsection_id = '" . $item_data['epobject_id'] . "'
3121
                            ORDER BY hpos
3122
                            LIMIT 1
3123
                            ")->first();
3124
            $childbody = $r['body'];
3125
            $speaker = $this->_get_speaker($r['person_id'], $r['hdate'], $r['htime'], $this->major );
3126
3127
            $debate['child'] = array(
3128
                'body' => $childbody,
3129
                'speaker' => $speaker
3130
            );
3131
3132
            $data[] = $debate;
3133
        }
3134
3135
        $data = array (
3136
            'info' => array(),
3137
            'data' => $data
3138
        );
3139
3140
        return $data;
3141
3142
    }
3143
3144
}
3145
3146
3147
class WRANSLIST extends HANSARDLIST {
3148
    public $major = 3;
3149
    public $listpage = 'wrans';
3150
    public $commentspage = 'wrans'; // We don't have a separate page for wrans comments.
3151
    public $gidprefix = 'uk.org.publicwhip/wrans/';
3152
3153
    public function total_questions() {
3154
        $q = $this->db->query("SELECT COUNT(*) AS count FROM hansard WHERE major = :major AND minor = 1", array(':major' => $this->major));
3155
        return $q->first()['count'];
3156
    }
3157
3158
    public function _get_data_by_recent_wrans ($args=array()) {
3159
        global $hansardmajors;
3160
3161
        // $args['days'] is the number of days back to look for biggest debates.
3162
        // (1 by default)
3163
        // $args['num'] is the number of links to return (1 by default).
3164
3165
        $data = array();
3166
3167
        $params = array();
3168
3169
        // Get the most recent day on which we have wrans.
3170
        $recentday = $this->most_recent_day();
3171
        if (!count($recentday)) {
3172
            return $data;
3173
        }
3174
3175
        if (!isset($args['days']) || !is_numeric($args['days'])) {
3176
            $args['days'] = 1;
3177
        }
3178
        if (!isset($args['num']) || !is_numeric($args['num'])) {
3179
            $args['num'] = 1;
3180
        }
3181
3182
        if ($args['num'] == 1) {
3183
            $datewhere = "h.hdate = :datewhere";
3184
            $params[':datewhere'] = $recentday['hdate'];
3185
        } else {
3186
            $firstdate = gmdate('Y-m-d', $recentday['timestamp'] - (86400 * $args['days']));
3187
            $datewhere = "h.hdate >= :firstdate AND h.hdate <= :hdate";
3188
            $params[':firstdate'] = $firstdate;
3189
            $params[':hdate'] = $recentday['hdate'];
3190
        }
3191
3192
3193
        // Get a random selection of subsections in wrans.
3194
        if (in_array($hansardmajors[$this->major]['location'], ['Scotland', 'London'])) {
3195
            $htype = 'htype = 10 and section_id = 0';
3196
        } else {
3197
            $htype = 'htype = 11 and section_id != 0';
3198
        }
3199
3200
        $params[':limit'] = $args['num'];
3201
        $params[':major'] = $this->major;
3202
3203
        $query = "SELECT e.body,
3204
                    h.hdate,
3205
                    h.htype,
3206
                    h.gid,
3207
                    h.subsection_id,
3208
                    h.section_id,
3209
                    h.epobject_id
3210
            FROM    hansard h, epobject e
3211
            WHERE   h.major = :major
3212
            AND     $htype
3213
            AND     subsection_id = 0
3214
            AND     $datewhere
3215
            AND     h.epobject_id = e.epobject_id
3216
            ORDER BY RAND()
3217
            LIMIT   :limit";
3218
3219
        $q = $this->db->query($query, $params);
3220
3221
        foreach ($q as $row) {
3222
            // This array just used for getting further data about this debate.
3223
            $item_data = array (
3224
                'major' => $this->major,
3225
                'gid' => fix_gid_from_db( $row['gid'] ),
3226
                'htype' => $row['htype'],
3227
                'section_id' => $row['section_id'],
3228
                'subsection_id' => $row['subsection_id'],
3229
                'epobject_id' => $row['epobject_id']
3230
            );
3231
3232
            $list_url 		= $this->_get_listurl( $item_data );
3233
            $totalcomments	= $this->_get_comment_count_for_epobject( $item_data );
3234
3235
            $body = $row['body'];
3236
            $hdate = $row['hdate'];
3237
3238
            // Get the parent section for this item.
3239
            $parentbody = '';
3240
            if ($row['section_id']) {
3241
                $r = $this->db->query("SELECT e.body
3242
                            FROM	hansard h, epobject e
3243
                            WHERE	h.epobject_id = e.epobject_id
3244
                            AND		h.epobject_id = '" . $row['section_id'] . "'
3245
                            ")->first();
3246
                $parentbody = $r['body'];
3247
            }
3248
3249
            // Get the question for this item.
3250
            $r = $this->db->query("SELECT e.body,
3251
                                    h.person_id, h.hdate, h.htime
3252
                            FROM	hansard h, epobject e
3253
                            WHERE	h.epobject_id = e.epobject_id
3254
                            AND 	h.subsection_id = '" . $row['epobject_id'] . "'
3255
                            ORDER BY hpos
3256
                            LIMIT 1
3257
                            ")->first();
3258
            $childbody = $r['body'];
3259
            $speaker = $this->_get_speaker($r['person_id'], $r['hdate'], $r['htime'], $this->major );
3260
3261
            $data[] = array (
3262
                'body'			=> $body,
3263
                'hdate'			=> $hdate,
3264
                'list_url'		=> $list_url,
3265
                'totalcomments'	=> $totalcomments,
3266
                'child'			=> array (
3267
                    'body'		=> $childbody,
3268
                    'speaker'	=> $speaker
3269
                ),
3270
                'parent'		=> array (
3271
                    'body'		=> $parentbody
3272
                )
3273
            );
3274
3275
        }
3276
3277
        $data = array (
3278
            'info' => array(),
3279
            'data' => $data
3280
        );
3281
3282
        return $data;
3283
3284
    }
3285
3286
}
3287
3288
class StandingCommittee extends DEBATELIST {
3289
    public $major = 6;
3290
    public $listpage = 'pbc_clause';
3291
    public $commentspage = 'pbc_speech';
3292
    public $gidprefix = 'uk.org.publicwhip/standing/';
3293
3294
    public function __construct($session='', $title='') {
3295
        parent::__construct();
3296
        $this->bill_title = $title;
3297
        $title = str_replace(' ', '_', $title);
3298
        $this->url = urlencode($session) . '/' . urlencode($title) . '/';
3299
    }
3300
3301
    public function _get_committee($bill_id) {
3302
        include_once INCLUDESPATH."easyparliament/member.php";
3303
        $q = $this->db->query(
3304
            'select count(*) as c from hansard
3305
                where major=6 and minor=:bill_id and htype=10',
3306
            array(':bill_id' => $bill_id)
3307
        )->first();
3308
        $sittings = $q['c'];
3309
        $q = $this->db->query(
3310
            'select person_id,sum(attending) as attending, sum(chairman) as chairman
3311
                from pbc_members
3312
                where bill_id = :bill_id group by person_id',
3313
            array(':bill_id' => $bill_id));
3314
        $comm = array('sittings' => $sittings, 'chairmen' => array(), 'members' => array());
3315
        foreach ($q as $row) {
3316
            $person_id = $row['person_id'];
3317
            $mp = new MEMBER(array('person_id' => $person_id));
3318
            $attending = $row['attending'];
3319
            $chairman = $row['chairman'];
3320
            $arr = array(
3321
                'name' => $mp->full_name(),
3322
                'attending' => $attending,
3323
            );
3324
            if ($chairman) {
3325
                $comm['chairmen'][$person_id] = $arr;
3326
            } else {
3327
                $comm['members'][$person_id] = $arr;
3328
            }
3329
        }
3330
        return $comm;
3331
    }
3332
3333
    public function _get_data_by_bill($args) {
3334
        global $DATA, $this_page;
3335
        $data = array();
3336
        $input = array (
3337
            'amount' => array (
3338
                'body' => true,
3339
                'comment' => true,
3340
                'excerpt' => true
3341
            ),
3342
            'where' => array (
3343
                'htype=' => '10',
3344
                'major=' => $this->major,
3345
                'minor=' => $args['id'],
3346
            ),
3347
            'order' => 'hdate,hpos'
3348
        );
3349
        $sections = $this->_get_hansard_data($input);
3350
        if (count($sections) > 0) {
3351
            $data['rows'] = array();
3352
            $num_sections = count($sections);
3353
            for ($n=0; $n<$num_sections; $n++) {
3354
                $sectionrow = $sections[$n];
3355
                list($sitting, $part) = $this->_get_sitting($sectionrow['gid']);
3356
                $sectionrow['sitting'] = $sitting;
3357
                $sectionrow['part'] = $part;
3358
                $input = array (
3359
                    'amount' => array (
3360
                        'body' => true,
3361
                        'comment' => true,
3362
                        'excerpt' => true
3363
                    ),
3364
                    'where' => array (
3365
                        'section_id='	=> $sectionrow['epobject_id'],
3366
                        'htype='	=> '11',
3367
                        'major='	=> $this->major
3368
                    ),
3369
                    'order' => 'hpos'
3370
                );
3371
                $rows = $this->_get_hansard_data($input);
3372
                array_unshift ($rows, $sectionrow);
3373
                $data['rows'] = array_merge ($data['rows'], $rows);
3374
            }
3375
        }
3376
        $data['info']['bill'] = $args['title'];
3377
        $data['info']['major'] = $this->major;
3378
        $data['info']['committee'] = $this->_get_committee($args['id']);
3379
        $DATA->set_page_metadata($this_page, 'title', $args['title']);
3380
        return $data;
3381
    }
3382
3383
    public function _get_data_by_session($args) {
3384
        global $DATA, $this_page;
3385
        $session = $args['session'];
3386
        $q = $this->db->query(
3387
            'select id, title from bills where session = :session order by title',
3388
            array(':session' => $session)
3389
        );
3390
        $bills = array();
3391
        foreach ($q as $row) {
3392
            $bills[$row['id']] = $row['title'];
3393
        }
3394
        if (!count($bills)) {
3395
            return array();
3396
        }
3397
        $q = $this->db->query('select minor,count(*) as c from hansard where major=6 and htype=12
3398
            and minor in (' . join(',', array_keys($bills)) . ')
3399
            group by minor');
3400
        $counts = array();
3401
        # $comments = array();
3402
        foreach ($q as $row) {
3403
            $minor = $row['minor'];
3404
            $counts[$minor] = $row['c'];
3405
            # $comments[$minor] = 0;
3406
        }
3407
        /*
3408
        $q = $this->db->query('select minor,epobject_id from hansard where major=6 and htype=10
3409
            and minor in (' . join(',', array_keys($bills)) . ')');
3410
        foreach ($q as $row) {
3411
            $comments[$row['minor']] += $this->_get_comment_count_for_epobject(array(
3412
                'epobject_id' => $row['epobject_id'],
3413
                'htype' => 10,
3414
            ));
3415
        }
3416
        */
3417
        $data = array();
3418
        foreach ($bills as $id => $title) {
3419
            $data[] = array(
3420
                'title' => $title,
3421
                'url' => "/pbc/" . urlencode($session) . '/' . urlencode(str_replace(' ', '_', $title)) . '/',
3422
                'contentcount' => isset($counts[$id]) ? $counts[$id] : '???',
3423
                # 'totalcomments' => isset($comments[$id]) ? $comments[$id] : '???',
3424
            );
3425
        }
3426
3427
        $YEARURL = new \MySociety\TheyWorkForYou\Url('pbc_session');
3428
        $nextprev = array();
3429
        $nextprev['prev'] = array ('body' => 'Previous session', 'title'=>'');
3430
        $nextprev['next'] = array ('body' => 'Next session', 'title'=>'');
3431
        $q = $this->db->query(
3432
            "SELECT session FROM bills WHERE session < :session ORDER BY session DESC LIMIT 1",
3433
            array(':session' => $session)
3434
        )->first();
3435
        if ($q) {
3436
            $nextprev['prev']['url'] = $YEARURL->generate() . $q['session'] . '/';
3437
        }
3438
        $q = $this->db->query(
3439
            "SELECT session FROM bills WHERE session > :session ORDER BY session ASC LIMIT 1",
3440
            array(':session' => $session)
3441
        )->first();
3442
        if ($q) {
3443
            $nextprev['next']['url'] = $YEARURL->generate() . $q['session'] . '/';
3444
        }
3445
        $DATA->set_page_metadata($this_page, 'nextprev', $nextprev);
3446
3447
        return $data;
3448
    }
3449
3450
    public function _get_data_by_recent_pbc_debates($args) {
3451
        if (!isset($args['num'])) {
3452
            $args['num'] = 20;
3453
        }
3454
        $q = $this->db->query('select gid, minor, hdate from hansard
3455
            where htype=10 and major=6
3456
            order by hdate desc limit ' . $args['num']);
3457
        $data = array();
3458
        foreach ($q as $row) {
3459
            $minor = $row['minor'];
3460
            $gid = $row['gid'];
3461
            $hdate = format_date($row['hdate'], LONGDATEFORMAT);
3462
            $qq = $this->db->query('select title, session from bills where id=' . $minor)->first();
3463
            $title = $qq['title'];
3464
            $session = $qq['session'];
3465
            list($sitting, $part) = $this->_get_sitting($gid);
3466
            $sitting_txt = make_ranking($sitting) . ' sitting';
3467
            if ($part>0) {
3468
                $sitting .= ", part $part";
3469
            }
3470
            $data[$hdate][] = array(
3471
                'bill'=> $title,
3472
                'sitting' => $sitting_txt,
3473
                'url' => "/pbc/$session/" . urlencode(str_replace(' ','_',$title)) . '/#sitting' . $sitting,
3474
            );
3475
        }
3476
        return $data;
3477
    }
3478
3479
    # Given a GID, parse out the sitting number and optional part from it
3480
    public function _get_sitting($gid) {
3481
        if (preg_match('#_(\d\d)-(\d)_#', $gid, $m)) {
3482
            return array($m[1]+0, $m[2]);
3483
        }
3484
        return array(0, 0);
3485
    }
3486
}
3487