xref: /dokuwiki/lib/plugins/popularity/helper.php (revision 3213bf4e5dd55220bd7614bf0030e6e680b4c227)
1<?php
2/**
3 * Popularity Feedback Plugin
4 *
5 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 */
7
8class helper_plugin_popularity extends Dokuwiki_Plugin
9{
10    /**
11     * The url where the data should be sent
12     */
13    public $submitUrl = 'http://update.dokuwiki.org/popularity.php';
14
15    /**
16     * Name of the file which determine if the the autosubmit is enabled,
17     * and when it was submited for the last time
18     */
19    public $autosubmitFile;
20
21    /**
22     * File where the last error which happened when we tried to autosubmit, will be log
23     */
24    public $autosubmitErrorFile;
25
26    /**
27     * Name of the file which determine when the popularity data was manually
28     * submitted for the last time
29     * (If this file doesn't exist, the data has never been sent)
30     */
31    public $popularityLastSubmitFile;
32
33    /**
34     * helper_plugin_popularity constructor.
35     */
36    public function __construct()
37    {
38        global $conf;
39        $this->autosubmitFile = $conf['cachedir'].'/autosubmit.txt';
40        $this->autosubmitErrorFile = $conf['cachedir'].'/autosubmitError.txt';
41        $this->popularityLastSubmitFile = $conf['cachedir'].'/lastSubmitTime.txt';
42    }
43
44    /**
45     * Check if autosubmit is enabled
46     *
47     * @return boolean TRUE if we should send data once a month, FALSE otherwise
48     */
49    public function isAutoSubmitEnabled()
50    {
51        return file_exists($this->autosubmitFile);
52    }
53
54    /**
55     * Send the data, to the submit url
56     *
57     * @param string $data The popularity data
58     * @return string An empty string if everything worked fine, a string describing the error otherwise
59     */
60    public function sendData($data)
61    {
62        $error = '';
63        $httpClient = new DokuHTTPClient();
64        $status = $httpClient->sendRequest($this->submitUrl, array('data' => $data), 'POST');
65        if (! $status) {
66            $error = $httpClient->error;
67        }
68        return $error;
69    }
70
71    /**
72     * Compute the last time the data was sent. If it has never been sent, we return 0.
73     *
74     * @return int
75     */
76    public function lastSentTime()
77    {
78        $manualSubmission = @filemtime($this->popularityLastSubmitFile);
79        $autoSubmission   = @filemtime($this->autosubmitFile);
80
81        return max((int) $manualSubmission, (int) $autoSubmission);
82    }
83
84    /**
85     * Gather all information
86     *
87     * @return string The popularity data as a string
88     */
89    public function gatherAsString()
90    {
91        $data = $this->gather();
92        $string = '';
93        foreach ($data as $key => $val) {
94            if (is_array($val)) foreach ($val as $v) {
95                $string .=  hsc($key)."\t".hsc($v)."\n";
96            } else {
97                $string .= hsc($key)."\t".hsc($val)."\n";
98            }
99        }
100        return $string;
101    }
102
103    /**
104     * Gather all information
105     *
106     * @return array The popularity data as an array
107     */
108    protected function gather()
109    {
110        global $conf;
111        /** @var $auth DokuWiki_Auth_Plugin */
112        global $auth;
113        $data = array();
114        $phptime = ini_get('max_execution_time');
115        @set_time_limit(0);
116        $pluginInfo = $this->getInfo();
117
118        // version
119        $data['anon_id'] = md5(auth_cookiesalt());
120        $data['version'] = getVersion();
121        $data['popversion'] = $pluginInfo['date'];
122        $data['language'] = $conf['lang'];
123        $data['now']      = time();
124        $data['popauto']  = (int) $this->isAutoSubmitEnabled();
125
126        // some config values
127        $data['conf_useacl']   = $conf['useacl'];
128        $data['conf_authtype'] = $conf['authtype'];
129        $data['conf_template'] = $conf['template'];
130
131        // number and size of pages
132        $list = array();
133        search($list, $conf['datadir'], array($this, 'searchCountCallback'), array('all'=>false), '');
134        $data['page_count']    = $list['file_count'];
135        $data['page_size']     = $list['file_size'];
136        $data['page_biggest']  = $list['file_max'];
137        $data['page_smallest'] = $list['file_min'];
138        $data['page_nscount']  = $list['dir_count'];
139        $data['page_nsnest']   = $list['dir_nest'];
140        if ($list['file_count']) $data['page_avg'] = $list['file_size'] / $list['file_count'];
141        $data['page_oldest']   = $list['file_oldest'];
142        unset($list);
143
144        // number and size of media
145        $list = array();
146        search($list, $conf['mediadir'], array($this, 'searchCountCallback'), array('all'=>true));
147        $data['media_count']    = $list['file_count'];
148        $data['media_size']     = $list['file_size'];
149        $data['media_biggest']  = $list['file_max'];
150        $data['media_smallest'] = $list['file_min'];
151        $data['media_nscount']  = $list['dir_count'];
152        $data['media_nsnest']   = $list['dir_nest'];
153        if ($list['file_count']) $data['media_avg'] = $list['file_size'] / $list['file_count'];
154        unset($list);
155
156        // number and size of cache
157        $list = array();
158        search($list, $conf['cachedir'], array($this, 'searchCountCallback'), array('all'=>true));
159        $data['cache_count']    = $list['file_count'];
160        $data['cache_size']     = $list['file_size'];
161        $data['cache_biggest']  = $list['file_max'];
162        $data['cache_smallest'] = $list['file_min'];
163        if ($list['file_count']) $data['cache_avg'] = $list['file_size'] / $list['file_count'];
164        unset($list);
165
166        // number and size of index
167        $list = array();
168        search($list, $conf['indexdir'], array($this, 'searchCountCallback'), array('all'=>true));
169        $data['index_count']    = $list['file_count'];
170        $data['index_size']     = $list['file_size'];
171        $data['index_biggest']  = $list['file_max'];
172        $data['index_smallest'] = $list['file_min'];
173        if ($list['file_count']) $data['index_avg'] = $list['file_size'] / $list['file_count'];
174        unset($list);
175
176        // number and size of meta
177        $list = array();
178        search($list, $conf['metadir'], array($this, 'searchCountCallback'), array('all'=>true));
179        $data['meta_count']    = $list['file_count'];
180        $data['meta_size']     = $list['file_size'];
181        $data['meta_biggest']  = $list['file_max'];
182        $data['meta_smallest'] = $list['file_min'];
183        if ($list['file_count']) $data['meta_avg'] = $list['file_size'] / $list['file_count'];
184        unset($list);
185
186        // number and size of attic
187        $list = array();
188        search($list, $conf['olddir'], array($this, 'searchCountCallback'), array('all'=>true));
189        $data['attic_count']    = $list['file_count'];
190        $data['attic_size']     = $list['file_size'];
191        $data['attic_biggest']  = $list['file_max'];
192        $data['attic_smallest'] = $list['file_min'];
193        if ($list['file_count']) $data['attic_avg'] = $list['file_size'] / $list['file_count'];
194        $data['attic_oldest']   = $list['file_oldest'];
195        unset($list);
196
197        // user count
198        if ($auth && $auth->canDo('getUserCount')) {
199            $data['user_count'] = $auth->getUserCount();
200        }
201
202        // calculate edits per day
203        $list = @file($conf['metadir'].'/_dokuwiki.changes');
204        $count = count($list);
205        if ($count > 2) {
206            $first = (int) substr(array_shift($list), 0, 10);
207            $last  = (int) substr(array_pop($list), 0, 10);
208            $dur = ($last - $first)/(60*60*24); // number of days in the changelog
209            $data['edits_per_day'] = $count/$dur;
210        }
211        unset($list);
212
213        // plugins
214        $data['plugin'] = plugin_list();
215
216        // pcre info
217        if (defined('PCRE_VERSION')) $data['pcre_version'] = PCRE_VERSION;
218        $data['pcre_backtrack'] = ini_get('pcre.backtrack_limit');
219        $data['pcre_recursion'] = ini_get('pcre.recursion_limit');
220
221        // php info
222        $data['os'] = PHP_OS;
223        $data['webserver'] = $_SERVER['SERVER_SOFTWARE'];
224        $data['php_version'] = phpversion();
225        $data['php_sapi'] = php_sapi_name();
226        $data['php_memory'] = php_to_byte(ini_get('memory_limit'));
227        $data['php_exectime'] = $phptime;
228        $data['php_extension'] = get_loaded_extensions();
229
230        // plugin usage data
231        $this->addPluginUsageData($data);
232
233        return $data;
234    }
235
236    /**
237     * Triggers event to let plugins add their own data
238     *
239     * @param $data
240     */
241    protected function addPluginUsageData(&$data)
242    {
243        $pluginsData = array();
244        trigger_event('PLUGIN_POPULARITY_DATA_SETUP', $pluginsData);
245        foreach ($pluginsData as $plugin => $d) {
246            if (is_array($d)) {
247                foreach ($d as $key => $value) {
248                    $data['plugin_' . $plugin . '_' . $key] = $value;
249                }
250            } else {
251                $data['plugin_' . $plugin] = $d;
252            }
253        }
254    }
255
256    /**
257     * Callback to search and count the content of directories in DokuWiki
258     *
259     * @param array &$data  Reference to the result data structure
260     * @param string $base  Base usually $conf['datadir']
261     * @param string $file  current file or directory relative to $base
262     * @param string $type  Type either 'd' for directory or 'f' for file
263     * @param int    $lvl   Current recursion depht
264     * @param array  $opts  option array as given to search()
265     * @return bool
266     */
267    protected function searchCountCallback(&$data, $base, $file, $type, $lvl, $opts)
268    {
269        // traverse
270        if ($type == 'd') {
271            if ($data['dir_nest'] < $lvl) $data['dir_nest'] = $lvl;
272            $data['dir_count']++;
273            return true;
274        }
275
276        //only search txt files if 'all' option not set
277        if ($opts['all'] || substr($file, -4) == '.txt') {
278            $size = filesize($base.'/'.$file);
279            $date = filemtime($base.'/'.$file);
280            $data['file_count']++;
281            $data['file_size'] += $size;
282            if (!isset($data['file_min']) || $data['file_min'] > $size) $data['file_min'] = $size;
283            if ($data['file_max'] < $size) $data['file_max'] = $size;
284            if (!isset($data['file_oldest']) || $data['file_oldest'] > $date) $data['file_oldest'] = $date;
285        }
286
287        return false;
288    }
289}
290