1<?php
2/**
3 * DokuWiki Plugin tos (Action Component)
4 *
5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
6 * @author  Andreas Gohr <dokuwiki@cosmocode.de>
7 */
8
9class action_plugin_tos extends DokuWiki_Action_Plugin
10{
11
12    const FORMFIELD = 'plugin__tos_accept';
13
14    /**
15     * @inheritDoc
16     */
17    public function register(Doku_Event_Handler $controller)
18    {
19        $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'checkTosAccept');
20        $controller->register_hook('TPL_ACT_UNKNOWN', 'BEFORE', $this, 'showTos');
21    }
22
23    /**
24     * Check if the TOCs have been accepted and update the state if needed
25     *
26     * @param Doku_Event $event event object by reference
27     * @param mixed $param
28     * @return void
29     */
30    public function checkTosAccept(Doku_Event $event, $param)
31    {
32        global $INPUT;
33        global $INFO;
34        $user = $INPUT->server->str('REMOTE_USER');
35        if ($user === '') return;
36        $act = act_clean($event->data);
37        if ($act === 'logout') return;
38        if ($INFO['ismanager']) return;
39
40        // if user accepted the TOCs right now, no further checks needed
41        if ($INPUT->bool(self::FORMFIELD)) {
42            $this->userTosState($user, true);
43            $event->data = 'redirect';
44            return;
45        }
46
47        // ensure newest TOCs have been accepted before
48        $newest = $this->newestTOS();
49        if (!$newest) return; // we don't have a valid TOS, yet
50        $accepted = $this->userTosState($user);
51        if ($accepted >= $newest) return;
52
53        $event->data = 'plugin_tos';
54    }
55
56    /**
57     * Display the TOS and ask to accept
58     *
59     * @param Doku_Event $event event object by reference
60     * @param mixed $param
61     * @return void
62     */
63    public function showTos(Doku_Event $event, $param)
64    {
65        global $ID;
66        global $INPUT;
67
68        if ($event->data !== 'plugin_tos') return;
69        $event->preventDefault();
70
71        echo '<div class="plugin-tos">';
72        echo $this->locale_xhtml('intro');
73
74        $accepted = $this->userTosState($INPUT->server->str('REMOTE_USER'));
75        if ($accepted) {
76            echo '<label for="plugin__tos_showdiff">';
77            echo sprintf($this->getLang('showdiff'), dformat($accepted, '%f'));
78            echo '</label>';
79            echo '<input type="checkbox" id="plugin__tos_showdiff">';
80            echo $this->diffTos($accepted);
81        }
82
83        echo '<div class="tos-content">';
84        echo p_wiki_xhtml($this->getConf('tos'));
85        echo '</div>';
86
87        echo '<ul class="tos-form">';
88        echo '<li class="tos-nope"><a href="' . wl($ID,
89                ['do' => 'logout', 'sectok'=>getSecurityToken()]) . '">' . $this->getLang('nope') . '</a></li>';
90        echo '<li class="tos-accept"><a href="' . wl($ID,
91                [self::FORMFIELD => '1']) . '">' . $this->getLang('accept') . '</a></li>';
92        echo '</ul>';
93
94        echo '</div>';
95    }
96
97    /**
98     * Get the most recent, non-minor revision of the TOS page
99     *
100     * @return int
101     */
102    protected function newestTOS()
103    {
104        $state = $this->sessionState();
105        if ($state) return $state;
106
107        $changes = new \dokuwiki\ChangeLog\PageChangeLog($this->getConf('tos'));
108        if (!$changes->hasRevisions()) return 0;
109
110        $start = -1; // also check current revision
111        while ($revisions = $changes->getRevisions($start, 25)) {
112            $start += count($revisions);
113            foreach ($revisions as $rev) {
114                $info = $changes->getRevisionInfo($rev);
115                if ($info['type'] === 'E' || $info['type'] === 'C') {
116                    return $this->sessionState($rev);
117                }
118            }
119        }
120
121        return 0;
122    }
123
124    /**
125     * Check and optionally update the last TOS-Acceptance
126     *
127     * @param string $user the user to check/update
128     * @param bool $accept set true is the user accepted the TOS right now
129     * @return int the last acceptance of the TOS
130     */
131    protected function userTosState($user, $accept = false)
132    {
133        $tosfile = getCacheName($user, '.tos');
134
135        if ($accept) {
136            io_makeFileDir($tosfile);
137            io_saveFile($tosfile, $user);
138        }
139
140        return (int)@filemtime($tosfile);
141    }
142
143    /**
144     * Read or write the current TOS time to the session
145     *
146     * @param int $set
147     * @return int 0 when no info is available
148     */
149    protected function sessionState($set = 0)
150    {
151        if (!$this->getConf('usesession')) return $set;
152
153        if ($set) {
154            session_start();
155            $_SESSION[self::FORMFIELD] = $set;
156            session_write_close();
157        }
158
159        if (isset($_SESSION[self::FORMFIELD])) {
160            return $_SESSION[self::FORMFIELD];
161        }
162        return 0;
163    }
164
165    /**
166     * Create a inline diff of changes since the last accept
167     *
168     * @param int $lastaccept when the TOS was last accepted
169     * @return string
170     */
171    protected function diffTos($lastaccept)
172    {
173        $change = new \dokuwiki\ChangeLog\PageChangeLog($this->getConf('tos'));
174        $oldrev = $change->getLastRevisionAt($lastaccept);
175        $old = rawWiki($this->getConf('tos'), $oldrev);
176        $new = rawWiki($this->getConf('tos'));
177        $diff = new Diff(explode("\n", $old), explode("\n", $new));
178        $formatter = new InlineDiffFormatter();
179
180        $html = '<div class="table tos-diff">';
181        $html .= '<table class="diff diff_inline">';
182        $html .= html_insert_softbreaks($formatter->format($diff));
183        $html .= '</table>';
184        $html .= '</div>';
185
186        return $html;
187    }
188
189}
190
191