1<?php
2/**
3 * Plugin Countdown: Displays countdown from a specific date
4 *                   Syntax: <COUNTDOWN:date|description>
5 * date has to be formatted as GNU date (see strtotime)
6 * e.g.              <COUNTDOWN:yyyy-mm-dd|description>
7 *                   <COUNTDOWN:mm/dd/yyy|description>
8 *
9 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
10 * @author  Ekkart Kleinod <ekkart [at] ekkart.de> (V 2.x)
11 * @author  Ron Peters <rbpeters [at] peterro.com> (V 1.0)
12 * @author  Luis Machuca <luis [dot] machuca [at] gulix [dot] cl> (V 2.5+)
13 * @version 3.0
14 *
15 */
16// must be run within Dokuwiki
17if (!defined('DOKU_INC')) {
18    die();
19}
20if(!defined('DW_LF')) define('DW_LF',"\n");
21require_once(DOKU_PLUGIN.'syntax.php');
22
23/**
24 * Plugin-Class for Countdown-Plugin.
25 */
26class syntax_plugin_countdown extends DokuWiki_Syntax_Plugin {
27
28    function syntax_plugin_countdown () {
29        global $ACT;
30        if ($ACT==='show' && $this->getConf('time_message') > 0)
31        msg ('Server time for Countdown plugin is: '. hsc(date('Y-m-d H:i')), 2);
32    }
33
34    function getType(){
35        return 'substition';
36    }
37
38    public function getPType()
39    {
40        return 'normal';
41    }
42
43    function getSort(){
44        return 721;
45    }
46
47    function connectTo($mode) {
48        $this->Lexer->addSpecialPattern('\<COUNTDOWN\:.+?\>',$mode,'plugin_countdown');
49    }
50
51    // fix by Steffan Huehner
52    function handle($match, $state, $pos, Doku_Handler $handler) {
53        $stripped = substr($match,11,-1);
54        // separate date from description
55        $stripped = preg_split('/(?<!\\\\)\|/', $stripped, 3);
56        // $stripped has the form <date>|<description>|<format>
57        // convert all the escaped "\|" into "|"
58        for ($i=0; $i < count($stripped); $i++) {
59          $stripped[$i] = str_replace('\|', '|', $stripped[$i]);
60          }
61        return $stripped;
62    }
63
64    /**
65     * Create output.
66     */
67	// fix by Steffan Huehner
68    function render($mode, Doku_Renderer $renderer, $data) {
69        list($dest, $descr, $fmt) = $data;
70        $Hoy= new DateTimeImmutable();
71        $Next= new DateTimeImmutable($dest);
72        $dt_diff= date_diff($Hoy,$Next);
73        $dt_days = $dt_diff->days * ($dt_diff->invert ? -1 : 1);
74        if (!$descr) $descr = $this->getLang('nodesc');
75/*
76        $thatDate = strtotime ($dest);
77        if ($thatDate <= 0) {
78            $renderer->doc .= $this->getLang('wrongformat'). ': '. $dest. '.';
79            return true;
80        }
81*/
82        // compose msg
83
84        if ($dt_days == 0 && $this->getConf('use_today') ) {
85            $Text1= 'Today '. $dt_diff->format("%H:%I");
86            $WithHorus= false;
87        } else {
88            $ldays= $this->getLang('days');
89            $Text1= $dt_diff->format();
90            if ($this->getConf('with_hours')) {
91                $Text1.=  $dt_diff->format("%a ${ldays} %H:%I");
92            } else {
93                $Text1.=  $dt_diff->format("%a ${ldays}");
94            }
95
96        }
97        $TextArray[]= $Text1;
98        $TextArray[]= ($dt_diff->invert ? $this->getLang('since') : $this->getLang('until'));
99        $pdesc= p_render('xhtml', p_get_instructions($descr), $info);
100        $pdesc= str_replace('<p>', '', $pdesc);
101        $pdesc= str_replace('</p>', '', $pdesc);
102        $TextArray[] = $pdesc;
103        //$renderer->doc.= print_r($dt_diff, true);
104        //$difdata= $this->compute($thatDate);
105        $msg= implode(' ', $TextArray);
106        if ('xhtml' === $mode) {
107            $renderer->doc .= '<!-- countdown begin '. $data[0]. '-->';
108            $renderer->doc .= '<span class="countdown">';
109            $renderer->doc .= $msg; // sprintf("%d", $dt_days) . ' ';
110
111            //if ($this->getConf('business_days') > 0) $renderer->doc .= sprintf("(%u)", $difdata['bdays']). ' ';
112            //$renderer->doc .= ($difdata['days'] == 1) ? $this->getLang('oneday') : $this->getLang('days');
113            //$renderer->doc .= ' ';
114            // if with_hours, ... hh:mm left ...
115            //if ($this->getConf('with_hours') ) $renderer->doc .= sprintf('%02d',$difdata['hours']). ':'. sprintf('%02d',$difdata['minutes']) . ' ';
116            // "since" or "until"
117            //$renderer->doc .= ($difdata['distance'] < 0) ? $this->getLang('since') : $this->getLang('until');
118            //$renderer->doc .= ;
119
120
121            //$renderer->doc .= $pdesc;
122            // output date?
123            if ($this->getConf('include_date') ) {
124                $renderer->doc .= " (" . strftime($this->getLang('outputformat'), $Next->getTimestamp()) . ")";
125            }
126            // end tag
127            $renderer->doc .= '</span><!-- countdown end -->'. DW_LF;
128            return true;
129        }
130        else if ('text' === $mode) {
131            $renderer->doc .= sprintf("%u %s %02u:%02u %s"
132            , abs($difdata['days']), ($difdata['days'] == 1 ? $this->getLang('oneday') : $this->getLang('days') )
133            , $difdata['hours'], $difdata['minutes']
134            , ($difdata['distance'] < 0 ? $this->getLang('since') : $this->getLang('until') )
135            );
136            $renderer->doc .= ' '. p_render('text', p_get_instructions($description), $info);
137            if ($this->getConf('include_date') ) {
138                $renderer->doc .= " (" . strftime($this->getLang('outputformat'), $thatDate) . ")";
139            }
140            return true;
141        }
142        else
143        {
144            // no adequate renderer found
145        }
146        return false;
147    }
148
149    /**
150     @brief Returns an array with the time difference to the specified timestamp.
151    **/
152    private function compute ($targetdate) {
153        // compute date difference in days: 86400 = 24*60*60 = seconds of one day
154        $t= time();
155        $diffseconds = $targetdate - $t;
156        $difference = $diffseconds / 86400;
157
158        return array(
159            'distance' => $difference,
160            'days'     => abs(($difference)),
161            'bdays'    => $this->bdaysbetween( $t, $targetdate, array() ),
162            'hours'    => abs($diffseconds / 3600) % 24,
163            'minutes'  => abs($diffseconds / 60) % 60,
164            'seconds'  => abs($diffseconds ) % 60
165            );
166    }
167
168    /** @fn bdaysbetween
169        @ brief Business days between date1 and date2
170        Lifted from php.net
171    **/
172    private function bdaysbetween($startDate,$endDate,$holidays) {
173    //The total number of days between the two dates. We compute the no. of seconds and divide it to 60*60*24
174    //We add one to inlude both dates in the interval.
175    $days = ($endDate - $startDate) / 86400 + 1;
176
177    $no_full_weeks = floor($days / 7);
178    $no_remaining_days = fmod($days, 7);
179
180    //It will return 1 if it's Monday,.. ,7 for Sunday
181    $the_first_day_of_week = date("N",$startDate);
182    $the_last_day_of_week = date("N",$endDate);
183
184    //-->The two can be equal in leap years when february has 29 days,
185    //the equal sign is added here
186    //In the first case the whole interval is within a week,
187    //in the second case the interval falls in two weeks.
188    if ($the_first_day_of_week <= $the_last_day_of_week){
189        if ($the_first_day_of_week <= 6 && 6 <= $the_last_day_of_week) $no_remaining_days--;
190        if ($the_first_day_of_week <= 7 && 7 <= $the_last_day_of_week) $no_remaining_days--;
191    }
192    else{
193        if ($the_first_day_of_week <= 6) {
194        //In the case when the interval falls in two weeks, there will be a weekend for sure
195            $no_remaining_days = $no_remaining_days - 2;
196        }
197    }
198
199    //The no. of business days is: (number of weeks between the two dates) * (5 working days) + the remainder
200//---->february in none leap years gave a remainder of 0 but still calculated weekends between first and last day, this is one way to fix it
201   $workingDays = $no_full_weeks * 5;
202    if ($no_remaining_days > 0 )
203    {
204      $workingDays += $no_remaining_days;
205    }
206
207    //We subtract the holidays
208    foreach($holidays as $holiday){
209        $time_stamp=strtotime($holiday);
210        //If the holiday doesn't fall in weekend
211        if ($startDate <= $time_stamp && $time_stamp <= $endDate && date("N",$time_stamp) != 6 && date("N",$time_stamp) != 7)
212            $workingDays--;
213    }
214
215    return $workingDays;
216    }
217
218
219}
220