Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 222
0.00% covered (danger)
0.00%
0 / 12
CRAP
0.00% covered (danger)
0.00%
0 / 1
CalendarTable
0.00% covered (danger)
0.00%
0 / 222
0.00% covered (danger)
0.00%
0 / 12
7140
0.00% covered (danger)
0.00%
0 / 1
 dayOfWeek
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isLeapYear
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 dayOfWeekOfFirstOfMonth
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getMonthName
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getMonthNameAbbrev
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getWeekday
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getWeekdayAbbrev
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 cutLength
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getLang
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 replace
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 buildTable
0.00% covered (danger)
0.00%
0 / 74
0.00% covered (danger)
0.00%
0 / 1
1056
 setParameters
0.00% covered (danger)
0.00%
0 / 127
0.00% covered (danger)
0.00%
0 / 1
1560
1<?php
2
3namespace MediaWiki\Extension\Calendar;
4
5use DateTime;
6use Language;
7use MediaWiki\MediaWikiServices;
8
9class CalendarTable {
10
11    /** @var string */
12    protected $lng = 'content';
13    /** @var string */
14    protected $prevLink = '';
15    /** @var string */
16    protected $nextLink = '';
17    /** @var string */
18    protected $calTitle = '';
19    /** @var string[] */
20    protected $highlightedDays = [];
21    /** @var string[] */
22    protected $dailyLinks = [];
23    /** @var int */
24    protected $weekStart = 0;
25    /** @var string */
26    protected $position = '';
27    /** @var string */
28    protected $titleLink = '';
29    /** @var string */
30    protected $generalLinks = '';
31    /** @var int */
32    protected $dayCharsCount = 0;
33    /** @var int */
34    protected $monthCharsCount = 0;
35    /** @var string */
36    protected $tableWidth = 'default';
37    /** @var bool */
38    protected $showToday = true;
39
40    /** @var DateTime */
41    protected $today;
42    /** @var int */
43    protected $curDay;
44    /** @var int */
45    protected $curMonth;
46    /** @var int */
47    protected $curYear;
48    /** @var int */
49    protected $month;
50    /** @var int */
51    protected $year;
52    /** @var string[] */
53    protected $monthArr;
54    /** @var string[] */
55    protected $dayArr;
56
57    /**
58     * @param int $timestamp
59     * @return int
60     */
61    public function dayOfWeek( $timestamp ) {
62        return intval( date( 'w', $timestamp ) );
63    }
64
65    /**
66     * @param int $aYear
67     * @return bool
68     */
69    public function isLeapYear( $aYear ) {
70        return cal_days_in_month( CAL_GREGORIAN, 2, $aYear ) === 29;
71    }
72
73    /**
74     * @param int $aMonth
75     * @param int $aYear
76     * @return int
77     */
78    public function dayOfWeekOfFirstOfMonth( $aMonth, $aYear ) {
79        return $this->dayOfWeek( mktime( 0, 0, 0, $aMonth, 1, $aYear ) );
80    }
81
82    /**
83     * @param int $aMonth
84     * @param int $aLen
85     * @return string
86     */
87    public function getMonthName( $aMonth, $aLen ) {
88        $out = $this->getLang()->getMonthName( $aMonth );
89        return $this->cutLength( $out, $aLen );
90    }
91
92    /**
93     * @param int $aMonth
94     * @param int $aLen
95     * @return string
96     */
97    public function getMonthNameAbbrev( $aMonth, $aLen ) {
98        $out = $this->getLang()->getMonthAbbreviation( $aMonth );
99        return $this->cutLength( $out, $aLen );
100    }
101
102    /**
103     * @param int $aDay
104     * @param int $aLen
105     * @return string
106     */
107    public function getWeekday( $aDay, $aLen ) {
108        // Language::getWeekdayName will subtract 1 from key
109        $out = $this->getLang()->getWeekdayName( $aDay % 7 + 1 );
110        return $this->cutLength( $out, $aLen );
111    }
112
113    /**
114     * @param int $aDay
115     * @param int $aLen
116     * @return string
117     */
118    public function getWeekdayAbbrev( $aDay, $aLen ) {
119        // Language::getWeekdayAbbreviation will subtract 1 from key
120        $out = $this->getLang()->getWeekdayAbbreviation( $aDay % 7 + 1 );
121        return $this->cutLength( $out, $aLen );
122    }
123
124    /**
125     * @param string $string
126     * @param int $len
127     * @return string
128     */
129    private function cutLength( $string, $len ) {
130        if ( $len > 0 ) {
131            $string = mb_substr( $string, 0, $len );
132        }
133        return ucfirst( $string );
134    }
135
136    /**
137     * @return Language
138     */
139    private function getLang() {
140        if ( $this->lng === 'user' ) {
141            global $wgLang;
142            return $wgLang;
143        }
144        return MediaWikiServices::getInstance()->getContentLanguage();
145    }
146
147    /**
148     * @param string $str
149     * @param string[] $array
150     * @return string
151     */
152    public function replace( $str, array $array ) {
153        foreach ( $array as $key => $val ) {
154            $str = str_replace( '$' . $key, $val, $str );
155        }
156        return str_replace( '$%', '$', $str );
157    }
158
159    /**
160     * @return string
161     */
162    public function buildTable() {
163        $day1 = $this->dayOfWeekOfFirstOfMonth( $this->month, $this->year );
164        if ( ( $this->weekStart > 0 ) && ( $day1 === 0 ) ) {
165            $day1 = 7;
166        }
167        $days = cal_days_in_month( CAL_GREGORIAN, $this->month, $this->year );
168
169        $style = '';
170        $result = 'calendar';
171        switch ( $this->position ) {
172            case 'left':
173                $result .= ' calLeft';
174                break;
175            case 'right':
176                $result .= ' calRight';
177                break;
178            case 'center':
179                $result .= ' calCenter';
180                $style = ' align="center"';
181        }
182
183        if ( $this->tableWidth === 'default' ) {
184            $result .= ' calWidth';
185        } elseif ( $this->tableWidth !== 'none' ) {
186            $style .= ' style="width: ' . $this->tableWidth . '"';
187        }
188        $result = '{| class="' . $result . '"' . $style . "\n|-\n";
189
190        $colSpan = 5;
191        if ( $this->calTitle === '' ) {
192            $this->calTitle = $this->getMonthName( $this->month, $this->monthCharsCount )
193                . ' ' . $this->year;
194        }
195        if ( $this->titleLink !== '' ) {
196            $this->calTitle = '[[' . $this->titleLink . '|' . $this->calTitle . ']]';
197        }
198        if ( $this->nextLink === '' ) {
199            $colSpan++;
200        }
201        if ( $this->prevLink === '' ) {
202            $colSpan++;
203        } else {
204            $result .= '| class="prevNext" | ' . $this->prevLink . "\n";
205        }
206        $result .= '| class="calTitle" colspan="' . $colSpan . '" | ' . $this->calTitle . "\n";
207        if ( $this->nextLink !== '' ) {
208            $result .= '| class="prevNext" | ' . $this->nextLink . "\n";
209        }
210
211        $result .= "|-\n";
212        for ( $i = $this->weekStart; $i < 7; $i++ ) {
213            $result .= "! " . $this->getWeekdayAbbrev( $i, $this->dayCharsCount ) . "\n";
214        }
215        if ( $this->weekStart > 0 ) {
216            for ( $i = 0; $i < $this->weekStart; $i++ ) {
217                $result .= "! " . $this->getWeekdayAbbrev( $i, $this->dayCharsCount ) . "\n";
218            }
219        }
220
221        $c = 1 - $day1 + $this->weekStart;
222        while ( $c <= $days ) {
223            $result .= "|-\n";
224            for ( $i = 0; $i < 7; $i++ ) {
225                $this->dayArr['a'] = $this->getWeekdayAbbrev( $i + $this->weekStart, 0 );
226                $this->dayArr['A'] = $this->getWeekday( $i + $this->weekStart, 0 );
227                $this->dayArr['D'] = (string)$c;
228                $this->dayArr['d'] = sprintf( '%02s', $c );
229                $this->dayArr['e'] = sprintf( '%2s', $c );
230                $styles = [];
231                if ( $i === 0 && $this->weekStart === 0 ) {
232                    $styles[] = 'sundays';
233                } elseif ( $i === 6 && $this->weekStart === 1 ) {
234                    $styles[] = 'sundays';
235                }
236                if ( $this->showToday && ( $c === $this->curDay ) && ( $this->curMonth === $this->month )
237                    && ( $this->curYear === $this->year )
238                ) {
239                    $styles[] = 'today';
240                }
241                if ( in_array( $c, $this->highlightedDays ) ) {
242                    $styles[] = 'highlighted';
243                }
244                $allStyles = implode( ' ', $styles );
245                if ( $allStyles === '' ) {
246                    $result .= '| ';
247                } else {
248                    $result .= '| class="' . $allStyles . '" | ';
249                }
250                if ( $c > 0 && $c <= $days ) {
251                    if ( $this->dailyLinks[$c] != '' ) {
252                        $result .= '[[' . $this->dailyLinks[$c] . '|' . $c . ']]';
253                    } elseif ( $this->generalLinks != '' ) {
254                        $result .= '[[' . $this->replace( $this->generalLinks, $this->dayArr ) . '|' . $c . ']]';
255                    } else {
256                        $result .= $c;
257                    }
258                } else {
259                    $result .= '&nbsp;';
260                }
261                $result .= "\n";
262                $c++;
263            }
264        }
265        return $result . "|}\n";
266    }
267
268    /**
269     * @param array $args
270     */
271    public function setParameters( array $args ) {
272        $this->today = new DateTime( "now" );
273        $this->curDay = intval( $this->today->format( "d" ) );
274        $this->curMonth = intval( $this->today->format( "m" ) );
275        $this->curYear = intval( $this->today->format( "Y" ) );
276        $this->month = $this->curMonth;
277        $this->year = $this->curYear;
278        for ( $i = 0; $i < 32; $i++ ) {
279            $this->dailyLinks[$i] = '';
280        }
281        $offset = 0;
282
283        foreach ( $args as $arg ) {
284            $parts = array_map( 'trim', explode( '=', $arg, 2 ) );
285            if ( ( count( $parts ) === 2 ) && ( $parts[0] > 0 ) && ( $parts[0] < 32 ) ) {
286                $this->dailyLinks[$parts[0]] = $parts[1];
287            } else {
288                if ( count( $parts ) !== 2 ) {
289                    continue;
290                }
291                switch ( $parts[0] ) {
292                    case 'month':
293                        $this->month = intval( $parts[1] );
294                        break;
295                    case 'year':
296                        $this->year = intval( $parts[1] );
297                        break;
298                    case 'offset':
299                        $offset = intval( $parts[1] );
300                        break;
301                    case 'lang':
302                        $this->lng = strtolower( $parts[1] );
303                        if ( $this->lng !== 'user' ) {
304                            $this->lng = 'content';
305                        }
306                        break;
307                    case 'prevLink':
308                        $this->prevLink = $parts[1];
309                        break;
310                    case 'nextLink':
311                        $this->nextLink = $parts[1];
312                        break;
313                    case 'title':
314                        $this->calTitle = $parts[1];
315                        break;
316                    case 'highlightedDays':
317                        $this->highlightedDays =
318                            preg_split( '/\s+/', $parts[1], 32, PREG_SPLIT_NO_EMPTY );
319                        break;
320                    case 'start':
321                        $this->weekStart = (int)$parts[1];
322                        if ( $this->weekStart !== 1 ) {
323                            $this->weekStart = 0;
324                        }
325                        break;
326                    case 'position':
327                        $this->position = strtolower( $parts[1] );
328                        break;
329                    case 'titleLink':
330                        $this->titleLink = $parts[1];
331                        break;
332                    case 'generalLinks':
333                        $this->generalLinks = $parts[1];
334                        break;
335                    case 'dayCharsCount':
336                        $this->dayCharsCount = intval( $parts[1] );
337                        if ( $this->dayCharsCount < 0 ) {
338                            $this->dayCharsCount = 0;
339                        }
340                        break;
341                    case 'monthCharsCount':
342                        $this->monthCharsCount = intval( $parts[1] );
343                        if ( $this->monthCharsCount < 0 ) {
344                            $this->monthCharsCount = 0;
345                        }
346                        break;
347                    case 'tableWidth':
348                        $this->tableWidth = $parts[1];
349                        if ( $this->tableWidth !== 'default' && $this->tableWidth !== 'none'
350                            && preg_match( '/^\d+(\%|em|ex|pc|pt|px|in|mm|cm)$/', $this->tableWidth ) == 0
351                        ) {
352                            $this->tableWidth = 'default';
353                        }
354                        break;
355                    case 'showToday':
356                        $this->showToday = $parts[1] === 'true';
357                        break;
358                }
359            }
360        }
361
362        if ( ( $this->month < 1 ) || ( $this->month > 12 ) ) {
363            $this->month = $this->curMonth;
364            $this->year = $this->curYear;
365        }
366
367        if ( $offset != 0 ) {
368            $offM = $offset % 12;
369            $offset = (int)round( ( $offset - $offM ) / 12 );
370            $this->month += $offM;
371            $this->year += $offset;
372            if ( $this->month > 12 ) {
373                $this->month -= 12;
374                $this->year++;
375            }
376            if ( $this->month < 1 ) {
377                $this->month += 12;
378                $this->year--;
379            }
380        }
381        if ( $this->year < 1970 ) {
382            $this->month = 1;
383            $this->year = 1970;
384        } elseif ( $this->year > 2037 ) {
385            $this->month = 12;
386            $this->year = 2037;
387        }
388
389        $prevMonthYear = $this->year;
390        $prevMonth = $this->month - 1;
391        if ( $prevMonth == 0 ) {
392            $prevMonth = 12;
393            $prevMonthYear--;
394        }
395        $nextMonthYear = $this->year;
396        $nextMonth = $this->month + 1;
397        if ( $nextMonth == 13 ) {
398            $nextMonth = 1;
399            $nextMonthYear++;
400        }
401        $this->monthArr = [
402            'b' => $this->getMonthNameAbbrev( $this->month, 0 ),
403            'B' => $this->getMonthName( $this->month, 0 ),
404            'm' => sprintf( '%02s', $this->month ),
405            'M' => sprintf( '%02s', $nextMonth ),
406            'n' => $this->getMonthNameAbbrev( $nextMonth, 0 ),
407            'N' => $this->getMonthName( $nextMonth, 0 ),
408            'o' => substr( (string)$nextMonthYear, 2, 2 ),
409            'O' => (string)$nextMonthYear,
410            'p' => $this->getMonthNameAbbrev( $prevMonth, 0 ),
411            'P' => $this->getMonthName( $prevMonth, 0 ),
412            'q' => substr( (string)$prevMonthYear, 2, 2 ),
413            'Q' => (string)$prevMonthYear,
414            'R' => sprintf( '%02s', $prevMonth ),
415            'y' => substr( (string)$this->year, 2, 2 ),
416            'Y' => (string)$this->year
417        ];
418        $this->dayArr = $this->monthArr;
419        $this->titleLink = $this->replace( $this->titleLink, $this->monthArr );
420        $this->prevLink = $this->replace( $this->prevLink, $this->monthArr );
421        $this->nextLink = $this->replace( $this->nextLink, $this->monthArr );
422    }
423}