Dotclear

source: inc/core/class.dc.store.php @ 3812:bc19aeafa1c2

Revision 3812:bc19aeafa1c2, 8.4 KB checked in by franck <carnet.franck.paul@…>, 7 years ago (diff)

Allows superadmin to force check update of plugins/themes

Line 
1<?php
2/**
3 * @brief Repository modules XML feed reader
4 *
5 * Provides an object to parse XML feed of modules from repository.
6 *
7 * @package Dotclear
8 * @subpackage Core
9 *
10 * @copyright Olivier Meunier & Association Dotclear
11 * @copyright GPL-2.0-only
12 *
13 * @since 2.6
14 */
15
16if (!defined('DC_RC_PATH')) {return;}
17
18class dcStore
19{
20    /** @var    object    dcCore instance */
21    public $core;
22    /** @var    object    dcModules instance */
23    public $modules;
24
25    /** @var    array    Modules fields to search on and their weighting */
26    public static $weighting = array('id' => 10, 'name' => 8, 'author' => 6, 'tags' => 4, 'desc' => 2);
27
28    /** @var    string    User agent used to query repository */
29    protected $user_agent = 'DotClear.org RepoBrowser/0.1';
30    /** @var    string    XML feed URL */
31    protected $xml_url;
32    /** @var    array    Array of new/update modules from repository */
33    protected $data;
34
35    /**
36     * Constructor.
37     *
38     * @param    object    $modules        dcModules instance
39     * @param    string    $xml_url        XML feed URL
40     * @param    boolean   $force          Force query repository
41     */
42    public function __construct(dcModules $modules, $xml_url, $force = false)
43    {
44        $this->core       = $modules->core;
45        $this->modules    = $modules;
46        $this->xml_url    = $xml_url;
47        $this->user_agent = sprintf('Dotclear/%s)', DC_VERSION);
48
49        $this->check($force);
50    }
51
52    /**
53     * Check repository.
54     *
55     * @param    boolean    $force        Force query repository
56     * @return    boolean    True if get feed or cache
57     */
58    public function check($force = false)
59    {
60        if (!$this->xml_url) {
61            return false;
62        }
63        if (($parser = dcStoreReader::quickParse($this->xml_url, DC_TPL_CACHE, $force)) === false) {
64            return false;
65        }
66
67        $raw_datas = $parser->getModules();
68
69        uasort($raw_datas, array('self', 'sort'));
70
71        $skipped = array_keys($this->modules->getDisabledModules());
72        foreach ($skipped as $p_id) {
73            if (isset($raw_datas[$p_id])) {
74                unset($raw_datas[$p_id]);
75            }
76        }
77
78        $updates = array();
79        $current = $this->modules->getModules();
80        foreach ($current as $p_id => $p_infos) {
81            if (isset($raw_datas[$p_id])) {
82                if (dcUtils::versionsCompare($raw_datas[$p_id]['version'], $p_infos['version'], '>')) {
83                    $updates[$p_id]                    = $raw_datas[$p_id];
84                    $updates[$p_id]['root']            = $p_infos['root'];
85                    $updates[$p_id]['root_writable']   = $p_infos['root_writable'];
86                    $updates[$p_id]['current_version'] = $p_infos['version'];
87                }
88                unset($raw_datas[$p_id]);
89            }
90        }
91
92        $this->data = array(
93            'new'    => $raw_datas,
94            'update' => $updates
95        );
96
97        return true;
98    }
99
100    /**
101     * Get a list of modules.
102     *
103     * @param    boolean    $update    True to get update modules, false for new ones
104     * @return    array    List of update/new modules
105     */
106    public function get($update = false)
107    {
108        return $this->data[$update ? 'update' : 'new'];
109    }
110
111    /**
112     * Search a module.
113     *
114     * Search string is cleaned, split and compare to split:
115     * - module id and clean id,
116     * - module name, clean name,
117     * - module desccription.
118     *
119     * Every time a part of query is find on module,
120     * result accuracy grow. Result is sorted by accuracy.
121     *
122     * @param    string    $pattern    String to search
123     * @return    array    Match modules
124     */
125    public function search($pattern)
126    {
127        $result = array();
128
129        # Split query into small clean words
130        if (!($patterns = self::patternize($pattern))) {
131            return $result;
132        }
133
134        # For each modules
135        foreach ($this->data['new'] as $id => $module) {
136            $module['id'] = $id;
137
138            # Loop through required module fields
139            foreach (self::$weighting as $field => $weight) {
140
141                # Skip fields which not exsist on module
142                if (empty($module[$field])) {
143                    continue;
144                }
145
146                # Split field value into small clean word
147                if (!($subjects = self::patternize($module[$field]))) {
148                    continue;
149                }
150
151                # Check contents
152                if (!($nb = preg_match_all('/(' . implode('|', $patterns) . ')/', implode(' ', $subjects), $_))) {
153                    continue;
154                }
155
156                # Add module to result
157                if (!isset($sorter[$id])) {
158                    $sorter[$id] = 0;
159                    $result[$id] = $module;
160                }
161
162                # Increment score by matches count * field weight
163                $sorter[$id] += $nb * $weight;
164                $result[$id]['score'] = $sorter[$id];
165            }
166        }
167        # Sort response by matches count
168        if (!empty($result)) {
169            array_multisort($sorter, SORT_DESC, $result);
170        }
171        return $result;
172    }
173
174    /**
175     * Quick download and install module.
176     *
177     * @param    string    $url    Module package URL
178     * @param    string    $dest    Path to install module
179     * @return    integer        1 = installed, 2 = update
180     */
181    public function process($url, $dest)
182    {
183        $this->download($url, $dest);
184        return $this->install($dest);
185    }
186
187    /**
188     * Download a module.
189     *
190     * @param    string    $url    Module package URL
191     * @param    string    $dest    Path to put module package
192     */
193    public function download($url, $dest)
194    {
195        // Check and add default protocol if necessary
196        if (!preg_match('%^http[s]?:\/\/%', $url)) {
197            $url = 'http://' . $url;
198        }
199        // Download package
200        if ($client = netHttp::initClient($url, $path)) {
201            try {
202                $client->setUserAgent($this->user_agent);
203                $client->useGzip(false);
204                $client->setPersistReferers(false);
205                $client->setOutput($dest);
206                $client->get($path);
207                unset($client);
208            } catch (Exception $e) {
209                unset($client);
210                throw new Exception(__('An error occurred while downloading the file.'));
211            }
212        } else {
213            throw new Exception(__('An error occurred while downloading the file.'));
214        }
215    }
216
217    /**
218     * Install a previously downloaded module.
219     *
220     * @param    string    $path    Module package URL
221     * @param    string    $path    Path to module package
222     * @return    integer        1 = installed, 2 = update
223     */
224    public function install($path)
225    {
226        return dcModules::installPackage($path, $this->modules);
227    }
228
229    /**
230     * User Agent String.
231     *
232     * @param    string    $str        User agent string
233     */
234    public function agent($str)
235    {
236        $this->user_agent = $str;
237    }
238
239    /**
240     * Split and clean pattern.
241     *
242     * @param    string    $str        String to sanitize
243     * @return    array    Array of cleaned pieces of string or false if none
244     */
245    public static function patternize($str)
246    {
247        $arr = array();
248
249        foreach (explode(' ', $str) as $_) {
250            $_ = strtolower(preg_replace('/[^A-Za-z0-9]/', '', $_));
251            if (strlen($_) > 2) {
252                $arr[] = $_;
253            }
254        }
255
256        return empty($arr) ? false : $arr;
257    }
258
259    /**
260     * Compare version.
261     *
262     * @param    string    $v1        Version
263     * @param    string    $v2        Version
264     * @param    string    $op        Comparison operator
265     * @return    boolean    True is comparison is true, dude!
266     */
267    private static function compare($v1, $v2, $op)
268    {
269        return version_compare(
270            preg_replace('!-r(\d+)$!', '-p$1', $v1),
271            preg_replace('!-r(\d+)$!', '-p$1', $v2),
272            $op
273        );
274    }
275
276    /**
277     * Sort modules list.
278     *
279     * @param    array    $a        A module
280     * @param    array    $b        A module
281     * @return    integer
282     */
283    private static function sort($a, $b)
284    {
285        $c = strtolower($a['id']);
286        $d = strtolower($b['id']);
287        if ($c == $d) {
288            return 0;
289        }
290        return ($c < $d) ? -1 : 1;
291    }
292}
Note: See TracBrowser for help on using the repository browser.

Sites map