|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
/** |
|
4
|
|
|
* Module channel classes. |
|
5
|
|
|
* |
|
6
|
|
|
* @package WordPointsOrg |
|
7
|
|
|
* @since 1.0.0 |
|
8
|
|
|
*/ |
|
9
|
|
|
|
|
10
|
|
|
/** |
|
11
|
|
|
* Stores a list of the available module channels. |
|
12
|
|
|
* |
|
13
|
|
|
* Modules are installed and updated through various channels. This class is used to |
|
14
|
|
|
* maintain a list of the available channels. |
|
15
|
|
|
* |
|
16
|
|
|
* @since 1.0.0 |
|
17
|
|
|
* |
|
18
|
|
|
* @see WordPoints_Module_Channel The object used to represent each channel. |
|
19
|
|
|
*/ |
|
20
|
|
View Code Duplication |
final class WordPoints_Module_Channels extends WordPoints_Container_Static { |
|
|
|
|
|
|
21
|
|
|
|
|
22
|
|
|
/** |
|
23
|
|
|
* @since 1.0.0 |
|
24
|
|
|
*/ |
|
25
|
|
|
protected static $instance; |
|
26
|
|
|
|
|
27
|
|
|
/** |
|
28
|
|
|
* @since 1.0.0 |
|
29
|
|
|
*/ |
|
30
|
|
|
protected $item_class = 'WordPoints_Module_Channel'; |
|
31
|
|
|
|
|
32
|
|
|
/** |
|
33
|
|
|
* Initialize the container. |
|
34
|
|
|
* |
|
35
|
|
|
* This function must be called before the container can be used. |
|
36
|
|
|
* |
|
37
|
|
|
* @since 1.0.0 |
|
38
|
|
|
* |
|
39
|
|
|
* @return bool True if the container was initialized, false if it was already. |
|
40
|
|
|
*/ |
|
41
|
|
|
public static function init() { |
|
42
|
|
|
|
|
43
|
|
|
if ( ! isset( self::$instance ) ) { |
|
44
|
|
|
|
|
45
|
|
|
self::$instance = new self; |
|
46
|
|
|
return true; |
|
47
|
|
|
} |
|
48
|
|
|
|
|
49
|
|
|
return false; |
|
50
|
|
|
} |
|
51
|
|
|
|
|
52
|
|
|
/** |
|
53
|
|
|
* @since 1.0.0 |
|
54
|
|
|
* |
|
55
|
|
|
* @see WordPoints_Container::_add() |
|
56
|
|
|
*/ |
|
57
|
|
|
public static function register( $slug, $item, $class = null ) { |
|
58
|
|
|
return self::$instance->_add( $slug, $item, $class ); |
|
59
|
|
|
} |
|
60
|
|
|
|
|
61
|
|
|
/** |
|
62
|
|
|
* @since 1.0.0 |
|
63
|
|
|
* |
|
64
|
|
|
* @see WordPoints_Container::_remove() |
|
65
|
|
|
*/ |
|
66
|
|
|
public static function deregister( $slug ) { |
|
67
|
|
|
return self::$instance->_remove( $slug ); |
|
68
|
|
|
} |
|
69
|
|
|
|
|
70
|
|
|
/** |
|
71
|
|
|
* @since 1.0.0 |
|
72
|
|
|
* |
|
73
|
|
|
* @see WordPoints_Container::_contains() |
|
74
|
|
|
*/ |
|
75
|
|
|
public static function is_registered( $slug ) { |
|
76
|
|
|
return self::$instance->_contains( $slug ); |
|
77
|
|
|
} |
|
78
|
|
|
|
|
79
|
|
|
/** |
|
80
|
|
|
* @since 1.0.0 |
|
81
|
|
|
* |
|
82
|
|
|
* @see WordPoints_Container::_get() |
|
83
|
|
|
* |
|
84
|
|
|
* @return WordPoints_Module_Channel[]|WordPoints_Module_Channel |
|
85
|
|
|
*/ |
|
86
|
|
|
public static function get( $slug = null ) { |
|
87
|
|
|
return self::$instance->_get( $slug ); |
|
88
|
|
|
} |
|
89
|
|
|
} |
|
90
|
|
|
WordPoints_Module_Channels::init(); |
|
91
|
|
|
|
|
92
|
|
|
/** |
|
93
|
|
|
* Module API channel class. |
|
94
|
|
|
* |
|
95
|
|
|
* A channel is similar to a channel on TV or radio. In this case, a channel is one |
|
96
|
|
|
* of many available module providers. It is a module repository, which offers |
|
97
|
|
|
* modules for download, and may also provide updates, etc. |
|
98
|
|
|
* |
|
99
|
|
|
* A channel object is usually created automatically based on the Channel header of |
|
100
|
|
|
* one of the installed modules. This file header specifies the channel URL which |
|
101
|
|
|
* should be used with that module. |
|
102
|
|
|
* |
|
103
|
|
|
* A channel is accessed through an API, which actually handles the requests to the |
|
104
|
|
|
* remote URL of this channel. This class is only intended to represent the channel |
|
105
|
|
|
* itself. |
|
106
|
|
|
* |
|
107
|
|
|
* @since 1.0.0 |
|
108
|
|
|
* |
|
109
|
|
|
* @property-read WordPoints_Container_Object $modules |
|
110
|
|
|
* @property-read string $url |
|
111
|
|
|
*/ |
|
112
|
|
|
final class WordPoints_Module_Channel { |
|
113
|
|
|
|
|
114
|
|
|
/** |
|
115
|
|
|
* The URL of this channel. |
|
116
|
|
|
* |
|
117
|
|
|
* This is the channel URL, though it usually doesn't include the scheme. |
|
118
|
|
|
* |
|
119
|
|
|
* @since 1.0.0 |
|
120
|
|
|
* |
|
121
|
|
|
* @var string |
|
122
|
|
|
*/ |
|
123
|
|
|
private $url; |
|
124
|
|
|
|
|
125
|
|
|
/** |
|
126
|
|
|
* The list of installed modules that use this channel. |
|
127
|
|
|
* |
|
128
|
|
|
* @since 1.0.0 |
|
129
|
|
|
* |
|
130
|
|
|
* @var WordPoints_Container_Object |
|
131
|
|
|
*/ |
|
132
|
|
|
private $modules; |
|
133
|
|
|
|
|
134
|
|
|
/** |
|
135
|
|
|
* Construct the class. |
|
136
|
|
|
* |
|
137
|
|
|
* @since 1.0.0 |
|
138
|
|
|
* |
|
139
|
|
|
* @param string $url The channel's URL, sans the scheme. |
|
140
|
|
|
*/ |
|
141
|
|
|
public function __construct( $url ) { |
|
142
|
|
|
|
|
143
|
|
|
$this->url = $url; |
|
144
|
|
|
$this->modules = new WordPoints_Container_Object; |
|
145
|
|
|
} |
|
146
|
|
|
|
|
147
|
|
|
/** |
|
148
|
|
|
* @since 1.0.0 |
|
149
|
|
|
*/ |
|
150
|
|
|
public function __get( $var ) { |
|
151
|
|
|
|
|
152
|
|
|
if ( 'modules' === $var || 'url' === $var ) { |
|
153
|
|
|
return $this->$var; |
|
154
|
|
|
} |
|
155
|
|
|
} |
|
156
|
|
|
|
|
157
|
|
|
/** |
|
158
|
|
|
* Get the full channel URL, including the HTTP scheme. |
|
159
|
|
|
* |
|
160
|
|
|
* @since 1.0.0 |
|
161
|
|
|
* |
|
162
|
|
|
* @return string The channel's full URL. |
|
163
|
|
|
*/ |
|
164
|
|
|
public function get_full_url() { |
|
165
|
|
|
|
|
166
|
|
|
$url = 'http://' . $this->url; |
|
167
|
|
|
|
|
168
|
|
|
if ( $this->is_ssl_accessible() ) { |
|
169
|
|
|
$url = set_url_scheme( $url, 'https' ); |
|
170
|
|
|
} |
|
171
|
|
|
|
|
172
|
|
|
return $url; |
|
173
|
|
|
} |
|
174
|
|
|
|
|
175
|
|
|
/** |
|
176
|
|
|
* Check if this channel is accessible over SSL. |
|
177
|
|
|
* |
|
178
|
|
|
* @since 1.0.0 |
|
179
|
|
|
* |
|
180
|
|
|
* @return bool Whether the channel URL can be accessed over SSL. |
|
181
|
|
|
*/ |
|
182
|
|
|
public function is_ssl_accessible() { |
|
183
|
|
|
|
|
184
|
|
|
$transient = 'wrdpnts_' . md5( "module_channel_supports_ssl-{$this->url}" ); |
|
185
|
|
|
|
|
186
|
|
|
$supports_ssl = get_site_transient( $transient ); |
|
187
|
|
|
|
|
188
|
|
|
// If the transient has expired. |
|
189
|
|
|
if ( false === $supports_ssl ) { |
|
190
|
|
|
|
|
191
|
|
|
// The cached value is an integer so we can tell when the transient has expired. |
|
192
|
|
|
$supports_ssl = 0; |
|
193
|
|
|
|
|
194
|
|
|
if ( wp_http_supports( array( 'ssl' ) ) ) { |
|
195
|
|
|
|
|
196
|
|
|
$response = wp_safe_remote_get( 'https://' . $this->url ); |
|
197
|
|
|
|
|
198
|
|
|
if ( ! is_wp_error( $response ) ) { |
|
199
|
|
|
|
|
200
|
|
|
$status = wp_remote_retrieve_response_code( $response ); |
|
201
|
|
|
|
|
202
|
|
|
if ( 200 === (int) $status || 401 === (int) $status ) { |
|
203
|
|
|
$supports_ssl = 1; |
|
204
|
|
|
} |
|
205
|
|
|
} |
|
206
|
|
|
} |
|
207
|
|
|
|
|
208
|
|
|
set_site_transient( $transient, $supports_ssl, WEEK_IN_SECONDS ); |
|
209
|
|
|
} |
|
210
|
|
|
|
|
211
|
|
|
return (bool) $supports_ssl; |
|
212
|
|
|
} |
|
213
|
|
|
|
|
214
|
|
|
/** |
|
215
|
|
|
* Get the API used by this channel. |
|
216
|
|
|
* |
|
217
|
|
|
* @since 1.0.0 |
|
218
|
|
|
* |
|
219
|
|
|
* @return WordPoints_Module_API|false The module API handler, or false if none available. |
|
220
|
|
|
*/ |
|
221
|
|
|
public function get_api() { |
|
222
|
|
|
|
|
223
|
|
|
// Check if there is a cached value available. |
|
224
|
|
|
$transient = 'wrdpnts_' . md5( "module_channel_supports-{$this->url}" ); |
|
225
|
|
|
|
|
226
|
|
|
$api = get_site_transient( $transient ); |
|
227
|
|
|
|
|
228
|
|
|
// If the transient has expired. |
|
229
|
|
|
if ( false === $api ) { |
|
230
|
|
|
|
|
231
|
|
|
// Get the API specified by the remote URL. |
|
232
|
|
|
$api = $this->get_api_header(); |
|
233
|
|
|
|
|
234
|
|
|
// Save it as a string, so we can tell when it has expired. |
|
235
|
|
|
set_site_transient( $transient, (string) $api, WEEK_IN_SECONDS ); |
|
236
|
|
|
} |
|
237
|
|
|
|
|
238
|
|
|
return WordPoints_Module_APIs::get( $api ); |
|
239
|
|
|
} |
|
240
|
|
|
|
|
241
|
|
|
/** |
|
242
|
|
|
* Retrieve and parse the module API header from the remote channel. |
|
243
|
|
|
* |
|
244
|
|
|
* The remote channel can specify the supported API by sending the |
|
245
|
|
|
* x-wordpoints-module-api header. This allows the API to be looked up with |
|
246
|
|
|
* a single HEAD request. |
|
247
|
|
|
* |
|
248
|
|
|
* @since 1.0.0 |
|
249
|
|
|
* |
|
250
|
|
|
* @return array|false The slug of the API specified in the header, or false |
|
251
|
|
|
* the channel doesn't set this header. |
|
252
|
|
|
*/ |
|
253
|
|
|
protected function get_api_header() { |
|
254
|
|
|
|
|
255
|
|
|
$headers = wp_get_http_headers( $this->get_full_url() ); |
|
256
|
|
|
|
|
257
|
|
|
if ( ! isset( $headers['x-wordpoints-module-api'] ) ) { |
|
258
|
|
|
return false; |
|
259
|
|
|
} |
|
260
|
|
|
|
|
261
|
|
|
return sanitize_key( $headers['x-wordpoints-module-api'] ); |
|
262
|
|
|
} |
|
263
|
|
|
} |
|
264
|
|
|
|
|
265
|
|
|
// EOF |
|
266
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.