danielebarchiesi@4
|
1 <?php
|
danielebarchiesi@4
|
2
|
danielebarchiesi@4
|
3 /**
|
danielebarchiesi@4
|
4 * @file
|
danielebarchiesi@4
|
5 * Features module drush integration.
|
danielebarchiesi@4
|
6 */
|
danielebarchiesi@4
|
7
|
danielebarchiesi@4
|
8 /**
|
danielebarchiesi@4
|
9 * Implements hook_drush_command().
|
danielebarchiesi@4
|
10 *
|
danielebarchiesi@4
|
11 * @return
|
danielebarchiesi@4
|
12 * An associative array describing your command(s).
|
danielebarchiesi@4
|
13 *
|
danielebarchiesi@4
|
14 * @see drush_parse_command()
|
danielebarchiesi@4
|
15 */
|
danielebarchiesi@4
|
16 function features_drush_command() {
|
danielebarchiesi@4
|
17 $items = array();
|
danielebarchiesi@4
|
18
|
danielebarchiesi@4
|
19 $items['features-list'] = array(
|
danielebarchiesi@4
|
20 'description' => "List all the available features for your site.",
|
danielebarchiesi@4
|
21 'options' => array(
|
danielebarchiesi@4
|
22 'status' => "Feature status, can be 'enabled', 'disabled' or 'all'",
|
danielebarchiesi@4
|
23 ),
|
danielebarchiesi@4
|
24 'drupal dependencies' => array('features'),
|
danielebarchiesi@4
|
25 'aliases' => array('fl', 'features'),
|
danielebarchiesi@4
|
26 );
|
danielebarchiesi@4
|
27 $items['features-export'] = array(
|
danielebarchiesi@4
|
28 'description' => "Export a feature from your site into a module.",
|
danielebarchiesi@4
|
29 'arguments' => array(
|
danielebarchiesi@4
|
30 'feature' => 'Feature name to export.',
|
danielebarchiesi@4
|
31 'components' => 'Patterns of components to include, see features-components for the format of patterns.'
|
danielebarchiesi@4
|
32 ),
|
danielebarchiesi@4
|
33 'options' => array(
|
danielebarchiesi@4
|
34 'destination' => "Destination path (from Drupal root) of the exported feature. Defaults to 'sites/all/modules/features'",
|
danielebarchiesi@4
|
35 'version-set' => "Specify a version number for the feature.",
|
danielebarchiesi@4
|
36 'version-increment' => "Increment the feature's version number.",
|
danielebarchiesi@4
|
37 ),
|
danielebarchiesi@4
|
38 'drupal dependencies' => array('features'),
|
danielebarchiesi@4
|
39 'aliases' => array('fe'),
|
danielebarchiesi@4
|
40 );
|
danielebarchiesi@4
|
41 $items['features-add'] = array(
|
danielebarchiesi@4
|
42 'description' => "Add a component to a feature module. (DEPRECATED: use features-export)",
|
danielebarchiesi@4
|
43 'arguments' => array(
|
danielebarchiesi@4
|
44 'feature' => 'Feature name to add to.',
|
danielebarchiesi@4
|
45 'components' => 'List of components to add.',
|
danielebarchiesi@4
|
46 ),
|
danielebarchiesi@4
|
47 'options' => array(
|
danielebarchiesi@4
|
48 'version-set' => "Specify a version number for the feature.",
|
danielebarchiesi@4
|
49 'version-increment' => "Increment the feature's version number.",
|
danielebarchiesi@4
|
50 ),
|
danielebarchiesi@4
|
51 'drupal dependencies' => array('features'),
|
danielebarchiesi@4
|
52 'aliases' => array('fa'),
|
danielebarchiesi@4
|
53 );
|
danielebarchiesi@4
|
54 $items['features-components'] = array(
|
danielebarchiesi@4
|
55 'description' => 'List features components.',
|
danielebarchiesi@4
|
56 'arguments' => array(
|
danielebarchiesi@4
|
57 'patterns' => 'The features components type to list. Omit this argument to list all components.',
|
danielebarchiesi@4
|
58 ),
|
danielebarchiesi@4
|
59 'options' => array(
|
danielebarchiesi@4
|
60 'exported' => array(
|
danielebarchiesi@4
|
61 'description' => 'Show only components that have been exported.',
|
danielebarchiesi@4
|
62 ),
|
danielebarchiesi@4
|
63 'not-exported' => array(
|
danielebarchiesi@4
|
64 'description' => 'Show only components that have not been exported.',
|
danielebarchiesi@4
|
65 ),
|
danielebarchiesi@4
|
66 ),
|
danielebarchiesi@4
|
67 'aliases' => array('fc'),
|
danielebarchiesi@4
|
68 );
|
danielebarchiesi@4
|
69 $items['features-update'] = array(
|
danielebarchiesi@4
|
70 'description' => "Update a feature module on your site.",
|
danielebarchiesi@4
|
71 'arguments' => array(
|
danielebarchiesi@4
|
72 'feature' => 'A space delimited list of features.',
|
danielebarchiesi@4
|
73 ),
|
danielebarchiesi@4
|
74 'options' => array(
|
danielebarchiesi@4
|
75 'version-set' => "Specify a version number for the feature.",
|
danielebarchiesi@4
|
76 'version-increment' => "Increment the feature's version number.",
|
danielebarchiesi@4
|
77 ),
|
danielebarchiesi@4
|
78 'drupal dependencies' => array('features'),
|
danielebarchiesi@4
|
79 'aliases' => array('fu'),
|
danielebarchiesi@4
|
80 );
|
danielebarchiesi@4
|
81 $items['features-update-all'] = array(
|
danielebarchiesi@4
|
82 'description' => "Update all feature modules on your site.",
|
danielebarchiesi@4
|
83 'arguments' => array(
|
danielebarchiesi@4
|
84 'feature_exclude' => 'A space-delimited list of features to exclude from being updated.',
|
danielebarchiesi@4
|
85 ),
|
danielebarchiesi@4
|
86 'drupal dependencies' => array('features'),
|
danielebarchiesi@4
|
87 'aliases' => array('fu-all', 'fua'),
|
danielebarchiesi@4
|
88 );
|
danielebarchiesi@4
|
89 $items['features-revert'] = array(
|
danielebarchiesi@4
|
90 'description' => "Revert a feature module on your site.",
|
danielebarchiesi@4
|
91 'arguments' => array(
|
danielebarchiesi@4
|
92 'feature' => 'A space delimited list of features or feature.component pairs.',
|
danielebarchiesi@4
|
93 ),
|
danielebarchiesi@4
|
94 'options' => array(
|
danielebarchiesi@4
|
95 'force' => "Force revert even if Features assumes components' state are default.",
|
danielebarchiesi@4
|
96 ),
|
danielebarchiesi@4
|
97 'examples' => array(
|
danielebarchiesi@4
|
98 'drush fr foo.node foo.taxonomy bar' => 'Revert node and taxonomy components of feature "foo", but only if they are overriden. Revert all overriden components of feature "bar".',
|
danielebarchiesi@4
|
99 'drush fr foo.node foo.taxonomy bar --force' => 'Revert node and taxonomy components of feature "foo". Revert all components of feature "bar".',
|
danielebarchiesi@4
|
100 ),
|
danielebarchiesi@4
|
101 'drupal dependencies' => array('features'),
|
danielebarchiesi@4
|
102 'aliases' => array('fr'),
|
danielebarchiesi@4
|
103 );
|
danielebarchiesi@4
|
104 $items['features-revert-all'] = array(
|
danielebarchiesi@4
|
105 'description' => "Revert all enabled feature module on your site.",
|
danielebarchiesi@4
|
106 'arguments' => array(
|
danielebarchiesi@4
|
107 'feature_exclude' => 'A space-delimited list of features to exclude from being reverted.',
|
danielebarchiesi@4
|
108 ),
|
danielebarchiesi@4
|
109 'options' => array(
|
danielebarchiesi@4
|
110 'force' => "Force revert even if Features assumes components' state are default.",
|
danielebarchiesi@4
|
111 ),
|
danielebarchiesi@4
|
112 'drupal dependencies' => array('features'),
|
danielebarchiesi@4
|
113 'aliases' => array('fr-all', 'fra'),
|
danielebarchiesi@4
|
114 );
|
danielebarchiesi@4
|
115 $items['features-diff'] = array(
|
danielebarchiesi@4
|
116 'description' => "Show the difference between the default and overridden state of a feature.",
|
danielebarchiesi@4
|
117 'arguments' => array(
|
danielebarchiesi@4
|
118 'feature' => 'The feature in question.',
|
danielebarchiesi@4
|
119 ),
|
danielebarchiesi@4
|
120 'options' => array(
|
danielebarchiesi@4
|
121 'lines' => 'Generate diffs with <n> lines of context instead of the usual two.',
|
danielebarchiesi@4
|
122 ),
|
danielebarchiesi@4
|
123 'drupal dependencies' => array('features', 'diff'),
|
danielebarchiesi@4
|
124 'aliases' => array('fd'),
|
danielebarchiesi@4
|
125 );
|
danielebarchiesi@4
|
126
|
danielebarchiesi@4
|
127 return $items;
|
danielebarchiesi@4
|
128 }
|
danielebarchiesi@4
|
129
|
danielebarchiesi@4
|
130 /**
|
danielebarchiesi@4
|
131 * Implements hook_drush_help().
|
danielebarchiesi@4
|
132 */
|
danielebarchiesi@4
|
133 function features_drush_help($section) {
|
danielebarchiesi@4
|
134 switch ($section) {
|
danielebarchiesi@4
|
135 case 'drush:features':
|
danielebarchiesi@4
|
136 return dt("List all the available features for your site.");
|
danielebarchiesi@4
|
137 case 'drush:features-export':
|
danielebarchiesi@4
|
138 return dt("Export a feature from your site into a module. If called with no arguments, display a list of available components. If called with a single argument, attempt to create a feature including the given component with the same name. The option '--destination=foo' may be used to specify the path (from Drupal root) where the feature should be created. The default destination is 'sites/all/modules/features'. The option '--version-set=foo' may be used to specify a version number for the feature or the option '--version-increment' may also to increment the feature's version number.");
|
danielebarchiesi@4
|
139 case 'drush:features-components':
|
danielebarchiesi@4
|
140 return dt("List feature components matching patterns. The listing may be limited to exported/not-exported components.
|
danielebarchiesi@4
|
141
|
danielebarchiesi@4
|
142 A component pattern consists of a source, a colon and a component. Both source and component may be a full name (as in \"dependencies\"), a shorthand (for instance \"dep\") or a pattern (like \"%denci%\").
|
danielebarchiesi@4
|
143
|
danielebarchiesi@4
|
144 Shorthands are unique shortenings of a name. They will only match if exactly one option contains the shorthand. So in a standard installation, \"dep\" will work for dependencies, but \"user\" wont, as it matches both user_permission and user_role.
|
danielebarchiesi@4
|
145
|
danielebarchiesi@4
|
146 Patterns uses * or % for matching multiple sources/components. Unlike shorthands, patterns must match the whole name, so \"field:%article%\" should be used to select all fields containing \"article\" (which could both be those on the node type article, as well as those fields named article). * and % are equivalent, but the latter doesn't have to be escaped in UNIX shells.
|
danielebarchiesi@4
|
147
|
danielebarchiesi@4
|
148 Lastly, a pattern without a colon is interpreted as having \":%\" appended, for easy listing of all components of a source.
|
danielebarchiesi@4
|
149 ");
|
danielebarchiesi@4
|
150 case 'drush:features-update':
|
danielebarchiesi@4
|
151 return dt("Update a feature module on your site. The option '--version-set=foo' may be used to specify a version number for the feature or the option '--version-increment' may also to increment the feature's version number.");
|
danielebarchiesi@4
|
152 case 'drush:features-update-all':
|
danielebarchiesi@4
|
153 return dt("Update all feature modules on your site.");
|
danielebarchiesi@4
|
154 case 'drush:features-revert':
|
danielebarchiesi@4
|
155 return dt("Revert a feature module on your site.");
|
danielebarchiesi@4
|
156 case 'drush:features-revert-all':
|
danielebarchiesi@4
|
157 return dt("Revert all enabled feature module on your site.");
|
danielebarchiesi@4
|
158 case 'drush:features-diff':
|
danielebarchiesi@4
|
159 return dt("Show a diff of a feature module.");
|
danielebarchiesi@4
|
160 case 'drush:features-add':
|
danielebarchiesi@4
|
161 return dt("Add a component to a feature module. The option '--version-set=foo' may be used to specify a version number for the feature or the option '--version-increment' may also to increment the feature's version number.");
|
danielebarchiesi@4
|
162 }
|
danielebarchiesi@4
|
163 }
|
danielebarchiesi@4
|
164
|
danielebarchiesi@4
|
165 /**
|
danielebarchiesi@4
|
166 * Get a list of all feature modules.
|
danielebarchiesi@4
|
167 */
|
danielebarchiesi@4
|
168 function drush_features_list() {
|
danielebarchiesi@4
|
169 $status = drush_get_option('status') ? drush_get_option('status') : 'all';
|
danielebarchiesi@4
|
170 if (!in_array($status, array('enabled', 'disabled', 'all'))) {
|
danielebarchiesi@4
|
171 return drush_set_error('', dt('!status is not valid', array('!status' => $status)));
|
danielebarchiesi@4
|
172 }
|
danielebarchiesi@4
|
173
|
danielebarchiesi@4
|
174 module_load_include('inc', 'features', 'features.export');
|
danielebarchiesi@4
|
175 $rows = array(array(dt('Name'), dt('Feature'), dt('Status'), dt('Version'), dt('State')));
|
danielebarchiesi@4
|
176
|
danielebarchiesi@4
|
177 // Sort the Features list before compiling the output.
|
danielebarchiesi@4
|
178 $features = features_get_features(NULL, TRUE);
|
danielebarchiesi@4
|
179 ksort($features);
|
danielebarchiesi@4
|
180
|
danielebarchiesi@4
|
181 foreach ($features as $k => $m) {
|
danielebarchiesi@4
|
182 switch (features_get_storage($m->name)) {
|
danielebarchiesi@4
|
183 case FEATURES_DEFAULT:
|
danielebarchiesi@4
|
184 case FEATURES_REBUILDABLE:
|
danielebarchiesi@4
|
185 $storage = '';
|
danielebarchiesi@4
|
186 break;
|
danielebarchiesi@4
|
187 case FEATURES_OVERRIDDEN:
|
danielebarchiesi@4
|
188 $storage = dt('Overridden');
|
danielebarchiesi@4
|
189 break;
|
danielebarchiesi@4
|
190 case FEATURES_NEEDS_REVIEW:
|
danielebarchiesi@4
|
191 $storage = dt('Needs review');
|
danielebarchiesi@4
|
192 break;
|
danielebarchiesi@4
|
193 }
|
danielebarchiesi@4
|
194 if (
|
danielebarchiesi@4
|
195 ($m->status == 0 && ($status == 'all' || $status == 'disabled')) ||
|
danielebarchiesi@4
|
196 ($m->status == 1 && ($status == 'all' || $status == 'enabled'))
|
danielebarchiesi@4
|
197 ) {
|
danielebarchiesi@4
|
198 $rows[] = array(
|
danielebarchiesi@4
|
199 $m->info['name'],
|
danielebarchiesi@4
|
200 $m->name,
|
danielebarchiesi@4
|
201 $m->status ? dt('Enabled') : dt('Disabled'),
|
danielebarchiesi@4
|
202 $m->info['version'],
|
danielebarchiesi@4
|
203 $storage
|
danielebarchiesi@4
|
204 );
|
danielebarchiesi@4
|
205 }
|
danielebarchiesi@4
|
206 }
|
danielebarchiesi@4
|
207 drush_print_table($rows, TRUE);
|
danielebarchiesi@4
|
208 }
|
danielebarchiesi@4
|
209
|
danielebarchiesi@4
|
210 /**
|
danielebarchiesi@4
|
211 * List components, with pattern matching.
|
danielebarchiesi@4
|
212 */
|
danielebarchiesi@4
|
213 function drush_features_components() {
|
danielebarchiesi@4
|
214 $args = func_get_args();
|
danielebarchiesi@4
|
215 $components = _drush_features_component_list();
|
danielebarchiesi@4
|
216 // If no args supplied, prompt with a list.
|
danielebarchiesi@4
|
217 if (empty($args)) {
|
danielebarchiesi@4
|
218 $types = array_keys($components);
|
danielebarchiesi@4
|
219 array_unshift($types, 'all');
|
danielebarchiesi@4
|
220 $choice = drush_choice($types, 'Enter a number to choose which component type to list.');
|
danielebarchiesi@4
|
221 if ($choice === FALSE) {
|
danielebarchiesi@4
|
222 return;
|
danielebarchiesi@4
|
223 }
|
danielebarchiesi@4
|
224
|
danielebarchiesi@4
|
225 $args = ($choice == 0) ? array('*') : array($types[$choice]);
|
danielebarchiesi@4
|
226 }
|
danielebarchiesi@4
|
227 $options = array(
|
danielebarchiesi@4
|
228 'provided by' => TRUE,
|
danielebarchiesi@4
|
229 );
|
danielebarchiesi@4
|
230 if (drush_get_option(array('exported', 'e'), NULL)) {
|
danielebarchiesi@4
|
231 $options['not exported'] = FALSE;
|
danielebarchiesi@4
|
232 }
|
danielebarchiesi@4
|
233 elseif (drush_get_option(array('not-exported', 'o'), NULL)) {
|
danielebarchiesi@4
|
234 $options['exported'] = FALSE;
|
danielebarchiesi@4
|
235 }
|
danielebarchiesi@4
|
236
|
danielebarchiesi@4
|
237 $filtered_components = _drush_features_component_filter($components, $args, $options);
|
danielebarchiesi@4
|
238 if ($filtered_components){
|
danielebarchiesi@4
|
239 _drush_features_component_print($filtered_components);
|
danielebarchiesi@4
|
240 }
|
danielebarchiesi@4
|
241 }
|
danielebarchiesi@4
|
242
|
danielebarchiesi@4
|
243 /**
|
danielebarchiesi@4
|
244 * Returns a listing of all known components, indexed by source.
|
danielebarchiesi@4
|
245 */
|
danielebarchiesi@4
|
246 function _drush_features_component_list() {
|
danielebarchiesi@4
|
247 $components = array();
|
danielebarchiesi@4
|
248 foreach (features_get_feature_components() as $source => $info) {
|
danielebarchiesi@4
|
249 if ($options = features_invoke($source, 'features_export_options')) {
|
danielebarchiesi@4
|
250 foreach ($options as $name => $title) {
|
danielebarchiesi@4
|
251 $components[$source][$name] = $title;
|
danielebarchiesi@4
|
252 }
|
danielebarchiesi@4
|
253 }
|
danielebarchiesi@4
|
254 }
|
danielebarchiesi@4
|
255 return $components;
|
danielebarchiesi@4
|
256 }
|
danielebarchiesi@4
|
257
|
danielebarchiesi@4
|
258 /**
|
danielebarchiesi@4
|
259 * Filters components by patterns.
|
danielebarchiesi@4
|
260 */
|
danielebarchiesi@4
|
261 function _drush_features_component_filter($all_components, $patterns = array(), $options = array()) {
|
danielebarchiesi@4
|
262 $options += array(
|
danielebarchiesi@4
|
263 'exported' => TRUE,
|
danielebarchiesi@4
|
264 'not exported' => TRUE,
|
danielebarchiesi@4
|
265 'provided by' => FALSE,
|
danielebarchiesi@4
|
266 );
|
danielebarchiesi@4
|
267 $pool = array();
|
danielebarchiesi@4
|
268 // Maps exported components to feature modules.
|
danielebarchiesi@4
|
269 $components_map = features_get_component_map();
|
danielebarchiesi@4
|
270 // First filter on exported state.
|
danielebarchiesi@4
|
271 foreach ($all_components as $source => $components) {
|
danielebarchiesi@4
|
272 foreach ($components as $name => $title) {
|
danielebarchiesi@4
|
273 $exported = sizeof($components_map[$source][$name]) > 0;
|
danielebarchiesi@4
|
274 if ($exported) {
|
danielebarchiesi@4
|
275 if ($options['exported']) {
|
danielebarchiesi@4
|
276 $pool[$source][$name] = $title;
|
danielebarchiesi@4
|
277 }
|
danielebarchiesi@4
|
278 }
|
danielebarchiesi@4
|
279 else {
|
danielebarchiesi@4
|
280 if ($options['not exported']) {
|
danielebarchiesi@4
|
281 $pool[$source][$name] = $title;
|
danielebarchiesi@4
|
282 }
|
danielebarchiesi@4
|
283 }
|
danielebarchiesi@4
|
284 }
|
danielebarchiesi@4
|
285 }
|
danielebarchiesi@4
|
286
|
danielebarchiesi@4
|
287 $state_string = '';
|
danielebarchiesi@4
|
288
|
danielebarchiesi@4
|
289 if (!$options['exported']) {
|
danielebarchiesi@4
|
290 $state_string = 'unexported';
|
danielebarchiesi@4
|
291 }
|
danielebarchiesi@4
|
292 elseif (!$options['not exported']) {
|
danielebarchiesi@4
|
293 $state_string = 'exported';
|
danielebarchiesi@4
|
294 }
|
danielebarchiesi@4
|
295
|
danielebarchiesi@4
|
296 $selected = array();
|
danielebarchiesi@4
|
297 foreach ($patterns as $pattern) {
|
danielebarchiesi@4
|
298 // Rewrite * to %. Let users use both as wildcard.
|
danielebarchiesi@4
|
299 $pattern = strtr($pattern, array('*' => '%'));
|
danielebarchiesi@4
|
300 $sources = array();
|
danielebarchiesi@4
|
301 list($source_pattern, $component_pattern) = explode(':', $pattern, 2);
|
danielebarchiesi@4
|
302 // If source is empty, use a pattern.
|
danielebarchiesi@4
|
303 if ($source_pattern == '') {
|
danielebarchiesi@4
|
304 $source_pattern = '%';
|
danielebarchiesi@4
|
305 }
|
danielebarchiesi@4
|
306 if ($component_pattern == '') {
|
danielebarchiesi@4
|
307 $component_pattern = '%';
|
danielebarchiesi@4
|
308 }
|
danielebarchiesi@4
|
309
|
danielebarchiesi@4
|
310 $preg_source_pattern = strtr(preg_quote($source_pattern, '/'), array('%' => '.*'));
|
danielebarchiesi@4
|
311 $preg_component_pattern = strtr(preg_quote($component_pattern, '/'), array('%' => '.*'));
|
danielebarchiesi@4
|
312 /*
|
danielebarchiesi@4
|
313 * If it isn't a pattern, but a simple string, we don't anchor the
|
danielebarchiesi@4
|
314 * pattern, this allows for abbreviating. Else, we do, as this seems more
|
danielebarchiesi@4
|
315 * natural for patterns.
|
danielebarchiesi@4
|
316 */
|
danielebarchiesi@4
|
317 if (strpos($source_pattern, '%') !== FALSE) {
|
danielebarchiesi@4
|
318 $preg_source_pattern = '^' . $preg_source_pattern . '$';
|
danielebarchiesi@4
|
319 }
|
danielebarchiesi@4
|
320 if (strpos($component_pattern, '%') !== FALSE) {
|
danielebarchiesi@4
|
321 $preg_component_pattern = '^' . $preg_component_pattern . '$';
|
danielebarchiesi@4
|
322 }
|
danielebarchiesi@4
|
323 $matches = array();
|
danielebarchiesi@4
|
324
|
danielebarchiesi@4
|
325 // Find the sources.
|
danielebarchiesi@4
|
326 $all_sources = array_keys($pool);
|
danielebarchiesi@4
|
327 $matches = preg_grep('/' . $preg_source_pattern . '/', $all_sources);
|
danielebarchiesi@4
|
328 if (sizeof($matches) > 0) {
|
danielebarchiesi@4
|
329 // If we have multiple matches and the source string wasn't a
|
danielebarchiesi@4
|
330 // pattern, check if one of the matches is equal to the pattern, and
|
danielebarchiesi@4
|
331 // use that, or error out.
|
danielebarchiesi@4
|
332 if (sizeof($matches) > 1 and $preg_source_pattern[0] != '^') {
|
danielebarchiesi@4
|
333 if (in_array($source_pattern, $matches)) {
|
danielebarchiesi@4
|
334 $matches = array($source_pattern);
|
danielebarchiesi@4
|
335 }
|
danielebarchiesi@4
|
336 else {
|
danielebarchiesi@4
|
337 return drush_set_error('', dt('Ambiguous source "!source", matches !matches', array('!source' => $source_pattern, '!matches' => join(', ', $matches))));
|
danielebarchiesi@4
|
338 }
|
danielebarchiesi@4
|
339 }
|
danielebarchiesi@4
|
340 // Loose the indexes preg_grep preserved.
|
danielebarchiesi@4
|
341 $sources = array_values($matches);
|
danielebarchiesi@4
|
342 }
|
danielebarchiesi@4
|
343 else {
|
danielebarchiesi@4
|
344 return drush_set_error('', dt('No !state sources match "!source"', array('!state' => $state_string, '!source' => $source_pattern)));
|
danielebarchiesi@4
|
345 }
|
danielebarchiesi@4
|
346
|
danielebarchiesi@4
|
347
|
danielebarchiesi@4
|
348 // Now find the components.
|
danielebarchiesi@4
|
349 foreach ($sources as $source) {
|
danielebarchiesi@4
|
350 // Find the components.
|
danielebarchiesi@4
|
351 $all_components = array_keys($pool[$source]);
|
danielebarchiesi@4
|
352 // See if there's any matches.
|
danielebarchiesi@4
|
353 $matches = preg_grep('/' . $preg_component_pattern . '/', $all_components);
|
danielebarchiesi@4
|
354 if (sizeof($matches) > 0) {
|
danielebarchiesi@4
|
355 // If we have multiple matches and the components string wasn't a
|
danielebarchiesi@4
|
356 // pattern, check if one of the matches is equal to the pattern, and
|
danielebarchiesi@4
|
357 // use that, or error out.
|
danielebarchiesi@4
|
358 if (sizeof($matches) > 1 and $preg_component_pattern[0] != '^') {
|
danielebarchiesi@4
|
359 if (in_array($component_pattern, $matches)) {
|
danielebarchiesi@4
|
360 $matches = array($component_pattern);
|
danielebarchiesi@4
|
361 }
|
danielebarchiesi@4
|
362 else {
|
danielebarchiesi@4
|
363 return drush_set_error('', dt('Ambiguous component "!component", matches !matches', array('!component' => $component_pattern, '!matches' => join(', ', $matches))));
|
danielebarchiesi@4
|
364 }
|
danielebarchiesi@4
|
365 }
|
danielebarchiesi@4
|
366 if (!is_array($selected[$source])) {
|
danielebarchiesi@4
|
367 $selected[$source] = array();
|
danielebarchiesi@4
|
368 }
|
danielebarchiesi@4
|
369 $selected[$source] += array_intersect_key($pool[$source], array_flip($matches));
|
danielebarchiesi@4
|
370 }
|
danielebarchiesi@4
|
371 else {
|
danielebarchiesi@4
|
372 // No matches. If the source was a pattern, just carry on, else
|
danielebarchiesi@4
|
373 // error out. Allows for patterns like :*field*
|
danielebarchiesi@4
|
374 if ($preg_source_pattern[0] != '^') {
|
danielebarchiesi@4
|
375 return drush_set_error('', dt('No !state !source components match "!component"', array('!state' => $state_string, '!component' => $component_pattern, '!source' => $source)));
|
danielebarchiesi@4
|
376 }
|
danielebarchiesi@4
|
377 }
|
danielebarchiesi@4
|
378 }
|
danielebarchiesi@4
|
379 }
|
danielebarchiesi@4
|
380
|
danielebarchiesi@4
|
381 // Lastly, provide feature module information on the selected components, if
|
danielebarchiesi@4
|
382 // requested.
|
danielebarchiesi@4
|
383 $provided_by = array();
|
danielebarchiesi@4
|
384 if ($options['provided by'] && $options['exported'] ) {
|
danielebarchiesi@4
|
385 foreach ($selected as $source => $components) {
|
danielebarchiesi@4
|
386 foreach ($components as $name => $title) {
|
danielebarchiesi@4
|
387 $exported = sizeof($components_map[$source][$name]) > 0;
|
danielebarchiesi@4
|
388 if ($exported) {
|
danielebarchiesi@4
|
389 $provided_by[$source . ':' . $name] = join(', ', $components_map[$source][$name]);
|
danielebarchiesi@4
|
390 }
|
danielebarchiesi@4
|
391 }
|
danielebarchiesi@4
|
392 }
|
danielebarchiesi@4
|
393 }
|
danielebarchiesi@4
|
394
|
danielebarchiesi@4
|
395 return array(
|
danielebarchiesi@4
|
396 'components' => $selected,
|
danielebarchiesi@4
|
397 'sources' => $provided_by,
|
danielebarchiesi@4
|
398 );
|
danielebarchiesi@4
|
399 }
|
danielebarchiesi@4
|
400
|
danielebarchiesi@4
|
401 /**
|
danielebarchiesi@4
|
402 * Prints a list of filtered components.
|
danielebarchiesi@4
|
403 */
|
danielebarchiesi@4
|
404 function _drush_features_component_print($filtered_components) {
|
danielebarchiesi@4
|
405 $rows = array(array(dt('Available sources')));
|
danielebarchiesi@4
|
406 foreach ($filtered_components['components'] as $source => $components) {
|
danielebarchiesi@4
|
407 foreach ($components as $name => $value) {
|
danielebarchiesi@4
|
408 $row = array($source .':'. $name);
|
danielebarchiesi@4
|
409 if (isset($filtered_components['sources'][$source .':'. $name])) {
|
danielebarchiesi@4
|
410 $row[] = dt('Provided by') . ': ' . $filtered_components['sources'][$source .':'. $name];
|
danielebarchiesi@4
|
411 }
|
danielebarchiesi@4
|
412 $rows[] = $row;
|
danielebarchiesi@4
|
413 }
|
danielebarchiesi@4
|
414 }
|
danielebarchiesi@4
|
415
|
danielebarchiesi@4
|
416 drush_print_table($rows, TRUE);
|
danielebarchiesi@4
|
417 }
|
danielebarchiesi@4
|
418
|
danielebarchiesi@4
|
419 /**
|
danielebarchiesi@4
|
420 * Add a component to a features module, or create a new module with
|
danielebarchiesi@4
|
421 * the selected components.
|
danielebarchiesi@4
|
422 */
|
danielebarchiesi@4
|
423 function drush_features_export() {
|
danielebarchiesi@4
|
424 if ($args = func_get_args()) {
|
danielebarchiesi@4
|
425 $module = array_shift($args);
|
danielebarchiesi@4
|
426 if (empty($args)) {
|
danielebarchiesi@4
|
427 return drush_set_error('', 'No components supplied.');
|
danielebarchiesi@4
|
428 }
|
danielebarchiesi@4
|
429 $components = _drush_features_component_list();
|
danielebarchiesi@4
|
430 $options = array(
|
danielebarchiesi@4
|
431 'exported' => FALSE,
|
danielebarchiesi@4
|
432 );
|
danielebarchiesi@4
|
433
|
danielebarchiesi@4
|
434 $filtered_components = _drush_features_component_filter($components, $args, $options);
|
danielebarchiesi@4
|
435 $items = $filtered_components['components'];
|
danielebarchiesi@4
|
436
|
danielebarchiesi@4
|
437 if (empty($items)) {
|
danielebarchiesi@4
|
438 return drush_set_error('', 'No components to add.');
|
danielebarchiesi@4
|
439 }
|
danielebarchiesi@4
|
440
|
danielebarchiesi@4
|
441 $items = array_map('array_keys', $items);
|
danielebarchiesi@4
|
442
|
danielebarchiesi@4
|
443 if (($feature = features_load_feature($module, TRUE)) && module_exists($module)) {
|
danielebarchiesi@4
|
444 module_load_include('inc', 'features', 'features.export');
|
danielebarchiesi@4
|
445 _features_populate($items, $feature->info, $feature->name);
|
danielebarchiesi@4
|
446 _drush_features_export($feature->info, $feature->name, dirname($feature->filename));
|
danielebarchiesi@4
|
447 }
|
danielebarchiesi@4
|
448 elseif ($feature) {
|
danielebarchiesi@4
|
449 _features_drush_set_error($module, 'FEATURES_FEATURE_NOT_ENABLED');
|
danielebarchiesi@4
|
450 }
|
danielebarchiesi@4
|
451 else {
|
danielebarchiesi@4
|
452 // Same logic as in _drush_features_export. Should be refactored.
|
danielebarchiesi@4
|
453 $destination = drush_get_option(array('destination'), 'sites/all/modules/features');
|
danielebarchiesi@4
|
454 $directory = isset($directory) ? $directory : $destination . '/' . $module;
|
danielebarchiesi@4
|
455 drush_print(dt('Will create a new module in !dir', array('!dir' => $directory)));
|
danielebarchiesi@4
|
456 if (!drush_confirm(dt('Do you really want to continue?'))) {
|
danielebarchiesi@4
|
457 drush_die('Aborting.');
|
danielebarchiesi@4
|
458 }
|
danielebarchiesi@4
|
459 $export = _drush_features_generate_export($items, $module);
|
danielebarchiesi@4
|
460 _features_populate($items, $export[info], $export[name]);
|
danielebarchiesi@4
|
461 _drush_features_export($export['info'], $module, $directory);
|
danielebarchiesi@4
|
462 }
|
danielebarchiesi@4
|
463 }
|
danielebarchiesi@4
|
464 else {
|
danielebarchiesi@4
|
465 return drush_set_error('', 'No feature name given.');
|
danielebarchiesi@4
|
466 }
|
danielebarchiesi@4
|
467 }
|
danielebarchiesi@4
|
468
|
danielebarchiesi@4
|
469 /**
|
danielebarchiesi@4
|
470 * Add a component to a features module
|
danielebarchiesi@4
|
471 * the selected components.
|
danielebarchiesi@4
|
472 *
|
danielebarchiesi@4
|
473 * This is DEPRECATED, but keeping it around for a bit longer for user migration
|
danielebarchiesi@4
|
474 */
|
danielebarchiesi@4
|
475 function drush_features_add() {
|
danielebarchiesi@4
|
476 drush_print(dt('features-add is DEPRECATED.'));
|
danielebarchiesi@4
|
477 drush_print(dt('Calling features-export instead.'));
|
danielebarchiesi@4
|
478 drush_features_export();
|
danielebarchiesi@4
|
479 }
|
danielebarchiesi@4
|
480
|
danielebarchiesi@4
|
481
|
danielebarchiesi@4
|
482 /**
|
danielebarchiesi@4
|
483 * Update an existing feature module.
|
danielebarchiesi@4
|
484 */
|
danielebarchiesi@4
|
485 function drush_features_update() {
|
danielebarchiesi@4
|
486 if ($args = func_get_args()) {
|
danielebarchiesi@4
|
487 foreach ($args as $module) {
|
danielebarchiesi@4
|
488 if (($feature = features_load_feature($module, TRUE)) && module_exists($module)) {
|
danielebarchiesi@4
|
489 _drush_features_export($feature->info, $feature->name, dirname($feature->filename));
|
danielebarchiesi@4
|
490 }
|
danielebarchiesi@4
|
491 else if ($feature) {
|
danielebarchiesi@4
|
492 _features_drush_set_error($module, 'FEATURES_FEATURE_NOT_ENABLED');
|
danielebarchiesi@4
|
493 }
|
danielebarchiesi@4
|
494 else {
|
danielebarchiesi@4
|
495 _features_drush_set_error($module);
|
danielebarchiesi@4
|
496 }
|
danielebarchiesi@4
|
497 }
|
danielebarchiesi@4
|
498 }
|
danielebarchiesi@4
|
499 else {
|
danielebarchiesi@4
|
500 // By default just show contexts that are available.
|
danielebarchiesi@4
|
501 $rows = array(array(dt('Available features')));
|
danielebarchiesi@4
|
502 foreach (features_get_features(NULL, TRUE) as $name => $info) {
|
danielebarchiesi@4
|
503 $rows[] = array($name);
|
danielebarchiesi@4
|
504 }
|
danielebarchiesi@4
|
505 drush_print_table($rows, TRUE);
|
danielebarchiesi@4
|
506 }
|
danielebarchiesi@4
|
507 }
|
danielebarchiesi@4
|
508
|
danielebarchiesi@4
|
509 /**
|
danielebarchiesi@4
|
510 * Update all enabled features. Optionally pass in a list of features to
|
danielebarchiesi@4
|
511 * exclude from being updated.
|
danielebarchiesi@4
|
512 */
|
danielebarchiesi@4
|
513 function drush_features_update_all() {
|
danielebarchiesi@4
|
514 $features_to_update = array();
|
danielebarchiesi@4
|
515 $features_to_exclude = func_get_args();
|
danielebarchiesi@4
|
516 foreach (features_get_features() as $module) {
|
danielebarchiesi@4
|
517 if ($module->status && !in_array($module->name, $features_to_exclude)) {
|
danielebarchiesi@4
|
518 $features_to_update[] = $module->name;
|
danielebarchiesi@4
|
519 }
|
danielebarchiesi@4
|
520 }
|
danielebarchiesi@4
|
521 drush_print(dt('The following modules will be updated: !modules', array('!modules' => implode(', ', $features_to_update))));
|
danielebarchiesi@4
|
522 if (drush_confirm(dt('Do you really want to continue?'))) {
|
danielebarchiesi@4
|
523 foreach ($features_to_update as $module_name) {
|
danielebarchiesi@4
|
524 drush_invoke_process(drush_sitealias_get_record('@self'), 'features-update', array($module_name));
|
danielebarchiesi@4
|
525 }
|
danielebarchiesi@4
|
526 }
|
danielebarchiesi@4
|
527 else {
|
danielebarchiesi@4
|
528 drush_die('Aborting.');
|
danielebarchiesi@4
|
529 }
|
danielebarchiesi@4
|
530 }
|
danielebarchiesi@4
|
531
|
danielebarchiesi@4
|
532 /**
|
danielebarchiesi@4
|
533 * Write a module to the site dir.
|
danielebarchiesi@4
|
534 *
|
danielebarchiesi@4
|
535 * @param $info
|
danielebarchiesi@4
|
536 * The feature info associative array.
|
danielebarchiesi@4
|
537 * @param $module_name
|
danielebarchiesi@4
|
538 * Optional. The name for the exported module.
|
danielebarchiesi@4
|
539 */
|
danielebarchiesi@4
|
540 function _drush_features_export($info, $module_name = NULL, $directory = NULL) {
|
danielebarchiesi@4
|
541 $root = drush_get_option(array('r', 'root'), drush_locate_root());
|
danielebarchiesi@4
|
542 if ($root) {
|
danielebarchiesi@4
|
543 $destination = drush_get_option(array('destination'), 'sites/all/modules/features');
|
danielebarchiesi@4
|
544 $directory = isset($directory) ? $directory : $destination . '/' . $module_name;
|
danielebarchiesi@4
|
545 if (is_dir($directory)) {
|
danielebarchiesi@4
|
546 drush_print(dt('Module appears to already exist in !dir', array('!dir' => $directory)));
|
danielebarchiesi@4
|
547 if (!drush_confirm(dt('Do you really want to continue?'))) {
|
danielebarchiesi@4
|
548 drush_die('Aborting.');
|
danielebarchiesi@4
|
549 }
|
danielebarchiesi@4
|
550 }
|
danielebarchiesi@4
|
551 else {
|
danielebarchiesi@4
|
552 drush_op('mkdir', $directory);
|
danielebarchiesi@4
|
553 }
|
danielebarchiesi@4
|
554 if (is_dir($directory)) {
|
danielebarchiesi@4
|
555 drupal_flush_all_caches();
|
danielebarchiesi@4
|
556 $export = _drush_features_generate_export($info, $module_name);
|
danielebarchiesi@4
|
557 $files = features_export_render($export, $module_name, TRUE);
|
danielebarchiesi@4
|
558 foreach ($files as $extension => $file_contents) {
|
danielebarchiesi@4
|
559 if (!in_array($extension, array('module', 'info'))) {
|
danielebarchiesi@4
|
560 $extension .= '.inc';
|
danielebarchiesi@4
|
561 }
|
danielebarchiesi@4
|
562 drush_op('file_put_contents', "{$directory}/{$module_name}.$extension", $file_contents);
|
danielebarchiesi@4
|
563 }
|
danielebarchiesi@4
|
564 drush_log(dt("Created module: !module in !directory", array('!module' => $module_name, '!directory' => $directory)), 'ok');
|
danielebarchiesi@4
|
565 }
|
danielebarchiesi@4
|
566 else {
|
danielebarchiesi@4
|
567 drush_die(dt('Couldn\'t create directory !directory', array('!directory' => $directory)));
|
danielebarchiesi@4
|
568 }
|
danielebarchiesi@4
|
569 }
|
danielebarchiesi@4
|
570 else {
|
danielebarchiesi@4
|
571 drush_die(dt('Couldn\'t locate site root'));
|
danielebarchiesi@4
|
572 }
|
danielebarchiesi@4
|
573 }
|
danielebarchiesi@4
|
574
|
danielebarchiesi@4
|
575 /**
|
danielebarchiesi@4
|
576 * Helper function for _drush_feature_export.
|
danielebarchiesi@4
|
577 *
|
danielebarchiesi@4
|
578 * @param $info
|
danielebarchiesi@4
|
579 * The feature info associative array.
|
danielebarchiesi@4
|
580 * @param $module_name
|
danielebarchiesi@4
|
581 * Optional. The name for the exported module.
|
danielebarchiesi@4
|
582 */
|
danielebarchiesi@4
|
583 function _drush_features_generate_export(&$info, &$module_name) {
|
danielebarchiesi@4
|
584 module_load_include('inc', 'features', 'features.export');
|
danielebarchiesi@4
|
585 $export = features_populate($info, $module_name);
|
danielebarchiesi@4
|
586 if (!features_load_feature($module_name)) {
|
danielebarchiesi@4
|
587 $export['name'] = $module_name;
|
danielebarchiesi@4
|
588 }
|
danielebarchiesi@4
|
589 // Set the feature version if the --version-set or --version-increment option is passed.
|
danielebarchiesi@4
|
590 if ($version = drush_get_option(array('version-set'))) {
|
danielebarchiesi@4
|
591 preg_match('/^(?P<core>\d+\.x)-(?P<major>\d+)\.(?P<patch>\d+)-?(?P<extra>\w+)?$/', $version, $matches);
|
danielebarchiesi@4
|
592 if (!isset($matches['core'], $matches['major'])) {
|
danielebarchiesi@4
|
593 drush_die(dt('Please enter a valid version with core and major version number. Example: !example', array('!example' => '7.x-1.0')));
|
danielebarchiesi@4
|
594 }
|
danielebarchiesi@4
|
595 $export['version'] = $version;
|
danielebarchiesi@4
|
596 }
|
danielebarchiesi@4
|
597 else if ($version = drush_get_option(array('version-increment'))) {
|
danielebarchiesi@4
|
598 // Determine current version and increment it.
|
danielebarchiesi@4
|
599 $export_load = features_export_prepare($export, $module_name);
|
danielebarchiesi@4
|
600 $version = $export_load['version'];
|
danielebarchiesi@4
|
601 $version_explode = explode('.', $version);
|
danielebarchiesi@4
|
602 $version_minor = array_pop($version_explode);
|
danielebarchiesi@4
|
603 // Increment minor version number if numeric or not a dev release.
|
danielebarchiesi@4
|
604 if (is_numeric($version_minor) || strpos($version_minor, 'dev') !== (strlen($version_minor) - 3)) {
|
danielebarchiesi@4
|
605 // Check for prefixed versions (alpha, beta, rc).
|
danielebarchiesi@4
|
606 if (ctype_digit($version_minor)) {
|
danielebarchiesi@4
|
607 ++$version_minor;
|
danielebarchiesi@4
|
608 }
|
danielebarchiesi@4
|
609 else {
|
danielebarchiesi@4
|
610 // Split version number parts.
|
danielebarchiesi@4
|
611 $pattern = '/([0-9]-[a-z]+([0-9])+)/';
|
danielebarchiesi@4
|
612 $matches = array();
|
danielebarchiesi@4
|
613 preg_match($pattern, $version_minor, $matches);
|
danielebarchiesi@4
|
614 $number = array_pop($matches);
|
danielebarchiesi@4
|
615 ++$number;
|
danielebarchiesi@4
|
616 $pattern = '/[0-9]+$/';
|
danielebarchiesi@4
|
617 $version_minor = preg_replace($pattern, $number, $version_minor);
|
danielebarchiesi@4
|
618 }
|
danielebarchiesi@4
|
619 }
|
danielebarchiesi@4
|
620 array_push($version_explode, $version_minor);
|
danielebarchiesi@4
|
621 // Rebuild version string.
|
danielebarchiesi@4
|
622 $version = implode('.', $version_explode);
|
danielebarchiesi@4
|
623 $export['version'] = $version;
|
danielebarchiesi@4
|
624 }
|
danielebarchiesi@4
|
625 return $export;
|
danielebarchiesi@4
|
626 }
|
danielebarchiesi@4
|
627
|
danielebarchiesi@4
|
628 /**
|
danielebarchiesi@4
|
629 * Revert a feature to it's code definition.
|
danielebarchiesi@4
|
630 * Optionally accept a list of components to revert.
|
danielebarchiesi@4
|
631 */
|
danielebarchiesi@4
|
632 function drush_features_revert() {
|
danielebarchiesi@4
|
633 if ($args = func_get_args()) {
|
danielebarchiesi@4
|
634 module_load_include('inc', 'features', 'features.export');
|
danielebarchiesi@4
|
635 features_include();
|
danielebarchiesi@4
|
636
|
danielebarchiesi@4
|
637 // Determine if revert should be forced.
|
danielebarchiesi@4
|
638 $force = drush_get_option('force');
|
danielebarchiesi@4
|
639
|
danielebarchiesi@4
|
640 // Parse list of arguments.
|
danielebarchiesi@4
|
641 $modules = array();
|
danielebarchiesi@4
|
642 foreach ($args as $arg) {
|
danielebarchiesi@4
|
643 $arg = explode('.', $arg);
|
danielebarchiesi@4
|
644 $module = array_shift($arg);
|
danielebarchiesi@4
|
645 $component = array_shift($arg);
|
danielebarchiesi@4
|
646
|
danielebarchiesi@4
|
647 if (isset($module)) {
|
danielebarchiesi@4
|
648 if (empty($component)) {
|
danielebarchiesi@4
|
649 // If we received just a feature name, this means that we need all of it's components.
|
danielebarchiesi@4
|
650 $modules[$module] = TRUE;
|
danielebarchiesi@4
|
651 }
|
danielebarchiesi@4
|
652 elseif ($modules[$module] !== TRUE) {
|
danielebarchiesi@4
|
653 if (!isset($modules[$module])) {
|
danielebarchiesi@4
|
654 $modules[$module] = array();
|
danielebarchiesi@4
|
655 }
|
danielebarchiesi@4
|
656 $modules[$module][] = $component;
|
danielebarchiesi@4
|
657 }
|
danielebarchiesi@4
|
658 }
|
danielebarchiesi@4
|
659 }
|
danielebarchiesi@4
|
660
|
danielebarchiesi@4
|
661 // Process modules.
|
danielebarchiesi@4
|
662 foreach ($modules as $module => $components_needed) {
|
danielebarchiesi@4
|
663 if (($feature = features_load_feature($module, TRUE)) && module_exists($module)) {
|
danielebarchiesi@4
|
664
|
danielebarchiesi@4
|
665 $components = array();
|
danielebarchiesi@4
|
666 // Forcefully revert all components of a feature.
|
danielebarchiesi@4
|
667 if ($force) {
|
danielebarchiesi@4
|
668 foreach (array_keys($feature->info['features']) as $component) {
|
danielebarchiesi@4
|
669 if (features_hook($component, 'features_revert')) {
|
danielebarchiesi@4
|
670 $components[] = $component;
|
danielebarchiesi@4
|
671 }
|
danielebarchiesi@4
|
672 }
|
danielebarchiesi@4
|
673 }
|
danielebarchiesi@4
|
674 // Only revert components that are detected to be Overridden/Needs review/rebuildable.
|
danielebarchiesi@4
|
675 else {
|
danielebarchiesi@4
|
676 $states = features_get_component_states(array($feature->name), FALSE);
|
danielebarchiesi@4
|
677 foreach ($states[$feature->name] as $component => $state) {
|
danielebarchiesi@4
|
678 if (in_array($state, array(FEATURES_OVERRIDDEN, FEATURES_NEEDS_REVIEW, FEATURES_REBUILDABLE)) && features_hook($component, 'features_revert')) {
|
danielebarchiesi@4
|
679 $components[] = $component;
|
danielebarchiesi@4
|
680 }
|
danielebarchiesi@4
|
681 }
|
danielebarchiesi@4
|
682 }
|
danielebarchiesi@4
|
683
|
danielebarchiesi@4
|
684 if (!empty($components_needed) && is_array($components_needed)) {
|
danielebarchiesi@4
|
685 $components = array_intersect($components, $components_needed);
|
danielebarchiesi@4
|
686 }
|
danielebarchiesi@4
|
687 if (empty($components)) {
|
danielebarchiesi@4
|
688 drush_log(dt('Current state already matches defaults, aborting.'), 'ok');
|
danielebarchiesi@4
|
689 }
|
danielebarchiesi@4
|
690 else {
|
danielebarchiesi@4
|
691 foreach ($components as $component) {
|
danielebarchiesi@4
|
692 if (drush_confirm(dt('Do you really want to revert @component?', array('@component' => $component)))) {
|
danielebarchiesi@4
|
693 features_revert(array($module => array($component)));
|
danielebarchiesi@4
|
694 drush_log(dt('Reverted @component.', array('@component' => $component)), 'ok');
|
danielebarchiesi@4
|
695 }
|
danielebarchiesi@4
|
696 else {
|
danielebarchiesi@4
|
697 drush_log(dt('Skipping @component.', array('@component' => $component)), 'ok');
|
danielebarchiesi@4
|
698 }
|
danielebarchiesi@4
|
699 }
|
danielebarchiesi@4
|
700 }
|
danielebarchiesi@4
|
701 }
|
danielebarchiesi@4
|
702 else if ($feature) {
|
danielebarchiesi@4
|
703 _features_drush_set_error($module, 'FEATURES_FEATURE_NOT_ENABLED');
|
danielebarchiesi@4
|
704 }
|
danielebarchiesi@4
|
705 else {
|
danielebarchiesi@4
|
706 _features_drush_set_error($module);
|
danielebarchiesi@4
|
707 }
|
danielebarchiesi@4
|
708 }
|
danielebarchiesi@4
|
709 }
|
danielebarchiesi@4
|
710 else {
|
danielebarchiesi@4
|
711 drush_features_list();
|
danielebarchiesi@4
|
712 return;
|
danielebarchiesi@4
|
713 }
|
danielebarchiesi@4
|
714 }
|
danielebarchiesi@4
|
715
|
danielebarchiesi@4
|
716 /**
|
danielebarchiesi@4
|
717 * Revert all enabled features to their definitions in code.
|
danielebarchiesi@4
|
718 *
|
danielebarchiesi@4
|
719 * @param ...
|
danielebarchiesi@4
|
720 * (Optional) A list of features to exclude from being reverted.
|
danielebarchiesi@4
|
721 */
|
danielebarchiesi@4
|
722 function drush_features_revert_all() {
|
danielebarchiesi@4
|
723 module_load_include('inc', 'features', 'features.export');
|
danielebarchiesi@4
|
724 $force = drush_get_option('force');
|
danielebarchiesi@4
|
725 $features_to_exclude = func_get_args();
|
danielebarchiesi@4
|
726
|
danielebarchiesi@4
|
727 $features_to_revert = array();
|
danielebarchiesi@4
|
728 foreach (features_get_features(NULL, TRUE) as $module) {
|
danielebarchiesi@4
|
729 if ($module->status && !in_array($module->name, $features_to_exclude)) {
|
danielebarchiesi@4
|
730 // If forced, add module regardless of status.
|
danielebarchiesi@4
|
731 if ($force) {
|
danielebarchiesi@4
|
732 $features_to_revert[] = $module->name;
|
danielebarchiesi@4
|
733 }
|
danielebarchiesi@4
|
734 else {
|
danielebarchiesi@4
|
735 switch (features_get_storage($module->name)) {
|
danielebarchiesi@4
|
736 case FEATURES_OVERRIDDEN:
|
danielebarchiesi@4
|
737 case FEATURES_NEEDS_REVIEW:
|
danielebarchiesi@4
|
738 case FEATURES_REBUILDABLE:
|
danielebarchiesi@4
|
739 $features_to_revert[] = $module->name;
|
danielebarchiesi@4
|
740 break;
|
danielebarchiesi@4
|
741 }
|
danielebarchiesi@4
|
742 }
|
danielebarchiesi@4
|
743 }
|
danielebarchiesi@4
|
744 }
|
danielebarchiesi@4
|
745
|
danielebarchiesi@4
|
746 if ($features_to_revert) {
|
danielebarchiesi@4
|
747 drush_print(dt('The following modules will be reverted: !modules', array('!modules' => implode(', ', $features_to_revert))));
|
danielebarchiesi@4
|
748 if (drush_confirm(dt('Do you really want to continue?'))) {
|
danielebarchiesi@4
|
749 foreach ($features_to_revert as $module) {
|
danielebarchiesi@4
|
750 drush_invoke_process(drush_sitealias_get_record('@self'), 'features-revert', array($module), array('force' => $force, '#integrate' => TRUE));
|
danielebarchiesi@4
|
751 }
|
danielebarchiesi@4
|
752 }
|
danielebarchiesi@4
|
753 else {
|
danielebarchiesi@4
|
754 return drush_user_abort('Aborting.');
|
danielebarchiesi@4
|
755 }
|
danielebarchiesi@4
|
756 }
|
danielebarchiesi@4
|
757 else {
|
danielebarchiesi@4
|
758 drush_log(dt('Current state already matches defaults, aborting.'), 'ok');
|
danielebarchiesi@4
|
759 }
|
danielebarchiesi@4
|
760 }
|
danielebarchiesi@4
|
761
|
danielebarchiesi@4
|
762 /**
|
danielebarchiesi@4
|
763 * Show the diff of a feature module.
|
danielebarchiesi@4
|
764 */
|
danielebarchiesi@4
|
765 function drush_features_diff() {
|
danielebarchiesi@4
|
766 if (!$args = func_get_args()) {
|
danielebarchiesi@4
|
767 drush_features_list();
|
danielebarchiesi@4
|
768 return;
|
danielebarchiesi@4
|
769 }
|
danielebarchiesi@4
|
770 $module = $args[0];
|
danielebarchiesi@4
|
771 $feature = features_load_feature($module);
|
danielebarchiesi@4
|
772 if (!module_exists($module)) {
|
danielebarchiesi@4
|
773 drush_log(dt('No such feature is enabled: ' . $module), 'error');
|
danielebarchiesi@4
|
774 return;
|
danielebarchiesi@4
|
775 }
|
danielebarchiesi@4
|
776 module_load_include('inc', 'features', 'features.export');
|
danielebarchiesi@4
|
777 $overrides = features_detect_overrides($feature);
|
danielebarchiesi@4
|
778 if (empty($overrides)) {
|
danielebarchiesi@4
|
779 drush_log(dt('Feature is in its default state. No diff needed.'), 'ok');
|
danielebarchiesi@4
|
780 return;
|
danielebarchiesi@4
|
781 }
|
danielebarchiesi@4
|
782 module_load_include('inc', 'diff', 'diff.engine');
|
danielebarchiesi@4
|
783
|
danielebarchiesi@4
|
784 if (!class_exists('DiffFormatter')) {
|
danielebarchiesi@4
|
785 if (drush_confirm(dt('It seems that the Diff module is not available. Would you like to download and enable it?'))) {
|
danielebarchiesi@4
|
786 // Download it if it's not already here.
|
danielebarchiesi@4
|
787 $project_info = drush_get_projects();
|
danielebarchiesi@4
|
788 if (empty($project_info['diff']) && !drush_invoke_process(drush_sitealias_get_record('@self'), 'dl', array('diff'), array('#integrate' => TRUE))) {
|
danielebarchiesi@4
|
789 return drush_set_error(dt('Diff module could not be downloaded.'));
|
danielebarchiesi@4
|
790 }
|
danielebarchiesi@4
|
791
|
danielebarchiesi@4
|
792 if (!drush_invoke_process(drush_sitealias_get_record('@self'), 'en', array('diff'), array('#integrate' => TRUE))) {
|
danielebarchiesi@4
|
793 return drush_set_error(dt('Diff module could not be enabled.'));
|
danielebarchiesi@4
|
794 }
|
danielebarchiesi@4
|
795 }
|
danielebarchiesi@4
|
796 else {
|
danielebarchiesi@4
|
797 return drush_set_error(dt('Diff module is not enabled.'));
|
danielebarchiesi@4
|
798 }
|
danielebarchiesi@4
|
799 // If we're still here, now we can include the diff.engine again.
|
danielebarchiesi@4
|
800 module_load_include('inc', 'diff', 'diff.engine');
|
danielebarchiesi@4
|
801 }
|
danielebarchiesi@4
|
802
|
danielebarchiesi@4
|
803 $lines = (int) drush_get_option('lines');
|
danielebarchiesi@4
|
804 $lines = $lines > 0 ? $lines : 2;
|
danielebarchiesi@4
|
805
|
danielebarchiesi@4
|
806 $formatter = new DiffFormatter();
|
danielebarchiesi@4
|
807 $formatter->leading_context_lines = $lines;
|
danielebarchiesi@4
|
808 $formatter->trailing_context_lines = $lines;
|
danielebarchiesi@4
|
809 $formatter->show_header = FALSE;
|
danielebarchiesi@4
|
810
|
danielebarchiesi@4
|
811 if (drush_get_context('DRUSH_NOCOLOR')) {
|
danielebarchiesi@4
|
812 $red = $green = "%s";
|
danielebarchiesi@4
|
813 }
|
danielebarchiesi@4
|
814 else {
|
danielebarchiesi@4
|
815 $red = "\033[31;40m\033[1m%s\033[0m";
|
danielebarchiesi@4
|
816 $green = "\033[0;32;40m\033[1m%s\033[0m";
|
danielebarchiesi@4
|
817 }
|
danielebarchiesi@4
|
818
|
danielebarchiesi@4
|
819 // Print key for colors
|
danielebarchiesi@4
|
820 drush_print(dt('Legend: '));
|
danielebarchiesi@4
|
821 drush_print(sprintf($red, dt('Code: drush features-revert will remove the overrides.')));
|
danielebarchiesi@4
|
822 drush_print(sprintf($green, dt('Overrides: drush features-update will update the exported feature with the displayed overrides')));
|
danielebarchiesi@4
|
823 drush_print();
|
danielebarchiesi@4
|
824
|
danielebarchiesi@4
|
825 foreach ($overrides as $component => $items) {
|
danielebarchiesi@4
|
826 $diff = new Diff(explode("\n", $items['default']), explode("\n", $items['normal']));
|
danielebarchiesi@4
|
827 drush_print();
|
danielebarchiesi@4
|
828 drush_print(dt("Component: !component", array('!component' => $component)));
|
danielebarchiesi@4
|
829 $rows = explode("\n", $formatter->format($diff));
|
danielebarchiesi@4
|
830 foreach ($rows as $row) {
|
danielebarchiesi@4
|
831 if (strpos($row, '>') === 0) {
|
danielebarchiesi@4
|
832 drush_print(sprintf($green, $row));
|
danielebarchiesi@4
|
833 }
|
danielebarchiesi@4
|
834 elseif (strpos($row, '<') === 0) {
|
danielebarchiesi@4
|
835 drush_print(sprintf($red, $row));
|
danielebarchiesi@4
|
836 }
|
danielebarchiesi@4
|
837 else {
|
danielebarchiesi@4
|
838 drush_print($row);
|
danielebarchiesi@4
|
839 }
|
danielebarchiesi@4
|
840 }
|
danielebarchiesi@4
|
841 }
|
danielebarchiesi@4
|
842 }
|
danielebarchiesi@4
|
843
|
danielebarchiesi@4
|
844 /**
|
danielebarchiesi@4
|
845 * Helper function to call drush_set_error().
|
danielebarchiesi@4
|
846 *
|
danielebarchiesi@4
|
847 * @param $feature
|
danielebarchiesi@4
|
848 * The string name of the feature.
|
danielebarchiesi@4
|
849 * @param $error
|
danielebarchiesi@4
|
850 * A text string identifying the type of error.
|
danielebarchiesi@4
|
851 * @return
|
danielebarchiesi@4
|
852 * FALSE. See drush_set_error().
|
danielebarchiesi@4
|
853 */
|
danielebarchiesi@4
|
854 function _features_drush_set_error($feature, $error = '') {
|
danielebarchiesi@4
|
855 $args = array('!feature' => $feature);
|
danielebarchiesi@4
|
856
|
danielebarchiesi@4
|
857 switch ($error) {
|
danielebarchiesi@4
|
858 case 'FEATURES_FEATURE_NOT_ENABLED':
|
danielebarchiesi@4
|
859 $message = 'The feature !feature is not enabled.';
|
danielebarchiesi@4
|
860 break;
|
danielebarchiesi@4
|
861 case 'FEATURES_COMPONENT_NOT_FOUND':
|
danielebarchiesi@4
|
862 $message = 'The given component !feature could not be found.';
|
danielebarchiesi@4
|
863 break;
|
danielebarchiesi@4
|
864 default:
|
danielebarchiesi@4
|
865 $error = 'FEATURES_FEATURE_NOT_FOUND';
|
danielebarchiesi@4
|
866 $message = 'The feature !feature could not be found.';
|
danielebarchiesi@4
|
867 }
|
danielebarchiesi@4
|
868
|
danielebarchiesi@4
|
869 return drush_set_error($error, dt($message, $args));
|
danielebarchiesi@4
|
870 }
|