Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 /**
|
Chris@0
|
4 * @file
|
Chris@0
|
5 * Hooks related to module and update systems.
|
Chris@0
|
6 */
|
Chris@0
|
7
|
Chris@0
|
8 use Drupal\Core\Database\Database;
|
Chris@18
|
9 use Drupal\Core\File\FileSystemInterface;
|
Chris@0
|
10 use Drupal\Core\Url;
|
Chris@0
|
11 use Drupal\Core\Utility\UpdateException;
|
Chris@0
|
12
|
Chris@0
|
13 /**
|
Chris@0
|
14 * @defgroup update_api Update API
|
Chris@0
|
15 * @{
|
Chris@0
|
16 * Updating minor versions of modules
|
Chris@0
|
17 *
|
Chris@0
|
18 * When you update code in a module, you may need to update stored data so that
|
Chris@0
|
19 * the stored data is compatible with the new code. If this update is between
|
Chris@0
|
20 * two minor versions of your module within the same major version of Drupal,
|
Chris@0
|
21 * you can use the Update API to update the data. This API is described in brief
|
Chris@0
|
22 * here; for more details, see https://www.drupal.org/node/2535316. If you are
|
Chris@0
|
23 * updating your module for a major version of Drupal (for instance, Drupal 7 to
|
Chris@0
|
24 * Drupal 8), updates will not run and you will need to use the
|
Chris@0
|
25 * @link migrate Migrate API @endlink instead.
|
Chris@0
|
26 *
|
Chris@0
|
27 * @section sec_when When to write update code
|
Chris@0
|
28 * You need to provide code that performs an update to stored data whenever your
|
Chris@0
|
29 * module makes a change to its data model. A data model change is any change
|
Chris@0
|
30 * that makes stored data on an existing site incompatible with that site's
|
Chris@0
|
31 * updated codebase. Examples:
|
Chris@0
|
32 * - Configuration changes: adding/removing/renaming a config key, changing the
|
Chris@0
|
33 * expected data type or value structure, changing dependencies, schema
|
Chris@0
|
34 * changes, etc.
|
Chris@0
|
35 * - Database schema changes: adding, changing, or removing a database table or
|
Chris@0
|
36 * field; moving stored data to different fields or tables; changing the
|
Chris@0
|
37 * format of stored data.
|
Chris@0
|
38 * - Content entity or field changes: adding, changing, or removing a field
|
Chris@0
|
39 * definition, entity definition, or any of their properties.
|
Chris@0
|
40 *
|
Chris@0
|
41 * @section sec_how How to write update code
|
Chris@0
|
42 * Update code for a module is put into an implementation of hook_update_N(),
|
Chris@0
|
43 * which goes into file mymodule.install (if your module's machine name is
|
Chris@0
|
44 * mymodule). See the documentation of hook_update_N() and
|
Chris@0
|
45 * https://www.drupal.org/node/2535316 for details and examples.
|
Chris@0
|
46 *
|
Chris@0
|
47 * @section sec_test Testing update code
|
Chris@0
|
48 * Update code should be tested both manually and by writing an automated test.
|
Chris@0
|
49 * Automated tests for update code extend
|
Chris@0
|
50 * \Drupal\system\Tests\Update\UpdatePathTestBase -- see that class for details,
|
Chris@0
|
51 * and find classes that extend it for examples.
|
Chris@0
|
52 *
|
Chris@0
|
53 * @see migration
|
Chris@0
|
54 * @}
|
Chris@0
|
55 */
|
Chris@0
|
56
|
Chris@0
|
57 /**
|
Chris@0
|
58 * @addtogroup hooks
|
Chris@0
|
59 * @{
|
Chris@0
|
60 */
|
Chris@0
|
61
|
Chris@0
|
62 /**
|
Chris@0
|
63 * Defines one or more hooks that are exposed by a module.
|
Chris@0
|
64 *
|
Chris@0
|
65 * Normally hooks do not need to be explicitly defined. However, by declaring a
|
Chris@0
|
66 * hook explicitly, a module may define a "group" for it. Modules that implement
|
Chris@0
|
67 * a hook may then place their implementation in either $module.module or in
|
Chris@0
|
68 * $module.$group.inc. If the hook is located in $module.$group.inc, then that
|
Chris@0
|
69 * file will be automatically loaded when needed.
|
Chris@0
|
70 * In general, hooks that are rarely invoked and/or are very large should be
|
Chris@0
|
71 * placed in a separate include file, while hooks that are very short or very
|
Chris@0
|
72 * frequently called should be left in the main module file so that they are
|
Chris@0
|
73 * always available.
|
Chris@0
|
74 *
|
Chris@0
|
75 * See system_hook_info() for all hook groups defined by Drupal core.
|
Chris@0
|
76 *
|
Chris@0
|
77 * @return
|
Chris@0
|
78 * An associative array whose keys are hook names and whose values are an
|
Chris@0
|
79 * associative array containing:
|
Chris@0
|
80 * - group: A string defining the group to which the hook belongs. The module
|
Chris@0
|
81 * system will determine whether a file with the name $module.$group.inc
|
Chris@0
|
82 * exists, and automatically load it when required.
|
Chris@0
|
83 */
|
Chris@0
|
84 function hook_hook_info() {
|
Chris@0
|
85 $hooks['token_info'] = [
|
Chris@0
|
86 'group' => 'tokens',
|
Chris@0
|
87 ];
|
Chris@0
|
88 $hooks['tokens'] = [
|
Chris@0
|
89 'group' => 'tokens',
|
Chris@0
|
90 ];
|
Chris@0
|
91 return $hooks;
|
Chris@0
|
92 }
|
Chris@0
|
93
|
Chris@0
|
94 /**
|
Chris@0
|
95 * Alter the registry of modules implementing a hook.
|
Chris@0
|
96 *
|
Chris@0
|
97 * This hook is invoked during \Drupal::moduleHandler()->getImplementations().
|
Chris@0
|
98 * A module may implement this hook in order to reorder the implementing
|
Chris@0
|
99 * modules, which are otherwise ordered by the module's system weight.
|
Chris@0
|
100 *
|
Chris@0
|
101 * Note that hooks invoked using \Drupal::moduleHandler->alter() can have
|
Chris@0
|
102 * multiple variations(such as hook_form_alter() and hook_form_FORM_ID_alter()).
|
Chris@0
|
103 * \Drupal::moduleHandler->alter() will call all such variants defined by a
|
Chris@0
|
104 * single module in turn. For the purposes of hook_module_implements_alter(),
|
Chris@0
|
105 * these variants are treated as a single hook. Thus, to ensure that your
|
Chris@0
|
106 * implementation of hook_form_FORM_ID_alter() is called at the right time,
|
Chris@0
|
107 * you will have to change the order of hook_form_alter() implementation in
|
Chris@0
|
108 * hook_module_implements_alter().
|
Chris@0
|
109 *
|
Chris@0
|
110 * @param $implementations
|
Chris@0
|
111 * An array keyed by the module's name. The value of each item corresponds
|
Chris@0
|
112 * to a $group, which is usually FALSE, unless the implementation is in a
|
Chris@0
|
113 * file named $module.$group.inc.
|
Chris@0
|
114 * @param $hook
|
Chris@0
|
115 * The name of the module hook being implemented.
|
Chris@0
|
116 */
|
Chris@0
|
117 function hook_module_implements_alter(&$implementations, $hook) {
|
Chris@0
|
118 if ($hook == 'form_alter') {
|
Chris@0
|
119 // Move my_module_form_alter() to the end of the list.
|
Chris@0
|
120 // \Drupal::moduleHandler()->getImplementations()
|
Chris@0
|
121 // iterates through $implementations with a foreach loop which PHP iterates
|
Chris@0
|
122 // in the order that the items were added, so to move an item to the end of
|
Chris@0
|
123 // the array, we remove it and then add it.
|
Chris@0
|
124 $group = $implementations['my_module'];
|
Chris@0
|
125 unset($implementations['my_module']);
|
Chris@0
|
126 $implementations['my_module'] = $group;
|
Chris@0
|
127 }
|
Chris@0
|
128 }
|
Chris@0
|
129
|
Chris@0
|
130 /**
|
Chris@0
|
131 * Alter the information parsed from module and theme .info.yml files.
|
Chris@0
|
132 *
|
Chris@18
|
133 * This hook is invoked in \Drupal\Core\Extension\ExtensionList::doList(). A
|
Chris@18
|
134 * module may implement this hook in order to add to or alter the data generated
|
Chris@18
|
135 * by reading the .info.yml file with \Drupal\Core\Extension\InfoParser.
|
Chris@0
|
136 *
|
Chris@0
|
137 * Using implementations of this hook to make modules required by setting the
|
Chris@0
|
138 * $info['required'] key is discouraged. Doing so will slow down the module
|
Chris@0
|
139 * installation and uninstallation process. Instead, use
|
Chris@0
|
140 * \Drupal\Core\Extension\ModuleUninstallValidatorInterface.
|
Chris@0
|
141 *
|
Chris@0
|
142 * @param array $info
|
Chris@0
|
143 * The .info.yml file contents, passed by reference so that it can be altered.
|
Chris@0
|
144 * @param \Drupal\Core\Extension\Extension $file
|
Chris@0
|
145 * Full information about the module or theme.
|
Chris@0
|
146 * @param string $type
|
Chris@0
|
147 * Either 'module' or 'theme', depending on the type of .info.yml file that
|
Chris@0
|
148 * was passed.
|
Chris@0
|
149 *
|
Chris@0
|
150 * @see \Drupal\Core\Extension\ModuleUninstallValidatorInterface
|
Chris@0
|
151 */
|
Chris@0
|
152 function hook_system_info_alter(array &$info, \Drupal\Core\Extension\Extension $file, $type) {
|
Chris@0
|
153 // Only fill this in if the .info.yml file does not define a 'datestamp'.
|
Chris@0
|
154 if (empty($info['datestamp'])) {
|
Chris@0
|
155 $info['datestamp'] = $file->getMTime();
|
Chris@0
|
156 }
|
Chris@0
|
157 }
|
Chris@0
|
158
|
Chris@0
|
159 /**
|
Chris@0
|
160 * Perform necessary actions before a module is installed.
|
Chris@0
|
161 *
|
Chris@0
|
162 * @param string $module
|
Chris@0
|
163 * The name of the module about to be installed.
|
Chris@0
|
164 */
|
Chris@0
|
165 function hook_module_preinstall($module) {
|
Chris@0
|
166 mymodule_cache_clear();
|
Chris@0
|
167 }
|
Chris@0
|
168
|
Chris@0
|
169 /**
|
Chris@0
|
170 * Perform necessary actions after modules are installed.
|
Chris@0
|
171 *
|
Chris@0
|
172 * This function differs from hook_install() in that it gives all other modules
|
Chris@0
|
173 * a chance to perform actions when a module is installed, whereas
|
Chris@0
|
174 * hook_install() is only called on the module actually being installed. See
|
Chris@0
|
175 * \Drupal\Core\Extension\ModuleInstaller::install() for a detailed description of
|
Chris@0
|
176 * the order in which install hooks are invoked.
|
Chris@0
|
177 *
|
Chris@0
|
178 * This hook should be implemented in a .module file, not in an .install file.
|
Chris@0
|
179 *
|
Chris@0
|
180 * @param $modules
|
Chris@0
|
181 * An array of the modules that were installed.
|
Chris@0
|
182 *
|
Chris@0
|
183 * @see \Drupal\Core\Extension\ModuleInstaller::install()
|
Chris@0
|
184 * @see hook_install()
|
Chris@0
|
185 */
|
Chris@0
|
186 function hook_modules_installed($modules) {
|
Chris@0
|
187 if (in_array('lousy_module', $modules)) {
|
Chris@0
|
188 \Drupal::state()->set('mymodule.lousy_module_compatibility', TRUE);
|
Chris@0
|
189 }
|
Chris@0
|
190 }
|
Chris@0
|
191
|
Chris@0
|
192 /**
|
Chris@0
|
193 * Perform setup tasks when the module is installed.
|
Chris@0
|
194 *
|
Chris@0
|
195 * If the module implements hook_schema(), the database tables will
|
Chris@0
|
196 * be created before this hook is fired.
|
Chris@0
|
197 *
|
Chris@0
|
198 * If the module provides a MODULE.routing.yml or alters routing information
|
Chris@0
|
199 * these changes will not be available when this hook is fired. If up-to-date
|
Chris@0
|
200 * router information is required, for example to use \Drupal\Core\Url, then
|
Chris@0
|
201 * (preferably) use hook_modules_installed() or rebuild the router in the
|
Chris@0
|
202 * hook_install() implementation.
|
Chris@0
|
203 *
|
Chris@0
|
204 * Implementations of this hook are by convention declared in the module's
|
Chris@0
|
205 * .install file. The implementation can rely on the .module file being loaded.
|
Chris@0
|
206 * The hook will only be called when a module is installed. The module's schema
|
Chris@0
|
207 * version will be set to the module's greatest numbered update hook. Because of
|
Chris@0
|
208 * this, any time a hook_update_N() is added to the module, this function needs
|
Chris@0
|
209 * to be updated to reflect the current version of the database schema.
|
Chris@0
|
210 *
|
Chris@0
|
211 * See the @link https://www.drupal.org/node/146843 Schema API documentation
|
Chris@0
|
212 * @endlink for details on hook_schema and how database tables are defined.
|
Chris@0
|
213 *
|
Chris@0
|
214 * Note that since this function is called from a full bootstrap, all functions
|
Chris@0
|
215 * (including those in modules enabled by the current page request) are
|
Chris@0
|
216 * available when this hook is called. Use cases could be displaying a user
|
Chris@0
|
217 * message, or calling a module function necessary for initial setup, etc.
|
Chris@0
|
218 *
|
Chris@0
|
219 * Please be sure that anything added or modified in this function that can
|
Chris@0
|
220 * be removed during uninstall should be removed with hook_uninstall().
|
Chris@0
|
221 *
|
Chris@0
|
222 * @see hook_schema()
|
Chris@0
|
223 * @see \Drupal\Core\Extension\ModuleInstaller::install()
|
Chris@0
|
224 * @see hook_uninstall()
|
Chris@0
|
225 * @see hook_modules_installed()
|
Chris@0
|
226 */
|
Chris@0
|
227 function hook_install() {
|
Chris@0
|
228 // Create the styles directory and ensure it's writable.
|
Chris@0
|
229 $directory = file_default_scheme() . '://styles';
|
Chris@18
|
230 \Drupal::service('file_system')->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS);
|
Chris@0
|
231 }
|
Chris@0
|
232
|
Chris@0
|
233 /**
|
Chris@0
|
234 * Perform necessary actions before a module is uninstalled.
|
Chris@0
|
235 *
|
Chris@0
|
236 * @param string $module
|
Chris@0
|
237 * The name of the module about to be uninstalled.
|
Chris@0
|
238 */
|
Chris@0
|
239 function hook_module_preuninstall($module) {
|
Chris@0
|
240 mymodule_cache_clear();
|
Chris@0
|
241 }
|
Chris@0
|
242
|
Chris@0
|
243 /**
|
Chris@0
|
244 * Perform necessary actions after modules are uninstalled.
|
Chris@0
|
245 *
|
Chris@0
|
246 * This function differs from hook_uninstall() in that it gives all other
|
Chris@0
|
247 * modules a chance to perform actions when a module is uninstalled, whereas
|
Chris@0
|
248 * hook_uninstall() is only called on the module actually being uninstalled.
|
Chris@0
|
249 *
|
Chris@0
|
250 * It is recommended that you implement this hook if your module stores
|
Chris@0
|
251 * data that may have been set by other modules.
|
Chris@0
|
252 *
|
Chris@0
|
253 * @param $modules
|
Chris@0
|
254 * An array of the modules that were uninstalled.
|
Chris@0
|
255 *
|
Chris@0
|
256 * @see hook_uninstall()
|
Chris@0
|
257 */
|
Chris@0
|
258 function hook_modules_uninstalled($modules) {
|
Chris@0
|
259 if (in_array('lousy_module', $modules)) {
|
Chris@0
|
260 \Drupal::state()->delete('mymodule.lousy_module_compatibility');
|
Chris@0
|
261 }
|
Chris@0
|
262 mymodule_cache_rebuild();
|
Chris@0
|
263 }
|
Chris@0
|
264
|
Chris@0
|
265 /**
|
Chris@0
|
266 * Remove any information that the module sets.
|
Chris@0
|
267 *
|
Chris@0
|
268 * The information that the module should remove includes:
|
Chris@0
|
269 * - state that the module has set using \Drupal::state()
|
Chris@0
|
270 * - modifications to existing tables
|
Chris@0
|
271 *
|
Chris@0
|
272 * The module should not remove its entry from the module configuration.
|
Chris@0
|
273 * Database tables defined by hook_schema() will be removed automatically.
|
Chris@0
|
274 *
|
Chris@0
|
275 * The uninstall hook must be implemented in the module's .install file. It
|
Chris@0
|
276 * will fire when the module gets uninstalled but before the module's database
|
Chris@0
|
277 * tables are removed, allowing your module to query its own tables during
|
Chris@0
|
278 * this routine.
|
Chris@0
|
279 *
|
Chris@0
|
280 * @see hook_install()
|
Chris@0
|
281 * @see hook_schema()
|
Chris@0
|
282 * @see hook_modules_uninstalled()
|
Chris@0
|
283 */
|
Chris@0
|
284 function hook_uninstall() {
|
Chris@0
|
285 // Remove the styles directory and generated images.
|
Chris@18
|
286 \Drupal::service('file_system')->deleteRecursive(file_default_scheme() . '://styles');
|
Chris@0
|
287 }
|
Chris@0
|
288
|
Chris@0
|
289 /**
|
Chris@0
|
290 * Return an array of tasks to be performed by an installation profile.
|
Chris@0
|
291 *
|
Chris@0
|
292 * Any tasks you define here will be run, in order, after the installer has
|
Chris@0
|
293 * finished the site configuration step but before it has moved on to the
|
Chris@0
|
294 * final import of languages and the end of the installation. This is invoked
|
Chris@0
|
295 * by install_tasks(). You can have any number of custom tasks to perform
|
Chris@0
|
296 * during this phase.
|
Chris@0
|
297 *
|
Chris@0
|
298 * Each task you define here corresponds to a callback function which you must
|
Chris@0
|
299 * separately define and which is called when your task is run. This function
|
Chris@0
|
300 * will receive the global installation state variable, $install_state, as
|
Chris@0
|
301 * input, and has the opportunity to access or modify any of its settings. See
|
Chris@0
|
302 * the install_state_defaults() function in the installer for the list of
|
Chris@0
|
303 * $install_state settings used by Drupal core.
|
Chris@0
|
304 *
|
Chris@0
|
305 * At the end of your task function, you can indicate that you want the
|
Chris@0
|
306 * installer to pause and display a page to the user by returning any themed
|
Chris@0
|
307 * output that should be displayed on that page (but see below for tasks that
|
Chris@0
|
308 * use the form API or batch API; the return values of these task functions are
|
Chris@0
|
309 * handled differently). You should also use #title within the task
|
Chris@0
|
310 * callback function to set a custom page title. For some tasks, however, you
|
Chris@0
|
311 * may want to simply do some processing and pass control to the next task
|
Chris@0
|
312 * without ending the page request; to indicate this, simply do not send back
|
Chris@0
|
313 * a return value from your task function at all. This can be used, for
|
Chris@0
|
314 * example, by installation profiles that need to configure certain site
|
Chris@0
|
315 * settings in the database without obtaining any input from the user.
|
Chris@0
|
316 *
|
Chris@0
|
317 * The task function is treated specially if it defines a form or requires
|
Chris@0
|
318 * batch processing; in that case, you should return either the form API
|
Chris@0
|
319 * definition or batch API array, as appropriate. See below for more
|
Chris@0
|
320 * information on the 'type' key that you must define in the task definition
|
Chris@0
|
321 * to inform the installer that your task falls into one of those two
|
Chris@0
|
322 * categories. It is important to use these APIs directly, since the installer
|
Chris@0
|
323 * may be run non-interactively (for example, via a command line script), all
|
Chris@0
|
324 * in one page request; in that case, the installer will automatically take
|
Chris@0
|
325 * care of submitting forms and processing batches correctly for both types of
|
Chris@0
|
326 * installations. You can inspect the $install_state['interactive'] boolean to
|
Chris@0
|
327 * see whether or not the current installation is interactive, if you need
|
Chris@0
|
328 * access to this information.
|
Chris@0
|
329 *
|
Chris@0
|
330 * Remember that a user installing Drupal interactively will be able to reload
|
Chris@0
|
331 * an installation page multiple times, so you should use \Drupal::state() to
|
Chris@0
|
332 * store any data that you may need later in the installation process. Any
|
Chris@0
|
333 * temporary state must be removed using \Drupal::state()->delete() before
|
Chris@0
|
334 * your last task has completed and control is handed back to the installer.
|
Chris@0
|
335 *
|
Chris@0
|
336 * @param array $install_state
|
Chris@0
|
337 * An array of information about the current installation state.
|
Chris@0
|
338 *
|
Chris@0
|
339 * @return array
|
Chris@0
|
340 * A keyed array of tasks the profile will perform during the final stage of
|
Chris@0
|
341 * the installation. Each key represents the name of a function (usually a
|
Chris@0
|
342 * function defined by this profile, although that is not strictly required)
|
Chris@0
|
343 * that is called when that task is run. The values are associative arrays
|
Chris@0
|
344 * containing the following key-value pairs (all of which are optional):
|
Chris@0
|
345 * - display_name: The human-readable name of the task. This will be
|
Chris@0
|
346 * displayed to the user while the installer is running, along with a list
|
Chris@0
|
347 * of other tasks that are being run. Leave this unset to prevent the task
|
Chris@0
|
348 * from appearing in the list.
|
Chris@0
|
349 * - display: This is a boolean which can be used to provide finer-grained
|
Chris@0
|
350 * control over whether or not the task will display. This is mostly useful
|
Chris@0
|
351 * for tasks that are intended to display only under certain conditions;
|
Chris@0
|
352 * for these tasks, you can set 'display_name' to the name that you want to
|
Chris@0
|
353 * display, but then use this boolean to hide the task only when certain
|
Chris@0
|
354 * conditions apply.
|
Chris@0
|
355 * - type: A string representing the type of task. This parameter has three
|
Chris@0
|
356 * possible values:
|
Chris@0
|
357 * - normal: (default) This indicates that the task will be treated as a
|
Chris@0
|
358 * regular callback function, which does its processing and optionally
|
Chris@0
|
359 * returns HTML output.
|
Chris@0
|
360 * - batch: This indicates that the task function will return a batch API
|
Chris@0
|
361 * definition suitable for batch_set() or an array of batch definitions
|
Chris@0
|
362 * suitable for consecutive batch_set() calls. The installer will then
|
Chris@0
|
363 * take care of automatically running the task via batch processing.
|
Chris@0
|
364 * - form: This indicates that the task function will return a standard
|
Chris@0
|
365 * form API definition (and separately define validation and submit
|
Chris@0
|
366 * handlers, as appropriate). The installer will then take care of
|
Chris@0
|
367 * automatically directing the user through the form submission process.
|
Chris@0
|
368 * - run: A constant representing the manner in which the task will be run.
|
Chris@0
|
369 * This parameter has three possible values:
|
Chris@0
|
370 * - INSTALL_TASK_RUN_IF_NOT_COMPLETED: (default) This indicates that the
|
Chris@0
|
371 * task will run once during the installation of the profile.
|
Chris@0
|
372 * - INSTALL_TASK_SKIP: This indicates that the task will not run during
|
Chris@0
|
373 * the current installation page request. It can be used to skip running
|
Chris@0
|
374 * an installation task when certain conditions are met, even though the
|
Chris@0
|
375 * task may still show on the list of installation tasks presented to the
|
Chris@0
|
376 * user.
|
Chris@0
|
377 * - INSTALL_TASK_RUN_IF_REACHED: This indicates that the task will run on
|
Chris@0
|
378 * each installation page request that reaches it. This is rarely
|
Chris@0
|
379 * necessary for an installation profile to use; it is primarily used by
|
Chris@0
|
380 * the Drupal installer for bootstrap-related tasks.
|
Chris@0
|
381 * - function: Normally this does not need to be set, but it can be used to
|
Chris@0
|
382 * force the installer to call a different function when the task is run
|
Chris@0
|
383 * (rather than the function whose name is given by the array key). This
|
Chris@0
|
384 * could be used, for example, to allow the same function to be called by
|
Chris@0
|
385 * two different tasks.
|
Chris@0
|
386 *
|
Chris@0
|
387 * @see install_state_defaults()
|
Chris@0
|
388 * @see batch_set()
|
Chris@0
|
389 * @see hook_install_tasks_alter()
|
Chris@0
|
390 * @see install_tasks()
|
Chris@0
|
391 */
|
Chris@0
|
392 function hook_install_tasks(&$install_state) {
|
Chris@0
|
393 // Here, we define a variable to allow tasks to indicate that a particular,
|
Chris@0
|
394 // processor-intensive batch process needs to be triggered later on in the
|
Chris@0
|
395 // installation.
|
Chris@0
|
396 $myprofile_needs_batch_processing = \Drupal::state()->get('myprofile.needs_batch_processing', FALSE);
|
Chris@0
|
397 $tasks = [
|
Chris@0
|
398 // This is an example of a task that defines a form which the user who is
|
Chris@0
|
399 // installing the site will be asked to fill out. To implement this task,
|
Chris@0
|
400 // your profile would define a function named myprofile_data_import_form()
|
Chris@0
|
401 // as a normal form API callback function, with associated validation and
|
Chris@0
|
402 // submit handlers. In the submit handler, in addition to saving whatever
|
Chris@0
|
403 // other data you have collected from the user, you might also call
|
Chris@0
|
404 // \Drupal::state()->set('myprofile.needs_batch_processing', TRUE) if the
|
Chris@0
|
405 // user has entered data which requires that batch processing will need to
|
Chris@0
|
406 // occur later on.
|
Chris@0
|
407 'myprofile_data_import_form' => [
|
Chris@0
|
408 'display_name' => t('Data import options'),
|
Chris@0
|
409 'type' => 'form',
|
Chris@0
|
410 ],
|
Chris@0
|
411 // Similarly, to implement this task, your profile would define a function
|
Chris@0
|
412 // named myprofile_settings_form() with associated validation and submit
|
Chris@0
|
413 // handlers. This form might be used to collect and save additional
|
Chris@0
|
414 // information from the user that your profile needs. There are no extra
|
Chris@0
|
415 // steps required for your profile to act as an "installation wizard"; you
|
Chris@0
|
416 // can simply define as many tasks of type 'form' as you wish to execute,
|
Chris@0
|
417 // and the forms will be presented to the user, one after another.
|
Chris@0
|
418 'myprofile_settings_form' => [
|
Chris@0
|
419 'display_name' => t('Additional options'),
|
Chris@0
|
420 'type' => 'form',
|
Chris@0
|
421 ],
|
Chris@0
|
422 // This is an example of a task that performs batch operations. To
|
Chris@0
|
423 // implement this task, your profile would define a function named
|
Chris@0
|
424 // myprofile_batch_processing() which returns a batch API array definition
|
Chris@0
|
425 // that the installer will use to execute your batch operations. Due to the
|
Chris@0
|
426 // 'myprofile.needs_batch_processing' variable used here, this task will be
|
Chris@0
|
427 // hidden and skipped unless your profile set it to TRUE in one of the
|
Chris@0
|
428 // previous tasks.
|
Chris@0
|
429 'myprofile_batch_processing' => [
|
Chris@0
|
430 'display_name' => t('Import additional data'),
|
Chris@0
|
431 'display' => $myprofile_needs_batch_processing,
|
Chris@0
|
432 'type' => 'batch',
|
Chris@0
|
433 'run' => $myprofile_needs_batch_processing ? INSTALL_TASK_RUN_IF_NOT_COMPLETED : INSTALL_TASK_SKIP,
|
Chris@0
|
434 ],
|
Chris@0
|
435 // This is an example of a task that will not be displayed in the list that
|
Chris@0
|
436 // the user sees. To implement this task, your profile would define a
|
Chris@0
|
437 // function named myprofile_final_site_setup(), in which additional,
|
Chris@0
|
438 // automated site setup operations would be performed. Since this is the
|
Chris@0
|
439 // last task defined by your profile, you should also use this function to
|
Chris@0
|
440 // call \Drupal::state()->delete('myprofile.needs_batch_processing') and
|
Chris@0
|
441 // clean up the state that was used above. If you want the user to pass
|
Chris@0
|
442 // to the final Drupal installation tasks uninterrupted, return no output
|
Chris@0
|
443 // from this function. Otherwise, return themed output that the user will
|
Chris@0
|
444 // see (for example, a confirmation page explaining that your profile's
|
Chris@0
|
445 // tasks are complete, with a link to reload the current page and therefore
|
Chris@0
|
446 // pass on to the final Drupal installation tasks when the user is ready to
|
Chris@0
|
447 // do so).
|
Chris@0
|
448 'myprofile_final_site_setup' => [],
|
Chris@0
|
449 ];
|
Chris@0
|
450 return $tasks;
|
Chris@0
|
451 }
|
Chris@0
|
452
|
Chris@0
|
453 /**
|
Chris@0
|
454 * Alter the full list of installation tasks.
|
Chris@0
|
455 *
|
Chris@0
|
456 * You can use this hook to change or replace any part of the Drupal
|
Chris@0
|
457 * installation process that occurs after the installation profile is selected.
|
Chris@0
|
458 *
|
Chris@0
|
459 * This hook is invoked on the install profile in install_tasks().
|
Chris@0
|
460 *
|
Chris@0
|
461 * @param $tasks
|
Chris@0
|
462 * An array of all available installation tasks, including those provided by
|
Chris@0
|
463 * Drupal core. You can modify this array to change or replace individual
|
Chris@0
|
464 * steps within the installation process.
|
Chris@0
|
465 * @param $install_state
|
Chris@0
|
466 * An array of information about the current installation state.
|
Chris@0
|
467 *
|
Chris@0
|
468 * @see hook_install_tasks()
|
Chris@0
|
469 * @see install_tasks()
|
Chris@0
|
470 */
|
Chris@0
|
471 function hook_install_tasks_alter(&$tasks, $install_state) {
|
Chris@0
|
472 // Replace the entire site configuration form provided by Drupal core
|
Chris@0
|
473 // with a custom callback function defined by this installation profile.
|
Chris@0
|
474 $tasks['install_configure_form']['function'] = 'myprofile_install_configure_form';
|
Chris@0
|
475 }
|
Chris@0
|
476
|
Chris@0
|
477 /**
|
Chris@0
|
478 * Perform a single update between minor versions.
|
Chris@0
|
479 *
|
Chris@17
|
480 * Hook hook_update_N() can only be used to update between minor versions of a
|
Chris@0
|
481 * module. To upgrade between major versions of Drupal (for example, between
|
Chris@0
|
482 * Drupal 7 and 8), use the @link migrate Migrate API @endlink instead.
|
Chris@0
|
483 *
|
Chris@0
|
484 * @section sec_naming Naming and documenting your function
|
Chris@0
|
485 * For each change in a module that requires one or more actions to be performed
|
Chris@0
|
486 * when updating a site, add a new implementation of hook_update_N() to your
|
Chris@0
|
487 * mymodule.install file (assuming mymodule is the machine name of your module).
|
Chris@0
|
488 * Implementations of hook_update_N() are named (module name)_update_(number).
|
Chris@0
|
489 * The numbers are normally composed of three parts:
|
Chris@0
|
490 * - 1 or 2 digits for Drupal core compatibility (Drupal 8, 9, 10, etc.). This
|
Chris@0
|
491 * convention must be followed.
|
Chris@0
|
492 * - 1 digit for your module's major release version; for example, for 8.x-1.*
|
Chris@0
|
493 * use 1, for 8.x-2.* use 2, for Core 8.0.x use 0, and for Core 8.1.x use 1.
|
Chris@0
|
494 * This convention is optional but suggested for clarity.
|
Chris@0
|
495 * - 2 digits for sequential counting, starting with 01. Note that the x000
|
Chris@0
|
496 * number can never be used: the lowest update number that will be recognized
|
Chris@0
|
497 * and run for major version x is x001.
|
Chris@0
|
498 * Examples:
|
Chris@0
|
499 * - node_update_8001(): The first update for the Drupal 8.0.x version of the
|
Chris@0
|
500 * Drupal Core node module.
|
Chris@0
|
501 * - mymodule_update_8101(): The first update for your custom or contributed
|
Chris@0
|
502 * module's 8.x-1.x versions.
|
Chris@0
|
503 * - mymodule_update_8201(): The first update for the 8.x-2.x versions.
|
Chris@0
|
504 *
|
Chris@0
|
505 * Never renumber update functions. The numeric part of the hook implementation
|
Chris@0
|
506 * function is stored in the database to keep track of which updates have run,
|
Chris@0
|
507 * so it is important to maintain this information consistently.
|
Chris@0
|
508 *
|
Chris@0
|
509 * The documentation block preceding this function is stripped of newlines and
|
Chris@0
|
510 * used as the description for the update on the pending updates task list,
|
Chris@0
|
511 * which users will see when they run the update.php script.
|
Chris@0
|
512 *
|
Chris@0
|
513 * @section sec_notes Notes about the function body
|
Chris@0
|
514 * Writing hook_update_N() functions is tricky. There are several reasons why
|
Chris@0
|
515 * this is the case:
|
Chris@0
|
516 * - You do not know when updates will be run: someone could be keeping up with
|
Chris@0
|
517 * every update and run them when the database and code are in the same state
|
Chris@0
|
518 * as when you wrote your update function, or they could have waited until a
|
Chris@0
|
519 * few more updates have come out, and run several at the same time.
|
Chris@0
|
520 * - You do not know the state of other modules' updates either.
|
Chris@0
|
521 * - Other modules can use hook_update_dependencies() to run updates between
|
Chris@0
|
522 * your module's updates, so you also cannot count on your functions running
|
Chris@0
|
523 * right after one another.
|
Chris@0
|
524 * - You do not know what environment your update will run in (which modules
|
Chris@0
|
525 * are installed, whether certain hooks are implemented or not, whether
|
Chris@0
|
526 * services are overridden, etc.).
|
Chris@0
|
527 *
|
Chris@0
|
528 * Because of these reasons, you'll need to use care in writing your update
|
Chris@0
|
529 * function. Some things to think about:
|
Chris@0
|
530 * - Never assume that the database schema is the same when the update will run
|
Chris@0
|
531 * as it is when you wrote the update function. So, when updating a database
|
Chris@0
|
532 * table or field, put the schema information you want to update to directly
|
Chris@0
|
533 * into your function instead of calling your hook_schema() function to
|
Chris@0
|
534 * retrieve it (this is one case where the right thing to do is copy and paste
|
Chris@0
|
535 * the code).
|
Chris@0
|
536 * - Never assume that the configuration schema is the same when the update will
|
Chris@0
|
537 * run as it is when you wrote the update function. So, when saving
|
Chris@0
|
538 * configuration, use the $has_trusted_data = TRUE parameter so that schema is
|
Chris@0
|
539 * ignored, and make sure that the configuration data you are saving matches
|
Chris@0
|
540 * the configuration schema at the time when you write the update function
|
Chris@0
|
541 * (later updates may change it again to match new schema changes).
|
Chris@0
|
542 * - Never assume your field or entity type definitions are the same when the
|
Chris@0
|
543 * update will run as they are when you wrote the update function. Always
|
Chris@0
|
544 * retrieve the correct version via
|
Chris@0
|
545 * \Drupal::entityDefinitionUpdateManager()::getEntityType() or
|
Chris@0
|
546 * \Drupal::entityDefinitionUpdateManager()::getFieldStorageDefinition(). When
|
Chris@0
|
547 * adding a new definition always replicate it in the update function body as
|
Chris@0
|
548 * you would do with a schema definition.
|
Chris@0
|
549 * - Never call \Drupal::entityDefinitionUpdateManager()::applyUpdates() in an
|
Chris@0
|
550 * update function, as it will apply updates for any module not only yours,
|
Chris@0
|
551 * which will lead to unpredictable results.
|
Chris@0
|
552 * - Be careful about API functions and especially CRUD operations that you use
|
Chris@0
|
553 * in your update function. If they invoke hooks or use services, they may
|
Chris@0
|
554 * not behave as expected, and it may actually not be appropriate to use the
|
Chris@0
|
555 * normal API functions that invoke all the hooks, use the database schema,
|
Chris@0
|
556 * and/or use services in an update function -- you may need to switch to
|
Chris@0
|
557 * using a more direct method (database query, etc.).
|
Chris@0
|
558 * - In particular, loading, saving, or performing any other CRUD operation on
|
Chris@0
|
559 * an entity is never safe to do (they always involve hooks and services).
|
Chris@0
|
560 * - Never rebuild the router during an update function.
|
Chris@0
|
561 *
|
Chris@0
|
562 * The following actions are examples of things that are safe to do during
|
Chris@0
|
563 * updates:
|
Chris@0
|
564 * - Cache invalidation.
|
Chris@0
|
565 * - Using \Drupal::configFactory()->getEditable() and \Drupal::config(), as
|
Chris@0
|
566 * long as you make sure that your update data matches the schema, and you
|
Chris@0
|
567 * use the $has_trusted_data argument in the save operation.
|
Chris@0
|
568 * - Marking a container for rebuild.
|
Chris@0
|
569 * - Using the API provided by \Drupal::entityDefinitionUpdateManager() to
|
Chris@0
|
570 * update the entity schema based on changes in entity type or field
|
Chris@0
|
571 * definitions provided by your module.
|
Chris@0
|
572 *
|
Chris@0
|
573 * See https://www.drupal.org/node/2535316 for more on writing update functions.
|
Chris@0
|
574 *
|
Chris@0
|
575 * @section sec_bulk Batch updates
|
Chris@0
|
576 * If running your update all at once could possibly cause PHP to time out, use
|
Chris@0
|
577 * the $sandbox parameter to indicate that the Batch API should be used for your
|
Chris@0
|
578 * update. In this case, your update function acts as an implementation of
|
Chris@0
|
579 * callback_batch_operation(), and $sandbox acts as the batch context
|
Chris@0
|
580 * parameter. In your function, read the state information from the previous
|
Chris@0
|
581 * run from $sandbox (or initialize), run a chunk of updates, save the state in
|
Chris@0
|
582 * $sandbox, and set $sandbox['#finished'] to a value between 0 and 1 to
|
Chris@0
|
583 * indicate the percent completed, or 1 if it is finished (you need to do this
|
Chris@0
|
584 * explicitly in each pass).
|
Chris@0
|
585 *
|
Chris@0
|
586 * See the @link batch Batch operations topic @endlink for more information on
|
Chris@0
|
587 * how to use the Batch API.
|
Chris@0
|
588 *
|
Chris@0
|
589 * @param array $sandbox
|
Chris@0
|
590 * Stores information for batch updates. See above for more information.
|
Chris@0
|
591 *
|
Chris@0
|
592 * @return string|null
|
Chris@0
|
593 * Optionally, update hooks may return a translated string that will be
|
Chris@0
|
594 * displayed to the user after the update has completed. If no message is
|
Chris@0
|
595 * returned, no message will be presented to the user.
|
Chris@0
|
596 *
|
Chris@0
|
597 * @throws \Drupal\Core\Utility\UpdateException|PDOException
|
Chris@0
|
598 * In case of error, update hooks should throw an instance of
|
Chris@0
|
599 * Drupal\Core\Utility\UpdateException with a meaningful message for the user.
|
Chris@0
|
600 * If a database query fails for whatever reason, it will throw a
|
Chris@0
|
601 * PDOException.
|
Chris@0
|
602 *
|
Chris@0
|
603 * @ingroup update_api
|
Chris@0
|
604 *
|
Chris@0
|
605 * @see batch
|
Chris@0
|
606 * @see schemaapi
|
Chris@0
|
607 * @see hook_update_last_removed()
|
Chris@0
|
608 * @see update_get_update_list()
|
Chris@0
|
609 * @see \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface
|
Chris@0
|
610 * @see node_update_8001
|
Chris@0
|
611 * @see system_update_8004
|
Chris@0
|
612 * @see https://www.drupal.org/node/2535316
|
Chris@0
|
613 */
|
Chris@0
|
614 function hook_update_N(&$sandbox) {
|
Chris@0
|
615 // For non-batch updates, the signature can simply be:
|
Chris@0
|
616 // function hook_update_N() {
|
Chris@0
|
617
|
Chris@0
|
618 // Example function body for adding a field to a database table, which does
|
Chris@0
|
619 // not require a batch operation:
|
Chris@0
|
620 $spec = [
|
Chris@0
|
621 'type' => 'varchar',
|
Chris@0
|
622 'description' => "New Col",
|
Chris@0
|
623 'length' => 20,
|
Chris@0
|
624 'not null' => FALSE,
|
Chris@0
|
625 ];
|
Chris@0
|
626 $schema = Database::getConnection()->schema();
|
Chris@0
|
627 $schema->addField('mytable1', 'newcol', $spec);
|
Chris@0
|
628
|
Chris@0
|
629 // Example of what to do if there is an error during your update.
|
Chris@0
|
630 if ($some_error_condition_met) {
|
Chris@0
|
631 throw new UpdateException('Something went wrong; here is what you should do.');
|
Chris@0
|
632 }
|
Chris@0
|
633
|
Chris@0
|
634 // Example function body for a batch update. In this example, the values in
|
Chris@0
|
635 // a database field are updated.
|
Chris@0
|
636 if (!isset($sandbox['progress'])) {
|
Chris@0
|
637 // This must be the first run. Initialize the sandbox.
|
Chris@0
|
638 $sandbox['progress'] = 0;
|
Chris@0
|
639 $sandbox['current_pk'] = 0;
|
Chris@0
|
640 $sandbox['max'] = Database::getConnection()->query('SELECT COUNT(myprimarykey) FROM {mytable1}')->fetchField() - 1;
|
Chris@0
|
641 }
|
Chris@0
|
642
|
Chris@0
|
643 // Update in chunks of 20.
|
Chris@0
|
644 $records = Database::getConnection()->select('mytable1', 'm')
|
Chris@0
|
645 ->fields('m', ['myprimarykey', 'otherfield'])
|
Chris@0
|
646 ->condition('myprimarykey', $sandbox['current_pk'], '>')
|
Chris@0
|
647 ->range(0, 20)
|
Chris@0
|
648 ->orderBy('myprimarykey', 'ASC')
|
Chris@0
|
649 ->execute();
|
Chris@0
|
650 foreach ($records as $record) {
|
Chris@0
|
651 // Here, you would make an update something related to this record. In this
|
Chris@0
|
652 // example, some text is added to the other field.
|
Chris@0
|
653 Database::getConnection()->update('mytable1')
|
Chris@0
|
654 ->fields(['otherfield' => $record->otherfield . '-suffix'])
|
Chris@0
|
655 ->condition('myprimarykey', $record->myprimarykey)
|
Chris@0
|
656 ->execute();
|
Chris@0
|
657
|
Chris@0
|
658 $sandbox['progress']++;
|
Chris@0
|
659 $sandbox['current_pk'] = $record->myprimarykey;
|
Chris@0
|
660 }
|
Chris@0
|
661
|
Chris@0
|
662 $sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['progress'] / $sandbox['max']);
|
Chris@0
|
663
|
Chris@0
|
664 // To display a message to the user when the update is completed, return it.
|
Chris@0
|
665 // If you do not want to display a completion message, return nothing.
|
Chris@0
|
666 return t('All foo bars were updated with the new suffix');
|
Chris@0
|
667 }
|
Chris@0
|
668
|
Chris@0
|
669 /**
|
Chris@0
|
670 * Executes an update which is intended to update data, like entities.
|
Chris@0
|
671 *
|
Chris@0
|
672 * These implementations have to be placed in a MODULE.post_update.php file.
|
Chris@0
|
673 *
|
Chris@0
|
674 * These updates are executed after all hook_update_N() implementations. At this
|
Chris@0
|
675 * stage Drupal is already fully repaired so you can use any API as you wish.
|
Chris@0
|
676 *
|
Chris@14
|
677 * NAME can be arbitrary machine names. In contrast to hook_update_N() the
|
Chris@14
|
678 * alphanumeric naming of functions in the file is the only thing which ensures
|
Chris@14
|
679 * the execution order of those functions. If update order is mandatory,
|
Chris@14
|
680 * you should add numerical prefix to NAME or make it completely numerical.
|
Chris@0
|
681 *
|
Chris@0
|
682 * Drupal also ensures to not execute the same hook_post_update_NAME() function
|
Chris@0
|
683 * twice.
|
Chris@0
|
684 *
|
Chris@17
|
685 * @section sec_bulk Batch updates
|
Chris@17
|
686 * If running your update all at once could possibly cause PHP to time out, use
|
Chris@17
|
687 * the $sandbox parameter to indicate that the Batch API should be used for your
|
Chris@17
|
688 * update. In this case, your update function acts as an implementation of
|
Chris@17
|
689 * callback_batch_operation(), and $sandbox acts as the batch context
|
Chris@17
|
690 * parameter. In your function, read the state information from the previous
|
Chris@17
|
691 * run from $sandbox (or initialize), run a chunk of updates, save the state in
|
Chris@17
|
692 * $sandbox, and set $sandbox['#finished'] to a value between 0 and 1 to
|
Chris@17
|
693 * indicate the percent completed, or 1 if it is finished (you need to do this
|
Chris@17
|
694 * explicitly in each pass).
|
Chris@17
|
695 *
|
Chris@17
|
696 * See the @link batch Batch operations topic @endlink for more information on
|
Chris@17
|
697 * how to use the Batch API.
|
Chris@17
|
698 *
|
Chris@0
|
699 * @param array $sandbox
|
Chris@0
|
700 * Stores information for batch updates. See above for more information.
|
Chris@0
|
701 *
|
Chris@0
|
702 * @return string|null
|
Chris@0
|
703 * Optionally, hook_post_update_NAME() hooks may return a translated string
|
Chris@0
|
704 * that will be displayed to the user after the update has completed. If no
|
Chris@0
|
705 * message is returned, no message will be presented to the user.
|
Chris@0
|
706 *
|
Chris@0
|
707 * @throws \Drupal\Core\Utility\UpdateException|PDOException
|
Chris@0
|
708 * In case of error, update hooks should throw an instance of
|
Chris@0
|
709 * \Drupal\Core\Utility\UpdateException with a meaningful message for the
|
Chris@0
|
710 * user. If a database query fails for whatever reason, it will throw a
|
Chris@0
|
711 * PDOException.
|
Chris@0
|
712 *
|
Chris@0
|
713 * @ingroup update_api
|
Chris@0
|
714 *
|
Chris@0
|
715 * @see hook_update_N()
|
Chris@0
|
716 */
|
Chris@0
|
717 function hook_post_update_NAME(&$sandbox) {
|
Chris@0
|
718 // Example of updating some content.
|
Chris@0
|
719 $node = \Drupal\node\Entity\Node::load(123);
|
Chris@0
|
720 $node->setTitle('foo');
|
Chris@0
|
721 $node->save();
|
Chris@0
|
722
|
Chris@0
|
723 $result = t('Node %nid saved', ['%nid' => $node->id()]);
|
Chris@0
|
724
|
Chris@0
|
725 // Example of disabling blocks with missing condition contexts. Note: The
|
Chris@0
|
726 // block itself is in a state which is valid at that point.
|
Chris@0
|
727 // @see block_update_8001()
|
Chris@0
|
728 // @see block_post_update_disable_blocks_with_missing_contexts()
|
Chris@0
|
729 $block_update_8001 = \Drupal::keyValue('update_backup')->get('block_update_8001', []);
|
Chris@0
|
730
|
Chris@0
|
731 $block_ids = array_keys($block_update_8001);
|
Chris@0
|
732 $block_storage = \Drupal::entityManager()->getStorage('block');
|
Chris@0
|
733 $blocks = $block_storage->loadMultiple($block_ids);
|
Chris@0
|
734 /** @var $blocks \Drupal\block\BlockInterface[] */
|
Chris@0
|
735 foreach ($blocks as $block) {
|
Chris@0
|
736 // This block has had conditions removed due to an inability to resolve
|
Chris@0
|
737 // contexts in block_update_8001() so disable it.
|
Chris@0
|
738
|
Chris@0
|
739 // Disable currently enabled blocks.
|
Chris@0
|
740 if ($block_update_8001[$block->id()]['status']) {
|
Chris@0
|
741 $block->setStatus(FALSE);
|
Chris@0
|
742 $block->save();
|
Chris@0
|
743 }
|
Chris@0
|
744 }
|
Chris@0
|
745
|
Chris@0
|
746 return $result;
|
Chris@0
|
747 }
|
Chris@0
|
748
|
Chris@0
|
749 /**
|
Chris@0
|
750 * Return an array of information about module update dependencies.
|
Chris@0
|
751 *
|
Chris@0
|
752 * This can be used to indicate update functions from other modules that your
|
Chris@0
|
753 * module's update functions depend on, or vice versa. It is used by the update
|
Chris@0
|
754 * system to determine the appropriate order in which updates should be run, as
|
Chris@0
|
755 * well as to search for missing dependencies.
|
Chris@0
|
756 *
|
Chris@0
|
757 * Implementations of this hook should be placed in a mymodule.install file in
|
Chris@0
|
758 * the same directory as mymodule.module.
|
Chris@0
|
759 *
|
Chris@0
|
760 * @return
|
Chris@0
|
761 * A multidimensional array containing information about the module update
|
Chris@0
|
762 * dependencies. The first two levels of keys represent the module and update
|
Chris@0
|
763 * number (respectively) for which information is being returned, and the
|
Chris@0
|
764 * value is an array of information about that update's dependencies. Within
|
Chris@0
|
765 * this array, each key represents a module, and each value represents the
|
Chris@0
|
766 * number of an update function within that module. In the event that your
|
Chris@0
|
767 * update function depends on more than one update from a particular module,
|
Chris@0
|
768 * you should always list the highest numbered one here (since updates within
|
Chris@0
|
769 * a given module always run in numerical order).
|
Chris@0
|
770 *
|
Chris@0
|
771 * @ingroup update_api
|
Chris@0
|
772 *
|
Chris@0
|
773 * @see update_resolve_dependencies()
|
Chris@0
|
774 * @see hook_update_N()
|
Chris@0
|
775 */
|
Chris@0
|
776 function hook_update_dependencies() {
|
Chris@0
|
777 // Indicate that the mymodule_update_8001() function provided by this module
|
Chris@0
|
778 // must run after the another_module_update_8003() function provided by the
|
Chris@0
|
779 // 'another_module' module.
|
Chris@0
|
780 $dependencies['mymodule'][8001] = [
|
Chris@0
|
781 'another_module' => 8003,
|
Chris@0
|
782 ];
|
Chris@0
|
783 // Indicate that the mymodule_update_8002() function provided by this module
|
Chris@0
|
784 // must run before the yet_another_module_update_8005() function provided by
|
Chris@0
|
785 // the 'yet_another_module' module. (Note that declaring dependencies in this
|
Chris@0
|
786 // direction should be done only in rare situations, since it can lead to the
|
Chris@0
|
787 // following problem: If a site has already run the yet_another_module
|
Chris@0
|
788 // module's database updates before it updates its codebase to pick up the
|
Chris@0
|
789 // newest mymodule code, then the dependency declared here will be ignored.)
|
Chris@0
|
790 $dependencies['yet_another_module'][8005] = [
|
Chris@0
|
791 'mymodule' => 8002,
|
Chris@0
|
792 ];
|
Chris@0
|
793 return $dependencies;
|
Chris@0
|
794 }
|
Chris@0
|
795
|
Chris@0
|
796 /**
|
Chris@0
|
797 * Return a number which is no longer available as hook_update_N().
|
Chris@0
|
798 *
|
Chris@0
|
799 * If you remove some update functions from your mymodule.install file, you
|
Chris@0
|
800 * should notify Drupal of those missing functions. This way, Drupal can
|
Chris@0
|
801 * ensure that no update is accidentally skipped.
|
Chris@0
|
802 *
|
Chris@0
|
803 * Implementations of this hook should be placed in a mymodule.install file in
|
Chris@0
|
804 * the same directory as mymodule.module.
|
Chris@0
|
805 *
|
Chris@0
|
806 * @return
|
Chris@0
|
807 * An integer, corresponding to hook_update_N() which has been removed from
|
Chris@0
|
808 * mymodule.install.
|
Chris@0
|
809 *
|
Chris@0
|
810 * @ingroup update_api
|
Chris@0
|
811 *
|
Chris@0
|
812 * @see hook_update_N()
|
Chris@0
|
813 */
|
Chris@0
|
814 function hook_update_last_removed() {
|
Chris@0
|
815 // We've removed the 8.x-1.x version of mymodule, including database updates.
|
Chris@0
|
816 // The next update function is mymodule_update_8200().
|
Chris@0
|
817 return 8103;
|
Chris@0
|
818 }
|
Chris@0
|
819
|
Chris@0
|
820 /**
|
Chris@0
|
821 * Provide information on Updaters (classes that can update Drupal).
|
Chris@0
|
822 *
|
Chris@0
|
823 * Drupal\Core\Updater\Updater is a class that knows how to update various parts
|
Chris@0
|
824 * of the Drupal file system, for example to update modules that have newer
|
Chris@0
|
825 * releases, or to install a new theme.
|
Chris@0
|
826 *
|
Chris@0
|
827 * @return
|
Chris@0
|
828 * An associative array of information about the updater(s) being provided.
|
Chris@0
|
829 * This array is keyed by a unique identifier for each updater, and the
|
Chris@0
|
830 * values are subarrays that can contain the following keys:
|
Chris@0
|
831 * - class: The name of the PHP class which implements this updater.
|
Chris@0
|
832 * - name: Human-readable name of this updater.
|
Chris@0
|
833 * - weight: Controls what order the Updater classes are consulted to decide
|
Chris@0
|
834 * which one should handle a given task. When an update task is being run,
|
Chris@0
|
835 * the system will loop through all the Updater classes defined in this
|
Chris@0
|
836 * registry in weight order and let each class respond to the task and
|
Chris@0
|
837 * decide if each Updater wants to handle the task. In general, this
|
Chris@0
|
838 * doesn't matter, but if you need to override an existing Updater, make
|
Chris@0
|
839 * sure your Updater has a lighter weight so that it comes first.
|
Chris@0
|
840 *
|
Chris@0
|
841 * @ingroup update_api
|
Chris@0
|
842 *
|
Chris@0
|
843 * @see drupal_get_updaters()
|
Chris@0
|
844 * @see hook_updater_info_alter()
|
Chris@0
|
845 */
|
Chris@0
|
846 function hook_updater_info() {
|
Chris@0
|
847 return [
|
Chris@0
|
848 'module' => [
|
Chris@0
|
849 'class' => 'Drupal\Core\Updater\Module',
|
Chris@0
|
850 'name' => t('Update modules'),
|
Chris@0
|
851 'weight' => 0,
|
Chris@0
|
852 ],
|
Chris@0
|
853 'theme' => [
|
Chris@0
|
854 'class' => 'Drupal\Core\Updater\Theme',
|
Chris@0
|
855 'name' => t('Update themes'),
|
Chris@0
|
856 'weight' => 0,
|
Chris@0
|
857 ],
|
Chris@0
|
858 ];
|
Chris@0
|
859 }
|
Chris@0
|
860
|
Chris@0
|
861 /**
|
Chris@0
|
862 * Alter the Updater information array.
|
Chris@0
|
863 *
|
Chris@0
|
864 * An Updater is a class that knows how to update various parts of the Drupal
|
Chris@0
|
865 * file system, for example to update modules that have newer releases, or to
|
Chris@0
|
866 * install a new theme.
|
Chris@0
|
867 *
|
Chris@0
|
868 * @param array $updaters
|
Chris@0
|
869 * Associative array of updaters as defined through hook_updater_info().
|
Chris@0
|
870 * Alter this array directly.
|
Chris@0
|
871 *
|
Chris@0
|
872 * @ingroup update_api
|
Chris@0
|
873 *
|
Chris@0
|
874 * @see drupal_get_updaters()
|
Chris@0
|
875 * @see hook_updater_info()
|
Chris@0
|
876 */
|
Chris@0
|
877 function hook_updater_info_alter(&$updaters) {
|
Chris@0
|
878 // Adjust weight so that the theme Updater gets a chance to handle a given
|
Chris@0
|
879 // update task before module updaters.
|
Chris@0
|
880 $updaters['theme']['weight'] = -1;
|
Chris@0
|
881 }
|
Chris@0
|
882
|
Chris@0
|
883 /**
|
Chris@0
|
884 * Check installation requirements and do status reporting.
|
Chris@0
|
885 *
|
Chris@0
|
886 * This hook has three closely related uses, determined by the $phase argument:
|
Chris@0
|
887 * - Checking installation requirements ($phase == 'install').
|
Chris@0
|
888 * - Checking update requirements ($phase == 'update').
|
Chris@0
|
889 * - Status reporting ($phase == 'runtime').
|
Chris@0
|
890 *
|
Chris@0
|
891 * Note that this hook, like all others dealing with installation and updates,
|
Chris@0
|
892 * must reside in a module_name.install file, or it will not properly abort
|
Chris@0
|
893 * the installation of the module if a critical requirement is missing.
|
Chris@0
|
894 *
|
Chris@0
|
895 * During the 'install' phase, modules can for example assert that
|
Chris@0
|
896 * library or server versions are available or sufficient.
|
Chris@0
|
897 * Note that the installation of a module can happen during installation of
|
Chris@0
|
898 * Drupal itself (by install.php) with an installation profile or later by hand.
|
Chris@0
|
899 * As a consequence, install-time requirements must be checked without access
|
Chris@0
|
900 * to the full Drupal API, because it is not available during install.php.
|
Chris@0
|
901 * If a requirement has a severity of REQUIREMENT_ERROR, install.php will abort
|
Chris@0
|
902 * or at least the module will not install.
|
Chris@0
|
903 * Other severity levels have no effect on the installation.
|
Chris@0
|
904 * Module dependencies do not belong to these installation requirements,
|
Chris@0
|
905 * but should be defined in the module's .info.yml file.
|
Chris@0
|
906 *
|
Chris@0
|
907 * During installation (when $phase == 'install'), if you need to load a class
|
Chris@0
|
908 * from your module, you'll need to include the class file directly.
|
Chris@0
|
909 *
|
Chris@0
|
910 * The 'runtime' phase is not limited to pure installation requirements
|
Chris@0
|
911 * but can also be used for more general status information like maintenance
|
Chris@0
|
912 * tasks and security issues.
|
Chris@0
|
913 * The returned 'requirements' will be listed on the status report in the
|
Chris@0
|
914 * administration section, with indication of the severity level.
|
Chris@0
|
915 * Moreover, any requirement with a severity of REQUIREMENT_ERROR severity will
|
Chris@0
|
916 * result in a notice on the administration configuration page.
|
Chris@0
|
917 *
|
Chris@0
|
918 * @param $phase
|
Chris@0
|
919 * The phase in which requirements are checked:
|
Chris@0
|
920 * - install: The module is being installed.
|
Chris@0
|
921 * - update: The module is enabled and update.php is run.
|
Chris@0
|
922 * - runtime: The runtime requirements are being checked and shown on the
|
Chris@0
|
923 * status report page.
|
Chris@0
|
924 *
|
Chris@0
|
925 * @return
|
Chris@0
|
926 * An associative array where the keys are arbitrary but must be unique (it
|
Chris@0
|
927 * is suggested to use the module short name as a prefix) and the values are
|
Chris@0
|
928 * themselves associative arrays with the following elements:
|
Chris@0
|
929 * - title: The name of the requirement.
|
Chris@0
|
930 * - value: The current value (e.g., version, time, level, etc). During
|
Chris@0
|
931 * install phase, this should only be used for version numbers, do not set
|
Chris@0
|
932 * it if not applicable.
|
Chris@0
|
933 * - description: The description of the requirement/status.
|
Chris@0
|
934 * - severity: The requirement's result/severity level, one of:
|
Chris@0
|
935 * - REQUIREMENT_INFO: For info only.
|
Chris@0
|
936 * - REQUIREMENT_OK: The requirement is satisfied.
|
Chris@0
|
937 * - REQUIREMENT_WARNING: The requirement failed with a warning.
|
Chris@0
|
938 * - REQUIREMENT_ERROR: The requirement failed with an error.
|
Chris@0
|
939 */
|
Chris@0
|
940 function hook_requirements($phase) {
|
Chris@0
|
941 $requirements = [];
|
Chris@0
|
942
|
Chris@0
|
943 // Report Drupal version
|
Chris@0
|
944 if ($phase == 'runtime') {
|
Chris@0
|
945 $requirements['drupal'] = [
|
Chris@0
|
946 'title' => t('Drupal'),
|
Chris@0
|
947 'value' => \Drupal::VERSION,
|
Chris@17
|
948 'severity' => REQUIREMENT_INFO,
|
Chris@0
|
949 ];
|
Chris@0
|
950 }
|
Chris@0
|
951
|
Chris@0
|
952 // Test PHP version
|
Chris@0
|
953 $requirements['php'] = [
|
Chris@0
|
954 'title' => t('PHP'),
|
Chris@0
|
955 'value' => ($phase == 'runtime') ? \Drupal::l(phpversion(), new Url('system.php')) : phpversion(),
|
Chris@0
|
956 ];
|
Chris@0
|
957 if (version_compare(phpversion(), DRUPAL_MINIMUM_PHP) < 0) {
|
Chris@0
|
958 $requirements['php']['description'] = t('Your PHP installation is too old. Drupal requires at least PHP %version.', ['%version' => DRUPAL_MINIMUM_PHP]);
|
Chris@0
|
959 $requirements['php']['severity'] = REQUIREMENT_ERROR;
|
Chris@0
|
960 }
|
Chris@0
|
961
|
Chris@0
|
962 // Report cron status
|
Chris@0
|
963 if ($phase == 'runtime') {
|
Chris@0
|
964 $cron_last = \Drupal::state()->get('system.cron_last');
|
Chris@0
|
965
|
Chris@0
|
966 if (is_numeric($cron_last)) {
|
Chris@0
|
967 $requirements['cron']['value'] = t('Last run @time ago', ['@time' => \Drupal::service('date.formatter')->formatTimeDiffSince($cron_last)]);
|
Chris@0
|
968 }
|
Chris@0
|
969 else {
|
Chris@0
|
970 $requirements['cron'] = [
|
Chris@0
|
971 'description' => t('Cron has not run. It appears cron jobs have not been setup on your system. Check the help pages for <a href=":url">configuring cron jobs</a>.', [':url' => 'https://www.drupal.org/cron']),
|
Chris@0
|
972 'severity' => REQUIREMENT_ERROR,
|
Chris@0
|
973 'value' => t('Never run'),
|
Chris@0
|
974 ];
|
Chris@0
|
975 }
|
Chris@0
|
976
|
Chris@18
|
977 $requirements['cron']['description'] .= ' ' . t('You can <a href=":cron">run cron manually</a>.', [':cron' => Url::fromRoute('system.run_cron')->toString()]);
|
Chris@0
|
978
|
Chris@0
|
979 $requirements['cron']['title'] = t('Cron maintenance tasks');
|
Chris@0
|
980 }
|
Chris@0
|
981
|
Chris@0
|
982 return $requirements;
|
Chris@0
|
983 }
|
Chris@0
|
984
|
Chris@0
|
985 /**
|
Chris@0
|
986 * @} End of "addtogroup hooks".
|
Chris@0
|
987 */
|