From 2.x to 3.0
===============

.. contents:: Contents
   :local:
   :depth: 1

PHP 7.0 is now required
-----------------------

5.6 is reaching it's end of life. PHP 7.0 is now required to install and run Elgg.

``$CONFIG`` is removed!
-----------------------

Not exactly, however you **must** audit its usage and *should* replace it with ``elgg_get_config()`` and ``elgg_set_config()``, as recommended since Elgg 1.9.

The global ``$CONFIG`` is now a proxy for Elgg's configuration container, and modifications **will fail** if you try to alter array properties directly. E.g. ``$CONFIG->cool_fruit[] = 'Pear';``. The silver lining is that failures will emit NOTICEs.

Removed views
-------------

 * ``forms/account/settings``: usersettings extension can now extend the view ``forms/usersettings/save``
 * ``forms/admin/site/advanced/system``
 * ``resources/file/download``
 * ``output/checkboxes``: use ``output/tags`` if you want the same behaviour
 * ``input/write_access``: mod/pages now uses the **access:collections:write** plugin hook.
 * ``invitefriends/form``
 * ``page/layouts/content``: use ``page/layouts/default``
 * ``page/layouts/one_column``: use ``page/layouts/default``
 * ``page/layouts/one_sidebar``: use ``page/layouts/default``
 * ``page/layouts/two_sidebar``: use ``page/layouts/default``
 * ``page/layouts/walled_garden``: use ``page/layouts/default``
 * ``page/layouts/walled_garden/cancel_button``
 * ``page/layouts/two_column_left_sidebar``
 * ``page/layouts/widgets/add_panel``
 * ``page/elements/topbar_wrapper``: update your use of ``page/elements/topbar`` to include a check for a logged in user
 * ``pages/icon``
 * ``groups/group_sort_menu``: use ``register, filter:menu:groups/all`` plugin hook
 * ``groups/my_status``
 * ``groups/profile/stats``
 * ``subscriptions/form/additions``: extend ``notifications/settings/other`` instead
 * ``likes/count``: modifications can now be done to the ``likes_count`` menu item
 * ``likes/css``: likes now uses ``elgg/likes.css``
 * ``resources/members/index``
 * ``messageboard/css``
 * ``notifications/subscriptions/personal``
 * ``notifications/subscriptions/collections``
 * ``notifications/subscriptions/form``
 * ``notifications/subscriptions/jsfuncs``
 * ``notifications/subscriptions/forminternals``
 * ``notifications/css``
 * ``pages/input/parent``
 * ``river/item``: use elgg_view_river_item() to render river items
 * ``river/user/default/profileupdate``
 * ``admin.js``
 * ``aalborg_theme/homepage.png``
 * ``aalborg_theme/css``
 * ``resources/avatar/view``: Use entity icon API
 * ``ajax_loader.gif``
 * ``button_background.gif``
 * ``button_graduation.png``
 * ``elgg_toolbar_logo.gif``
 * ``header_shadow.png``
 * ``powered_by_elgg_badge_drk_bckgnd.gif``
 * ``powered_by_elgg_badge_light_bckgnd.gif``
 * ``sidebar_background.gif``
 * ``spacer.gif``
 * ``toptoolbar_background.gif``
 * ``two_sidebar_background.gif``
 * ``ajax_loader_bw.gif``: use ``graphics/ajax_loader_bw.gif``
 * ``elgg_logo.png``: use ``graphics/elgg_logo.png``
 * ``favicon-128.png``: use ``graphics/favicon-128.png``
 * ``favicon-16.png``: use ``graphics/favicon-16.png``
 * ``favicon-32.png``: use ``graphics/favicon-32.png``
 * ``favicon-64.png``: use ``graphics/favicon-64.png``
 * ``favicon.ico``: use ``graphics/favicon.ico``
 * ``favicon.svg``: use ``graphics/favicon.svg``
 * ``friendspicker.png``: use ``graphics/friendspicker.png``
 * ``walled_garden.jpg``: use ``graphics/walled_garden.jpg``
 * ``core/friends/collection``
 * ``core/friends/collections``
 * ``core/friends/collectiontabs``
 * ``core/friends/tablelist``
 * ``core/friends/talbelistcountupdate``
 * ``lightbox/elgg-colorbox-theme/colorbox-images/*```
 * ``navigation/menu/page``: now uses ``navigation/menu/default`` and a prepare hook
 * ``navigation/menu/site``: now uses default view
 * ``page/elements/by_line``: Use ``object/elements/imprint``
 * ``forms/admin/site/advanced/security``: the site secret information has been moved to ``forms/admin/security/settings``
 * ``river/object/file/create``: check :doc:`/guides/river`
 * ``river/object/page/create``: check :doc:`/guides/river`
 * ``river/object/page_top/create``: check :doc:`/guides/river`
 * ``river/relationship/member``: check :doc:`/guides/river`
 * ``object/page_top``: use ``object/page``
 * ``ajax/discussion/reply/edit``: See :ref:`upgrade-discussion-replies`
 * ``discussion/replies``: See :ref:`upgrade-discussion-replies`
 * ``object/discussion_reply``: See :ref:`upgrade-discussion-replies`
 * ``resources/discussion/reply/edit``: See :ref:`upgrade-discussion-replies`
 * ``resources/elements/discussion_replies``: See :ref:`upgrade-discussion-replies`
 * ``river/elements/discussion_replies``: See :ref:`upgrade-discussion-replies`
 * ``river/object/discussion/create``
 * ``river/object/discussion_reply/create``: See :ref:`upgrade-discussion-replies`
 * ``search/object/discussion_reply/entity``: See :ref:`upgrade-discussion-replies`
 * ``rss/discussion/replies``: See :ref:`upgrade-discussion-replies`
 * ``search/header``
 * ``search/layout`` in both ``default`` and ``rss`` viewtypes
 * ``search/no_results``
 * ``search/object/comment/entity``
 * ``search/css``: Moved to ``search/search.css``
 * ``search/startblurb``
 * ``bookmarks/bookmarklet.gif``
 * ``blog_get_page_content_list``
 * ``blog_get_page_content_archive``
 * ``blog_get_page_content_edit``
 * ``forms/invitefriends/invite``: use ``forms/friends/invite`` 
 * ``resources/invitefriends/invite``: use ``resources/friends/invite`` 
 * ``resources/reportedcontent/add``
 * ``resources/reportedcontent/add_form``
 * ``resources/site_notifications/view``: Use ``resources/site_notifications/owner``
 * ``resources/site_notifications/everyone``: Use ``resources/site_notifications/all``

Removed functions/methods
-------------------------

All the functions in ``engine/lib/deprecated-1.9.php`` were removed. See https://github.com/Elgg/Elgg/blob/2.0/engine/lib/deprecated-1.9.php for these functions. Each ``@deprecated`` declaration includes instructions on what to use instead.
All the functions in ``engine/lib/deprecated-1.10.php`` were removed. See https://github.com/Elgg/Elgg/blob/2.0/engine/lib/deprecated-1.10.php for these functions. Each ``@deprecated`` declaration includes instructions on what to use instead.

 * ``elgg_register_library``: require your library files so they are available globally to other plugins
 * ``elgg_load_library``
 * ``activity_profile_menu``
 * ``can_write_to_container``: Use ``ElggEntity->canWriteToContainer()``
 * ``create_metadata_from_array``
 * ``metadata_array_to_values``
 * ``datalist_get``
 * ``datalist_set``
 * ``detect_extender_valuetype``
 * ``developers_setup_menu``
 * ``elgg_disable_metadata``
 * ``elgg_enable_metadata``
 * ``elgg_get_class_loader``
 * ``elgg_get_metastring_id``
 * ``elgg_get_metastring_map``
 * ``elgg_register_class``
 * ``elgg_register_classes``
 * ``elgg_register_viewtype``
 * ``elgg_is_registered_viewtype``
 * ``file_delete``: Use ``ElggFile->deleteIcon()``
 * ``file_get_type_cloud``
 * ``file_type_cloud_get_url``
 * ``get_default_filestore``
 * ``get_site_entity_as_row``
 * ``get_group_entity_as_row``
 * ``get_missing_language_keys``
 * ``get_object_entity_as_row``
 * ``get_user_entity_as_row``
 * ``update_river_access_by_object``
 * ``garbagecollector_orphaned_metastrings``
 * ``groups_access_collection_override``
 * ``groups_get_group_tool_options``: Use ``elgg()->group_tools->all()``
 * ``groups_join_group``: Use ``ElggGroup::join``
 * ``groups_prepare_profile_buttons``: Use ``register, menu:title`` hook
 * ``groups_register_profile_buttons``: Use ``register, menu:title`` hook
 * ``groups_setup_sidebar_menus``
 * ``groups_set_icon_url``
 * ``groups_setup_sidebar_menus``
 * ``messages_notification_msg``
 * ``set_default_filestore``
 * ``generate_user_password``: Use ``ElggUser::setPassword``
 * ``row_to_elggrelationship``
 * ``run_function_once``: Use ``Elgg\Upgrade\Batch`` interface
 * ``system_messages``
 * ``notifications_plugin_pagesetup``
 * ``elgg_format_url``: Use elgg_format_element() or the "output/text" view for HTML escaping.
 * ``get_site_by_url``
 * ``elgg_override_permissions``: No longer used as handler for ``permissions_check`` and ``container_permissions_check`` hooks
 * ``elgg_check_access_overrides``
 * ``AttributeLoader`` became obsolete and was removed
 * ``Application::loadSettings``
 * ``ElggEntity::addToSite``
 * ``ElggEntity::disableMetadata``
 * ``ElggEntity::enableMetadata``
 * ``ElggEntity::getSites``
 * ``ElggEntity::removeFromSite``
 * ``ElggEntity::isFullyLoaded``
 * ``ElggEntity::clearAllFiles``
 * ``ElggPlugin::getFriendlyName``: Use ``ElggPlugin::getDisplayName()``
 * ``ElggPlugin::setID``
 * ``ElggPlugin::unsetAllUsersSettings``
 * ``ElggFile::setFilestore``: ElggFile objects can no longer use custom filestores.
 * ``ElggFile::size``: Use ``getSize``
 * ``ElggDiskFilestore::makeFileMatrix``: Use ``Elgg\EntityDirLocator``
 * ``ElggData::get``: Usually can be replaced by property read
 * ``ElggData::getClassName``: Use ``get_class()``
 * ``ElggData::set``: Usually can be replaced by property write
 * ``ElggEntity::setURL``: See ``getURL`` for details on the plugin hook
 * ``ElggMenuBuilder::compareByWeight``: Use ``compareByPriority``
 * ``ElggMenuItem::getWeight``: Use ``getPriority``
 * ``ElggMenuItem::getContent``: Use ``elgg_view_menu_item()``
 * ``ElggMenuItem::setWeight``: Use ``setPriority``
 * ``ElggRiverItem::getPostedTime``: Use ``getTimePosted``
 * ``ElggSession`` has removed all deprecated methods
 * ``ElggSite::addEntity``
 * ``ElggSite::addObject``
 * ``ElggSite::addUser``
 * ``ElggSite::getEntities``: Use ``elgg_get_entities()``
 * ``ElggSite::getExportableValues``: Use ``toObject``
 * ``ElggSite::getMembers``: Use ``elgg_get_entities()``
 * ``ElggSite::getObjects``: Use ``elgg_get_entities()``
 * ``ElggSite::listMembers``: Use ``elgg_list_entities()``
 * ``ElggSite::removeEntity``
 * ``ElggSite::removeObject``
 * ``ElggSite::removeUser``
 * ``ElggSite::isPublicPage``: Logic moved to the router and should not be accessed directly
 * ``ElggSite::checkWalledGarden``: Logic moved to the router and should not be accessed directly
 * ``ElggUser::countObjects``: Use ``elgg_get_entities()``
 * ``Logger::getClassName``: Use ``get_class()``
 * ``Elgg\Application\Database::getTablePrefix``: Read the ``prefix`` property
 * ``elgg_view_access_collections()``
 * ``ElggSession::get_ignore_access``: Use ``getIgnoreAccess``
 * ``ElggSession::set_ignore_access``: Use ``setIgnoreAccess``
 * ``profile_pagesetup``
 * ``pages_can_delete_page``: Use ``$entity->canDelete()``
 * ``pages_search_pages``
 * ``pages_is_page``: use ``$entity instanceof ElggPage``
 * ``discussion_comment_override``: See :ref:`upgrade-discussion-replies`
 * ``discussion_can_edit_reply``: See :ref:`upgrade-discussion-replies`
 * ``discussion_reply_menu_setup``: See :ref:`upgrade-discussion-replies`
 * ``discussion_reply_container_logic_override``: See :ref:`upgrade-discussion-replies`
 * ``discussion_reply_container_permissions_override``: See :ref:`upgrade-discussion-replies`
 * ``discussion_update_reply_access_ids``: See :ref:`upgrade-discussion-replies`
 * ``discussion_search_discussion``: See :ref:`upgrade-discussion-replies`
 * ``discussion_add_to_river_menu``: See :ref:`upgrade-discussion-replies`
 * ``discussion_prepare_reply_notification``: See :ref:`upgrade-discussion-replies`
 * ``discussion_redirect_to_reply``: See :ref:`upgrade-discussion-replies`
 * ``discussion_ecml_views_hook``: See :ref:`upgrade-discussion-replies`
 * ``search_get_where_sql``
 * ``search_get_ft_min_max``
 * ``search_get_order_by_sql``
 * ``search_consolidate_substrings``
 * ``search_remove_ignored_words``
 * ``search_get_highlighted_relevant_substrings``
 * ``search_highlight_words``
 * ``search_get_search_view``
 * ``search_custom_types_tags_hook``
 * ``search_tags_hook``
 * ``search_users_hook``
 * ``search_groups_hook``
 * ``search_objects_hook``
 * ``members_list_popular``
 * ``members_list_newest``
 * ``members_list_online``
 * ``members_list_alpha``
 * ``members_nav_popular``
 * ``members_nav_newest``
 * ``members_nav_online``
 * ``members_nav_alpha``
 * ``uservalidationbyemail_generate_code``

All functions around entity subtypes table:
 * ``add_subtype``: Use ``elgg_set_entity_class`` at runtime
 * ``update_subtype``: Use ``elgg_set_entity_class`` at runtime
 * ``remove_subtype``
 * ``get_subtype_id``
 * ``get_subtype_from_id``
 * ``get_subtype_class``: Use ``elgg_get_entity_class``
 * ``get_subtype_class_from_id``

All caches have been consolidated into a single API layer. The following functions and methods have been removed:
 * ``is_memcache_available``
 * ``_elgg_get_memcache``
 * ``_elgg_invalidate_memcache_for_entity``
 * ``ElggMemcache``
 * ``ElggFileCache``
 * ``ElggStaticVariableCache``
 * ``ElggSharedMemoryCache``
 * ``Elgg\Cache\Pool`` interface and all extending classes

As a result of system log changes:
 * ``system_log_default_logger``: moved to ``system_log`` plugin
 * ``system_log_listener``: moved to ``system_log`` plugin
 * ``system_log``: moved to ``system_log`` plugin
 * ``get_system_log``: renamed to ``system_log_get_log`` and moved to ``system_log`` plugin
 * ``get_log_entry``: renamed to ``system_log_get_log_entry`` and moved to ``system_log`` plugin
 * ``get_object_from_log_entry``: renamed to ``system_log_get_object_from_log_entry`` and moved to ``system_log`` plugin
 * ``archive_log``: renamed to ``system_log_archive_log`` and moved to ``system_log`` plugin
 * ``logbrowser_user_hover_menu``: renamed to ``system_log_user_hover_menu`` and moved to ``system_log`` plugin
 * ``logrotate_archive_cron``: renamed to ``system_log_archive_cron`` and moved to ``system_log`` plugin
 * ``logrotate_delete_cron``: renamed to ``system_log_delete_cron`` and moved to ``system_log`` plugin
 * ``logrotate_get_seconds_in_period``: renamed to ``system_log_get_seconds_in_period`` and moved to ``system_log`` plugin
 * ``log_browser_delete_log``: renamed to ``system_log_browser_delete_log`` and moved to ``system_log`` plugin

Deprecated APIs
---------------

 * ``ban_user``: Use ``ElggUser->ban()``
 * ``create_metadata``: Use ``ElggEntity`` setter or ``ElggEntity->setMetadata()``
 * ``update_metadata``: Use ``ElggMetadata->save()``
 * ``get_metadata_url``
 * ``create_annotation``: Use ``ElggEntity->annotate()``
 * ``update_metadata``: Use ``ElggAnnotation->save()``
 * ``elgg_get_user_validation_status``: Use ``ElggUser->isValidated()``
 * ``make_user_admin``: Use ``ElggUser->makeAdmin()``
 * ``remove_user_admin``: Use ``ElggUser->removeAdmin()``
 * ``unban_user``: Use ``ElggUser->unban()``
 * ``elgg_get_entities_from_attributes``: Use ``elgg_get_entities()``
 * ``elgg_get_entities_from_metadata``: Use ``elgg_get_entities()``
 * ``elgg_get_entities_from_relationship``: Use ``elgg_get_entities()``
 * ``elgg_get_entities_from_private_settings``: Use ``elgg_get_entities()``
 * ``elgg_get_entities_from_access_id``: Use ``elgg_get_entities()``
 * ``elgg_list_entities_from_metadata``: Use ``elgg_list_entities()``
 * ``elgg_list_entities_from_relationship``: Use ``elgg_list_entities()``
 * ``elgg_list_entities_from_private_settings``: Use ``elgg_list_entities()``
 * ``elgg_list_entities_from_access_id``: Use ``elgg_list_entities()``
 * ``elgg_list_registered_entities``: Use ``elgg_list_entities()``
 * ``elgg_batch_delete_callback``
 * ``\Elgg\Project\Paths::sanitize``: Use ``\Elgg\Project\Paths::sanitize()``
 * ``elgg_group_gatekeeper``: Use ``elgg_entity_gatekeeper()``
 * ``get_entity_dates``: Use ``elgg_get_entity_dates()``
 * ``messages_set_url``: Use ``ElggEntity::getURL()``

Removed global vars
-------------------

 * ``$CURRENT_SYSTEM_VIEWTYPE``
 * ``$DEFAULT_FILE_STORE``
 * ``$ENTITY_CACHE``
 * ``$SESSION``: Use the API provided by ``elgg_get_session()``
 * ``$CONFIG->site_id``: Use ``1``
 * ``$CONFIG->search_info``
 * ``$CONFIG->input``: Use ``set_input`` and ``get_input``

Removed classes/interfaces
--------------------------

 * ``FilePluginFile``: replace with ``ElggFile`` (or load with ``get_entity()``)
 * ``Elgg_Notifications_Notification``
 * ``Elgg\Database\EntityTable\UserFetchResultException.php``
 * ``Elgg\Database\MetastringsTable``
 * ``Elgg\Database\SubtypeTable``
 * ``Exportable`` and its methods ``export`` and ``getExportableValues``: Use ``toObject``
 * ``ExportException``
 * ``Importable`` and its method ``import``.
 * ``ImportException``
 * ``ODD`` and all classes beginning with ``ODD*``.
 * ``XmlElement``
 * ``Elgg_Notifications_Event``: Use ``\Elgg\Notifications\Event``
 * ``Elgg\Mail\Address``: use ``Elgg\Email\Address``
 * ``ElggDiscussionReply``: user ``ElggComment`` see :ref:`upgrade-discussion-replies`

Schema changes
--------------
 
The storage engine for the database tables has been changed from MyISAM to InnoDB. You maybe need to optimize your database settings for this change.
The ``datalists`` table has been removed. All settings from datalists table have been merged into the ``config`` table.

Metastrings in the database have been denormalized for performance purposes. We removed the metastrings table and put all the string values directly in the 
metadata and annotation tables. You need to update your custom queries to reflect these changes. Also the ``msv`` and ``msn`` table aliases are no longer available.
It is best practice not to rely on the table aliases used in core queries. If you need to use custom clauses you should do your own joins.

From the "users_entity" table, the ``password`` and ``hash`` columns have been removed.

The ``geocode_cache`` table has been removed as it was no longer used.

``subtype`` column in ``entities`` table no longer holds a subtype ID, but a subtype string
``entity_subtypes`` table has been dropped.

``type``, ``subtype`` and ``access_id`` columns in ``river`` table have been dropped.
For queries without ``elgg_get_river()`` join the ``entities`` table on ``object_guid`` to check the type and the subtype of the entity.
Access column hasn't been in use for some time: queries are built to ensure access to all three entities (subject, object and target).

Changes in ``elgg_get_entities``, ``elgg_get_metadata`` and ``elgg_get_annotations`` getter functions
-----------------------------------------------------------------------------------------------------

``elgg_get_entities`` now accepts all options that were previously distributed between ``elgg_get_entities_from_metadata``,
``elgg_get_entities_from_annotations``, ``elgg_get_entities_from_relationship``, ``elgg_get_entities_from_private_settings``
and ``elgg_get_entities_from_access_id``. The latter have been been deprecated.

Passing raw MySQL statements to options is deprecated. Plugins are advised to use closures that receive an instance of
``\Elgg\Database\QueryBuilder`` and prepare the statement using database abstraction layer. On one hand this will ensure
that all statements are properly sanitized using the database driver, on the other hand it will allow us to transition
to testable object-oriented query building.

``wheres`` statements should not use raw SQL strings, instead pass an instance of ``\Elgg\Database\Clauses\WhereClause``
or a closure that returns an instance of ``\Doctrine\DBAL\Query\Expression\CompositeExpression``:

.. code-block:: php

    elgg_get_entities([
       'wheres' => [
            function(\Elgg\Database\QueryBuilder $qb, $alias) {
               $joined_alias = $qb->joinMetadataTable($alias, 'guid', 'status');
               return $qb->compare("$joined_alias.name", 'in', ['draft', 'unsaved_draft'], ELGG_VALUE_STRING);
            }
       ]
    ]);

``joins``, ``order_by``, ``group_by``, ``selects`` clauses should not use raw SQL strings. Use closures that receive
an instance of ``\Elgg\Database\QueryBuilder`` and return a prepared statement.

The ``reverse_order_by`` option has been removed.

Plugins should not rely on joined and selected table aliases. Closures passed to the options array will receive a second argument
that corresponds to the selected table alias. Plugins must perform their own joins and use joined aliases accordingly.

Note that all of the private API around building raw SQL strings has also been removed. If you were relying on them in your plugins,
be advised that anything marked as ``@access private`` or ``@internal`` in core can be modified and removed at any time, and we do not guarantee
any backward compatibility for those functions. DO NOT USE THEM. If you find yourself needing to use them, open an issue
on Github and we will consider adding a public equivalent.

Boolean entity properties
-------------------------

Storage of metadata, annotation and private setting values has been aligned.

**Boolean values are cast to integers when saved**: ``false`` is stored as ``0`` and ``true`` is stored as ``1``.
This has breaking implications for private settings, which were previously stored as empty strings for ``false`` values.
Plugins should write their own migration scripts to alter DB values from empty strings to ``0`` (for private settings that
are expected to store boolean values) to ensure that ``elgg_get_entities()`` can retrieve these values
with ``private_setting_name_value_pairs`` containing ``false`` values. This applies to plugin settings, as well as
any private settings added to entities.

Metadata Changes
----------------

Metadata is no longer access controlled. If your plugin created metadata with restricted access, those restrictions will not be honored. You should use annotations or entities instead, which do provide access control.

Do not read or write to the ``access_id`` property on ElggMetadata objects.

Metadata is no longer enabled or disabled. You can no longer perform the ``enable`` and ``disable`` API calls on metadata.

Metadata no longer has an ``owner_guid``. It is no longer possible to query metadata based on ``owner_guids``.
Subsequently, ``ElggMetadata::canEdit()`` will always return ``true`` regardless of the logged in user, unless explicitly overriden by a plugin hook.

Permissions and Access
----------------------

User capabilities service will no longer trigger permission check hooks when:

 - permissions are checked for an admin user
 - permissions are checked when access is ignored with ``elgg_set_ignore_access()``

This means that plugins can no longer alter permissions in aforementioned cases.

``elgg_check_access_overrides()`` has been removed, as plugins will no longer need to validate access overrides.

The translations for the default Elgg access levels have new translation language keys.

Multi Site Changes
------------------

Pre 3.0 Elgg has some (partial) support for having multiple sites in the same database. This Multi Site concept has been completely removed in 3.0.
Entities no longer have the site_guid attribute. This means there is no longer the ability to have entities on different sites.
If you currently have multiple sites in your database, upgrading Elgg to 3.0 will fail. 
You need to separate the different sites into separate databases/tables.

Related to the removal of the Multi Site concept in Elgg, there is no longer a need for entities having a 'member_of_site' relationship with the Site Entity.
All functions related to adding/removing this relationship has been removed. All existing relationships will be removed as part of this upgrade.

Setting ``ElggSite::$url`` has no effect. Reading the site URL always pulls from the ``$CONFIG->wwwroot`` set in
settings.php, or computed by Symphony Request.

``ElggSite::save()`` will fail if it isn't the main site.

Entity Subtable Changes
-----------------------

The subtable ``sites_entity`` for ``ElggSite`` no longer exists. All attributes have been moved to metadata.
The subtable ``groups_entity`` for ``ElggGroup`` no longer exists. All attributes have been moved to metadata.
The subtable ``objects_entity`` for ``ElggObject`` no longer exists. All attributes have been moved to metadata.
The subtable ``users_entity`` for ``ElggUser`` no longer exists. All attributes have been moved to metadata.

If you have custom queries referencing this table you need to update them.
If you have function that rely on ``Entity->getOriginalAttributes()`` be advised that this will only return the base attributes of an ``ElggEntity`` and
no longer contain the secondary attributes.

Friends and Group Access Collection
-----------------------------------

The access collections table now has a subtype column. This extra data helps identifying the purpose of the ACL.
The user owned access collections are assumed to be used as Friends Collections and now have the 'friends_collection' subtype.
The groups access collection information was previously stored in the group_acl metadata. With the introduction of the ACL subtype
this information has been moved to the ACL subtype attribute.

The ``ACCESS_FRIENDS`` access_id has been migrated to an actual access collection (with the subtype ``friends``). All entities and annotations have been updated to use the new 
access collection id. The access collection is created when a user is created. When a relationship of the type ``friends`` is created, the related guid will
also be added to the access collection. You can no longer save or update entities with the access id ``ACCESS_FRIENDS``.

Subtypes no longer have an ID
-----------------------------

Entity subtypes have been denormalized.
``entity_subtypes`` table has been removed and ``subtype`` column in entities table simply holds the string representation of the subtype.

Consequently, all API around adding/updating entity subtypes and classes have been removed.

Plugins can now use ``elgg_set_entity_class()`` and ``elgg_get_entity_class()`` to register a custom entity class at runtime (e.g. in system init handler).

All entities now **MUST** have a subtype. By default, the following subtypes are added and reserved:

 * ``user`` for users
 * ``group`` for groups
 * ``site`` for sites

Custom class loading
--------------------

Elgg no longer provides API functions to register custom classes. If you need custom classes you can
use ``PSR-0`` classes in the ``/classes`` folder of your plugin or use composer for autoloading of additional classes.

The following class registration related functions have been removed:

 * ``elgg_get_class_loader``
 * ``elgg_register_class``
 * ``elgg_register_classes``

Dependency Injection Container
------------------------------

Plugins can now define their services and attach them to Elgg's public DI container by providing definitions in
``elgg-services.php`` in the root of the plugin directory.

``elgg()`` no longer returns an instance of Elgg application, but a DI container instance.

Search changes
--------------

We have added a search service into core, consequently the ``search`` plugin now only provides a user interface for displaying forms and listing search results.
Many of the views in the search plugin have been affected by this change.

The FULLTEXT indices have been removed on various tables. The search plugin will now always use a like query when performing a search.

See :doc:`Search Service </guides/search>` and :ref:`Search hooks <guides/events-list#search>` documentation for detailed information about new search capabilities.

Form and field related changes
------------------------------

 * ``input/password``: by default this field will no longer show a value passed to it, this can be overridden by passing the view var ``always_empty`` and set it to false
 * ``input/submit``, ``input/reset`` and ``input/button`` are now rendered with a ``<button>`` instead of the ``<input>`` tag. These input view also accept ``text`` and ``icon`` parameters.
 * ``output/url`` now sets ``.elgg-anchor`` class on anchor elements and accepts ``icon`` parameter. If no ``text`` is set, the ``href`` parameter used as a label will be restricted to 100 characters.
 * ``output/url`` now supports a ``badge`` parameter, which can be used where a counter, a badge, or similar is required as a postfix (mainly in menu items that have counters).
 * ``output/tags`` no longer uses ``<ul>`` tags with floats and instead it relies on inherently inline elements such as ``<span>`` and ``<a>``

Entity and River Menu Changes
-----------------------------

The Entity and River menu now shows all the items in a dropdown. Social actions like liking or commenting are moved to an alternate menu called the social menu, which is meant for social actions.

Removed libraries
-----------------

``elgg_register_library`` and ``elgg_load_library`` have been removed.
These functions had little impact on performance (especially with OPCache enabled), and made it difficult for other plugins to work with APIs contained in libraries.
Additionally it was difficult for developers to know that APIs were contained in a library while there being autocompleted by IDE.

If you are concerned with performance, move the logic to classes and let PHP autoload them as necessary, otherwise use ``require_once`` and require your libraries.

Removed pagehandling
--------------------

 * ``file/download``
 * ``file/search``
 * ``groupicon``
 * ``twitterservice``
 * ``collections/pickercallback``
 * ``discussion/reply``: See :ref:`upgrade-discussion-replies`
 * ``expages``
 * ``invitefriends``: Use ``friends/{username}/invite``
 * ``messages/compose``: Use ``messages/add``
 * ``reportedcontent``

Removed actions
---------------

 * ``file/download``: Use ``elgg_get_inline_url`` or ``elgg_get_download_url``
 * ``file/delete``: Use ``entity/delete`` action
 * ``import/opendd``
 * ``discussion/reply/save``: See :ref:`upgrade-discussion-replies`
 * ``discussion/reply/delete``: See :ref:`upgrade-discussion-replies`
 * ``comment/delete``: Use ``entity/delete`` action
 * ``uservalidationbyemail/bulk_action``: use ``admin/user/bulk/validate`` or ``admin/user/bulk/delete``
 * ``uservalidationbyemail/delete``: use ``admin/user/bulk/delete``
 * ``uservalidationbyemail/validate``: use ``admin/user/bulk/validate``
 * ``invitefriends/invite``: use ``friends/invite``

Inheritance changes
-------------------

 * ``ElggData`` (and hence most Elgg domain objects) no longer implements ``Exportable``
 * ``ElggEntity`` no longer implements ``Importable``
 * ``ElggGroup`` no longer implements ``Friendable``
 * ``ElggRelationship`` no longer implements ``Importable``
 * ``ElggSession`` no longer implements ``ArrayAccess``
 * ``Elgg\Application\Database`` no longer extends ``Elgg\Database``

Removed JavaScript APIs
-----------------------

 * ``admin.js``
 * ``elgg.widgets``: Use the ``elgg/widgets`` module. The "widgets" layouts do this module automatically
 * ``lightbox.js``: Use the ``elgg/lightbox`` module as needed
 * ``lightbox/settings.js``: Use the ``getOptions, ui.lightbox`` JS hook or the ``data-colorbox-opts`` attribute
 * ``elgg.ui.popupClose``: Use the ``elgg/popup`` module
 * ``elgg.ui.popupOpen``: Use the ``elgg/popup`` module
 * ``elgg.ui.initAccessInputs``
 * ``elgg.ui.river``
 * ``elgg.ui.initDatePicker``: Use the ``input/date`` module
 * ``elgg.ui.likesPopupHandler``
 * ``elgg.embed``: Use the ``elgg/embed`` module
 * ``elgg.discussion``: Use the ``elgg/discussion`` module
 * ``embed/custom_insert_js``: Use the ``embed, editor`` JS hook
 * ``elgg/ckeditor.js``: replaced by ``elgg-ckeditor.js``
 * ``elgg/ckeditor/set-basepath.js``
 * ``elgg/ckeditor/insert.js``
 * ``jQuery.cookie``: Use ``elgg.session.cookie``
 * ``jquery.jeditable``
 * ``likes.js``: The ``elgg/likes`` module is loaded automatically
 * ``messageboard.js``
 * ``elgg.autocomplete`` is no longer defined.
 * ``elgg.messageboard`` is no longer defined.
 * ``jQuery.fn.friendsPicker``
 * ``elgg.ui.toggleMenu`` is no longer defined
 * ``elgg.ui.toggleMenuItems``: Use ``data-toggle`` attribute when registering toggleable menu items
 * ``uservalidationbyemail/js.php``: Use the ``elgg/uservalidationbyemail`` module
 * ``discussion.js``: See :ref:`upgrade-discussion-replies`

Removed hooks/events
--------------------

 * Event **login, user**: Use **login:before** or **login:after**. Note the user is not logged in during the **login:before** event
 * Event **delete, annotations**: Use **delete, annotation**
 * Event **pagesetup, system**: Use the menu or page shell hooks instead
 * Event **upgrade, upgrade**: Use **upgrade, system** instead
 * Hook **index, system**: Override the ``resources/index`` view
 * Hook **object:notifications, <type>**: Use the hook **send:before, notifications**
 * Hook **output:before, layout**: Use **view_vars, page/layout/<layout_name>**
 * Hook **output:after, layout**: Use **view, page/layout/<layout_name>**
 * Hook **email, system**: Use more granular **<hook>, system:email** hooks
 * Hook **email:message, system**: Use **zend:message, system:email** hook
 * Hook **members:list, <page>**: Use your own pagehandler or route hook
 * Hook **members:config, <page>**: Use **register, menu:filter:members**
 * Hook **profile_buttons, group**: Use **register, menu:title**

Removed forms/actions
---------------------

 * ``notificationsettings/save`` form and action
 * ``notificationsettings/groupsave`` form and action
 * ``discussion/reply/save`` form and action

APIs that now accept only an ``$options`` array
-----------------------------------------------

 * ``ElggEntity::getAnnotations``
 * ``ElggEntity::getEntitiesFromRelationship``
 * ``ElggGroup::getMembers``
 * ``ElggUser::getGroups``
 * ``ElggUser::getFriends`` (as part of ``Friendable``)
 * ``ElggUser::getFriendsOf`` (as part of ``Friendable``)
 * ``ElggUser::getFriendsObjects`` (as part of ``Friendable``)
 * ``ElggUser::getObjects`` (as part of ``Friendable``)
 * ``find_active_users``
 * ``elgg_get_admin_notices``

Plugin functions that now require an explicit ``$plugin_id``
------------------------------------------------------------

 * ``elgg_get_all_plugin_user_settings``
 * ``elgg_set_plugin_user_setting``
 * ``elgg_unset_plugin_user_setting``
 * ``elgg_get_plugin_user_setting``
 * ``elgg_set_plugin_setting``
 * ``elgg_get_plugin_setting``
 * ``elgg_unset_plugin_setting``
 * ``elgg_unset_all_plugin_settings``

Class constructors that now accept only a ``stdClass`` object or ``null``
-------------------------------------------------------------------------

 * ``ElggAnnotation``: No longer accepts an annotation ID
 * ``ElggGroup``: No longer accepts a GUID
 * ``ElggMetadata``: No longer accepts a metadata ID
 * ``ElggObject``: No longer accepts a GUID
 * ``ElggRelationship``: No longer accepts a relationship ID or ``null``
 * ``ElggSite``: No longer accepts a GUID or URL
 * ``ElggUser``: No longer accepts a GUID or username
 * ``ElggPlugin``: No longer accepts a GUID or path. Use ``ElggPlugin::fromId`` to construct a plugin from its path

Miscellaneous API changes
-------------------------

 * ``ElggBatch``: You may only access public properties
 * ``ElggEntity``: The ``tables_split`` and ``tables_loaded`` properties were removed
 * ``ElggEntity``: Empty URLs will no longer be normalized. This means entities without URLs will no longer result in the site URL
 * ``ElggGroup::removeObjectFromGroup`` requires passing in an ``ElggObject`` (no longer accepts a GUID)
 * ``ElggUser::$salt`` no longer exists as an attribute, nor is it used for authentication
 * ``ElggUser::$password`` no longer exists as an attribute, nor is it used for authentication
 * ``elgg_get_widget_types`` no longer supports ``$exact`` as the 2nd argument
 * ``elgg_instanceof`` no longer supports the fourth ``class`` argument
 * ``elgg_view``: The 3rd and 4th (unused) arguments have been removed. If you use the ``$viewtype`` argument, you must update your usage.
 * ``elgg_view_icon`` no longer supports ``true`` as the 2nd argument
 * ``elgg_list_entities`` no longer supports the option ``view_type_toggle``
 * ``elgg_list_registered_entities`` no longer supports the option ``view_type_toggle``
 * ``elgg_log`` no longer accepts the level ``"DEBUG"``
 * ``elgg_dump`` no longer accepts a ``$to_screen`` argument.
 * ``elgg_gatekeeper`` and ``elgg_admin_gatekeeper`` no longer report ``login`` or ``admin`` as forward reason, but ``403``
 * ``Application::getDb()`` no longer returns an instance of ``Elgg\Database``, but rather a ``Elgg\Application\Database``
 * ``$CONFIG`` is no longer available as a local variable inside plugin ``start.php`` files.
 * ``elgg_get_config('siteemail')`` is no longer available. Use ``elgg_get_site_entity()->email``.
 * ``ElggEntity::saveIconFromUploadedFile`` only saves `master` size, the other sizes are created when requested by ``ElggEntity::getIcon()`` based on the `master` size
 * ``ElggEntity::saveIconFromLocalFile`` only saves `master` size, the other sizes are created when requested by ``ElggEntity::getIcon()`` based on the `master` size
 * ``ElggEntity::saveIconFromElggFile`` only saves `master` size, the other sizes are created when requested by ``ElggEntity::getIcon()`` based on the `master` size
 * Group entities do no longer have the magic ``username`` attribute.
 * Pagehandling will no longer detect ``group:<guid>`` in the URL
 * The CRON interval ``reboot`` is removed.
 * The URL endpoints ``js/`` and ``css/`` are no longer supported. Use ``elgg_get_simplecache_url()``.
 * The generic comment save action no longer sends the notification directly, this has been offloaded to the notification system.
 * The script ``engine/start.php`` is removed.
 * The functions ``set_config``, ``unset_config`` and ``get_config`` have been deprecated and replaced by ``elgg_set_config``, ``elgg_remove_config`` and ``elgg_get_config``.
 * Config values ``path``, ``wwwroot``, and ``dataroot`` are not read from the database. The settings.php file values are always used.
 * Config functions like ``elgg_get_config`` no longer trim keys.
 * If you override the view ``navigation/menu/user_hover/placeholder``, you must change the config key ``lazy_hover:menus`` to ``elgg_lazy_hover_menus``.
 * The config value ``entity_types`` is no longer present or used.
 * Uploaded images are autorotated based on their orientation metadata.
 * The view ``object/widget/edit/num_display`` now uses an ``input/number`` field instead of ``input/select``; you might need to update your widget edit views accordingly.
 * Annotation names are no longer trimmed during save

View extension behaviour changed
--------------------------------

An extended view now will receive all the regular hooks (like the `view_vars` hook).
It now is also possible to extend view extensions. With this change in behaviour all view rendering will behave the same.
It no longer matters if it was used as an extension or not.

JavaScript hook calling order may change
----------------------------------------

When registering for hooks, the ``all`` keyword for wildcard matching no longer has any effect
on the order that handlers are called. To ensure your handler is called last, you must give it the
highest priority of all matching handlers, or to ensure your handler is called first, you must give
it the lowest priority of all matching handlers.

If handlers were registered with the same priority, these are called in the order they were registered.

To emulate prior behavior, Elgg core handlers registered with the ``all`` keyword have been raised in
priority. Some of these handlers will most likely be called in a different order.

Widget layout related changes
-----------------------------

The widget layout usage has been changed. Content is no longer drawn as part of the layout. You need to wrap you content
in another layout and use the widgets layout as part of your content. If you want some special content to show if there
are no widgets in the layout, you can now pass a special ``no_widgets`` parameter (as String or as a Closure).

When registering widgets you can no longer omit passing a context as the ``all`` context is no longer supported. You need
to explicitely pass the contexts for which the widget is intended. 

Routing
-------

Page handling using ``elgg_register_page_handler()`` has been deprecated.

We have added new routing API using ``elgg_register_route()``, which allows plugins to define
named routes, subsequently using route names to generate URLs using ``elgg_generate_url()``.

See :doc:`routing </guides/routing>` docs for details.

As a result of this change all core page handlers have been removed, and any logic contained
within these page handlers has been moved to respective resource views.

``elgg_generate_entity_url()`` has been added as shortcut way to generate URLs from named
routes that depend on entity type and subtype.

Use of ``handler`` parameter in entity menus has been deprecated in favour of named entity routes.

Gatekeeper function have been refactored to serve as middleware in the routing process, and as such they no longer
return values. These functions throw HTTP exceptions that are then routed to error pages and can be redirected
to other pages via hooks.

Labelling
---------

Entity and collection labelling conventions have changed to comply with the new routing patterns:

.. code-block:: php

   return [
      'item:object:blog' => 'Blog',
      'collection:object:blog' => 'Blogs',
      'collection:object:blog:all' => 'All site blogs',
      'collection:object:blog:owner' => '%s\'s blogs',
      'collection:object:blog:group' => 'Group blogs',
      'collection:object:blog:friends' => 'Friends\' blogs',
      'add:object:blog' => 'Add blog post',
      'edit:object:blog' => 'Edit blog post',
   ];

These conventions are used across the routing and navigation systems, so plugins are advised to follow them.

Request value filtering
-----------------------

``set_input()`` and ``get_input()`` no longer trim values.

Action responses
----------------

All core and core plugin actions now all use the new Http Response functions like `elgg_ok_response` and `elgg_error_response` instead of `forward()`.
The effect of this change is that is most cases the `'forward', 'system'` hook is no longer triggered. If you like to influence the responses you now can use the
`'response', 'action:<name/of/action>'` hook. This gives you more control over the response and allows to target a specific action very easily.


HtmLawed is no longer a plugin
------------------------------

 * Do not call ``elgg_load_library('htmlawed')``.
 * In the hook params for ``'config', 'htmlawed'``, the ``hook_tag`` function name changed.

New approach to page layouts
----------------------------

``one_column``, ``one_sidebar``, ``two_sidebar`` and ``content`` layouts have been removed - instead layout rendering has been centralized in the ``default``. Updated ``default`` layout provides full control over the layout elements via ``$vars``.
For maximum backwards compatibility, calls to ``elgg_view_layout()`` with these layout names will still yield expected output, but the plugins should start using the ``default`` layout with an updated set of parameters.

Page layouts have been decomposed into smaller elements, which should make it easier for themes to target specific layout elements without having to override layouts at large.

As a result of these changes:

 * all layouts are consistent in how they handle title and filter menus, breadcrumbs and layout subviews
 * all layouts can now be easily extended to have multiple tabs. Plugins can pass ``filter_id`` parameter that will allow other plugins to hook into ``register, menu:filter:<filter_id>`` hook and add new tabs. If no ``filter_id`` is provided, default ``register, menu:filter`` hook can be used.
 * layout views and subviews now receive ``identifier`` and ``segments`` of the page being rendered
 * layout parameters are available to title and filter menu hooks, which allows resources to provide additional context information, for example, an ``$entity`` in case of a profile resource

Plugins and themes should:

 * Update calls to ``elgg_view_layout()`` to use ``default`` layout
 * Update replace ``nav`` parameter in layout views with ``breadcrumbs`` parameter
 * Update their use of ``filter`` parameter in layout views by either providing a default set of filter tabs, or setting a ``filter_id`` parameter and using hooks
 * Remove ``page/layouts/one_column`` view
 * Remove ``page/layouts/one_sidebar`` view
 * Remove ``page/layouts/two_sidebar`` view
 * Remove ``page/layouts/content`` view
 * Update their use of ``page/layouts/default``
 * Update their use of ``page/layouts/error``
 * Update their use of ``page/layouts/elements/filter``
 * Update their use of ``page/layouts/elements/header``
 * Update their use of ``page/layouts/elements/footer``
 * Update their use of ``page/elements/title``
 * Update their use of ``navigation/breadcrumbs`` to pass ``$vars['breadcrumbs']`` to ``elgg_get_breadcrumbs()``
 * Update hook registrations for ``output:before, layout`` to ``view_vars, page/layout/<layout_name>``
 * Update hook registrations for ``output:after, layout`` to ``view, page/layout/<layout_name>``

Likes plugin
------------

Likes no longer uses Elgg's toggle API, so only a single ``likes`` menu item is used. The add/remove actions no longer return Ajax values directly, as likes status data is now returned with *every* Ajax request that sends a "guid". When the number of likes is zero, the ``likes_count`` menu item is now hidden by adding `.hidden` to the LI element, instead of the anchor. Also the ``likes_count`` menu item is a regular link, and is no longer created by the ``likes/count`` view.

Notifications plugin
--------------------

Notifications plugin has been rewritten dropping many views and actions. The purpose of this rewrite was
to implement a more efficient, extendable and scalable interface for managing notifications preferences.
We have implemented a much simpler markup and removed excessive styling and javascript that was
required to make the old interface work.

If your plugin is extending any of the views or relies on any actions in the notifications plugin,
it has to be updated.

Pages plugin
------------

The suptype ``page_top`` has been migrated into the subtype ``page``. The subtype ``page`` has it's own class namely ``ElggPage``. In order to check
if an ``ElggPage`` is a top page the class function ``ElggPage->isTopPage()`` was added.

All pages have a metadata value for ``parent_guid``, for top pages this contains ``0``.

Profile plugin
--------------

All profile related functionality has been moved out of core into this plugin. Most noteable are the profile field admin utility and the hook to set up the profile fields config data.

Data Views plugin
-----------------

The Data Views plugin no longer comes bundled. 

Twitter API plugin
------------------

The ``twitter_api`` plugin has been removed from the Elgg core. The plugin is still available as the Composer package 
`elgg/twitter_api <https://packagist.org/packages/elgg/twitter_api>`_, in order to install it add the following to you 
``composer.json`` ``require`` section:

.. code-block:: json
	
	{
		"require": {
			"elgg/twitter_api": "~1.9"
		}
	}

Legacy URLs plugin
------------------

The ``legacy_urls`` plugin has been removed from the Elgg core. The plugin is still available as the Composer package 
`elgg/legacy_urls <https://packagist.org/packages/elgg/legacy_urls>`_, in order to install it add the following to you 
``composer.json`` ``require`` section:

.. code-block:: json
	
	{
		"require": {
			"elgg/legacy_urls": "~2.3"
		}
	}

User validation by email plugin
-------------------------------

The listing view of unvalidated users has been moved from the plugin to Elgg core. Some generic action (eg. validate and delete) have also been moved
to Elgg core.

Email delivery
--------------

To provide for more granularity in email handling and delivery, **email, system** hook has been removed.
New email service provides for several other replacement hooks that allow plugins to control email
content, format, and transport used for delivery.

``elgg_set_email_transport()`` can now be used to replace the default Sendmail transport with another instance of
``\Zend\Mail\Transport\TransportInterface``, e.g. SMTP, in-memory, or file transport. Note that this function
must be called early in the boot process. Note that if you call this function on each request, using
plugin settings to determine transport config may not be very efficient - store these settings in
as datalist or site config values, so they are loaded from boot cache.

Theme and styling changes
-------------------------

Aalborg theme is no longer bundled with Elgg.
Default core theme is now based on Aalboard, but it has undergone major changes.

Notable changes in plugins:

 * Topbar, navbar and header have been combined into a single responsive topbar component
 * Default inner width is now 1280px (80rem * 16px/1rem)
 * Preferred unit of measurement is now `rem` and not `px`
 * The theme uses `8-point grid system <https://builttoadapt.io/intro-to-the-8-point-grid-system-d2573cde8632>`
 * Menus, layout elements and other components now use flexbox
 * Reset is done using `8-point grid system <https://necolas.github.io/normalize.css/>`
 * Media queries have been rewritten for mobile-first experience
 * Form elements (text inputs, buttons and selects) now have an equal height of 2.5rem
 * Layout header is now positioned outside of the layout columns, which have been wrapped into ``elgg-layout-columns``
 * z-index properties have been reviewed and stacked with simple iteration instead of `9999999 <https://hackernoon.com/my-approach-to-using-z-index-eca67feb079c>`.
 * Color scheme has been changed to highlight actionable elements and reduce abundance of gray shades
 * search plugin no longer extends ``page/elements/header`` and instead ``page/elements/topbar`` renders ``search/search_box`` view
 * ``.elgg-icon`` no longer has a global ``font-size``, ``line-height`` or ``color``: these values will be inherited from parent items
 * Support for ``.elgg-icon-hover`` has been dropped
 * User "hover" icons are no longer covered with a "caret" icon

Read more about :doc:`Theming Principles </guides/themes>`

Also note, CSS views served via ``/cache`` URLs are pre-processed using `CSS Crush <http://the-echoplex.net/csscrush/>`. If you make references to CSS variables or other elements, the definition must be located within the same view output. E.g. A variable defined in ``elgg.css`` cannot be referenced in a separate CSS file like ``colorbox.css``.

Comments
--------

Submitting comments is now AJAXed. After a succesful submission the comment list will be updated automatically.

The following changes have been made to the comment notifications.

 * The language keys related to comment notifications have changed. Check the ``generic_comment:notification:owner:`` language keys
 * The action for creating a comment (``action/comment/save``) was changed. If your plugin overruled this action you should have a look at it in order to prevent double notifications 

Object listing views
--------------------

 * ``object/elements/full/body`` now wraps the full listing body in a ``.elgg-listing-full-body`` wrapper
 * ``object/elements/full`` now supports ``attachments`` and ``responses`` which are rendered after listing body
 * In core plugins, resource views no longer render comments/replies - instead they pass a ``show_responses`` flag to the entity view, which renders the responses and passes them to the full listing view. Third party plugins will need to update their uses of ``object/<subtype>`` and ``resources/<handler>/view`` views.
 * Full discussion view is now rendered using ``object/elements/full`` view
 * ``object/file`` now passes image (specialcontent) view as an ``attachment`` to the full listing view

Menu changes
------------

Default sorting of menu items has been changed from ``text`` to ``priority``.

Note that ``register`` and ``prepare`` hooks now use collections API. For the most part, all hooks should continue working, as long as they are not performing complex operations with arrays.

Support for ``icon`` and ``badge`` parameters was added. Plugins should start using these parameters and prefer them to a single ``text`` parameter. CSS should be used to control visibility of the label, icon and badge, instead of conditionals in preparing menu items.

All menus are now wrapped with ``nav.elgg-menu-container`` to ensure that multiple menu sections have a single parent element, and can be styled using flexbox or floats.

All menu items are now identified with with ``data-menu-item`` attribute, sections - with ``data-menu-section``, containers with - ``data-menu-name`` attributes.

``topbar`` menu:

 * ``account`` menu item with priority ``800`` added to ``alt`` section
 * ``site_notifications`` menu item is now a child of ``account`` with priority ``100``
 * ``usersettings`` menu item is now a child of ``account`` with priority ``300``
 * ``administration`` menu item is now a child of ``account`` with priority ``800``
 * ``logout`` menu item is now a child of ``account`` with priority ``900``
 * ``dashboard`` menu item now is now a child of ``acount`` has priority of ``100``
 * In ``default`` section (``profile``, ``friends``, ``messages``), core menu items now use ``icon`` parameter and use CSS to hide the label. Plugins that register items to this section and expect a visible label need to update their CSS.
 * ``profile`` menu item is now a child of ``account``
 * ``friends`` menu item is now a child of ``account``

``entity`` menu:

 * ``access`` menu item has been removed. Access information is now rendered in the entity byline.

``user_hover`` menu:

 * All items use the ``icon`` parameter.
 * The layout of the dropdown has been changed. If you have modified the look and feel of this dropdown, you might need to update your HTML/CSS.

``widget`` menu:

 * ``collapse`` menu item has been removed and CSS updated accordingly
 
``title`` menu:

The ``profile`` plugin no longer uses the actions section of the ``user_hover`` menu, but registers regulare ``title`` menu items.

``extras`` menu:

This menu has been removed from the page layout. Menu items that registered for this menu have been moved to other menus.

``groups:my_status`` menu:

This menu has been removed from the group profile page.

``site_notifications`` menu:

This menu has been removed. Site Notification objects now use the entity menu for actions.

``site`` menu:

Registration of custom menu item defined in admin interface has been moved to ``register, menu:site`` hook.
``navigation/menu/site`` view has been removed. Site menu now adds a ``more` menu item directly to the ``default`` section.

Entity icons
------------

Default icon image files have been moved and re-mapped as follows:

 * Default icons: ``views/default/icon/default/$size.png``
 * User icons: ``views/default/icon/user/default/$size.gif``
 * Group icons: ``views/default/icon/group/default/$size.gif`` in the groups plugin

Groups icon files have been moved from ``groups/<guid><size>.jpg`` relative to group owner's directory on filestore to a location prescribed by the entity icon service. Plugins should stop accessing files on the filestore directly and use the entity icon API. Upgrade script is available via admin interface.

The generation of entity icons has ben changed. No longer will all the configured sizes be generated when calling one of the entity icon functions
(``ElggEntity::saveIconFromUploadedFile``, ``ElggEntity::saveIconFromLocalFile`` or ``ElggEntity::saveIconFromElggFile``), but only the `master` size.
The other configured sizes will be generated when requesting that size based of the `master` icon.

Icon glyphs
-----------

FontAwesome has been upgraded to version 5.0+. There were certain changes to how FontAwesome glyphs are rendered. The core will take care of most changes (e.g. mapping old icon names to new ones, and using the correct prefix for brand and solid icons).

Autocomplete (user and friends pickers)
---------------------------------------

Friends Picker input is now rendered using ``input/userpicker``.

Plugins should:

 * Update overriden ``input/userpicker`` to support new ``only_friends`` parameter
 * Remove friends picker CSS from their stylesheets

Friends collections
-------------------

Friends collections UI has been moved to its own plugins - ``friends_collections``.

Layout of ``.elgg-body`` elements
---------------------------------

In 3.0, these elements by default no longer stretch to fill available space in a block
context. They still clear floats and allow breaking words to wrap text.

Core modules and layouts that relied on space-filling have been reworked for Flexbox and
we encourage devs to do the same, rather than use the problematic ``overflow: hidden``.

Delete river items
------------------

The function ``elgg_delete_river()`` which was deprecated in 2.3, has been reinstated. Notable changes between the internals of this function are;

 * It accepts all ``$options`` from ``elgg_get_river()`` but requires at least one of the following params to be set id(s), annotation_id(s), subject_guid(s), object_guid(s), target_guid(s) or view(s)
 * Since ``elgg_get_river`` by default has a limit on the number of river items it fetches, if you wish to remove all river items you need to set ``limit`` to ``false``
 * Access is ignored when deleting river items
 * Events are fired just before and after a river item has been deleted

.. _upgrade-discussion-replies:

Discussion replies moved to comments
------------------------------------

Since discussion replies where mostly a carbon copy of comments, all discussion replies have been migrated to comments. All related action, hooks, 
event, language keys etc. have been removed.

.. note::

    Discussion comments will now show up in the Comments section of Search, no longer under the Discussion section.

Translations cleanup
--------------------

All plugins have been scanned for unused translation keys. The unused keys have been removed.
If there was a generic translation available for the custom translation key, these have also been updated.

System Log
----------

System log API has been moved out of core into a ``system_log`` plugin.
``logbrowser`` and ``logrotate`` plugins have been merged into the ``system_log`` plugin.

Error logging
-------------

Sending ``elgg_log()`` and PHP error messages to page output is now only possible via the developers plugin "Log to the screen" setting. See the ``settings.example.php`` file for more information on using ``$CONFIG->debug`` in your ``settings.php`` file. Debugging should generally be done via the ``xdebug`` extension or ``tail -f /path/to/error.log`` on your server.

Composer asset plugin no longer required
----------------------------------------

Assets are now loaded from https://asset-packagist.org. FXP composer asset plugin is no longer required when installing Elgg or updating composer dependencies.

Cron logs
---------

The cron logs are no longer stored in the database, but on the filesystem (in dataroot). This will allow longer output to be stored. A migration 
script was added to migrate the old database settings to the new location and remove the old values from the database.

Removed / changed language keys
-------------------------------

 * The language keys related to comment notifications have changed. Check the ``generic_comment:notification:owner:`` language keys

New MySQL schema features are not applied
-----------------------------------------

New 3.0 installations require MySQL 5.5.3 (or higher) and use the utf8mb4 character set and LONGTEXT content columns (notably allowing storing longer content and extended characters like emoji).

Miscellaneous changes
---------------------

The settings "Allow visitors to register" and "Restrict pages to logged-in users" now appear on the Basic Settings admin page.

Twitter API plugin
------------------

The ``twitter_api`` plugin no longer comes bundled with Elgg.

Unit and Integration Testing
----------------------------

Elgg's PHPUnit bootstrap can now handle both unit and integration tests. Please note that **you shouldn't run tests on a production site**,
as it may damage data integrity. To prevent data loss, you need to specify database settings via environment variables.
You can do so via the phpunit.xml bootstrap.

Plugins can now implement their own PHPUnit tests by extending ``\Elgg\UnitTestCase`` and ``\Elgg\IntegrationTestCase`` classes.
``plugins`` test suite will automatically autoload PHPUnit tests from ``mod/<plugin_id>/tests/phpunit/unit`` and
``mod/<plugin_id>/tests/phpunit/integration``.

Prior to running integration tests, you need to enable the plugins that you wish to test alongside core API.

``\Elgg\IntegrationTestCase`` uses ``\Elgg\Seeding`` trait, which can be used to conveniently build new entities and
write them to the database.

``\Elgg\UnitTestCase`` does not use the database, but provides a database mocking interface, which allows tests to
define query specs with predefined returns.

By default, both unit and integration tests will be run whenever ``phpunit`` is called. You can use ``--testsuite`` flag to only run a specific suite: ``phpunit --testsuite unit`` or ``phpunit --testsuite integration`` or ``phpunit --testsuite plugins``.

For integration testing to run properly, plugins are advised to not put any logic into the root of ``start.php``, and instead
return a Closure. This allows the testsuite to build a new Application instance without loosing plugin initialization logic.

Plugins with simpletests will continue working as perviously. However, method signatures in the ``ElggCoreUnitTest`` abstract class
have changed and you will need to update your tests accordingly. Namely, it's discouraged to use ``__construct`` and
``__desctruct`` methods. ``setUp`` and ``tearDown`` have been marked as private and are used for consistent test
boostrapping and asserting pre and post conditions, your test case should use ``up`` and ``down`` methods instead.

Simpletests can no longer be executed from the admin interface of the developers plugin.
Use Elgg cli command: ``elgg-cli simpletest``
