# Civix Upgrade Guide Extensions produced by `civix` include a mix of custom code and boilerplate code. From time-to-time, you may wish to update the boilerplate code (eg to enable new functionality or to fix bugs). In `civix` v22.05+, there is a built-in upgrade assistant: ```bash cd myextension civix upgrade ``` This command may perform common tasks like: * Add or remove tags in `info.xml` * Add or remove stub functions in the main PHP file (like `myextension.php`) * Regenerate reserved files (like `myextension.civix.php`) This process is semi-automatic. Some well-defined tasks run automatically; others require extra communication or decision-making. ## Typical workflow (abstract) 1. Make sure you have a backup of your code. If you use version-control (`git`/`svn`), then you should be good to go. 2. In the shell, navigate to the target extension directory. (If the extension is `org.example.myext`, then the path may look like `/var/www/extensions/org.example.myext`.) 3. Run the `civix upgrade` command. This will inspect the codebase, regenerate boilerplate (eg `*.civix.php`), provide a log of changes, and (in some cases) provide extra questions or extra information about the upgrade. 4. Compare the new code with the old code (e.g. `git diff` or `svn diff`). 5. Review any new/relevant items [Special Tasks](#special-tasks). 6. Perform any QA (as you normally would for changes in the extension). ## Typical workflow (git) ```bash cd myextension git checkout -b my-civix-upgrade civix upgrade git status git diff git add . git commit -m 'Upgraded civix templates' ``` ## Special Tasks Some changes are not automated. These changes are (generally) optional and subjective -- e.g. they may introduce new options, methods, or template-code that improves the system. If you ignore them, the extension will continue working as before (unless noted otherwise). Special-tasks are organized based on when the functionality was introduced. (Ex: `v21.09.*` would indicate functionality that was added or modified circa September 2021.) ### Upgrade to v21.09.0+: Angular Module Angular code in Civi extensions usually has one of these layouts: * (A) (*default, best supported*) There is **one** Angular module, and its name **exactly matches** the Civi extension. * (B) There is **one** Angular module, and its name does *not* match the Civi extension. * (C) There are **multiple** Angular modules. It is **impossible** for them to all match. This version improves support for (B) (*one module, mismatched name*). You may now provide a hint via `info.xml`. For example, if the extension is `foobar` and the Angular module is `crmFoobar`, then set: ```xml foobar crmFoobar ``` For (B), this will improve usability - when calling `generate:angular-*` commands, it will use a better the default value of `--am=...`. There is no impact for (A) and (C). ### Upgrade to v20.09.0+: APIv3 Entity Some versions of `generate:entity` (late 2019/early 2020) created incorrect boilerplate for APIv3. This affected the file `api/v3/{MyEntity}.php` and the function `civicrm_api3_{my_entity}_get()`. The function may look like one of these 3 revisions: ```php // Revision 1 - The results will conform with APIv3 standards, but this may not be robust if // there are other problems with entity metadata. return _civicrm_api3_basic_get(_civicrm_api3_get_BAO(__FUNCTION__), $params); // Revision 2 - This is more robust against metadata problems, but the result-format does not conform // with APIv3 standards. It omits the header/wrapper ("is_error", "values", etc) and has an unquoted string. return _civicrm_api3_basic_get(_civicrm_api3_get_BAO(__FUNCTION__), $params, FALSE, MyEntity); // Revision 3 - This is conformant and robust. return _civicrm_api3_basic_get(_civicrm_api3_get_BAO(__FUNCTION__), $params, TRUE, 'MyEntity'); ``` If you currently have revision 2, then you should certainly fix the missing quotes. However, there is a choice about whether to fix the boolean: * __Switch to `TRUE`__: The output will have standard APIv3 formatting, but any existing callers may break. * __Leave as `FALSE`__: The output will have non-conventional formatting, but existing callers will work. ### Upgrade to v19.11.0+: APIv4 and PSR-4 APIv4 looks for classes in the `Civi\Api4` namespace and `Civi/Api4` folder. To support generation of APIv4 code, the `info.xml` should have a corresponding classloader: ```xml ``` ### Upgrade to v18.02.0+: PHPUnit (Optional) The template for `tests/phpunit/bootstrap.php` changed slightly to make `phpunit` work in symlinked directory structures. You may want to manually apply the changes from /~https://github.com/totten/civix/pull/121. ### Upgrade to v17.10.0+: Test Files The PHPUnit bootstrap file (`tests/phpunit/bootstrap.php`) has been updated to support autoloading of utility classes within your extensions `tests` folder. To follow this revised convention, update `bootstrap.php`. After the the call to `eval(...);`, say: ```php $loader = new \Composer\Autoload\ClassLoader(); $loader->add('CRM_', __DIR__); $loader->add('Civi\\', __DIR__); $loader->register(); ``` ### Upgrade to v17.08.1+: The Big `E` civix v17.08.1 makes corrections to the behavior of the new helpers, `E::path()` and `E::url()`. They are now more consistent in that: * `E::path()` and `E::url()` (without arguments) both return the folder *without* a trailing `/`. * `E::path($file)` and `E::url($file)` (with an argument) both return the folder plus `/` plus filename. Suggestion: search your codebase for instances of `E::path` or `E::url` to ensure consistent path construction. ### Upgrade to v17.08.0+: The Big `E` civix v17.08.0+ introduces a new helper class. You can generate it by following the "General Tasks" (above). No other changes are required. Optionally, if you want to *use* this helper class, then add a line like this to your other `*.php` files: ```php use CRM_Myextension_ExtensionUtil as E; ``` ### Upgrade to v16.10+: Upgrader postInstall (optional) *(See also: "General Tasks: Upgrader Class")* In version 16.10.0, hook_civicrm_postInstall was implemented in the extension's main PHP file and delegated to the Upgrader base class. If you wish to run your own code post-install, you should copy the following snippet (or something like it) into the Upgrader class (e.g. "/var/www/extensions/org.example.myext/CRM/Myext/Upgrader.php"): ```php /** * Example: Work with entities usually not available during the install step. * * This method can be used for any post-install tasks. For example, if a step * of your installation depends on accessing an entity that is itself * created during the installation (e.g., a setting or a managed entity), do * so here to avoid order of operation problems. */ public function postInstall() { $customFieldId = civicrm_api3('CustomField', 'getvalue', array( 'return' => array("id"), 'name' => "customFieldCreatedViaManagedHook", )); civicrm_api3('Setting', 'create', array( 'myWeirdFieldSetting' => array('id' => $customFieldId, 'weirdness' => 1), )); } ``` ### Upgrade v16.03.2+: Test Files Prior to civix v16.03, civix included the commands `civix generate:test` and `civix test`. Beginning with v16.03, civix templates now comply with the [Testapalooza PHPUnit Template](/~https://github.com/civicrm/org.civicrm.testapalooza/tree/phpunit). The key changes: * Tests are now executed directly with standalone `phpunit`. There is no need for `civix test` or for Civi's embedded `phpunit`. This should enable better support for IDEs and other tools. * The code-generator creates two additional files, `phpunit.xml.dist` and `tests/phpunit/bootstrap.php`. These are requirements for using standalone `phpunit`. * Tests use `PHPUnit_Framework_TestCase` with [`Civi\Test`](/~https://github.com/civicrm/org.civicrm.testapalooza/blob/master/civi-test.md) instead of `CiviUnitTestCase`. This gives you more control over the test environment. * You must have [`cv`](/~https://github.com/civicrm/cv) installed when running tests. Given that there isn't a very large body of existing extension tests, we haven't thoroughly tested the migration path, but here are some expectations: * The command `civix test` hasn't changed. If you used it before to run your existing tests, then you should still be able to use it now. However, it's deprecated. * The civicrm-core repo still has `tools/scripts/phpunit`. If you used it before run your existing tests, then you should still be able to use it now. * If you want to start using standalone `phpunit`, then: * You need to create `phpunit.xml.dist` and `tests/phpunit/bootstrap.php`. These files will be autogenerated the next time you use `civix generate:test`. * You should update the existing tests to implement the `HeadlessInterface`, to define function `setupHeadless()`, and to declare `@group headless`. This will ensure that the headless system boots correctly when running your test. * Try creating a new test using the `legacy` template, e.g. `civix generate:test --template=legacy CRM_Myextension_DummyTest`. This will generate `phpunit.xml.dist` and `tests/phpunit/bootstrap.php`, *and* it will create an example of using `CiviUnitTestCase`. * Note: Legacy tests executed this way may reset key variables (e.g. `CRM_Core_Config::singleton()`) extra times. However, the pool of existing extension tests is fairly small, so we don't expect this to have a big real-world impact. ### Upgrade to v15.10+: hook_civicrm_navigationMenu Prior to v4.7, the hook for manipulating the navigation menu required that the extension author compute a `navID` and `parentID` for each new menu entry, but the common examples for doing this were error-prone. In v4.7, the `navID` and `parentID` may be omitted and computed automatically. For backward compatibility, `civix` provides an adapter -- simply declare the menu item (without `navID` or `parentID`; as you would in v4.7) and then delegate to the helper function for `navigationMenu`. ```php /** * Implements hook_civicrm_navigationMenu(). * * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_navigationMenu */ function myext_civicrm_navigationMenu(&$menu) { _myext_civix_insert_navigation_menu($menu, NULL, array( 'label' => ts('The Page', array('domain' => 'org.example.myext')), 'name' => 'the_page', 'url' => 'civicrm/the-page', 'permission' => 'access CiviReport,access CiviContribute', 'operator' => 'OR', 'separator' => 0, )); _myext_civix_navigationMenu($menu); } ```