Dotclear

source: inc/core/class.dc.store.php @ 3025:50e647316e34

Revision 3025:50e647316e34, 6.8 KB checked in by franck <carnet.franck.paul@…>, 10 years ago (diff)

Add  http:// scheme in not present in URL, better cope with netHttp::initClient() error, fixes #2091

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

Sites map