1<?php
2/**
3 * DokuWiki Plugin authorstats (Action Component)
4 *
5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
6 * @author  George Chatzisofroniou <sophron@latthi.com>
7 * @author  Constantinos Xanthopoulos <conx@xanthopoulos.info>
8 */
9
10// must be run within Dokuwiki
11if (!defined('DOKU_INC')) die();
12
13if (!defined('DOKU_LF')) define('DOKU_LF', "\n");
14if (!defined('DOKU_TAB')) define('DOKU_TAB', "\t");
15if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
16
17require_once DOKU_PLUGIN.'action.php';
18require_once DOKU_PLUGIN.'authorstats/helpers.php';
19
20class action_plugin_authorstats extends DokuWiki_Action_Plugin
21{
22
23    var $supportedModes = array('xhtml', 'metadata');
24
25    public function register(Doku_Event_Handler $controller)
26    {
27        $controller->register_hook('ACTION_SHOW_REDIRECT', 'BEFORE', $this, '_updateSavedStats');
28        $controller->register_hook('PARSER_CACHE_USE','BEFORE', $this, '_cachePrepare');
29        $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE',  $this, 'allow_show_author_pages');
30        $controller->register_hook('TPL_ACT_UNKNOWN', 'BEFORE',  $this, 'show_author_pages');
31    }
32
33    public function allow_show_author_pages(Doku_Event $event, $param) {
34        if($event->data != 'authorstats_pages') return;
35        $event->preventDefault();
36    }
37
38    public function show_author_pages(Doku_Event $event, $param) {
39        if($event->data != 'authorstats_pages') return;
40        $event->preventDefault();
41        // output your XHTML content here
42
43        $flags = explode(',', str_replace(" ", "", $this->getConf('pagelist_flags')));
44        $name  = hsc($_REQUEST['name']);
45        $usd = authorstatsReadUserJSON($name);
46        $ids = $usd["pages"][$_REQUEST['type']];
47
48        if ((!$pagelist = $this->loadHelper('pagelist'))) {
49            return false;
50        }
51
52        /* @var helper_plugin_pagelist $pagelist */
53        $pagelist->setFlags($flags);
54        $pagelist->startList();
55        foreach ($ids as $key => $value) {
56            $page = array('id' => urldecode($key));
57            $pagelist->addPage($page);
58        }
59        $type = "";
60        switch($_REQUEST['type']) {
61            case 'C':
62                $type = "Creates";
63	    break;
64            case 'E':
65                $type = "Edits";
66            break;
67            case 'e':
68                $type = "Minor edits";
69            break;
70            case 'D':
71                $type = "Deletes";
72            break;
73            case 'R':
74                $type = "Reverts";
75            break;
76        }
77        print '<h1>Pages[' . $type . ']: ' . userlink($_REQUEST['name']) . '</h1>' . DOKU_LF;
78        print '<div class="level1">' . DOKU_LF;
79        print $pagelist->finishList();
80        print '</div>' . DOKU_LF;
81
82    }
83
84    // Updates the saved statistics by checking the last lines
85    // in the /data/meta/ directory
86    public function _updateSavedStats()
87    {
88        global $conf;
89        $dir = $conf['metadir'] . '/';
90
91        authorstatsCreateDirIfMissing("data");
92
93        $conf_mtime = @filemtime(DOKU_CONF."local.php");
94
95        // Return the files in the directory /data/meta
96        $files = $this->_getChangeLogs($dir);
97
98        // Read saved data from JSON file
99        $sd = authorstatsReadJSON();
100
101        // Get last change
102        $lastchange = empty($sd) ?  (-1*PHP_INT_MAX )-1 : (int) $sd["lastchange"];
103
104	// Delete JSON files and update everything if config file has changed
105	if ($lastchange < $conf_mtime) {
106		$lastchange = (-1*PHP_INT_MAX)-1;
107		array_map("unlink", glob(DOKU_PLUGIN."authorstats/data/*.json"));
108		$sd = Array();
109	}
110        $newlast = $lastchange;
111        foreach ($files as $file)
112        {
113            $file_contents = array_reverse(file($file));
114            foreach($file_contents as $line)
115            {
116                $r = $this->_parseChange($line);
117                if ($r["timestamp"] <= $lastchange)
118                    break;
119
120                // Update the last if there is a more recent change
121                $newlast = max($newlast, $r["timestamp"]);
122
123                // If the author is not in the array, initialize his stats
124                if (!isset($sd["authors"][$r["author"]]))
125                {
126                    $sd["authors"][$r["author"]]["C"] = 0;
127                    $sd["authors"][$r["author"]]["E"] = 0;
128                    $sd["authors"][$r["author"]]["e"] = 0;
129                    $sd["authors"][$r["author"]]["D"] = 0;
130                    $sd["authors"][$r["author"]]["R"] = 0;
131                    $sd["authors"][$r["author"]]["pm"] = Array();
132                }
133                else
134                {
135                    // Initialize month if doesn't exist
136                    // else increment it
137                    if (!isset($sd["authors"][$r["author"]]["pm"][$r["date"]]))
138                        $sd["authors"][$r["author"]]["pm"][$r["date"]] = 1;
139                    else
140                        $sd["authors"][$r["author"]]["pm"][$r["date"]]++;
141                }
142                $sd["authors"][$r["author"]][$r["type"]]++;
143
144                if($r["author"] != "") {
145                    $usd = authorstatsReadUserJSON($r["author"]);
146                    $key = str_replace($dir, "", $file);
147                    $key = str_replace(".changes", "", $key);
148                    $key = str_replace("/", ":", $key);
149                    $usd["pages"][$r["type"]][$key] = 1;
150                    authorstatsSaveUserJSON($r["author"], $usd);
151                }
152            }
153        }
154        $sd["lastchange"] = $newlast;
155        authorstatsSaveJSON($sd);
156    }
157
158    // If the page is no more recent than the modification of the json file, refresh the page.
159    public function _cachePrepare(&$event, $param)
160    {
161        $cache =& $event->data;
162
163        if(!isset($cache->page)) return;
164        if(!isset($cache->mode) || !in_array($cache->mode, $this->supportedModes)) return;
165
166        $enabled = p_get_metadata($cache->page, 'authorstats-enabled');
167
168        if (isset($enabled))
169        {
170            if (@filemtime($cache->cache) < @filemtime(DOKU_PLUGIN."authorstats/data/authorstats.json"))
171            {
172                $event->preventDefault();
173                $event->stopPropagation();
174                $event->result = false;
175            }
176        }
177    }
178
179    function _getChangeLogs($dir, &$files = array())
180    {
181        if ($dh = opendir($dir))
182        {
183            while (($res = readdir($dh)) !== false)
184            {
185                if(is_dir($dir . $res . '/') && $res != '.' && $res != '..') array_merge($files, $this->_getChangeLogs($dir . $res . '/', $files));
186                else
187                {
188                    if (strpos($res, '.changes') !== false && $res[0] != '_') $files[] = $dir . $res;
189                }
190            }
191            closedir($dh);
192        }
193        return $files;
194    }
195
196    function _parseChange($line)
197    {
198        $record = Array();
199        $parts = explode(DOKU_TAB, $line);
200        if ($parts && $parts[4] != "")
201        {
202            $record["timestamp"] = $parts[0];
203            $record["type"] = $parts[2];
204            $record["author"] = $parts[4];
205            $record["date"] = date("Ym",$parts[0]);
206        }
207        return $record;
208    }
209
210}
211// vim:ts=4:sw=4:et:
212