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

RevLine 
[2146]1<?php
[3731]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
[3730]16if (!defined('DC_RC_PATH')) {return;}
[2146]17
[2216]18class dcStore
[2146]19{
[3730]20    /** @var    object    dcCore instance */
21    public $core;
22    /** @var    object    dcModules instance */
23    public $modules;
[2146]24
[3730]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);
[2222]27
[3730]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;
[2146]34
[3730]35    /**
36     * Constructor.
37     *
38     * @param    object    $modules        dcModules instance
39     * @param    string    $xml_url        XML feed URL
[3812]40     * @param    boolean   $force          Force query repository
[3730]41     */
[3812]42    public function __construct(dcModules $modules, $xml_url, $force = false)
[3730]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);
[2215]48
[3812]49        $this->check($force);
[3730]50    }
[2146]51
[3730]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        }
[2146]66
[3730]67        $raw_datas = $parser->getModules();
[2146]68
[3730]69        uasort($raw_datas, array('self', 'sort'));
[2146]70
[3730]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        }
[2146]77
[3730]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        }
[2146]91
[3730]92        $this->data = array(
93            'new'    => $raw_datas,
94            'update' => $updates
95        );
[2146]96
[3730]97        return true;
98    }
[2146]99
[3730]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    }
[2146]110
[3730]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();
[2146]128
[3730]129        # Split query into small clean words
130        if (!($patterns = self::patternize($pattern))) {
131            return $result;
132        }
[2148]133
[3730]134        # For each modules
135        foreach ($this->data['new'] as $id => $module) {
136            $module['id'] = $id;
[2148]137
[3730]138            # Loop through required module fields
139            foreach (self::$weighting as $field => $weight) {
[2148]140
[3730]141                # Skip fields which not exsist on module
142                if (empty($module[$field])) {
143                    continue;
144                }
[2222]145
[3730]146                # Split field value into small clean word
147                if (!($subjects = self::patternize($module[$field]))) {
148                    continue;
149                }
[2222]150
[3730]151                # Check contents
152                if (!($nb = preg_match_all('/(' . implode('|', $patterns) . ')/', implode(' ', $subjects), $_))) {
153                    continue;
154                }
[2222]155
[3730]156                # Add module to result
157                if (!isset($sorter[$id])) {
158                    $sorter[$id] = 0;
159                    $result[$id] = $module;
160                }
[2222]161
[3730]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    }
[2146]173
[3730]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    }
[2149]186
[3730]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    }
[2146]216
[3730]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    }
[2146]228
[3730]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    }
[2146]238
[3730]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();
[2222]248
[3730]249        foreach (explode(' ', $str) as $_) {
250            $_ = strtolower(preg_replace('/[^A-Za-z0-9]/', '', $_));
251            if (strlen($_) > 2) {
252                $arr[] = $_;
253            }
254        }
[2222]255
[3730]256        return empty($arr) ? false : $arr;
257    }
[2148]258
[3730]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    }
[2146]275
[3730]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    }
[2146]292}
Note: See TracBrowser for help on using the repository browser.

Sites map