summaryrefslogtreecommitdiff
path: root/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4
diff options
context:
space:
mode:
Diffstat (limited to 'www/crm/wp-content/plugins/civicrm/civicrm/ext/api4')
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/.gitignore74
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/CRM/Api4/Page/AJAX.php66
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/CRM/Api4/Page/Api4Explorer.php24
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/CRM/Api4/Upgrader.php158
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/CRM/Api4/Upgrader/Base.php375
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/ACL.php19
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Address/Create.php43
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Address/Update.php43
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Contact/Create.php22
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Contact/GetFields.php19
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Contribution/Create.php18
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/CustomValue/Create.php11
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/CustomValue/Delete.php11
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/CustomValue/Get.php11
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/CustomValue/GetActions.php11
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/CustomValue/GetFields.php34
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/CustomValue/Replace.php11
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/CustomValue/Update.php11
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Entity/Get.php102
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Entity/GetLinks.php51
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/GetActions.php101
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/GroupContact/Create.php38
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/GroupContact/Update.php38
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Navigation/Get.php19
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Participant/Get.php18
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/ActionSchedule.php18
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Activity.php20
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/ActivityContact.php16
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Address.php32
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Contact.php39
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/ContactType.php18
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Contribution.php19
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/CustomField.php12
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/CustomGroup.php12
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/CustomValue.php79
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Email.php16
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Entity.php48
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event.php12
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Events.php25
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/GetSpecEvent.php35
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/PostSelectQueryEvent.php64
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/SchemaMapBuildEvent.php39
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/AbstractPrepareSubscriber.php24
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/ActivityPreCreationSubscriber.php40
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/ActivitySchemaMapSubscriber.php40
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/ContactSchemaMapSubscriber.php54
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/CustomFieldPreCreationSubscriber.php91
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/CustomGroupPreCreationSubscriber.php29
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/OptionValuePreCreationSubscriber.php50
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/PermissionCheckSubscriber.php65
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/PostSelectQuerySubscriber.php369
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/PreCreationSubscriber.php50
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/ValidateFieldsSubscriber.php97
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/AbstractAction.php391
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/AbstractBatchAction.php64
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/AbstractCreateAction.php56
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/AbstractEntity.php88
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/AbstractGetAction.php23
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/AbstractQueryAction.php105
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/AbstractUpdateAction.php45
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/BasicBatchAction.php72
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/BasicCreateAction.php64
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/BasicGetAction.php144
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/BasicGetFieldsAction.php120
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/BasicReplaceAction.php81
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/BasicUpdateAction.php67
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/DAOCreateAction.php58
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/DAODeleteAction.php73
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/DAOEntity.php52
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/DAOGetAction.php21
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/DAOGetFieldsAction.php53
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/DAOUpdateAction.php31
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/Result.php105
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/Traits/ArrayQueryActionTrait.php197
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/Traits/CustomValueActionTrait.php83
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/Traits/DAOActionTrait.php229
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Group.php12
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/GroupContact.php29
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/IM.php12
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Navigation.php19
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Note.php12
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/OpenID.php12
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/OptionGroup.php12
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/OptionValue.php12
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Participant.php19
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Phone.php16
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Provider/ActionObjectProvider.php155
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Query/Api4SelectQuery.php535
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Relationship.php12
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/RelationshipType.php12
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/Joinable/ActivityToActivityContactAssigneesJoinable.php40
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/Joinable/BridgeJoinable.php23
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/Joinable/CustomGroupJoinable.php71
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/Joinable/Joinable.php277
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/Joinable/OptionValueJoinable.php61
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/Joiner.php98
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/SchemaMap.php140
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/SchemaMapBuilder.php217
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/Table.php128
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/CustomFieldSpec.php118
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/FieldSpec.php320
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/ActionScheduleCreationSpecProvider.php27
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/ActivityCreationSpecProvider.php27
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/AddressCreationSpecProvider.php29
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/ContactCreationSpecProvider.php32
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/ContactTypeCreationSpecProvider.php29
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/ContributionCreationSpecProvider.php23
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/CustomGroupCreationSpecProvider.php22
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/CustomValueSpecProvider.php34
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/EmailCreationSpecProvider.php25
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/EventCreationSpecProvider.php22
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/GroupCreationSpecProvider.php22
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/NavigationCreationSpecProvider.php22
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/NoteCreationSpecProvider.php28
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/OptionValueCreationSpecProvider.php23
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/PhoneCreationSpecProvider.php24
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/SpecProviderInterface.php23
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/RequestSpec.php110
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/SpecFormatter.php117
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/SpecGatherer.php131
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/UFGroup.php12
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/UFJoin.php12
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Utils/ActionUtil.php27
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Utils/ArrayInsertionUtil.php73
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Utils/CoreUtil.php42
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Utils/FormattingUtil.php106
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Utils/ReflectionUtils.php119
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Website.php12
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/LICENSE.txt667
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4.ang.php12
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4.js4
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4/crmApi4.js37
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4Explorer.ang.php18
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4Explorer.js4
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4Explorer/Chain.html4
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4Explorer/Explorer.html137
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4Explorer/Explorer.js789
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4Explorer/WhereClause.html39
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/api4.civix.php460
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/api4.php193
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/css/explorer.css191
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/images/ApiExplorer.pngbin0 -> 207956 bytes
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/info.xml29
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/js/api4.js43
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/js/load-bootstrap.js7
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/lib/js-yaml.js3919
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/lib/shoreditch/bootstrap.css1
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/lib/shoreditch/dropdown.js165
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/phpunit.xml.dist29
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/readme.md120
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/services.xml122
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/templates/CRM/Api4/Page/Api4Explorer.tpl0
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/BaseCustomValueTest.php31
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/BasicActionsTest.php154
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/BasicCustomFieldTest.php187
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/ChainTest.php53
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/ComplexQueryTest.php87
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/ContactApiKeyTest.php170
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/CreateCustomValueTest.php64
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/CreateWithOptionGroupTest.php190
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/CustomValuePerformanceTest.php95
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/CustomValueTest.php174
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/DateTest.php46
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/EvaluateConditionTest.php38
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/ExtendFromIndividualTest.php54
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/FkJoinTest.php75
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/GetExtraFieldsTest.php26
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/GetFromArrayTest.php163
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/IndexTest.php48
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/NullValueTest.php55
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/ReplaceTest.php171
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/UpdateContactTest.php34
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/UpdateCustomValueTest.php56
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/DataSets/ConformanceTest.json28
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/DataSets/DefaultDataSet.json45
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/DataSets/MultiContactMultiEmail.json42
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/DataSets/SingleContact.json81
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Entity/ConformanceTest.php226
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Entity/ContactJoinTest.php103
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Entity/EntityTest.php35
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Entity/ParticipantTest.php200
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Mock/Api4/Action/MockArrayEntity/Get.php53
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Mock/Api4/MockArrayEntity.php21
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Mock/Api4/MockBasicEntity.php94
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Mock/MockEntityDataStorage.php31
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Mock/MockV4ReflectionBase.php22
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Mock/MockV4ReflectionChild.php16
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Mock/MockV4ReflectionGrandchild.php17
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Query/Api4SelectQueryComplexJoinTest.php85
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Query/Api4SelectQueryTest.php91
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Query/OneToOneJoinTest.php46
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Query/OptionValueJoinTest.php46
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Query/SelectQueryMultiJoinTest.php74
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Service/Schema/SchemaMapRealTableTest.php22
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Service/Schema/SchemaMapperTest.php90
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Service/TestCreationParameterProvider.php148
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Spec/RequestSpecTest.php42
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Spec/SpecFormatterTest.php90
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Spec/SpecGathererTest.php96
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Traits/OptionCleanupTrait.php24
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Traits/QueryCounterTrait.php43
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Traits/TableDropperTrait.php23
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Traits/TestDataLoaderTrait.php69
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/UnitTestCase.php235
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Utils/ArrayInsertionServiceTest.php67
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Utils/ReflectionUtilsTest.php46
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/bootstrap.php56
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/services.xml10
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/xml/Menu/api4.xml14
209 files changed, 19890 insertions, 0 deletions
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/.gitignore b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/.gitignore
new file mode 100644
index 00000000..0d74f047
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/.gitignore
@@ -0,0 +1,74 @@
+# Created by .ignore support plugin (hsz.mobi)
+### Example user template template
+### Example user template
+
+# IntelliJ project files
+.idea
+*.iml
+out
+gen### Linux template
+*~
+
+# temporary files which can be created if a process still has a handle open of a deleted file
+.fuse_hidden*
+
+# KDE directory preferences
+.directory
+
+# Linux trash folder which might appear on any partition or disk
+.Trash-*
+
+# .nfs files are created when an open file is removed but is still being accessed
+.nfs*
+### macOS template
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+### Windows template
+# Windows thumbnail cache files
+Thumbs.db
+ehthumbs.db
+ehthumbs_vista.db
+
+# Dump file
+*.stackdump
+
+# Folder config file
+[Dd]esktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/CRM/Api4/Page/AJAX.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/CRM/Api4/Page/AJAX.php
new file mode 100644
index 00000000..6a1bc2de
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/CRM/Api4/Page/AJAX.php
@@ -0,0 +1,66 @@
+<?php
+
+class CRM_Api4_Page_AJAX extends CRM_Core_Page {
+
+ /**
+ * Handler for api4 ajax requests
+ */
+ public function run() {
+ try {
+ // Call multiple
+ if (empty($this->urlPath[3])) {
+ $calls = CRM_Utils_Request::retrieve('calls', 'String', CRM_Core_DAO::$_nullObject, TRUE, NULL, 'POST', TRUE);
+ $calls = json_decode($calls, TRUE);
+ $response = [];
+ foreach ($calls as $index => $call) {
+ $response[$index] = call_user_func_array([$this, 'execute'], $call);
+ }
+ }
+ // Call single
+ else {
+ $entity = $this->urlPath[3];
+ $action = $this->urlPath[4];
+ $params = CRM_Utils_Request::retrieve('params', 'String');
+ $params = $params ? json_decode($params, TRUE) : [];
+ $index = CRM_Utils_Request::retrieve('index', 'String');
+ $response = $this->execute($entity, $action, $params, $index);
+ }
+ }
+ catch (Exception $e) {
+ http_response_code(500);
+ $response = [
+ 'error_code' => $e->getCode(),
+ ];
+ if (CRM_Core_Permission::check('view debug output')) {
+ $response['error_message'] = $e->getMessage();
+ if (CRM_Core_BAO_Setting::getItem(NULL, 'backtrace')) {
+ $response['backtrace'] = $e->getTrace();
+ }
+ }
+ }
+ CRM_Utils_System::setHttpHeader('Content-Type', 'application/json');
+ echo json_encode($response);
+ CRM_Utils_System::civiExit();
+ }
+
+ /**
+ * Run api call & prepare result for json encoding
+ *
+ * @param string $entity
+ * @param string $action
+ * @param array $params
+ * @param string $index
+ * @return array
+ */
+ protected function execute($entity, $action, $params = [], $index = NULL) {
+ $params['checkPermissions'] = TRUE;
+ $result = civicrm_api4($entity, $action, $params, $index);
+ // Convert arrayObject into something more suitable for json
+ $vals = ['values' => (array) $result];
+ foreach (get_class_vars(get_class($result)) as $key => $val) {
+ $vals[$key] = $result->$key;
+ }
+ return $vals;
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/CRM/Api4/Page/Api4Explorer.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/CRM/Api4/Page/Api4Explorer.php
new file mode 100644
index 00000000..50aa0c3d
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/CRM/Api4/Page/Api4Explorer.php
@@ -0,0 +1,24 @@
+<?php
+
+class CRM_Api4_Page_Api4Explorer extends CRM_Core_Page {
+
+ public function run() {
+ $vars = [
+ 'operators' => \CRM_Core_DAO::acceptedSQLOperators(),
+ 'basePath' => Civi::resources()->getUrl('org.civicrm.api4'),
+ ];
+ Civi::resources()
+ ->addVars('api4', $vars)
+ ->addScriptFile('org.civicrm.api4', 'js/load-bootstrap.js');
+
+ $loader = new Civi\Angular\AngularLoader();
+ $loader->setModules(['api4Explorer']);
+ $loader->setPageName('civicrm/api4');
+ $loader->useApp([
+ 'defaultRoute' => '/explorer',
+ ]);
+ $loader->load();
+ parent::run();
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/CRM/Api4/Upgrader.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/CRM/Api4/Upgrader.php
new file mode 100644
index 00000000..10ec9da5
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/CRM/Api4/Upgrader.php
@@ -0,0 +1,158 @@
+<?php
+
+/**
+ * Collection of upgrade steps.
+ */
+class CRM_Api4_Upgrader extends CRM_Api4_Upgrader_Base {
+
+ // By convention, functions that look like "function upgrade_NNNN()" are
+ // upgrade tasks. They are executed in order (like Drupal's hook_update_N).
+
+ /**
+ * Install script
+ */
+ public function install() {
+ // Add menu item for api explorer; rename v3 explorer menu item.
+ try {
+ $v3Item = civicrm_api3('Navigation', 'get', [
+ 'name' => 'API Explorer',
+ 'return' => ['id', 'parent_id'],
+ 'sequential' => 1,
+ 'api.Navigation.create' => ['label' => ts("Api Explorer v3")],
+ ]);
+ civicrm_api3('Navigation', 'create', [
+ 'parent_id' => $v3Item['values'][0]['parent_id'],
+ 'label' => ts("Api Explorer v4"),
+ 'weight' => 2,
+ 'name' => "Api Explorer v4",
+ 'permission' => "administer CiviCRM",
+ 'url' => "civicrm/api4#/explorer",
+ 'is_active' => 1,
+ ]);
+ }
+ catch (Exception $e) {
+ // Couldn't create menu item.
+ }
+ }
+
+ /**
+ * 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),
+ ));
+ }
+
+ /**
+ * Uninstall script
+ */
+ public function uninstall() {
+ // Remove Api4 Explorer navigation menu item
+ civicrm_api3('Navigation', 'get', [
+ 'name' => 'Api Explorer v4',
+ 'return' => ['id'],
+ 'api.Navigation.delete' => [],
+ ]);
+ }
+
+ /**
+ * Example: Run a simple query when a module is enabled.
+ *
+ public function enable() {
+ CRM_Core_DAO::executeQuery('UPDATE foo SET is_active = 1 WHERE bar = "whiz"');
+ }
+
+ /**
+ * Example: Run a simple query when a module is disabled.
+ *
+ public function disable() {
+ CRM_Core_DAO::executeQuery('UPDATE foo SET is_active = 0 WHERE bar = "whiz"');
+ }
+
+ /**
+ * Example: Run a couple simple queries.
+ *
+ * @return TRUE on success
+ * @throws Exception
+ */
+ public function upgrade_1000() {
+ $this->ctx->log->info('Applying update 1000');
+ $domain = CRM_Core_Config::domainID();
+ CRM_Core_DAO::executeQuery('UPDATE civicrm_navigation SET url = "civicrm/api4#/explorer" WHERE url = "civicrm/a/#/api4" AND domain_id = ' . $domain);
+ return TRUE;
+ }
+
+
+ /**
+ * Example: Run an external SQL script.
+ *
+ * @return TRUE on success
+ * @throws Exception
+ public function upgrade_4201() {
+ $this->ctx->log->info('Applying update 4201');
+ // this path is relative to the extension base dir
+ $this->executeSqlFile('sql/upgrade_4201.sql');
+ return TRUE;
+ } // */
+
+
+ /**
+ * Example: Run a slow upgrade process by breaking it up into smaller chunk.
+ *
+ * @return TRUE on success
+ * @throws Exception
+ public function upgrade_4202() {
+ $this->ctx->log->info('Planning update 4202'); // PEAR Log interface
+
+ $this->addTask(ts('Process first step'), 'processPart1', $arg1, $arg2);
+ $this->addTask(ts('Process second step'), 'processPart2', $arg3, $arg4);
+ $this->addTask(ts('Process second step'), 'processPart3', $arg5);
+ return TRUE;
+ }
+ public function processPart1($arg1, $arg2) { sleep(10); return TRUE; }
+ public function processPart2($arg3, $arg4) { sleep(10); return TRUE; }
+ public function processPart3($arg5) { sleep(10); return TRUE; }
+ // */
+
+
+ /**
+ * Example: Run an upgrade with a query that touches many (potentially
+ * millions) of records by breaking it up into smaller chunks.
+ *
+ * @return TRUE on success
+ * @throws Exception
+ public function upgrade_4203() {
+ $this->ctx->log->info('Planning update 4203'); // PEAR Log interface
+
+ $minId = CRM_Core_DAO::singleValueQuery('SELECT coalesce(min(id),0) FROM civicrm_contribution');
+ $maxId = CRM_Core_DAO::singleValueQuery('SELECT coalesce(max(id),0) FROM civicrm_contribution');
+ for ($startId = $minId; $startId <= $maxId; $startId += self::BATCH_SIZE) {
+ $endId = $startId + self::BATCH_SIZE - 1;
+ $title = ts('Upgrade Batch (%1 => %2)', array(
+ 1 => $startId,
+ 2 => $endId,
+ ));
+ $sql = '
+ UPDATE civicrm_contribution SET foobar = whiz(wonky()+wanker)
+ WHERE id BETWEEN %1 and %2
+ ';
+ $params = array(
+ 1 => array($startId, 'Integer'),
+ 2 => array($endId, 'Integer'),
+ );
+ $this->addTask($title, 'executeSql', $sql, $params);
+ }
+ return TRUE;
+ } // */
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/CRM/Api4/Upgrader/Base.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/CRM/Api4/Upgrader/Base.php
new file mode 100644
index 00000000..bfea5fef
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/CRM/Api4/Upgrader/Base.php
@@ -0,0 +1,375 @@
+<?php
+
+// AUTO-GENERATED FILE -- Civix may overwrite any changes made to this file
+
+/**
+ * Base class which provides helpers to execute upgrade logic
+ */
+class CRM_Api4_Upgrader_Base {
+
+ /**
+ * @var varies, subclass of ttis
+ */
+ static $instance;
+
+ /**
+ * @var CRM_Queue_TaskContext
+ */
+ protected $ctx;
+
+ /**
+ * @var string, eg 'com.example.myextension'
+ */
+ protected $extensionName;
+
+ /**
+ * @var string, full path to the extension's source tree
+ */
+ protected $extensionDir;
+
+ /**
+ * @var array(revisionNumber) sorted numerically
+ */
+ private $revisions;
+
+ /**
+ * @var boolean
+ * Flag to clean up extension revision data in civicrm_setting
+ */
+ private $revisionStorageIsDeprecated = FALSE;
+
+ /**
+ * Obtain a reference to the active upgrade handler.
+ */
+ static public function instance() {
+ if (!self::$instance) {
+ // FIXME auto-generate
+ self::$instance = new CRM_Api4_Upgrader(
+ 'org.civicrm.api4',
+ realpath(__DIR__ . '/../../../')
+ );
+ }
+ return self::$instance;
+ }
+
+ /**
+ * Adapter that lets you add normal (non-static) member functions to the queue.
+ *
+ * Note: Each upgrader instance should only be associated with one
+ * task-context; otherwise, this will be non-reentrant.
+ *
+ * @code
+ * CRM_Api4_Upgrader_Base::_queueAdapter($ctx, 'methodName', 'arg1', 'arg2');
+ * @endcode
+ */
+ static public function _queueAdapter() {
+ $instance = self::instance();
+ $args = func_get_args();
+ $instance->ctx = array_shift($args);
+ $instance->queue = $instance->ctx->queue;
+ $method = array_shift($args);
+ return call_user_func_array([$instance, $method], $args);
+ }
+
+ public function __construct($extensionName, $extensionDir) {
+ $this->extensionName = $extensionName;
+ $this->extensionDir = $extensionDir;
+ }
+
+ // ******** Task helpers ********
+
+ /**
+ * Run a CustomData file.
+ *
+ * @param string $relativePath the CustomData XML file path (relative to this extension's dir)
+ * @return bool
+ */
+ public function executeCustomDataFile($relativePath) {
+ $xml_file = $this->extensionDir . '/' . $relativePath;
+ return $this->executeCustomDataFileByAbsPath($xml_file);
+ }
+
+ /**
+ * Run a CustomData file
+ *
+ * @param string $xml_file the CustomData XML file path (absolute path)
+ *
+ * @return bool
+ */
+ protected static function executeCustomDataFileByAbsPath($xml_file) {
+ $import = new CRM_Utils_Migrate_Import();
+ $import->run($xml_file);
+ return TRUE;
+ }
+
+ /**
+ * Run a SQL file.
+ *
+ * @param string $relativePath the SQL file path (relative to this extension's dir)
+ *
+ * @return bool
+ */
+ public function executeSqlFile($relativePath) {
+ CRM_Utils_File::sourceSQLFile(
+ CIVICRM_DSN,
+ $this->extensionDir . DIRECTORY_SEPARATOR . $relativePath
+ );
+ return TRUE;
+ }
+
+ /**
+ * @param string $tplFile
+ * The SQL file path (relative to this extension's dir).
+ * Ex: "sql/mydata.mysql.tpl".
+ * @return bool
+ */
+ public function executeSqlTemplate($tplFile) {
+ // Assign multilingual variable to Smarty.
+ $upgrade = new CRM_Upgrade_Form();
+
+ $tplFile = CRM_Utils_File::isAbsolute($tplFile) ? $tplFile : $this->extensionDir . DIRECTORY_SEPARATOR . $tplFile;
+ $smarty = CRM_Core_Smarty::singleton();
+ $smarty->assign('domainID', CRM_Core_Config::domainID());
+ CRM_Utils_File::sourceSQLFile(
+ CIVICRM_DSN, $smarty->fetch($tplFile), NULL, TRUE
+ );
+ return TRUE;
+ }
+
+ /**
+ * Run one SQL query.
+ *
+ * This is just a wrapper for CRM_Core_DAO::executeSql, but it
+ * provides syntatic sugar for queueing several tasks that
+ * run different queries
+ */
+ public function executeSql($query, $params = []) {
+ // FIXME verify that we raise an exception on error
+ CRM_Core_DAO::executeQuery($query, $params);
+ return TRUE;
+ }
+
+ /**
+ * Syntatic sugar for enqueuing a task which calls a function in this class.
+ *
+ * The task is weighted so that it is processed
+ * as part of the currently-pending revision.
+ *
+ * After passing the $funcName, you can also pass parameters that will go to
+ * the function. Note that all params must be serializable.
+ */
+ public function addTask($title) {
+ $args = func_get_args();
+ $title = array_shift($args);
+ $task = new CRM_Queue_Task(
+ [get_class($this), '_queueAdapter'],
+ $args,
+ $title
+ );
+ return $this->queue->createItem($task, ['weight' => -1]);
+ }
+
+ // ******** Revision-tracking helpers ********
+
+ /**
+ * Determine if there are any pending revisions.
+ *
+ * @return bool
+ */
+ public function hasPendingRevisions() {
+ $revisions = $this->getRevisions();
+ $currentRevision = $this->getCurrentRevision();
+
+ if (empty($revisions)) {
+ return FALSE;
+ }
+ if (empty($currentRevision)) {
+ return TRUE;
+ }
+
+ return ($currentRevision < max($revisions));
+ }
+
+ /**
+ * Add any pending revisions to the queue.
+ */
+ public function enqueuePendingRevisions(CRM_Queue_Queue $queue) {
+ $this->queue = $queue;
+
+ $currentRevision = $this->getCurrentRevision();
+ foreach ($this->getRevisions() as $revision) {
+ if ($revision > $currentRevision) {
+ $title = ts('Upgrade %1 to revision %2', [
+ 1 => $this->extensionName,
+ 2 => $revision,
+ ]);
+
+ // note: don't use addTask() because it sets weight=-1
+
+ $task = new CRM_Queue_Task(
+ [get_class($this), '_queueAdapter'],
+ ['upgrade_' . $revision],
+ $title
+ );
+ $this->queue->createItem($task);
+
+ $task = new CRM_Queue_Task(
+ [get_class($this), '_queueAdapter'],
+ ['setCurrentRevision', $revision],
+ $title
+ );
+ $this->queue->createItem($task);
+ }
+ }
+ }
+
+ /**
+ * Get a list of revisions.
+ *
+ * @return array(revisionNumbers) sorted numerically
+ */
+ public function getRevisions() {
+ if (!is_array($this->revisions)) {
+ $this->revisions = [];
+
+ $clazz = new ReflectionClass(get_class($this));
+ $methods = $clazz->getMethods();
+ foreach ($methods as $method) {
+ if (preg_match('/^upgrade_(.*)/', $method->name, $matches)) {
+ $this->revisions[] = $matches[1];
+ }
+ }
+ sort($this->revisions, SORT_NUMERIC);
+ }
+
+ return $this->revisions;
+ }
+
+ public function getCurrentRevision() {
+ $revision = CRM_Core_BAO_Extension::getSchemaVersion($this->extensionName);
+ if (!$revision) {
+ $revision = $this->getCurrentRevisionDeprecated();
+ }
+ return $revision;
+ }
+
+ private function getCurrentRevisionDeprecated() {
+ $key = $this->extensionName . ':version';
+ if ($revision = CRM_Core_BAO_Setting::getItem('Extension', $key)) {
+ $this->revisionStorageIsDeprecated = TRUE;
+ }
+ return $revision;
+ }
+
+ public function setCurrentRevision($revision) {
+ CRM_Core_BAO_Extension::setSchemaVersion($this->extensionName, $revision);
+ // clean up legacy schema version store (CRM-19252)
+ $this->deleteDeprecatedRevision();
+ return TRUE;
+ }
+
+ private function deleteDeprecatedRevision() {
+ if ($this->revisionStorageIsDeprecated) {
+ $setting = new CRM_Core_BAO_Setting();
+ $setting->name = $this->extensionName . ':version';
+ $setting->delete();
+ CRM_Core_Error::debug_log_message("Migrated extension schema revision ID for {$this->extensionName} from civicrm_setting (deprecated) to civicrm_extension.\n");
+ }
+ }
+
+ // ******** Hook delegates ********
+
+ /**
+ * @see https://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_install
+ */
+ public function onInstall() {
+ $files = glob($this->extensionDir . '/sql/*_install.sql');
+ if (is_array($files)) {
+ foreach ($files as $file) {
+ CRM_Utils_File::sourceSQLFile(CIVICRM_DSN, $file);
+ }
+ }
+ $files = glob($this->extensionDir . '/sql/*_install.mysql.tpl');
+ if (is_array($files)) {
+ foreach ($files as $file) {
+ $this->executeSqlTemplate($file);
+ }
+ }
+ $files = glob($this->extensionDir . '/xml/*_install.xml');
+ if (is_array($files)) {
+ foreach ($files as $file) {
+ $this->executeCustomDataFileByAbsPath($file);
+ }
+ }
+ if (is_callable([$this, 'install'])) {
+ $this->install();
+ }
+ }
+
+ /**
+ * @see https://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_postInstall
+ */
+ public function onPostInstall() {
+ $revisions = $this->getRevisions();
+ if (!empty($revisions)) {
+ $this->setCurrentRevision(max($revisions));
+ }
+ if (is_callable([$this, 'postInstall'])) {
+ $this->postInstall();
+ }
+ }
+
+ /**
+ * @see https://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_uninstall
+ */
+ public function onUninstall() {
+ $files = glob($this->extensionDir . '/sql/*_uninstall.mysql.tpl');
+ if (is_array($files)) {
+ foreach ($files as $file) {
+ $this->executeSqlTemplate($file);
+ }
+ }
+ if (is_callable([$this, 'uninstall'])) {
+ $this->uninstall();
+ }
+ $files = glob($this->extensionDir . '/sql/*_uninstall.sql');
+ if (is_array($files)) {
+ foreach ($files as $file) {
+ CRM_Utils_File::sourceSQLFile(CIVICRM_DSN, $file);
+ }
+ }
+ }
+
+ /**
+ * @see https://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_enable
+ */
+ public function onEnable() {
+ // stub for possible future use
+ if (is_callable([$this, 'enable'])) {
+ $this->enable();
+ }
+ }
+
+ /**
+ * @see https://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_disable
+ */
+ public function onDisable() {
+ // stub for possible future use
+ if (is_callable([$this, 'disable'])) {
+ $this->disable();
+ }
+ }
+
+ public function onUpgrade($op, CRM_Queue_Queue $queue = NULL) {
+ switch ($op) {
+ case 'check':
+ return [$this->hasPendingRevisions()];
+
+ case 'enqueue':
+ return $this->enqueuePendingRevisions($queue);
+
+ default:
+ }
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/ACL.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/ACL.php
new file mode 100644
index 00000000..754a0499
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/ACL.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Civi\Api4;
+
+/**
+ * ACL Entity.
+ *
+ * This entity holds the ACL informatiom. With this entity you add/update/delete an ACL permission which consists of
+ * an Operation (e.g. 'View' or 'Edit'), a set of Data that the operation can be performed on (e.g. a group of contacts),
+ * and a Role that has permission to do this operation. For more info refer to
+ * https://docs.civicrm.org/user/en/latest/initial-set-up/permissions-and-access-control for more info.
+ *
+ * Creating a new ACL requires at minimum a entity table, entity ID and object_table
+ *
+ * @package Civi\Api4
+ */
+class ACL extends Generic\DAOEntity {
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Address/Create.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Address/Create.php
new file mode 100644
index 00000000..641b76f6
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Address/Create.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace Civi\Api4\Action\Address;
+
+use Civi\Api4\Generic\Result;
+
+/**
+ * @inheritDoc
+ */
+class Create extends \Civi\Api4\Generic\DAOCreateAction {
+
+ /**
+ * Optional param to indicate you want the street_address field parsed into individual params
+ *
+ * @var bool
+ */
+ protected $streetParsing = TRUE;
+
+ /**
+ * Optional param to indicate you want to skip geocoding (useful when importing a lot of addresses at once, the job Geocode and Parse Addresses can execute this task after the import)
+ *
+ * @var bool
+ */
+ protected $skipGeocode = FALSE;
+
+ /**
+ * When true, apply various fixes to the address before insert.
+ *
+ * @var bool
+ */
+ protected $fixAddress = TRUE;
+
+ /**
+ * @inheritDoc
+ */
+ public function _run(Result $result) {
+ $this->values['street_parsing'] = $this->streetParsing;
+ $this->values['skip_geocode'] = $this->skipGeocode;
+ $this->values['fix_address'] = $this->fixAddress;
+ parent::_run($result);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Address/Update.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Address/Update.php
new file mode 100644
index 00000000..862a8b9e
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Address/Update.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace Civi\Api4\Action\Address;
+
+use Civi\Api4\Generic\Result;
+
+/**
+ * @inheritDoc
+ */
+class Update extends \Civi\Api4\Generic\DAOUpdateAction {
+
+ /**
+ * Optional param to indicate you want the street_address field parsed into individual params
+ *
+ * @var bool
+ */
+ protected $streetParsing = TRUE;
+
+ /**
+ * Optional param to indicate you want to skip geocoding (useful when importing a lot of addresses at once, the job Geocode and Parse Addresses can execute this task after the import)
+ *
+ * @var bool
+ */
+ protected $skipGeocode = FALSE;
+
+ /**
+ * When true, apply various fixes to the address before insert.
+ *
+ * @var bool
+ */
+ protected $fixAddress = TRUE;
+
+ /**
+ * @inheritDoc
+ */
+ public function _run(Result $result) {
+ $this->values['street_parsing'] = $this->streetParsing;
+ $this->values['skip_geocode'] = $this->skipGeocode;
+ $this->values['fix_address'] = $this->fixAddress;
+ parent::_run($result);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Contact/Create.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Contact/Create.php
new file mode 100644
index 00000000..1bd0bcec
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Contact/Create.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Civi\Api4\Action\Contact;
+
+/**
+ * @inheritDoc
+ */
+class Create extends \Civi\Api4\Generic\DAOCreateAction {
+
+ protected function fillDefaults(&$params) {
+ // Guess which type of contact is being created
+ if (empty($params['contact_type']) && !empty($params['organization_name'])) {
+ $params['contact_type'] = 'Organization';
+ }
+ if (empty($params['contact_type']) && !empty($params['household_name'])) {
+ $params['contact_type'] = 'Household';
+ }
+ // Will default to Individual per fieldSpec
+ parent::fillDefaults($params);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Contact/GetFields.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Contact/GetFields.php
new file mode 100644
index 00000000..1d6ddf66
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Contact/GetFields.php
@@ -0,0 +1,19 @@
+<?php
+namespace Civi\Api4\Action\Contact;
+
+use Civi\Api4\Generic\DAOGetFieldsAction;
+
+class GetFields extends DAOGetFieldsAction {
+
+ protected function getRecords() {
+ $fields = parent::getRecords();
+
+ $apiKeyPerms = ['edit api keys', 'administer CiviCRM'];
+ if ($this->checkPermissions && !\CRM_Core_Permission::check([$apiKeyPerms])) {
+ unset($fields['api_key']);
+ }
+
+ return $fields;
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Contribution/Create.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Contribution/Create.php
new file mode 100644
index 00000000..52ee63c8
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Contribution/Create.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Civi\Api4\Action\Contribution;
+
+use Civi\Api4\Generic\Result;
+
+/**
+ * @inheritDoc
+ */
+class Create extends \Civi\Api4\Generic\DAOCreateAction {
+
+ public function _run(Result $result) {
+ // Required by Contribution BAO
+ $this->values['skipCleanMoney'] = TRUE;
+ parent::_run($result);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/CustomValue/Create.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/CustomValue/Create.php
new file mode 100644
index 00000000..7d059b24
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/CustomValue/Create.php
@@ -0,0 +1,11 @@
+<?php
+
+namespace Civi\Api4\Action\CustomValue;
+
+/**
+ * @inheritDoc
+ */
+class Create extends \Civi\Api4\Generic\DAOCreateAction {
+ use \Civi\Api4\Generic\Traits\CustomValueActionTrait;
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/CustomValue/Delete.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/CustomValue/Delete.php
new file mode 100644
index 00000000..7c521748
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/CustomValue/Delete.php
@@ -0,0 +1,11 @@
+<?php
+
+namespace Civi\Api4\Action\CustomValue;
+
+/**
+ * Delete one or more items, based on criteria specified in Where param.
+ */
+class Delete extends \Civi\Api4\Generic\DAODeleteAction {
+ use \Civi\Api4\Generic\Traits\CustomValueActionTrait;
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/CustomValue/Get.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/CustomValue/Get.php
new file mode 100644
index 00000000..47f3f514
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/CustomValue/Get.php
@@ -0,0 +1,11 @@
+<?php
+
+namespace Civi\Api4\Action\CustomValue;
+
+/**
+ * Get fields for a custom group.
+ */
+class Get extends \Civi\Api4\Generic\DAOGetAction {
+ use \Civi\Api4\Generic\Traits\CustomValueActionTrait;
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/CustomValue/GetActions.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/CustomValue/GetActions.php
new file mode 100644
index 00000000..8af9088d
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/CustomValue/GetActions.php
@@ -0,0 +1,11 @@
+<?php
+
+namespace Civi\Api4\Action\CustomValue;
+
+/**
+ * @inheritDoc
+ */
+class GetActions extends \Civi\Api4\Action\GetActions {
+ use \Civi\Api4\Generic\Traits\CustomValueActionTrait;
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/CustomValue/GetFields.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/CustomValue/GetFields.php
new file mode 100644
index 00000000..733776b7
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/CustomValue/GetFields.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace Civi\Api4\Action\CustomValue;
+
+use Civi\Api4\Service\Spec\SpecGatherer;
+use Civi\Api4\Service\Spec\SpecFormatter;
+
+/**
+ * Get fields for a custom group.
+ */
+class GetFields extends \Civi\Api4\Generic\DAOGetFieldsAction {
+ use \Civi\Api4\Generic\Traits\CustomValueActionTrait;
+
+ protected function getRecords() {
+ $fields = $this->_itemsToGet('name');
+ /** @var SpecGatherer $gatherer */
+ $gatherer = \Civi::container()->get('spec_gatherer');
+ $spec = $gatherer->getSpec('Custom_' . $this->getCustomGroup(), $this->getAction(), $this->includeCustom);
+ return SpecFormatter::specToArray($spec->getFields($fields), (array) $this->select, $this->loadOptions);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getParamInfo($param = NULL) {
+ $info = parent::getParamInfo($param);
+ if (!$param) {
+ // This param is meaningless here.
+ unset($info['includeCustom']);
+ }
+ return $info;
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/CustomValue/Replace.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/CustomValue/Replace.php
new file mode 100644
index 00000000..457be9ca
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/CustomValue/Replace.php
@@ -0,0 +1,11 @@
+<?php
+
+namespace Civi\Api4\Action\CustomValue;
+
+/**
+ * Given a set of records, will appropriately update the database.
+ */
+class Replace extends \Civi\Api4\Generic\BasicReplaceAction {
+ use \Civi\Api4\Generic\Traits\CustomValueActionTrait;
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/CustomValue/Update.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/CustomValue/Update.php
new file mode 100644
index 00000000..14f66f29
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/CustomValue/Update.php
@@ -0,0 +1,11 @@
+<?php
+
+namespace Civi\Api4\Action\CustomValue;
+
+/**
+ * Update one or more records with new values. Use the where clause to select them.
+ */
+class Update extends \Civi\Api4\Generic\DAOUpdateAction {
+ use \Civi\Api4\Generic\Traits\CustomValueActionTrait;
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Entity/Get.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Entity/Get.php
new file mode 100644
index 00000000..020c6398
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Entity/Get.php
@@ -0,0 +1,102 @@
+<?php
+
+namespace Civi\Api4\Action\Entity;
+
+use Civi\Api4\CustomGroup;
+use Civi\Api4\Utils\ReflectionUtils;
+
+/**
+ * Get entities
+ *
+ * @method $this setIncludeCustom(bool $value)
+ * @method bool getIncludeCustom()
+ */
+class Get extends \Civi\Api4\Generic\BasicGetAction {
+
+ /**
+ * Include custom-field-based pseudo-entities?
+ *
+ * @var bool
+ */
+ protected $includeCustom = TRUE;
+
+ /**
+ * Scan all api directories to discover entities
+ */
+ protected function getRecords() {
+ $entities = [];
+ foreach (explode(PATH_SEPARATOR, get_include_path()) as $path) {
+ $dir = \CRM_Utils_File::addTrailingSlash($path) . 'Civi/Api4';
+ if (is_dir($dir)) {
+ foreach (glob("$dir/*.php") as $file) {
+ $matches = [];
+ preg_match('/(\w*).php/', $file, $matches);
+ $entity = ['name' => $matches[1]];
+ if ($this->_isFieldSelected('description') || $this->_isFieldSelected('comment')) {
+ $this->addDocs($entity);
+ }
+ $entities[$matches[1]] = $entity;
+ }
+ }
+ }
+ unset($entities['CustomValue']);
+
+ if ($this->includeCustom) {
+ $this->addCustomEntities($entities);
+ }
+
+ ksort($entities);
+ return $entities;
+ }
+
+ /**
+ * Add custom-field pseudo-entities
+ *
+ * @param $entities
+ * @throws \API_Exception
+ */
+ private function addCustomEntities(&$entities) {
+ $customEntities = CustomGroup::get()
+ ->addWhere('is_multiple', '=', 1)
+ ->addWhere('is_active', '=', 1)
+ ->setSelect(['name', 'title', 'help_pre', 'help_post', 'extends'])
+ ->setCheckPermissions(FALSE)
+ ->execute();
+ foreach ($customEntities as $customEntity) {
+ $fieldName = 'Custom_' . $customEntity['name'];
+ $entities[$fieldName] = [
+ 'name' => $fieldName,
+ 'description' => $customEntity['title'] . ' custom group - extends ' . $customEntity['extends'],
+ ];
+ if (!empty($customEntity['help_pre'])) {
+ $entities[$fieldName]['comment'] = $this->plainTextify($customEntity['help_pre']);
+ }
+ if (!empty($customEntity['help_post'])) {
+ $pre = empty($entities[$fieldName]['comment']) ? '' : $entities[$fieldName]['comment'] . "\n\n";
+ $entities[$fieldName]['comment'] = $pre . $this->plainTextify($customEntity['help_post']);
+ }
+ }
+ }
+
+ /**
+ * Convert html to plain text.
+ *
+ * @param $input
+ * @return mixed
+ */
+ private function plainTextify($input) {
+ return html_entity_decode(strip_tags($input), ENT_QUOTES | ENT_HTML5, 'UTF-8');
+ }
+
+ /**
+ * Add info from code docblock.
+ *
+ * @param $entity
+ */
+ private function addDocs(&$entity) {
+ $reflection = new \ReflectionClass("\\Civi\\Api4\\" . $entity['name']);
+ $entity += ReflectionUtils::getCodeDocs($reflection);
+ unset($entity['package'], $entity['method']);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Entity/GetLinks.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Entity/GetLinks.php
new file mode 100644
index 00000000..ee274bf9
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Entity/GetLinks.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace Civi\Api4\Action\Entity;
+
+use \CRM_Core_DAO_AllCoreTables as AllCoreTables;
+
+/**
+ * Get a list of FK links between entities
+ */
+class GetLinks extends \Civi\Api4\Generic\BasicGetAction {
+
+ public function getRecords() {
+ $result = [];
+ /** @var \Civi\Api4\Service\Schema\SchemaMap $schema */
+ $schema = \Civi::container()->get('schema_map');
+ foreach ($schema->getTables() as $table) {
+ $entity = AllCoreTables::getBriefName(AllCoreTables::getClassForTable($table->getName()));
+ // Since this is an api function, exclude tables that don't have an api
+ if (class_exists('\Civi\Api4\\' . $entity)) {
+ $item = [
+ 'entity' => $entity,
+ 'table' => $table->getName(),
+ 'links' => [],
+ ];
+ foreach ($table->getTableLinks() as $link) {
+ $link = $link->toArray();
+ $link['entity'] = AllCoreTables::getBriefName(AllCoreTables::getClassForTable($link['targetTable']));
+ $item['links'][] = $link;
+ }
+ $result[] = $item;
+ }
+ }
+ return $result;
+ }
+
+ public function fields() {
+ return [
+ [
+ 'name' => 'entity',
+ ],
+ [
+ 'name' => 'table',
+ ],
+ [
+ 'name' => 'links',
+ 'data_type' => 'Array',
+ ],
+ ];
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/GetActions.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/GetActions.php
new file mode 100644
index 00000000..1dc4ebbb
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/GetActions.php
@@ -0,0 +1,101 @@
+<?php
+
+namespace Civi\Api4\Action;
+
+use Civi\API\Exception\NotImplementedException;
+use Civi\Api4\Generic\BasicGetAction;
+use Civi\Api4\Utils\ActionUtil;
+use Civi\Api4\Utils\ReflectionUtils;
+
+/**
+ * Get actions for an entity with a list of accepted params
+ */
+class GetActions extends BasicGetAction {
+
+ private $_actions = [];
+
+ private $_actionsToGet;
+
+ protected function getRecords() {
+ $this->_actionsToGet = $this->_itemsToGet('name');
+
+ $entityReflection = new \ReflectionClass('\Civi\Api4\\' . $this->_entityName);
+ foreach ($entityReflection->getMethods(\ReflectionMethod::IS_STATIC | \ReflectionMethod::IS_PUBLIC) as $method) {
+ $actionName = $method->getName();
+ if ($actionName != 'permissions' && $actionName[0] != '_') {
+ $this->loadAction($actionName);
+ }
+ }
+ if (!$this->_actionsToGet || count($this->_actionsToGet) > count($this->_actions)) {
+ $includePaths = array_unique(explode(PATH_SEPARATOR, get_include_path()));
+ // Search entity-specific actions (including those provided by extensions)
+ foreach ($includePaths as $path) {
+ $dir = \CRM_Utils_File::addTrailingSlash($path) . 'Civi/Api4/Action/' . $this->_entityName;
+ $this->scanDir($dir);
+ }
+ }
+ ksort($this->_actions);
+ return $this->_actions;
+ }
+
+ /**
+ * @param $dir
+ */
+ private function scanDir($dir) {
+ if (is_dir($dir)) {
+ foreach (glob("$dir/*.php") as $file) {
+ $matches = [];
+ preg_match('/(\w*).php/', $file, $matches);
+ $actionName = array_pop($matches);
+ $this->loadAction(lcfirst($actionName));
+ }
+ }
+ }
+
+ /**
+ * @param $actionName
+ */
+ private function loadAction($actionName) {
+ try {
+ if (!isset($this->_actions[$actionName]) && (!$this->_actionsToGet || in_array($actionName, $this->_actionsToGet))) {
+ $action = ActionUtil::getAction($this->getEntityName(), $actionName);
+ if (is_object($action)) {
+ $this->_actions[$actionName] = ['name' => $actionName];
+ if ($this->_isFieldSelected('description') || $this->_isFieldSelected('comment')) {
+ $actionReflection = new \ReflectionClass($action);
+ $actionInfo = ReflectionUtils::getCodeDocs($actionReflection);
+ unset($actionInfo['method']);
+ $this->_actions[$actionName] += $actionInfo;
+ }
+ if ($this->_isFieldSelected('params')) {
+ $this->_actions[$actionName]['params'] = $action->getParamInfo();
+ }
+ }
+ }
+ }
+ catch (NotImplementedException $e) {
+ }
+ }
+
+ public function fields() {
+ return [
+ [
+ 'name' => 'name',
+ 'data_type' => 'String',
+ ],
+ [
+ 'name' => 'description',
+ 'data_type' => 'String',
+ ],
+ [
+ 'name' => 'comment',
+ 'data_type' => 'String',
+ ],
+ [
+ 'name' => 'params',
+ 'data_type' => 'Array',
+ ],
+ ];
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/GroupContact/Create.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/GroupContact/Create.php
new file mode 100644
index 00000000..44e61f2e
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/GroupContact/Create.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Civi\Api4\Action\GroupContact;
+
+use Civi\Api4\Generic\Result;
+
+/**
+ * @inheritDoc
+ *
+ * @method $this setMethod(string $method) Indicate who added/removed the group.
+ * @method $this setTracking(string $tracking) Specify ip address or other tracking info.
+ */
+class Create extends \Civi\Api4\Generic\DAOCreateAction {
+
+ /**
+ * String to indicate who added/removed the group.
+ *
+ * @var string
+ */
+ protected $method = 'API';
+
+ /**
+ * IP address or other tracking info about who performed this group subscription.
+ *
+ * @var string
+ */
+ protected $tracking = '';
+
+ /**
+ * @inheritDoc
+ */
+ public function _run(Result $result) {
+ $this->values['method'] = $this->method;
+ $this->values['tracking'] = $this->tracking;
+ parent::_run($result);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/GroupContact/Update.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/GroupContact/Update.php
new file mode 100644
index 00000000..edb8a902
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/GroupContact/Update.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Civi\Api4\Action\GroupContact;
+
+use Civi\Api4\Generic\Result;
+
+/**
+ * @inheritDoc
+ *
+ * @method $this setMethod(string $method) Indicate who added/removed the group.
+ * @method $this setTracking(string $tracking) Specify ip address or other tracking info.
+ */
+class Update extends \Civi\Api4\Generic\DAOUpdateAction {
+
+ /**
+ * String to indicate who added/removed the group.
+ *
+ * @var string
+ */
+ protected $method = 'API';
+
+ /**
+ * IP address or other tracking info about who performed this group subscription.
+ *
+ * @var string
+ */
+ protected $tracking = '';
+
+ /**
+ * @inheritDoc
+ */
+ public function _run(Result $result) {
+ $this->values['method'] = $this->method;
+ $this->values['tracking'] = $this->tracking;
+ parent::_run($result);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Navigation/Get.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Navigation/Get.php
new file mode 100644
index 00000000..dcecc06a
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Navigation/Get.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Civi\Api4\Action\Navigation;
+
+/**
+ * @inheritDoc
+ *
+ * Fetch items from the navigation menu. By default this will fetch items from the current domain.
+ */
+class Get extends \Civi\Api4\Generic\DAOGetAction {
+
+ /**
+ * @inheritDoc
+ */
+ protected $where = [
+ ['domain_id', '=', 'current_domain'],
+ ];
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Participant/Get.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Participant/Get.php
new file mode 100644
index 00000000..c5efec93
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Action/Participant/Get.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Civi\Api4\Action\Participant;
+
+/**
+ * @inheritDoc
+ */
+class Get extends \Civi\Api4\Generic\DAOGetAction {
+
+ /**
+ * @inheritDoc
+ * $example->addWhere('contact_id.contact_type', 'IN', array('Individual', 'Household'))
+ */
+ protected $where = [
+ ['is_test', '=', 0],
+ ];
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/ActionSchedule.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/ActionSchedule.php
new file mode 100644
index 00000000..a8235f64
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/ActionSchedule.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Civi\Api4;
+
+/**
+ * ActionSchedule Entity.
+ *
+ * This entity exposes CiviCRM schedule reminders, which allows us to send messages (through email or SMS)
+ * to contacts when certain criteria are met. Using this API you can create schedule reminder for
+ * supported entities like Contact, Activity, Event, Membership or Contribution.
+ *
+ * Creating a new ActionSchedule requires at minimum a title, mapping_id and entity_value.
+ *
+ * @package Civi\Api4
+ */
+class ActionSchedule extends Generic\DAOEntity {
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Activity.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Activity.php
new file mode 100644
index 00000000..23ee4c15
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Activity.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace Civi\Api4;
+
+/**
+ * Activity entity.
+ *
+ * This entity adds record of any scheduled or completed interaction with one or more contacts.
+ * Each activity record is tightly linked to other CiviCRM constituents. With this API you can manually
+ * create an activity of desired type for your organisation or any other contact.
+ *
+ * Creating a new Activity requires at minimum a activity_type_id, entity ID and object_table
+ *
+ * An activity is a record of some type of interaction with one or more contacts.
+ *
+ * @package Civi\Api4
+ */
+class Activity extends Generic\DAOEntity {
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/ActivityContact.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/ActivityContact.php
new file mode 100644
index 00000000..7ef438a5
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/ActivityContact.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Civi\Api4;
+
+/**
+ * ActivityContact Entity.
+ *
+ * This entity adds a record which relate a contact to activity.
+ *
+ * Creating a new ActivityContact requires at minimum a contact_id and activity_id.
+ *
+ * @package Civi\Api4
+ */
+class ActivityContact extends Generic\DAOEntity {
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Address.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Address.php
new file mode 100644
index 00000000..e52b6283
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Address.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace Civi\Api4;
+
+/**
+ * Address Entity.
+ *
+ * This entity holds the address informatiom of a contact. Each contact may hold
+ * one or more addresses but must have different location types respectively.
+ *
+ * Creating a new address requires at minimum a contact's ID and location type ID
+ * and other attributes (although optional) like street address, city, country etc.
+ *
+ * @package Civi\Api4
+ */
+class Address extends Generic\DAOEntity {
+
+ /**
+ * @return \Civi\Api4\Action\Address\Create
+ */
+ public static function create() {
+ return new \Civi\Api4\Action\Address\Create(__CLASS__, __FUNCTION__);
+ }
+
+ /**
+ * @return \Civi\Api4\Action\Address\Update
+ */
+ public static function update() {
+ return new \Civi\Api4\Action\Address\Update(__CLASS__, __FUNCTION__);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Contact.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Contact.php
new file mode 100644
index 00000000..cca8c335
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Contact.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Civi\Api4;
+
+/**
+ * Contacts - Individuals, Organizations, Households.
+ *
+ * This is the central entity in the CiviCRM database, and links to
+ * many other entities (Email, Phone, Participant, etc.).
+ *
+ * Creating a new contact requires at minimum a name or email address.
+ *
+ * @package Civi\Api4
+ */
+class Contact extends Generic\DAOEntity {
+
+ /**
+ * @return Action\Contact\Create
+ */
+ public static function create() {
+ return new Action\Contact\Create(__CLASS__, __FUNCTION__);
+ }
+
+ /**
+ * @return \Civi\Api4\Generic\DAOUpdateAction
+ */
+ public static function update() {
+ // For some reason the contact bao requires this for updating
+ return new Generic\DAOUpdateAction(__CLASS__, __FUNCTION__, ['id', 'contact_type']);
+ }
+
+ /**
+ * @return \Civi\Api4\Action\Contact\GetFields
+ */
+ public static function getFields() {
+ return new Action\Contact\GetFields(__CLASS__, __FUNCTION__);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/ContactType.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/ContactType.php
new file mode 100644
index 00000000..8ce6c8dd
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/ContactType.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Civi\Api4;
+
+/**
+ * ContactType entity.
+ *
+ * With this entity you can create or update any new or existing Contact type or a sub type
+ * In case of updating existing ContactType, id of that particular ContactType must
+ * be in $params array.
+ *
+ * Creating a new contact type requires at minimum a label and parent_id.
+ *
+ * @package Civi\Api4
+ */
+class ContactType extends Generic\DAOEntity {
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Contribution.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Contribution.php
new file mode 100644
index 00000000..903c4753
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Contribution.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Civi\Api4;
+
+/**
+ * Contribution entity.
+ *
+ * @package Civi\Api4
+ */
+class Contribution extends Generic\DAOEntity {
+
+ /**
+ * @return \Civi\Api4\Action\Contribution\Create
+ */
+ public static function create() {
+ return new \Civi\Api4\Action\Contribution\Create(__CLASS__, __FUNCTION__);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/CustomField.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/CustomField.php
new file mode 100644
index 00000000..245c9f4c
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/CustomField.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace Civi\Api4;
+
+/**
+ * CustomField entity.
+ *
+ * @package Civi\Api4
+ */
+class CustomField extends Generic\DAOEntity {
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/CustomGroup.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/CustomGroup.php
new file mode 100644
index 00000000..780ccd2f
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/CustomGroup.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace Civi\Api4;
+
+/**
+ * CustomGroup entity.
+ *
+ * @package Civi\Api4
+ */
+class CustomGroup extends Generic\DAOEntity {
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/CustomValue.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/CustomValue.php
new file mode 100644
index 00000000..9cf4da43
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/CustomValue.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace Civi\Api4;
+
+/**
+ * CustomGroup entity.
+ *
+ * @package Civi\Api4
+ */
+class CustomValue extends Generic\AbstractEntity {
+
+ /**
+ * @param string $customGroup
+ * @return Action\CustomValue\Get
+ */
+ public static function get($customGroup) {
+ return new Action\CustomValue\Get($customGroup, __FUNCTION__);
+ }
+
+ /**
+ * @param string $customGroup
+ * @return Action\CustomValue\GetFields
+ */
+ public static function getFields($customGroup = NULL) {
+ return new Action\CustomValue\GetFields($customGroup, __FUNCTION__);
+ }
+
+ /**
+ * @param string $customGroup
+ * @return Action\CustomValue\Create
+ */
+ public static function create($customGroup) {
+ return new Action\CustomValue\Create($customGroup, __FUNCTION__);
+ }
+
+ /**
+ * @param string $customGroup
+ * @return Action\CustomValue\Update
+ */
+ public static function update($customGroup) {
+ return new Action\CustomValue\Update($customGroup, __FUNCTION__);
+ }
+
+ /**
+ * @param string $customGroup
+ * @return Action\CustomValue\Delete
+ */
+ public static function delete($customGroup) {
+ return new Action\CustomValue\Delete($customGroup, __FUNCTION__);
+ }
+
+ /**
+ * @param string $customGroup
+ * @return Action\CustomValue\Replace
+ */
+ public static function replace($customGroup) {
+ return new Action\CustomValue\Replace($customGroup, __FUNCTION__);
+ }
+
+ /**
+ * @param string $customGroup
+ * @return Action\CustomValue\GetActions
+ */
+ public static function getActions($customGroup = NULL) {
+ return new Action\CustomValue\GetActions($customGroup, __FUNCTION__);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function permissions() {
+ $entity = 'contact';
+ $permissions = \CRM_Core_Permission::getEntityActionPermissions();
+
+ // Merge permissions for this entity with the defaults
+ return \CRM_Utils_Array::value($entity, $permissions, []) + $permissions['default'];
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Email.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Email.php
new file mode 100644
index 00000000..cb743e3a
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Email.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Civi\Api4;
+
+/**
+ * Email entity.
+ *
+ * This entity allows user to add, update, retrieve or delete emails address(es) of a contact.
+ *
+ * Creating a new email address requires at minimum a contact's ID and email
+ *
+ * @package Civi\Api4
+ */
+class Email extends Generic\DAOEntity {
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Entity.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Entity.php
new file mode 100644
index 00000000..bc759d05
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Entity.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace Civi\Api4;
+
+/**
+ * Retrieves information about all Api4 entities.
+ *
+ * @package Civi\Api4
+ */
+class Entity extends Generic\AbstractEntity {
+
+ /**
+ * @return Action\Entity\Get
+ */
+ public static function get() {
+ return new Action\Entity\Get('Entity', __FUNCTION__);
+ }
+
+ /**
+ * @return \Civi\Api4\Generic\BasicGetFieldsAction
+ */
+ public static function getFields() {
+ return new \Civi\Api4\Generic\BasicGetFieldsAction('Entity', __FUNCTION__, function() {
+ return [
+ ['name' => 'name'],
+ ['name' => 'description'],
+ ['name' => 'comment'],
+ ];
+ });
+ }
+
+ /**
+ * @return Action\Entity\GetLinks
+ */
+ public static function getLinks() {
+ return new Action\Entity\GetLinks('Entity', __FUNCTION__);
+ }
+
+ /**
+ * @return array
+ */
+ public static function permissions() {
+ return [
+ 'default' => ['access CiviCRM']
+ ];
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event.php
new file mode 100644
index 00000000..1a07cc3d
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace Civi\Api4;
+
+/**
+ * Event entity.
+ *
+ * @package Civi\Api4
+ */
+class Event extends Generic\DAOEntity {
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Events.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Events.php
new file mode 100644
index 00000000..0bf4a993
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Events.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace Civi\Api4\Event;
+
+class Events {
+
+ /**
+ * Prepare the specification for a request. Fired from within a request to
+ * get fields.
+ *
+ * @see GetSpecEvent
+ */
+ const GET_SPEC = 'civi.api.get_spec';
+
+ /**
+ * Build the database schema, allow adding of custom joins and tables.
+ */
+ const SCHEMA_MAP_BUILD = 'api.schema_map.build';
+
+ /**
+ * Alter query results of APIv4 select query
+ */
+ const POST_SELECT_QUERY = 'api.select_query.post';
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/GetSpecEvent.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/GetSpecEvent.php
new file mode 100644
index 00000000..cc247853
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/GetSpecEvent.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace Civi\Api4\Event;
+
+use Civi\Api4\Generic\AbstractAction;
+use Symfony\Component\EventDispatcher\Event as BaseEvent;
+
+class GetSpecEvent extends BaseEvent {
+ /**
+ * @var AbstractAction
+ */
+ protected $request;
+
+ /**
+ * @param AbstractAction $request
+ */
+ public function __construct(AbstractAction $request) {
+ $this->request = $request;
+ }
+
+ /**
+ * @return AbstractAction
+ */
+ public function getRequest() {
+ return $this->request;
+ }
+
+ /**
+ * @param $request
+ */
+ public function setRequest(AbstractAction $request) {
+ $this->request = $request;
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/PostSelectQueryEvent.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/PostSelectQueryEvent.php
new file mode 100644
index 00000000..4489033b
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/PostSelectQueryEvent.php
@@ -0,0 +1,64 @@
+<?php
+
+namespace Civi\Api4\Event;
+
+use Civi\Api4\Query\Api4SelectQuery;
+use Symfony\Component\EventDispatcher\Event;
+
+class PostSelectQueryEvent extends Event {
+
+ /**
+ * @var array
+ */
+ protected $results;
+
+ /**
+ * @var Api4SelectQuery
+ */
+ protected $query;
+
+ /**
+ * PostSelectQueryEvent constructor.
+ * @param array $results
+ * @param Api4SelectQuery $query
+ */
+ public function __construct(array $results, Api4SelectQuery $query) {
+ $this->results = $results;
+ $this->query = $query;
+ }
+
+ /**
+ * @return array
+ */
+ public function getResults() {
+ return $this->results;
+ }
+
+ /**
+ * @param array $results
+ * @return $this
+ */
+ public function setResults($results) {
+ $this->results = $results;
+
+ return $this;
+ }
+
+ /**
+ * @return Api4SelectQuery
+ */
+ public function getQuery() {
+ return $this->query;
+ }
+
+ /**
+ * @param Api4SelectQuery $query
+ * @return $this
+ */
+ public function setQuery($query) {
+ $this->query = $query;
+
+ return $this;
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/SchemaMapBuildEvent.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/SchemaMapBuildEvent.php
new file mode 100644
index 00000000..f79f6b4b
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/SchemaMapBuildEvent.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Civi\Api4\Event;
+
+use Civi\Api4\Service\Schema\SchemaMap;
+use Symfony\Component\EventDispatcher\Event as BaseEvent;
+
+class SchemaMapBuildEvent extends BaseEvent {
+ /**
+ * @var SchemaMap
+ */
+ protected $schemaMap;
+
+ /**
+ * @param SchemaMap $schemaMap
+ */
+ public function __construct(SchemaMap $schemaMap) {
+ $this->schemaMap = $schemaMap;
+ }
+
+ /**
+ * @return SchemaMap
+ */
+ public function getSchemaMap() {
+ return $this->schemaMap;
+ }
+
+ /**
+ * @param SchemaMap $schemaMap
+ *
+ * @return $this
+ */
+ public function setSchemaMap($schemaMap) {
+ $this->schemaMap = $schemaMap;
+
+ return $this;
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/AbstractPrepareSubscriber.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/AbstractPrepareSubscriber.php
new file mode 100644
index 00000000..d4725e0d
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/AbstractPrepareSubscriber.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace Civi\Api4\Event\Subscriber;
+
+use Civi\API\Event\PrepareEvent;
+use Civi\API\Events;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+abstract class AbstractPrepareSubscriber implements EventSubscriberInterface {
+ /**
+ * @return array
+ */
+ public static function getSubscribedEvents() {
+ return [
+ Events::PREPARE => 'onApiPrepare',
+ ];
+ }
+
+ /**
+ * @param PrepareEvent $event
+ */
+ abstract public function onApiPrepare(PrepareEvent $event);
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/ActivityPreCreationSubscriber.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/ActivityPreCreationSubscriber.php
new file mode 100644
index 00000000..1938ce0b
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/ActivityPreCreationSubscriber.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace Civi\Api4\Event\Subscriber;
+
+use Civi\Api4\Generic\DAOCreateAction;
+use Civi\Api4\OptionValue;
+
+class ActivityPreCreationSubscriber extends PreCreationSubscriber {
+ /**
+ * @param DAOCreateAction $request
+ * @throws \API_Exception
+ * @throws \Exception
+ */
+ protected function modify(DAOCreateAction $request) {
+ $activityType = $request->getValue('activity_type');
+ if ($activityType) {
+ $result = OptionValue::get()
+ ->setCheckPermissions(FALSE)
+ ->addWhere('name', '=', $activityType)
+ ->addWhere('option_group.name', '=', 'activity_type')
+ ->execute();
+
+ if ($result->count() !== 1) {
+ throw new \Exception('Activity type must match a *single* type');
+ }
+
+ $request->addValue('activity_type_id', $result->first()['value']);
+ }
+ }
+
+ /**
+ * @param DAOCreateAction $request
+ *
+ * @return bool
+ */
+ protected function applies(DAOCreateAction $request) {
+ return $request->getEntityName() === 'Activity';
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/ActivitySchemaMapSubscriber.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/ActivitySchemaMapSubscriber.php
new file mode 100644
index 00000000..52d58397
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/ActivitySchemaMapSubscriber.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace Civi\Api4\Event\Subscriber;
+
+use Civi\Api4\Event\Events;
+use Civi\Api4\Event\SchemaMapBuildEvent;
+use Civi\Api4\Service\Schema\Joinable\ActivityToActivityContactAssigneesJoinable;
+use Civi\Api4\Service\Schema\Joinable\BridgeJoinable;
+use Civi\Api4\Service\Schema\Joinable\Joinable;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use \CRM_Utils_String as StringHelper;
+
+class ActivitySchemaMapSubscriber implements EventSubscriberInterface {
+ /**
+ * @return array
+ */
+ public static function getSubscribedEvents() {
+ return [
+ Events::SCHEMA_MAP_BUILD => 'onSchemaBuild',
+ ];
+ }
+
+ /**
+ * @param SchemaMapBuildEvent $event
+ */
+ public function onSchemaBuild(SchemaMapBuildEvent $event) {
+ $schema = $event->getSchemaMap();
+ $table = $schema->getTableByName('civicrm_activity');
+
+ $middleAlias = StringHelper::createRandom(10, implode(range('a', 'z')));
+ $middleLink = new ActivityToActivityContactAssigneesJoinable($middleAlias);
+
+ $bridge = new BridgeJoinable('civicrm_contact', 'id', 'assignees', $middleLink);
+ $bridge->setBaseTable('civicrm_activity_contact');
+ $bridge->setJoinType(Joinable::JOIN_TYPE_ONE_TO_MANY);
+
+ $table->addTableLink('contact_id', $bridge);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/ContactSchemaMapSubscriber.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/ContactSchemaMapSubscriber.php
new file mode 100644
index 00000000..edea3de6
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/ContactSchemaMapSubscriber.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace Civi\Api4\Event\Subscriber;
+
+use Civi\Api4\Event\Events;
+use Civi\Api4\Event\SchemaMapBuildEvent;
+use Civi\Api4\Service\Schema\Joinable\Joinable;
+use Civi\Api4\Service\Schema\Table;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+class ContactSchemaMapSubscriber implements EventSubscriberInterface {
+ /**
+ * @return array
+ */
+ public static function getSubscribedEvents() {
+ return [
+ Events::SCHEMA_MAP_BUILD => 'onSchemaBuild',
+ ];
+ }
+
+ /**
+ * @param SchemaMapBuildEvent $event
+ */
+ public function onSchemaBuild(SchemaMapBuildEvent $event) {
+ $schema = $event->getSchemaMap();
+ $table = $schema->getTableByName('civicrm_contact');
+ $this->addCreatedActivitiesLink($table);
+ $this->fixPreferredLanguageAlias($table);
+ }
+
+ /**
+ * @param Table $table
+ */
+ private function addCreatedActivitiesLink($table) {
+ $alias = 'created_activities';
+ $joinable = new Joinable('civicrm_activity_contact', 'contact_id', $alias);
+ $joinable->addCondition($alias . '.record_type_id = 1');
+ $joinable->setJoinType($joinable::JOIN_TYPE_ONE_TO_MANY);
+ $table->addTableLink('id', $joinable);
+ }
+
+ /**
+ * @param Table $table
+ */
+ private function fixPreferredLanguageAlias($table) {
+ foreach ($table->getExternalLinks() as $link) {
+ if ($link->getAlias() === 'languages') {
+ $link->setAlias('preferred_language');
+ return;
+ }
+ }
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/CustomFieldPreCreationSubscriber.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/CustomFieldPreCreationSubscriber.php
new file mode 100644
index 00000000..289e1057
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/CustomFieldPreCreationSubscriber.php
@@ -0,0 +1,91 @@
+<?php
+
+namespace Civi\Api4\Event\Subscriber;
+
+use Civi\Api4\Generic\DAOCreateAction;
+
+class CustomFieldPreCreationSubscriber extends PreCreationSubscriber {
+
+ const OPTION_TYPE_NEW = 1;
+ const OPTION_STATUS_ACTIVE = 1;
+
+ /**
+ * @param DAOCreateAction $request
+ */
+ public function modify(DAOCreateAction $request) {
+ $this->formatOptionParams($request);
+ $this->setDefaults($request);
+ }
+
+ /**
+ * @param DAOCreateAction $request
+ *
+ * @return bool
+ */
+ protected function applies(DAOCreateAction $request) {
+ return $request->getEntityName() === 'CustomField';
+ }
+
+ /**
+ * Sets defaults required for option group and value creation
+ * @see CRM_Core_BAO_CustomField::create()
+ *
+ * @param DAOCreateAction $request
+ */
+ protected function formatOptionParams(DAOCreateAction $request) {
+ $options = $request->getValue('options');
+
+ if (!is_array($options)) {
+ return;
+ }
+
+ $dataTypeKey = 'data_type';
+ $optionLabelKey = 'option_label';
+ $optionWeightKey = 'option_weight';
+ $optionStatusKey = 'option_status';
+ $optionValueKey = 'option_value';
+ $optionTypeKey = 'option_type';
+
+ $dataType = $request->getValue($dataTypeKey);
+ $optionLabel = $request->getValue($optionLabelKey);
+ $optionWeight = $request->getValue($optionWeightKey);
+ $optionStatus = $request->getValue($optionStatusKey);
+ $optionValue = $request->getValue($optionValueKey);
+ $optionType = $request->getValue($optionTypeKey);
+
+ if (!$optionType) {
+ $request->addValue($optionTypeKey, self::OPTION_TYPE_NEW);
+ }
+
+ if (!$dataType) {
+ $request->addValue($dataTypeKey, 'String');
+ }
+
+ if (!$optionLabel) {
+ $request->addValue($optionLabelKey, array_values($options));
+ }
+
+ if (!$optionValue) {
+ $request->addValue($optionValueKey, array_keys($options));
+ }
+
+ if (!$optionStatus) {
+ $statuses = array_fill(0, count($options), self::OPTION_STATUS_ACTIVE);
+ $request->addValue($optionStatusKey, $statuses);
+ }
+
+ if (!$optionWeight) {
+ $request->addValue($optionWeightKey, range(1, count($options)));
+ }
+ }
+
+ /**
+ * @param DAOCreateAction $request
+ */
+ private function setDefaults(DAOCreateAction $request) {
+ if (!$request->getValue('option_type')) {
+ $request->addValue('option_type', NULL);
+ }
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/CustomGroupPreCreationSubscriber.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/CustomGroupPreCreationSubscriber.php
new file mode 100644
index 00000000..70f6e426
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/CustomGroupPreCreationSubscriber.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace Civi\Api4\Event\Subscriber;
+
+use Civi\Api4\Generic\DAOCreateAction;
+
+class CustomGroupPreCreationSubscriber extends PreCreationSubscriber {
+ /**
+ * @param DAOCreateAction $request
+ */
+ protected function modify(DAOCreateAction $request) {
+ $extends = $request->getValue('extends');
+ $title = $request->getValue('title');
+ $name = $request->getValue('name');
+
+ if (is_string($extends)) {
+ $request->addValue('extends', [$extends]);
+ }
+
+ if (NULL === $title && $name) {
+ $request->addValue('title', $name);
+ }
+ }
+
+ protected function applies(DAOCreateAction $request) {
+ return $request->getEntityName() === 'CustomGroup';
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/OptionValuePreCreationSubscriber.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/OptionValuePreCreationSubscriber.php
new file mode 100644
index 00000000..3e671d61
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/OptionValuePreCreationSubscriber.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace Civi\Api4\Event\Subscriber;
+
+use Civi\Api4\Generic\DAOCreateAction;
+use Civi\Api4\OptionGroup;
+
+class OptionValuePreCreationSubscriber extends PreCreationSubscriber {
+
+ /**
+ * @param DAOCreateAction $request
+ */
+ protected function modify(DAOCreateAction $request) {
+ $this->setOptionGroupId($request);
+ }
+
+ /**
+ * @param DAOCreateAction $request
+ *
+ * @return bool
+ */
+ protected function applies(DAOCreateAction $request) {
+ return $request->getEntityName() === 'OptionValue';
+ }
+
+ /**
+ * @param DAOCreateAction $request
+ * @throws \API_Exception
+ * @throws \Exception
+ */
+ private function setOptionGroupId(DAOCreateAction $request) {
+ $optionGroupName = $request->getValue('option_group');
+ if (!$optionGroupName || $request->getValue('option_group_id')) {
+ return;
+ }
+
+ $optionGroup = OptionGroup::get()
+ ->setCheckPermissions(FALSE)
+ ->addSelect('id')
+ ->addWhere('name', '=', $optionGroupName)
+ ->execute();
+
+ if ($optionGroup->count() !== 1) {
+ throw new \Exception('Option group name must match only a single group');
+ }
+
+ $request->addValue('option_group_id', $optionGroup->first()['id']);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/PermissionCheckSubscriber.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/PermissionCheckSubscriber.php
new file mode 100644
index 00000000..62d542d0
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/PermissionCheckSubscriber.php
@@ -0,0 +1,65 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 4.7 |
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC (c) 2004-2017 |
+ +--------------------------------------------------------------------+
+ | This file is a part of CiviCRM. |
+ | |
+ | CiviCRM is free software; you can copy, modify, and distribute it |
+ | under the terms of the GNU Affero General Public License |
+ | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
+ | |
+ | CiviCRM is distributed in the hope that it will be useful, but |
+ | WITHOUT ANY WARRANTY; without even the implied warranty of |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
+ | See the GNU Affero General Public License for more details. |
+ | |
+ | You should have received a copy of the GNU Affero General Public |
+ | License and the CiviCRM Licensing Exception along |
+ | with this program; if not, contact CiviCRM LLC |
+ | at info[AT]civicrm[DOT]org. If you have questions about the |
+ | GNU Affero General Public License or the licensing of CiviCRM, |
+ | see the CiviCRM license FAQ at http://civicrm.org/licensing |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Api4\Event\Subscriber;
+
+use Civi\API\Events;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * For any API requests that correspond to a Doctrine entity
+ * ($apiRequest['doctrineClass']), check permissions specified in
+ * Civi\API\Annotation\Permission.
+ */
+class PermissionCheckSubscriber implements EventSubscriberInterface {
+ /**
+ * @return array
+ */
+ public static function getSubscribedEvents() {
+ return [
+ Events::AUTHORIZE => [
+ ['onApiAuthorize', Events::W_LATE],
+ ],
+ ];
+ }
+
+ /**
+ * @param \Civi\API\Event\AuthorizeEvent $event
+ * API authorization event.
+ */
+ public function onApiAuthorize(\Civi\API\Event\AuthorizeEvent $event) {
+ /* @var \Civi\Api4\Generic\AbstractAction $apiRequest */
+ $apiRequest = $event->getApiRequest();
+ if ($apiRequest['version'] == 4) {
+ if (!$apiRequest->getCheckPermissions() || $apiRequest->isAuthorized()) {
+ $event->authorize();
+ $event->stopPropagation();
+ }
+ }
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/PostSelectQuerySubscriber.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/PostSelectQuerySubscriber.php
new file mode 100644
index 00000000..ff7e6d20
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/PostSelectQuerySubscriber.php
@@ -0,0 +1,369 @@
+<?php
+
+namespace Civi\Api4\Event\Subscriber;
+
+use Civi\Api4\Event\Events;
+use Civi\Api4\Event\PostSelectQueryEvent;
+use Civi\Api4\Query\Api4SelectQuery;
+use Civi\Api4\Service\Schema\Joinable\Joinable;
+use Civi\Api4\Utils\ArrayInsertionUtil;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Changes the results of a select query, doing 1-n joins and unserializing data
+ */
+class PostSelectQuerySubscriber implements EventSubscriberInterface {
+
+ /**
+ * @inheritdoc
+ */
+ public static function getSubscribedEvents() {
+ return [
+ Events::POST_SELECT_QUERY => 'onPostQuery'
+ ];
+ }
+
+ /**
+ * @param PostSelectQueryEvent $event
+ */
+ public function onPostQuery(PostSelectQueryEvent $event) {
+ $results = $event->getResults();
+ $event->setResults($this->postRun($results, $event->getQuery()));
+ }
+
+ /**
+ * @param array $results
+ * @param Api4SelectQuery $query
+ *
+ * @return array
+ */
+ protected function postRun(array $results, Api4SelectQuery $query) {
+ if (empty($results)) {
+ return $results;
+ }
+
+ $fieldSpec = $query->getApiFieldSpec();
+ $this->unserializeFields($results, $query->getEntity(), $fieldSpec);
+
+ // Group the selects to avoid queries for each field
+ $groupedSelects = $this->getJoinedDotSelects($query);
+ foreach ($groupedSelects as $finalAlias => $selects) {
+ $joinPath = $this->getJoinPathInfo($selects[0], $query);
+ $selects = $this->formatSelects($finalAlias, $selects, $query);
+ $joinResults = $this->getJoinResults($query, $finalAlias, $selects);
+ $this->formatJoinResults($joinResults, $query, $finalAlias);
+
+ // Insert join results into original result
+ foreach ($results as &$primaryResult) {
+ $baseId = $primaryResult['id'];
+ $filtered = array_filter($joinResults, function ($res) use ($baseId) {
+ return ($res['_base_id'] === $baseId);
+ });
+ $filtered = array_values($filtered);
+ ArrayInsertionUtil::insert($primaryResult, $joinPath, $filtered);
+ }
+ }
+
+ return array_values($results);
+ }
+
+ /**
+ * @param array $joinResults
+ * @param Api4SelectQuery $query
+ * @param string $alias
+ */
+ private function formatJoinResults(&$joinResults, $query, $alias) {
+ $join = $query->getJoinedTable($alias);
+ $fields = [];
+ foreach ($join->getEntityFields() as $field) {
+ $name = explode('.', $field->getName());
+ $fields[array_pop($name)] = $field->toArray();
+ }
+ if ($fields) {
+ $this->unserializeFields($joinResults, NULL, $fields);
+ }
+ }
+
+ /**
+ * Unserialize values
+ *
+ * @param array $results
+ * @param string $entity
+ * @param array $fields
+ */
+ protected function unserializeFields(&$results, $entity, $fields = []) {
+ if (empty($fields)) {
+ $params = ['action' => 'get', 'includeCustom' => FALSE];
+ $fields = civicrm_api4($entity, 'getFields', $params)->indexBy('name');
+ }
+
+ foreach ($results as &$result) {
+ foreach ($result as $field => &$value) {
+ if (!empty($fields[$field]['serialize']) && is_string($value)) {
+ $serializationType = $fields[$field]['serialize'];
+ $value = \CRM_Core_DAO::unSerializeField($value, $serializationType);
+ }
+ }
+ }
+ }
+
+ /**
+ * @param Api4SelectQuery $query
+ *
+ * @return array
+ */
+ private function getJoinedDotSelects(Api4SelectQuery $query) {
+ // Remove selects that are not in a joined table
+ $fkAliases = $query->getFkSelectAliases();
+ $joinedDotSelects = array_filter(
+ $query->getSelect(),
+ function ($select) use ($fkAliases) {
+ return isset($fkAliases[$select]);
+ }
+ );
+
+ $selects = [];
+ // group related selects by alias so they can be executed in one query
+ foreach ($joinedDotSelects as $select) {
+ $parts = explode('.', $select);
+ $finalAlias = $parts[count($parts) - 2];
+ $selects[$finalAlias][] = $select;
+ }
+
+ // sort by depth, e.g. email selects should be done before email.location
+ uasort($selects, function ($a, $b) {
+ $aFirst = $a[0];
+ $bFirst = $b[0];
+ return substr_count($aFirst, '.') > substr_count($bFirst, '.');
+ });
+
+ return $selects;
+ }
+
+
+ /**
+ * @param array $selects
+ * @param $serializationType
+ * @param Api4SelectQuery $query
+ *
+ * @return array
+ */
+ private function getResultsForSerializedField(
+ array $selects,
+ $serializationType,
+ Api4SelectQuery $query
+ ) {
+ // Get the alias (Selects are grouped and all target the same table)
+ $sampleField = current($selects);
+ $alias = strstr($sampleField, '.', TRUE);
+
+ // Fetch the results with the serialized field
+ $selects['serialized'] = $query::MAIN_TABLE_ALIAS . '.' . $alias;
+ $serializedResults = $this->runWithNewSelects($selects, $query);
+ $newResults = [];
+
+ // Create a new results array, with a separate entry for each option value
+ foreach ($serializedResults as $result) {
+ $optionValues = \CRM_Core_DAO::unSerializeField(
+ $result['serialized'],
+ $serializationType
+ );
+ unset($result['serialized']);
+ foreach ($optionValues as $value) {
+ $newResults[] = array_merge($result, ['value' => $value]);
+ }
+ }
+
+ $optionValueValues = array_unique(array_column($newResults, 'value'));
+ $optionValues = $this->getOptionValuesFromValues(
+ $selects,
+ $query,
+ $optionValueValues
+ );
+ $valueField = $alias . '.value';
+
+ // Index by value
+ foreach ($optionValues as $key => $subResult) {
+ $optionValues[$subResult['value']] = $subResult;
+ unset($subResult[$key]);
+
+ // Exclude 'value' if not in original selects
+ if (!in_array($valueField, $selects)) {
+ unset($optionValues[$subResult['value']]['value']);
+ }
+ }
+
+ // Replace serialized with the sub-select results
+ foreach ($newResults as &$result) {
+ $result = array_merge($result, $optionValues[$result['value']]);
+ unset($result['value']);
+ }
+
+ return $newResults;
+ }
+
+
+ /**
+ * Prepares selects for the subquery to fetch join results
+ *
+ * @param string $alias
+ * @param array $selects
+ * @param Api4SelectQuery $query
+ *
+ * @return array
+ */
+ private function formatSelects($alias, $selects, Api4SelectQuery $query) {
+ $mainAlias = $query::MAIN_TABLE_ALIAS;
+ $selectFields = [];
+
+ foreach ($selects as $select) {
+ $selectAlias = $query->getFkSelectAliases()[$select];
+ $fieldAlias = substr($select, strrpos($select, '.') + 1);
+ $selectFields[$fieldAlias] = $selectAlias;
+ }
+
+ $firstSelect = $selects[0];
+ $pathParts = explode('.', $firstSelect);
+ $numParts = count($pathParts);
+ $parentAlias = $numParts > 2 ? $pathParts[$numParts - 3] : $mainAlias;
+
+ $selectFields['id'] = sprintf('%s.id', $alias);
+ $selectFields['_parent_id'] = $parentAlias . '.id';
+ $selectFields['_base_id'] = $mainAlias . '.id';
+
+ return $selectFields;
+ }
+
+ /**
+ * @param array $selects
+ * @param Api4SelectQuery $query
+ *
+ * @return array
+ */
+ private function runWithNewSelects(array $selects, Api4SelectQuery $query) {
+ $aliasedSelects = array_map(function ($field, $alias) {
+ return sprintf('%s as "%s"', $field, $alias);
+ }, $selects, array_keys($selects));
+
+ $newSelect = sprintf('SELECT DISTINCT %s', implode(", ", $aliasedSelects));
+ $sql = str_replace("\n", ' ', $query->getQuery()->toSQL());
+ $originalSelect = substr($sql, 0, strpos($sql, ' FROM'));
+ $sql = str_replace($originalSelect, $newSelect, $sql);
+
+ $relatedResults = [];
+ $resultDAO = \CRM_Core_DAO::executeQuery($sql);
+ while ($resultDAO->fetch()) {
+ $relatedResult = [];
+ foreach ($selects as $alias => $column) {
+ $returnName = $alias;
+ $alias = str_replace('.', '_', $alias);
+ if (property_exists($resultDAO, $alias)) {
+ $relatedResult[$returnName] = $resultDAO->$alias;
+ }
+ };
+ $relatedResults[] = $relatedResult;
+ }
+
+ return $relatedResults;
+ }
+
+ /**
+ * @param Api4SelectQuery $query
+ * @param $alias
+ * @param $selects
+ * @return array
+ */
+ protected function getJoinResults(Api4SelectQuery $query, $alias, $selects) {
+ $apiFieldSpec = $query->getApiFieldSpec();
+ if (!empty($apiFieldSpec[$alias]['serialize'])) {
+ $type = $apiFieldSpec[$alias]['serialize'];
+ $joinResults = $this->getResultsForSerializedField($selects, $type, $query);
+ }
+ else {
+ $joinResults = $this->runWithNewSelects($selects, $query);
+ }
+
+ // Remove results with no matching entries
+ $joinResults = array_filter($joinResults, function ($result) {
+ return !empty($result['id']);
+ });
+
+ return $joinResults;
+ }
+
+ /**
+ * Separates a string like 'emails.location_type.label' into an array, where
+ * each value in the array tells whether it is 1-1 or 1-n join type
+ *
+ * @param string $pathString
+ * Dot separated path to the field
+ * @param Api4SelectQuery $query
+ *
+ * @return array
+ * Index is table alias and value is boolean whether is 1-to-many join
+ */
+ private function getJoinPathInfo($pathString, $query) {
+ $pathParts = explode('.', $pathString);
+ array_pop($pathParts); // remove field
+ $path = [];
+ $isMultipleChecker = function($alias) use ($query) {
+ foreach ($query->getJoinedTables() as $table) {
+ if ($table->getAlias() === $alias) {
+ return $table->getJoinType() === Joinable::JOIN_TYPE_ONE_TO_MANY;
+ }
+ }
+ return FALSE;
+ };
+
+ foreach ($pathParts as $part) {
+ $path[$part] = $isMultipleChecker($part);
+ }
+
+ return $path;
+ }
+
+ /**
+ * Get all the option_value values required in the query
+ *
+ * @param array $selects
+ * @param Api4SelectQuery $query
+ * @param array $values
+ *
+ * @return array
+ */
+ private function getOptionValuesFromValues(
+ array $selects,
+ Api4SelectQuery $query,
+ array $values
+ ) {
+ $sampleField = current($selects);
+ $alias = strstr($sampleField, '.', TRUE);
+
+ // Get the option value table that was joined
+ $relatedTable = NULL;
+ foreach ($query->getJoinedTables() as $joinedTable) {
+ if ($joinedTable->getAlias() === $alias) {
+ $relatedTable = $joinedTable;
+ }
+ }
+
+ // We only want subselects related to the joined table
+ $subSelects = array_filter($selects, function ($select) use ($alias) {
+ return strpos($select, $alias) === 0;
+ });
+
+ // Fetch all related option_value entries
+ $valueField = $alias . '.value';
+ $subSelects[] = $valueField;
+ $tableName = $relatedTable->getTargetTable();
+ $conditions = $relatedTable->getExtraJoinConditions();
+ $conditions[] = $valueField . ' IN ("' . implode('", "', $values) . '")';
+ $subQuery = new \CRM_Utils_SQL_Select($tableName . ' ' . $alias);
+ $subQuery->where($conditions);
+ $subQuery->select($subSelects);
+ $subResults = $subQuery->execute()->fetchAll();
+
+ return $subResults;
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/PreCreationSubscriber.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/PreCreationSubscriber.php
new file mode 100644
index 00000000..6737a9f3
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/PreCreationSubscriber.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace Civi\Api4\Event\Subscriber;
+
+use Civi\API\Event\PrepareEvent;
+use Civi\Api4\Generic\DAOCreateAction;
+
+abstract class PreCreationSubscriber extends AbstractPrepareSubscriber {
+ /**
+ * @param PrepareEvent $event
+ */
+ public function onApiPrepare(PrepareEvent $event) {
+ $apiRequest = $event->getApiRequest();
+ if (!$apiRequest instanceof DAOCreateAction) {
+ return;
+ }
+
+ $this->addDefaultCreationValues($apiRequest);
+ if ($this->applies($apiRequest)) {
+ $this->modify($apiRequest);
+ }
+ }
+
+ /**
+ * Modify the request
+ *
+ * @param DAOCreateAction $request
+ *
+ * @return void
+ */
+ abstract protected function modify(DAOCreateAction $request);
+
+ /**
+ * Check if this subscriber should be applied to the request
+ *
+ * @param DAOCreateAction $request
+ *
+ * @return bool
+ */
+ abstract protected function applies(DAOCreateAction $request);
+
+ /**
+ * Sets default values common to all creation requests
+ *
+ * @param DAOCreateAction $request
+ */
+ protected function addDefaultCreationValues(DAOCreateAction $request) {
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/ValidateFieldsSubscriber.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/ValidateFieldsSubscriber.php
new file mode 100644
index 00000000..5e2c6f33
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Event/Subscriber/ValidateFieldsSubscriber.php
@@ -0,0 +1,97 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 4.7 |
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC (c) 2004-2017 |
+ +--------------------------------------------------------------------+
+ | This file is a part of CiviCRM. |
+ | |
+ | CiviCRM is free software; you can copy, modify, and distribute it |
+ | under the terms of the GNU Affero General Public License |
+ | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
+ | |
+ | CiviCRM is distributed in the hope that it will be useful, but |
+ | WITHOUT ANY WARRANTY; without even the implied warranty of |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
+ | See the GNU Affero General Public License for more details. |
+ | |
+ | You should have received a copy of the GNU Affero General Public |
+ | License and the CiviCRM Licensing Exception along |
+ | with this program; if not, contact CiviCRM LLC |
+ | at info[AT]civicrm[DOT]org. If you have questions about the |
+ | GNU Affero General Public License or the licensing of CiviCRM, |
+ | see the CiviCRM license FAQ at http://civicrm.org/licensing |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Api4\Event\Subscriber;
+
+use Civi\API\Event\PrepareEvent;
+
+/**
+ * Validate field inputs based on annotations in the action class
+ */
+class ValidateFieldsSubscriber extends AbstractPrepareSubscriber {
+
+ /**
+ * @param PrepareEvent $event
+ * @throws \Exception
+ */
+ public function onApiPrepare(PrepareEvent $event) {
+ /** @var \Civi\Api4\Generic\AbstractAction $apiRequest */
+ $apiRequest = $event->getApiRequest();
+ if (is_a($apiRequest, 'Civi\Api4\Generic\AbstractAction')) {
+ $paramInfo = $apiRequest->getParamInfo();
+ foreach ($paramInfo as $param => $info) {
+ $getParam = 'get' . ucfirst($param);
+ $value = $apiRequest->$getParam();
+ // Required fields
+ if (!empty($info['required']) && (!$value && $value !== 0 && $value !== '0')) {
+ throw new \API_Exception('Parameter "' . $param . '" is required.');
+ }
+ if (!empty($info['type']) && !self::checkType($value, $info['type'])) {
+ throw new \API_Exception('Parameter "' . $param . '" is not of the correct type. Expecting ' . implode(' or ', $info['type']) . '.');
+ }
+ }
+ }
+ }
+
+ /**
+ * Validate variable type on input
+ *
+ * @param $value
+ * @param $types
+ * @return bool
+ * @throws \API_Exception
+ */
+ public static function checkType($value, $types) {
+ if ($value === NULL) {
+ return TRUE;
+ }
+ foreach ($types as $type) {
+ switch ($type) {
+ case 'array':
+ case 'bool':
+ case 'string':
+ case 'object':
+ $tester = 'is_' . $type;
+ if ($tester($value)) {
+ return TRUE;
+ }
+ break;
+
+ case 'int':
+ if (\CRM_Utils_Rule::integer($value)) {
+ return TRUE;
+ }
+ break;
+
+ default:
+ throw new \API_Exception('Unknown parameter type: ' . $type);
+ }
+ }
+ return FALSE;
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/AbstractAction.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/AbstractAction.php
new file mode 100644
index 00000000..1b0786ea
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/AbstractAction.php
@@ -0,0 +1,391 @@
+<?php
+namespace Civi\Api4\Generic;
+
+use Civi\API\Exception\UnauthorizedException;
+use Civi\API\Kernel;
+use Civi\Api4\Generic\Result;
+use Civi\Api4\Utils\ReflectionUtils;
+
+/**
+ * Base class for all api actions.
+ *
+ * @method $this setCheckPermissions(bool $value)
+ * @method bool getCheckPermissions()
+ * @method $this setChain(array $chain)
+ * @method array getChain()
+ */
+abstract class AbstractAction implements \ArrayAccess {
+
+ /**
+ * Api version number; cannot be changed.
+ *
+ * @var int
+ */
+ protected $version = 4;
+
+ /**
+ * Additional api requests - will be called once per result.
+ *
+ * Keys can be any string - this will be the name given to the output.
+ *
+ * You can reference other values in the api results in this call by prefixing them with $
+ *
+ * For example, you could create a contact and place them in a group by chaining the
+ * GroupContact api to the Contact api:
+ *
+ * Contact::create()
+ * ->setValue('first_name', 'Hello')
+ * ->addChain('add_to_a_group', GroupContact::create()->setValue('contact_id', '$id')->setValue('group_id', 123))
+ *
+ * This will substitute the id of the newly created contact with $id.
+ *
+ * @var array
+ */
+ protected $chain = [];
+
+ /**
+ * Whether to enforce acl permissions based on the current user.
+ *
+ * Setting to FALSE will disable permission checks and override ACLs.
+ * In REST/javascript this cannot be disabled.
+ *
+ * @var bool
+ */
+ protected $checkPermissions = TRUE;
+
+ /* @var string */
+ protected $_entityName;
+
+ /* @var string */
+ protected $_actionName;
+
+ /* @var \ReflectionClass */
+ private $thisReflection;
+
+ /* @var array */
+ private $thisParamInfo;
+
+ /* @var array */
+ private $entityFields;
+
+ /* @var array */
+ private $thisArrayStorage;
+
+ /**
+ * Action constructor.
+ *
+ * @param string $entityName
+ * @param string $actionName
+ * @throws \API_Exception
+ */
+ public function __construct($entityName, $actionName) {
+ // If a namespaced class name is passed in
+ if (strpos($entityName, '\\') !== FALSE) {
+ $entityName = substr($entityName, strrpos($entityName, '\\') + 1);
+ }
+ $this->_entityName = $entityName;
+ $this->_actionName = $actionName;
+ }
+
+ /**
+ * Strictly enforce api parameters
+ * @param $name
+ * @param $value
+ * @throws \Exception
+ */
+ public function __set($name, $value) {
+ throw new \API_Exception('Unknown api parameter');
+ }
+
+ /**
+ * @param int $val
+ * @return $this
+ * @throws \API_Exception
+ */
+ public function setVersion($val) {
+ if ($val != 4) {
+ throw new \API_Exception('Cannot modify api version');
+ }
+ return $this;
+ }
+
+ /**
+ * @param string $name
+ * Unique name for this chained request
+ * @param \Civi\Api4\Generic\AbstractAction $apiRequest
+ * @param string|int $index
+ * Either a string for how the results should be indexed e.g. 'name'
+ * or the index of a single result to return e.g. 0 for the first result.
+ * @return $this
+ */
+ public function addChain($name, AbstractAction $apiRequest, $index = NULL) {
+ $this->chain[$name] = [$apiRequest->getEntityName(), $apiRequest->getActionName(), $apiRequest->getParams(), $index];
+ return $this;
+ }
+
+ /**
+ * Magic function to provide addFoo, getFoo and setFoo for params.
+ *
+ * @param $name
+ * @param $arguments
+ * @return static|mixed
+ * @throws \API_Exception
+ */
+ public function __call($name, $arguments) {
+ $param = lcfirst(substr($name, 3));
+ if (!$param || $param[0] == '_') {
+ throw new \API_Exception('Unknown api parameter: ' . $name);
+ }
+ $mode = substr($name, 0, 3);
+ // Handle plural when adding to e.g. $values with "addValue" method.
+ if ($mode == 'add' && $this->paramExists($param . 's')) {
+ $param .= 's';
+ }
+ if ($this->paramExists($param)) {
+ switch ($mode) {
+ case 'get':
+ return $this->$param;
+
+ case 'set':
+ $this->$param = $arguments[0];
+ return $this;
+
+ case 'add':
+ if (!is_array($this->$param)) {
+ throw new \API_Exception('Cannot add to non-array param');
+ }
+ if (array_key_exists(1, $arguments)) {
+ $this->{$param}[$arguments[0]] = $arguments[1];
+ }
+ else {
+ $this->{$param}[] = $arguments[0];
+ }
+ return $this;
+ }
+ }
+ throw new \API_Exception('Unknown api parameter: ' . $name);
+ }
+
+ /**
+ * Invoke api call.
+ *
+ * At this point all the params have been sent in and we initiate the api call & return the result.
+ * This is basically the outer wrapper for api v4.
+ *
+ * @return Result|array
+ * @throws UnauthorizedException
+ */
+ final public function execute() {
+ /** @var Kernel $kernel */
+ $kernel = \Civi::service('civi_api_kernel');
+
+ return $kernel->runRequest($this);
+ }
+
+ /**
+ * @param \Civi\Api4\Generic\Result $result
+ */
+ abstract public function _run(Result $result);
+
+ /**
+ * Serialize this object's params into an array
+ * @return array
+ */
+ public function getParams() {
+ $params = [];
+ foreach ($this->getReflection()->getProperties(\ReflectionProperty::IS_PROTECTED) as $property) {
+ $name = $property->getName();
+ // Skip variables starting with an underscore
+ if ($name[0] != '_') {
+ $params[$name] = $this->$name;
+ }
+ }
+ return $params;
+ }
+
+ /**
+ * Get documentation for one or all params
+ *
+ * @param string $param
+ * @return array of arrays [description, type, default, (comment)]
+ */
+ public function getParamInfo($param = NULL) {
+ if (!isset($this->thisParamInfo)) {
+ $defaults = $this->getParamDefaults();
+ foreach ($this->getReflection()->getProperties(\ReflectionProperty::IS_PROTECTED) as $property) {
+ $name = $property->getName();
+ if ($name != 'version' && $name[0] != '_') {
+ $this->thisParamInfo[$name] = ReflectionUtils::getCodeDocs($property, 'Property');
+ $this->thisParamInfo[$name]['default'] = $defaults[$name];
+ }
+ }
+ }
+ return $param ? $this->thisParamInfo[$param] : $this->thisParamInfo;
+ }
+
+ /**
+ * @return string
+ */
+ public function getEntityName() {
+ return $this->_entityName;
+ }
+
+ /**
+ *
+ * @return string
+ */
+ public function getActionName() {
+ return $this->_actionName;
+ }
+
+ /**
+ * @param string $param
+ * @return bool
+ */
+ protected function paramExists($param) {
+ return array_key_exists($param, $this->getParams());
+ }
+
+ /**
+ * @return array
+ */
+ protected function getParamDefaults() {
+ return array_intersect_key($this->getReflection()->getDefaultProperties(), $this->getParams());
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function offsetExists($offset) {
+ return in_array($offset, ['entity', 'action', 'params', 'version', 'check_permissions']) || isset($this->thisArrayStorage[$offset]);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function &offsetGet($offset) {
+ $val = NULL;
+ if (in_array($offset, ['entity', 'action'])) {
+ $offset .= 'Name';
+ }
+ if (in_array($offset, ['entityName', 'actionName', 'params', 'version'])) {
+ $getter = 'get' . ucfirst($offset);
+ $val = $this->$getter();
+ return $val;
+ }
+ if ($offset == 'check_permissions') {
+ return $this->checkPermissions;
+ }
+ if (isset ($this->thisArrayStorage[$offset])) {
+ return $this->thisArrayStorage[$offset];
+ }
+ return $val;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function offsetSet($offset, $value) {
+ if (in_array($offset, ['entity', 'action', 'entityName', 'actionName', 'params', 'version'])) {
+ throw new \API_Exception('Cannot modify api4 state via array access');
+ }
+ if ($offset == 'check_permissions') {
+ $this->setCheckPermissions($value);
+ }
+ else {
+ $this->thisArrayStorage[$offset] = $value;
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function offsetUnset($offset) {
+ if (in_array($offset, ['entity', 'action', 'entityName', 'actionName', 'params', 'check_permissions', 'version'])) {
+ throw new \API_Exception('Cannot modify api4 state via array access');
+ }
+ unset($this->thisArrayStorage[$offset]);
+ }
+
+ /**
+ * Is this api call permitted?
+ *
+ * This function is called if checkPermissions is set to true.
+ *
+ * @return bool
+ */
+ public function isAuthorized() {
+ $permissions = $this->getPermissions();
+ return \CRM_Core_Permission::check($permissions);
+ }
+
+ public function getPermissions() {
+ $permissions = call_user_func(["\\Civi\\Api4\\" . $this->_entityName, 'permissions']);
+ $permissions += [
+ // applies to getFields, getActions, etc.
+ 'meta' => ['access CiviCRM'],
+ // catch-all, applies to create, get, delete, etc.
+ 'default' => ['administer CiviCRM'],
+ ];
+ $action = $this->getActionName();
+ if (isset($permissions[$action])) {
+ return $permissions[$action];
+ }
+ elseif (in_array($action, ['getActions', 'getFields'])) {
+ return $permissions['meta'];
+ }
+ return $permissions['default'];
+ }
+
+ /**
+ * Returns schema fields for this entity & action.
+ *
+ * @return array
+ * @throws \API_Exception
+ */
+ protected function getEntityFields() {
+ if (!$this->entityFields) {
+ $params = [
+ 'action' => $this->getActionName(),
+ 'checkPermissions' => $this->checkPermissions,
+ ];
+ if (method_exists($this, 'getBaoName')) {
+ $params['includeCustom'] = FALSE;
+ }
+ $this->entityFields = (array) civicrm_api4($this->getEntityName(), 'getFields', $params, 'name');
+ }
+ return $this->entityFields;
+ }
+
+ /**
+ * @return \ReflectionClass
+ */
+ protected function getReflection() {
+ if (!$this->thisReflection) {
+ $this->thisReflection = new \ReflectionClass($this);
+ }
+ return $this->thisReflection;
+ }
+
+ /**
+ * This function is used internally for evaluating field annotations.
+ *
+ * It should never be passed raw user input.
+ *
+ * @param string $expr
+ * Conditional in php format e.g. $foo > $bar
+ * @param array $vars
+ * Variable name => value
+ * @return bool
+ * @throws \API_Exception
+ * @throws \Exception
+ */
+ protected function evaluateCondition($expr, $vars) {
+ if (strpos($expr, '}') !== FALSE || strpos($expr, '{') !== FALSE) {
+ throw new \API_Exception('Illegal character in expression');
+ }
+ $tpl = "{if $expr}1{else}0{/if}";
+ return (bool) trim(\CRM_Core_Smarty::singleton()->fetchWith('string:' . $tpl, $vars));
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/AbstractBatchAction.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/AbstractBatchAction.php
new file mode 100644
index 00000000..91c3b461
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/AbstractBatchAction.php
@@ -0,0 +1,64 @@
+<?php
+
+namespace Civi\Api4\Generic;
+
+/**
+ * Base class for all batch actions (Update, Delete, Replace).
+ *
+ * This differs from the AbstractQuery class in that the "Where" clause is required.
+ *
+ * @package Civi\Api4\Generic
+ */
+abstract class AbstractBatchAction extends AbstractQueryAction {
+
+ /**
+ * Criteria for selecting items to process.
+ *
+ * @required
+ * @var array
+ */
+ protected $where = [];
+
+ /**
+ * @var array
+ */
+ private $select;
+
+ /**
+ * QueryAction constructor.
+ * @param string $entityName
+ * @param string $actionName
+ * @param string|array $select
+ * One or more fields to load for each item.
+ */
+ public function __construct($entityName, $actionName, $select = 'id') {
+ $this->select = (array) $select;
+ parent::__construct($entityName, $actionName);
+ }
+
+ /**
+ * @return array
+ */
+ protected function getBatchRecords() {
+ $params = [
+ 'checkPermissions' => $this->checkPermissions,
+ 'where' => $this->where,
+ 'orderBy' => $this->orderBy,
+ 'limit' => $this->limit,
+ 'offset' => $this->offset,
+ ];
+ if (empty($this->reload)) {
+ $params['select'] = $this->select;
+ }
+
+ return (array) civicrm_api4($this->getEntityName(), 'get', $params);
+ }
+
+ /**
+ * @return array
+ */
+ protected function getSelect() {
+ return $this->select;
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/AbstractCreateAction.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/AbstractCreateAction.php
new file mode 100644
index 00000000..0cb55d10
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/AbstractCreateAction.php
@@ -0,0 +1,56 @@
+<?php
+
+namespace Civi\Api4\Generic;
+
+/**
+ * Base class for all "Create" api actions.
+ *
+ * @method $this setValues(array $values) Set all field values from an array of key => value pairs.
+ * @method $this addValue($field, $value) Set field value.
+ * @method array getValues() Get field values.
+ *
+ * @package Civi\Api4\Generic
+ */
+abstract class AbstractCreateAction extends AbstractAction {
+
+ /**
+ * Field values to set
+ *
+ * @var array
+ */
+ protected $values = [];
+
+ /**
+ * @param string $key
+ *
+ * @return mixed|null
+ */
+ public function getValue($key) {
+ return isset($this->values[$key]) ? $this->values[$key] : NULL;
+ }
+
+ /**
+ * @throws \API_Exception
+ */
+ protected function validateValues() {
+ $unmatched = [];
+ $params = NULL;
+ foreach ($this->getEntityFields() as $fieldName => $fieldInfo) {
+ if (!$this->getValue($fieldName)) {
+ if (!empty($fieldInfo['required']) && !isset($fieldInfo['default_value'])) {
+ $unmatched[] = $fieldName;
+ }
+ elseif (!empty($fieldInfo['required_if'])) {
+ $params = $params ?: $this->getParams();
+ if ($this->evaluateCondition($fieldInfo['required_if'], $params)) {
+ $unmatched[] = $fieldName;
+ }
+ }
+ }
+ }
+ if ($unmatched) {
+ throw new \API_Exception("Mandatory values missing from Api4 {$this->getEntityName()}::{$this->getActionName()}: '" . implode("', '", $unmatched) . "'", "mandatory_missing", ["fields" => $unmatched]);
+ }
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/AbstractEntity.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/AbstractEntity.php
new file mode 100644
index 00000000..e774baca
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/AbstractEntity.php
@@ -0,0 +1,88 @@
+<?php
+namespace Civi\Api4\Generic;
+
+use Civi\API\Exception\NotImplementedException;
+
+/**
+ * Base class for all api entities.
+ *
+ * When adding your own api from an extension, extend this class only
+ * if your entity does not have an associated DAO. Otherwise extend DAOEntity.
+ *
+ * The recommended way to create a non-DAO-based api is to extend this class
+ * and then add a getFields function and any other actions you wish, e.g.
+ * - a get() function which returns BasicGetAction using your custom getter callback
+ * - a create() function which returns BasicCreateAction using your custom setter callback
+ * - an update() function which returns BasicUpdateAction using your custom setter callback
+ * - a delete() function which returns BasicBatchAction using your custom delete callback
+ * - a replace() function which returns BasicReplaceAction (no callback needed but
+ * depends on the existence of get, create, update & delete actions)
+ *
+ * Note that you can use the same setter callback function for update as create -
+ * that function can distinguish between new & existing records by checking if the
+ * unique identifier has been set (identifier field defaults to "id" but you can change
+ * that when constructing BasicUpdateAction)
+ */
+abstract class AbstractEntity {
+
+ /**
+ * @return \Civi\Api4\Action\GetActions
+ */
+ public static function getActions() {
+ return new \Civi\Api4\Action\GetActions(self::getEntityName(), __FUNCTION__);
+ }
+
+ /**
+ * Should return \Civi\Api4\Generic\BasicGetFieldsAction
+ * @todo make this function abstract when we require php 7.
+ * @throws \Civi\API\Exception\NotImplementedException
+ */
+ public static function getFields() {
+ throw new NotImplementedException(self::getEntityName() . ' should implement getFields action.');
+ }
+
+ /**
+ * Returns a list of permissions needed to access the various actions in this api.
+ *
+ * @return array
+ */
+ public static function permissions() {
+ $permissions = \CRM_Core_Permission::getEntityActionPermissions();
+
+ // For legacy reasons the permissions are keyed by lowercase entity name
+ $lcentity = _civicrm_api_get_entity_name_from_camel(self::getEntityName());
+ // Merge permissions for this entity with the defaults
+ return \CRM_Utils_Array::value($lcentity, $permissions, []) + $permissions['default'];
+ }
+
+ /**
+ * Get entity name from called class
+ *
+ * @return string
+ */
+ protected static function getEntityName() {
+ return substr(static::class, strrpos(static::class, '\\') + 1);
+ }
+
+ /**
+ * Magic method to return the action object for an api.
+ *
+ * @param string $action
+ * @param null $args
+ * @return AbstractAction
+ * @throws NotImplementedException
+ */
+ public static function __callStatic($action, $args) {
+ $entity = self::getEntityName();
+ // Find class for this action
+ $entityAction = "\\Civi\\Api4\\Action\\$entity\\" . ucfirst($action);
+ if (class_exists($entityAction)) {
+ $actionObject = new $entityAction($entity, $action);
+ }
+ else {
+ throw new NotImplementedException("Api $entity $action version 4 does not exist.");
+ }
+ return $actionObject;
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/AbstractGetAction.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/AbstractGetAction.php
new file mode 100644
index 00000000..f8374cf4
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/AbstractGetAction.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Civi\Api4\Generic;
+
+/**
+ * Base class for all "Get" api actions.
+ *
+ * @package Civi\Api4\Generic
+ *
+ * @method $this addSelect(string $select)
+ * @method $this setSelect(array $selects)
+ * @method array getSelect()
+ */
+abstract class AbstractGetAction extends AbstractQueryAction {
+
+ /**
+ * Fields to return. Defaults to all non-custom fields.
+ *
+ * @var array
+ */
+ protected $select = [];
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/AbstractQueryAction.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/AbstractQueryAction.php
new file mode 100644
index 00000000..993383dc
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/AbstractQueryAction.php
@@ -0,0 +1,105 @@
+<?php
+
+namespace Civi\Api4\Generic;
+
+/**
+ * Base class for all actions that need to fetch records (Get, Update, Delete, etc)
+ *
+ * @package Civi\Api4\Generic
+ *
+ * @method $this setWhere(array $wheres)
+ * @method array getWhere()
+ * @method $this setOrderBy(array $order)
+ * @method array getOrderBy()
+ * @method $this setLimit(int $limit)
+ * @method int getLimit()
+ * @method $this setOffset(int $offset)
+ * @method int getOffset()
+ */
+abstract class AbstractQueryAction extends AbstractAction {
+
+ /**
+ * Criteria for selecting items.
+ *
+ * $example->addWhere('contact_type', 'IN', array('Individual', 'Household'))
+ *
+ * @var array
+ */
+ protected $where = [];
+
+ /**
+ * Array of field(s) to use in ordering the results
+ *
+ * Defaults to id ASC
+ *
+ * $example->addOrderBy('sort_name', 'ASC')
+ *
+ * @var array
+ */
+ protected $orderBy = [];
+
+ /**
+ * Maximum number of results to return.
+ *
+ * Defaults to unlimited.
+ *
+ * Note: the Api Explorer sets this to 25 by default to avoid timeouts.
+ * Change or remove this default for your application code.
+ *
+ * @var int
+ */
+ protected $limit = 0;
+
+ /**
+ * Zero-based index of first result to return.
+ *
+ * Defaults to "0" - first record.
+ *
+ * @var int
+ */
+ protected $offset = 0;
+
+ /**
+ * @param string $field
+ * @param string $op
+ * @param mixed $value
+ * @return $this
+ * @throws \API_Exception
+ */
+ public function addWhere($field, $op, $value = NULL) {
+ if (!in_array($op, \CRM_Core_DAO::acceptedSQLOperators())) {
+ throw new \API_Exception('Unsupported operator');
+ }
+ $this->where[] = [$field, $op, $value];
+ return $this;
+ }
+
+ /**
+ * Adds one or more AND/OR/NOT clause groups
+ *
+ * @param string $operator
+ * @param mixed $condition1 ... $conditionN
+ * Either a nested array of arguments, or a variable number of arguments passed to this function.
+ *
+ * @return $this
+ * @throws \API_Exception
+ */
+ public function addClause($operator, $condition1) {
+ if (!is_array($condition1[0])) {
+ $condition1 = array_slice(func_get_args(), 1);
+ }
+ $this->where[] = [$operator, $condition1];
+ return $this;
+ }
+
+ /**
+ * @param string $field
+ * @param string $direction
+ * @return $this
+ */
+ public function addOrderBy($field, $direction = 'ASC') {
+ $this->orderBy[$field] = $direction;
+ return $this;
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/AbstractUpdateAction.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/AbstractUpdateAction.php
new file mode 100644
index 00000000..a1904970
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/AbstractUpdateAction.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace Civi\Api4\Generic;
+
+/**
+ * Base class for all "Update" api actions
+ *
+ * @method $this setValues(array $values) Set all field values from an array of key => value pairs.
+ * @method $this addValue($field, $value) Set field value.
+ * @method array getValues() Get field values.
+ * @method $this setReload(bool $reload) Specify whether complete objects will be returned after saving.
+ * @method bool getReload()
+ *
+ * @package Civi\Api4\Generic
+ */
+abstract class AbstractUpdateAction extends AbstractBatchAction {
+
+ /**
+ * Field values to update.
+ *
+ * @required
+ * @var array
+ */
+ protected $values = [];
+
+ /**
+ * Reload objects after saving.
+ *
+ * Setting to TRUE will load complete records and return them as the api result.
+ * If FALSE the api usually returns only the fields specified to be updated.
+ *
+ * @var bool
+ */
+ protected $reload = FALSE;
+
+ /**
+ * @param string $key
+ *
+ * @return mixed|null
+ */
+ public function getValue($key) {
+ return isset($this->values[$key]) ? $this->values[$key] : NULL;
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/BasicBatchAction.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/BasicBatchAction.php
new file mode 100644
index 00000000..2f39cf23
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/BasicBatchAction.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace Civi\Api4\Generic;
+use Civi\API\Exception\NotImplementedException;
+
+/**
+ * Basic action for deleting or performing some other task with a set of records. Ex:
+ *
+ * $myAction = new BasicBatchAction('Entity', 'action', function($item) {
+ * // Do something with $item
+ * $return $item;
+ * });
+ *
+ * @package Civi\Api4\Generic
+ */
+class BasicBatchAction extends AbstractBatchAction {
+
+ /**
+ * @var callable
+ *
+ * Function(array $item, BasicBatchAction $thisAction) => array
+ */
+ private $doer;
+
+ /**
+ * BasicBatchAction constructor.
+ *
+ * @param string $entityName
+ * @param string $actionName
+ * @param string|array $select
+ * One or more fields to select from each matching item.
+ * @param callable $doer
+ * Function(array $item, BasicBatchAction $thisAction) => array
+ */
+ public function __construct($entityName, $actionName, $select = 'id', $doer = NULL) {
+ parent::__construct($entityName, $actionName, $select);
+ $this->doer = $doer;
+ }
+
+ /**
+ * We pass the doTask function an array representing one item to update.
+ * We expect to get the same format back.
+ *
+ * @param \Civi\Api4\Generic\Result $result
+ */
+ public function _run(Result $result) {
+ foreach ($this->getBatchRecords() as $item) {
+ $result[] = $this->doTask($item);
+ }
+ }
+
+ /**
+ * This Basic Batch class can be used in one of two ways:
+ *
+ * 1. Use this class directly by passing a callable ($doer) to the constructor.
+ * 2. Extend this class and override this function.
+ *
+ * Either way, this function should return an array with an output record
+ * for the item.
+ *
+ * @param array $item
+ * @return array
+ * @throws \Civi\API\Exception\NotImplementedException
+ */
+ protected function doTask($item) {
+ if (is_callable($this->doer)) {
+ return call_user_func($this->doer, $item, $this);
+ }
+ throw new NotImplementedException('Doer function not found for api4 ' . $this->getEntityName() . '::' . $this->getActionName());
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/BasicCreateAction.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/BasicCreateAction.php
new file mode 100644
index 00000000..ddd238f4
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/BasicCreateAction.php
@@ -0,0 +1,64 @@
+<?php
+
+namespace Civi\Api4\Generic;
+
+use Civi\API\Exception\NotImplementedException;
+
+/**
+ * Create a new object from supplied values.
+ *
+ * This function will create 1 new object. It cannot be used to update existing objects. Use the Update or Replace actions for that.
+ */
+class BasicCreateAction extends AbstractCreateAction {
+
+ /**
+ * @var callable
+ *
+ * Function(array $item, BasicCreateAction $thisAction) => array
+ */
+ private $setter;
+
+ /**
+ * Basic Create constructor.
+ *
+ * @param string $entityName
+ * @param string $actionName
+ * @param callable $setter
+ * Function(array $item, BasicCreateAction $thisAction) => array
+ */
+ public function __construct($entityName, $actionName, $setter = NULL) {
+ parent::__construct($entityName, $actionName);
+ $this->setter = $setter;
+ }
+
+ /**
+ * We pass the writeRecord function an array representing one item to write.
+ * We expect to get the same format back.
+ *
+ * @param \Civi\Api4\Generic\Result $result
+ */
+ public function _run(Result $result) {
+ $this->validateValues();
+ $result->exchangeArray([$this->writeRecord($this->values)]);
+ }
+
+ /**
+ * This Basic Create class can be used in one of two ways:
+ *
+ * 1. Use this class directly by passing a callable ($setter) to the constructor.
+ * 2. Extend this class and override this function.
+ *
+ * Either way, this function should return an array representing the one new object.
+ *
+ * @param array $item
+ * @return array
+ * @throws \Civi\API\Exception\NotImplementedException
+ */
+ protected function writeRecord($item) {
+ if (is_callable($this->setter)) {
+ return call_user_func($this->setter, $item, $this);
+ }
+ throw new NotImplementedException('Setter function not found for api4 ' . $this->getEntityName() . '::' . $this->getActionName());
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/BasicGetAction.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/BasicGetAction.php
new file mode 100644
index 00000000..23d47a13
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/BasicGetAction.php
@@ -0,0 +1,144 @@
+<?php
+
+namespace Civi\Api4\Generic;
+
+use Civi\API\Exception\NotImplementedException;
+
+/**
+ * Retrieve items based on criteria specified in the 'where' param.
+ *
+ * Use the 'select' param to determine which fields are returned, defaults to *.
+ */
+class BasicGetAction extends AbstractGetAction {
+ use Traits\ArrayQueryActionTrait;
+
+ /**
+ * @var callable
+ *
+ * Function(BasicGetAction $thisAction) => array<array>
+ */
+ private $getter;
+
+ /**
+ * Basic Get constructor.
+ *
+ * @param string $entityName
+ * @param string $actionName
+ * @param callable $getter
+ */
+ public function __construct($entityName, $actionName, $getter = NULL) {
+ parent::__construct($entityName, $actionName);
+ $this->getter = $getter;
+ }
+
+ /**
+ * Fetch results from the getter then apply filter/sort/select/limit.
+ *
+ * @param \Civi\Api4\Generic\Result $result
+ */
+ public function _run(Result $result) {
+ $values = $this->getRecords();
+ $result->exchangeArray($this->queryArray($values));
+ }
+
+ /**
+ * This Basic Get class is a general-purpose api for non-DAO-based entities.
+ *
+ * Useful for fetching records from files or other places.
+ * You can specify any php function to retrieve the records, and this class will
+ * automatically filter, sort, select & limit the raw data from your callback.
+ *
+ * You can implement this action in one of two ways:
+ * 1. Use this class directly by passing a callable ($getter) to the constructor.
+ * 2. Extend this class and override this function.
+ *
+ * Either way, this function should return an array of arrays, each representing one retrieved object.
+ *
+ * The simplest thing for your getter function to do is return every full record
+ * and allow this class to automatically do the sorting and filtering.
+ *
+ * Sometimes however that may not be practical for performance reasons.
+ * To optimize your getter, it can use the following helpers from $this:
+ *
+ * Use this->_itemsToGet() to match records to field values in the WHERE clause.
+ * Note the WHERE clause can potentially be very complex and it is not recommended
+ * to parse $this->where yourself.
+ *
+ * Use $this->_isFieldSelected() to check if a field value is called for - useful
+ * if loading the field involves expensive calculations.
+ *
+ * Be careful not to make assumptions, e.g. if LIMIT 100 is specified and your getter "helpfully" truncates the list
+ * at 100 without accounting for WHERE, ORDER BY and LIMIT clauses, the final filtered result may be very incorrect.
+ *
+ * @return array
+ * @throws \Civi\API\Exception\NotImplementedException
+ */
+ protected function getRecords() {
+ if (is_callable($this->getter)) {
+ return call_user_func($this->getter, $this);
+ }
+ throw new NotImplementedException('Getter function not found for api4 ' . $this->getEntityName() . '::' . $this->getActionName());
+ }
+
+ /**
+ * Helper to parse the WHERE param for getRecords to perform simple pre-filtering.
+ *
+ * This is intended to optimize some common use-cases e.g. calling the api to get
+ * one or more records by name or id.
+ *
+ * Ex: If getRecords fetches a long list of items each with a unique name,
+ * but the user has specified a single record to retrieve, you can optimize the call
+ * by checking $this->_itemsToGet('name') and only fetching the item(s) with that name.
+ *
+ * @param string $field
+ * @return array|null
+ */
+ public function _itemsToGet($field) {
+ foreach ($this->where as $clause) {
+ if ($clause[0] == $field && in_array($clause[1], ['=', 'IN'])) {
+ return (array) $clause[2];
+ }
+ }
+ return NULL;
+ }
+
+ /**
+ * Helper to see if a field should be selected by the getRecords function.
+ *
+ * Checks the SELECT, WHERE and ORDER BY params to see what fields are needed.
+ *
+ * Note that if no SELECT clause has been set then all fields should be selected
+ * and this function will always return TRUE.
+ *
+ * @param string $field
+ * @return bool
+ */
+ public function _isFieldSelected($field) {
+ if (!$this->select || in_array($field, $this->select) || isset($this->orderBy[$field])) {
+ return TRUE;
+ }
+ return $this->_whereContains($field, $this->where);
+ }
+
+ /**
+ * Walk through the where clause and check if a field is in use.
+ *
+ * @param string $field
+ * @param array $clauses
+ * @return bool
+ */
+ private function _whereContains($field, $clauses) {
+ foreach ($clauses as $clause) {
+ if (is_array($clause) && is_string($clause[0])) {
+ if ($clause[0] == $field) {
+ return TRUE;
+ }
+ elseif (is_array($clause[1])) {
+ return $this->_whereContains($field, $clause[1]);
+ }
+ }
+ }
+ return FALSE;
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/BasicGetFieldsAction.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/BasicGetFieldsAction.php
new file mode 100644
index 00000000..c9869d5e
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/BasicGetFieldsAction.php
@@ -0,0 +1,120 @@
+<?php
+
+namespace Civi\Api4\Generic;
+
+use Civi\Api4\Utils\ActionUtil;
+
+/**
+ * Get fields for an entity.
+ *
+ * @method $this setLoadOptions(bool $value)
+ * @method bool getLoadOptions()
+ * @method $this setAction(string $value)
+ */
+class BasicGetFieldsAction extends BasicGetAction {
+
+ /**
+ * Fetch option lists for fields?
+ *
+ * @var bool
+ */
+ protected $loadOptions = FALSE;
+
+ /**
+ * @var string
+ */
+ protected $action = 'get';
+
+ /**
+ * To implement getFields for your own entity:
+ *
+ * 1. From your entity class add a static getFields method.
+ * 2. That method should construct and return this class.
+ * 3. The 3rd argument passed to this constructor should be a function that returns an
+ * array of fields for your entity's CRUD actions.
+ * 4. For non-crud actions that need a different set of fields, you can override the
+ * list from step 3 on a per-action basis by defining a fields() method in that action.
+ * See for example BasicGetFieldsAction::fields() or GetActions::fields().
+ *
+ * @param Result $result
+ * @throws \Civi\API\Exception\NotImplementedException
+ */
+ public function _run(Result $result) {
+ $actionClass = ActionUtil::getAction($this->getEntityName(), $this->action);
+ if (method_exists($actionClass, 'fields')) {
+ $values = $actionClass->fields();
+ }
+ else {
+ $values = $this->getRecords();
+ }
+ $this->padResults($values);
+ $result->exchangeArray($this->queryArray($values));
+ }
+
+ /**
+ * @param array $values
+ */
+ private function padResults(&$values) {
+ foreach ($values as &$field) {
+ $field += [
+ 'title' => ucwords(str_replace('_', ' ', $field['name'])),
+ 'entity' => $this->getEntityName(),
+ 'required' => FALSE,
+ 'options' => FALSE,
+ 'data_type' => 'String',
+ ];
+ if (!$this->loadOptions) {
+ $field['options'] = (bool) $field['options'];
+ }
+ }
+ }
+
+ /**
+ * @return string
+ */
+ public function getAction() {
+ return $this->action;
+ }
+
+ public function fields() {
+ return [
+ [
+ 'name' => 'name',
+ 'data_type' => 'String',
+ ],
+ [
+ 'name' => 'title',
+ 'data_type' => 'String',
+ ],
+ [
+ 'name' => 'description',
+ 'data_type' => 'String',
+ ],
+ [
+ 'name' => 'default_value',
+ 'data_type' => 'String',
+ ],
+ [
+ 'name' => 'required',
+ 'data_type' => 'Boolean',
+ ],
+ [
+ 'name' => 'options',
+ 'data_type' => 'Array',
+ ],
+ [
+ 'name' => 'data_type',
+ 'data_type' => 'String',
+ ],
+ [
+ 'name' => 'fk_entity',
+ 'data_type' => 'String',
+ ],
+ [
+ 'name' => 'serialize',
+ 'data_type' => 'Integer',
+ ],
+ ];
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/BasicReplaceAction.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/BasicReplaceAction.php
new file mode 100644
index 00000000..8e0dd22e
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/BasicReplaceAction.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace Civi\Api4\Generic;
+
+use Civi\Api4\Generic\Result;
+
+/**
+ * Given a set of records, will appropriately update the database.
+ *
+ * @method $this setRecords(array $records) Array of records.
+ * @method $this addRecord($record) Add a record to update.
+ * @method array getRecords()
+ * @method $this setReload(bool $reload) Specify whether complete objects will be returned after saving.
+ * @method bool getReload()
+ */
+class BasicReplaceAction extends AbstractBatchAction {
+
+ /**
+ * Array of records.
+ *
+ * Should be in the same format as returned by Get.
+ *
+ * @required
+ * @var array
+ */
+ protected $records = [];
+
+ /**
+ * Reload objects after saving.
+ *
+ * Setting to TRUE will load complete records and return them as the api result.
+ * If FALSE the api usually returns only the fields specified to be updated.
+ *
+ * @var bool
+ */
+ protected $reload = FALSE;
+
+ /**
+ * @inheritDoc
+ */
+ public function _run(Result $result) {
+ $items = $this->getBatchRecords();
+
+ // Copy params from where clause if the operator is =
+ $paramsFromWhere = [];
+ foreach ($this->where as $clause) {
+ if (is_array($clause) && $clause[1] === '=') {
+ $paramsFromWhere[$clause[0]] = $clause[2];
+ }
+ }
+
+ $idField = $this->getSelect()[0];
+ $toDelete = array_column($items, NULL, $idField);
+
+ foreach ($this->records as $record) {
+ $record += $paramsFromWhere;
+ if (!empty($record[$idField])) {
+ $id = $record[$idField];
+ unset($toDelete[$id], $record[$idField]);
+ $result[] = civicrm_api4($this->getEntityName(), 'update', [
+ 'reload' => $this->reload,
+ 'where' => [[$idField, '=', $id]],
+ 'values' => $record,
+ ])->first();
+ }
+ else {
+ $result[] = civicrm_api4($this->getEntityName(), 'create', [
+ 'values' => $record,
+ ])->first();
+ }
+ }
+
+ $result->deleted = [];
+ if ($toDelete) {
+ $result->deleted = (array) civicrm_api4($this->getEntityName(), 'delete', [
+ 'where' => [[$idField, 'IN', array_keys($toDelete)]],
+ ]);
+ }
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/BasicUpdateAction.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/BasicUpdateAction.php
new file mode 100644
index 00000000..40c93624
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/BasicUpdateAction.php
@@ -0,0 +1,67 @@
+<?php
+
+namespace Civi\Api4\Generic;
+
+use Civi\API\Exception\NotImplementedException;
+
+/**
+ * Update one or more records with new values.
+ *
+ * Use the where clause (required) to select them.
+ */
+class BasicUpdateAction extends AbstractUpdateAction {
+
+ /**
+ * @var callable
+ *
+ * Function(array $item, BasicUpdateAction $thisAction) => array
+ */
+ private $setter;
+
+ /**
+ * BasicUpdateAction constructor.
+ *
+ * @param string $entityName
+ * @param string $actionName
+ * @param string|array $select
+ * One or more fields to select from each matching item.
+ * @param callable $setter
+ * Function(array $item, BasicUpdateAction $thisAction) => array
+ */
+ public function __construct($entityName, $actionName, $select = 'id', $setter = NULL) {
+ parent::__construct($entityName, $actionName, $select);
+ $this->setter = $setter;
+ }
+
+ /**
+ * We pass the writeRecord function an array representing one item to update.
+ * We expect to get the same format back.
+ *
+ * @param \Civi\Api4\Generic\Result $result
+ */
+ public function _run(Result $result) {
+ foreach ($this->getBatchRecords() as $item) {
+ $result[] = $this->writeRecord($this->values + $item);
+ }
+ }
+
+ /**
+ * This Basic Update class can be used in one of two ways:
+ *
+ * 1. Use this class directly by passing a callable ($setter) to the constructor.
+ * 2. Extend this class and override this function.
+ *
+ * Either way, this function should return an array representing the one modified object.
+ *
+ * @param array $item
+ * @return array
+ * @throws \Civi\API\Exception\NotImplementedException
+ */
+ protected function writeRecord($item) {
+ if (is_callable($this->setter)) {
+ return call_user_func($this->setter, $item, $this);
+ }
+ throw new NotImplementedException('Setter function not found for api4 ' . $this->getEntityName() . '::' . $this->getActionName());
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/DAOCreateAction.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/DAOCreateAction.php
new file mode 100644
index 00000000..d7a0e869
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/DAOCreateAction.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace Civi\Api4\Generic;
+
+use Civi\Api4\Generic\Result;
+
+/**
+ * Create a new object from supplied values.
+ *
+ * This function will create 1 new object. It cannot be used to update existing objects. Use the Update or Replace actions for that.
+ */
+class DAOCreateAction extends AbstractCreateAction {
+ use Traits\DAOActionTrait;
+
+ /**
+ * @inheritDoc
+ */
+ public function _run(Result $result) {
+ $this->validateValues();
+ $params = $this->values;
+ $this->fillDefaults($params);
+
+ $resultArray = $this->writeObjects([$params]);
+
+ $result->exchangeArray($resultArray);
+ }
+
+ /**
+ * @throws \API_Exception
+ */
+ protected function validateValues() {
+ if (!empty($this->values['id'])) {
+ throw new \API_Exception('Cannot pass id to Create action. Use Update action instead.');
+ }
+ parent::validateValues();
+ }
+
+ /**
+ * Fill field defaults which were declared by the api.
+ *
+ * Note: default values from core are ignored because the BAO or database layer will supply them.
+ *
+ * @param array $params
+ */
+ protected function fillDefaults(&$params) {
+ $fields = $this->getEntityFields();
+ $bao = $this->getBaoName();
+ $coreFields = array_column($bao::fields(), NULL, 'name');
+
+ foreach ($fields as $name => $field) {
+ // If a default value is set in the api but not in core, the api should supply it.
+ if (!isset($params[$name]) && !empty($field['default_value']) && empty($coreFields[$name]['default'])) {
+ $params[$name] = $field['default_value'];
+ }
+ }
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/DAODeleteAction.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/DAODeleteAction.php
new file mode 100644
index 00000000..f61af3f1
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/DAODeleteAction.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace Civi\Api4\Generic;
+
+use Civi\Api4\Generic\Result;
+
+/**
+ * Delete one or more items, based on criteria specified in Where param (required).
+ */
+class DAODeleteAction extends AbstractBatchAction {
+ use Traits\DAOActionTrait;
+
+ /**
+ * Batch delete function
+ */
+ public function _run(Result $result) {
+ $defaults = $this->getParamDefaults();
+ if ($defaults['where'] && !array_diff_key($this->where, $defaults['where'])) {
+ throw new \API_Exception('Cannot delete with no "where" parameter specified');
+ }
+
+ $items = $this->getObjects();
+
+ $ids = $this->deleteObjects($items);
+
+ $result->exchangeArray($ids);
+ }
+
+ /**
+ * @param $items
+ * @return array
+ * @throws \API_Exception
+ */
+ protected function deleteObjects($items) {
+ $ids = [];
+ $baoName = $this->getBaoName();
+
+ if ($this->getCheckPermissions()) {
+ foreach ($items as $item) {
+ $this->checkContactPermissions($baoName, $item);
+ }
+ }
+
+ if ($this->getEntityName() !== 'EntityTag' && method_exists($baoName, 'del')) {
+ foreach ($items as $item) {
+ $args = [$item['id']];
+ $bao = call_user_func_array([$baoName, 'del'], $args);
+ if ($bao !== FALSE) {
+ $ids[] = $item['id'];
+ }
+ else {
+ throw new \API_Exception("Could not delete {$this->getEntityName()} id {$item['id']}");
+ }
+ }
+ }
+ else {
+ foreach ($items as $item) {
+ $bao = new $baoName();
+ $bao->id = $item['id'];
+ // delete it
+ $action_result = $bao->delete();
+ if ($action_result) {
+ $ids[] = $item['id'];
+ }
+ else {
+ throw new \API_Exception("Could not delete {$this->getEntityName()} id {$item['id']}");
+ }
+ }
+ }
+ return $ids;
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/DAOEntity.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/DAOEntity.php
new file mode 100644
index 00000000..1ad175da
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/DAOEntity.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace Civi\Api4\Generic;
+
+/**
+ * Base class for DAO-based entities.
+ */
+abstract class DAOEntity extends AbstractEntity {
+
+ /**
+ * @return DAOGetAction
+ */
+ public static function get() {
+ return new DAOGetAction(static::class, __FUNCTION__);
+ }
+
+ /**
+ * @return DAOGetFieldsAction
+ */
+ public static function getFields() {
+ return new DAOGetFieldsAction(static::class, __FUNCTION__);
+ }
+
+ /**
+ * @return DAOCreateAction
+ */
+ public static function create() {
+ return new DAOCreateAction(static::class, __FUNCTION__);
+ }
+
+ /**
+ * @return DAOUpdateAction
+ */
+ public static function update() {
+ return new DAOUpdateAction(static::class, __FUNCTION__);
+ }
+
+ /**
+ * @return DAODeleteAction
+ */
+ public static function delete() {
+ return new DAODeleteAction(static::class, __FUNCTION__);
+ }
+
+ /**
+ * @return BasicReplaceAction
+ */
+ public static function replace() {
+ return new BasicReplaceAction(static::class, __FUNCTION__);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/DAOGetAction.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/DAOGetAction.php
new file mode 100644
index 00000000..0216f0da
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/DAOGetAction.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace Civi\Api4\Generic;
+
+use Civi\Api4\Generic\Result;
+
+/**
+ * Retrieve items based on criteria specified in the 'where' param.
+ *
+ * Use the 'select' param to determine which fields are returned, defaults to *.
+ *
+ * Perform joins on other related entities using a dot notation.
+ */
+class DAOGetAction extends AbstractGetAction {
+ use Traits\DAOActionTrait;
+
+ public function _run(Result $result) {
+ $result->exchangeArray($this->getObjects());
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/DAOGetFieldsAction.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/DAOGetFieldsAction.php
new file mode 100644
index 00000000..e86d99bc
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/DAOGetFieldsAction.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace Civi\Api4\Generic;
+
+use Civi\Api4\Service\Spec\SpecGatherer;
+use Civi\Api4\Service\Spec\SpecFormatter;
+
+/**
+ * Get fields for a DAO-based entity.
+ *
+ * @method $this setIncludeCustom(bool $value)
+ * @method bool getIncludeCustom()
+ */
+class DAOGetFieldsAction extends BasicGetFieldsAction {
+
+ /**
+ * Include custom fields for this entity, or only core fields?
+ *
+ * @var bool
+ */
+ protected $includeCustom = TRUE;
+
+ /**
+ * Get fields for a DAO-based entity
+ *
+ * @return array
+ */
+ protected function getRecords() {
+ $fields = $this->_itemsToGet('name');
+ /** @var SpecGatherer $gatherer */
+ $gatherer = \Civi::container()->get('spec_gatherer');
+ // Any fields name with a dot in it is custom
+ if ($fields) {
+ $this->includeCustom = strpos(implode('', $fields), '.') !== FALSE;
+ }
+ $spec = $gatherer->getSpec($this->getEntityName(), $this->action, $this->includeCustom);
+ return SpecFormatter::specToArray($spec->getFields($fields), (array) $this->select, $this->loadOptions);
+ }
+
+ public function fields() {
+ $fields = parent::fields();
+ $fields[] = [
+ 'name' => 'custom_field_id',
+ 'data_type' => 'Integer',
+ ];
+ $fields[] = [
+ 'name' => 'custom_group_id',
+ 'data_type' => 'Integer',
+ ];
+ return $fields;
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/DAOUpdateAction.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/DAOUpdateAction.php
new file mode 100644
index 00000000..62da8796
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/DAOUpdateAction.php
@@ -0,0 +1,31 @@
+<?php
+
+namespace Civi\Api4\Generic;
+
+use Civi\Api4\Generic\Result;
+
+/**
+ * Update one or more records with new values.
+ *
+ * Use the where clause (required) to select them.
+ */
+class DAOUpdateAction extends AbstractUpdateAction {
+ use Traits\DAOActionTrait;
+
+ /**
+ * @inheritDoc
+ */
+ public function _run(Result $result) {
+ if (!empty($this->values['id'])) {
+ throw new \Exception("Cannot update the id of an existing " . $this->getEntityName() . '.');
+ }
+
+ $items = $this->getObjects();
+ foreach ($items as &$item) {
+ $item = $this->values + $item;
+ }
+
+ $result->exchangeArray($this->writeObjects($items));
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/Result.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/Result.php
new file mode 100644
index 00000000..35fb6fb0
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/Result.php
@@ -0,0 +1,105 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 4.7 |
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC (c) 2004-2015 |
+ +--------------------------------------------------------------------+
+ | This file is a part of CiviCRM. |
+ | |
+ | CiviCRM is free software; you can copy, modify, and distribute it |
+ | under the terms of the GNU Affero General Public License |
+ | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
+ | |
+ | CiviCRM is distributed in the hope that it will be useful, but |
+ | WITHOUT ANY WARRANTY; without even the implied warranty of |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
+ | See the GNU Affero General Public License for more details. |
+ | |
+ | You should have received a copy of the GNU Affero General Public |
+ | License and the CiviCRM Licensing Exception along |
+ | with this program; if not, contact CiviCRM LLC |
+ | at info[AT]civicrm[DOT]org. If you have questions about the |
+ | GNU Affero General Public License or the licensing of CiviCRM, |
+ | see the CiviCRM license FAQ at http://civicrm.org/licensing |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Api4\Generic;
+
+/**
+ * Container for api results.
+ */
+class Result extends \ArrayObject {
+ /**
+ * @var string
+ */
+ public $entity;
+ /**
+ * @var string
+ */
+ public $action;
+ /**
+ * Api version
+ * @var int
+ */
+ public $version = 4;
+
+ /**
+ * Return first result.
+ * @return array|null
+ */
+ public function first() {
+ foreach ($this as $values) {
+ return $values;
+ }
+ return NULL;
+ }
+
+ /**
+ * Return last result.
+ * @return array|null
+ */
+ public function last() {
+ $items = $this->getArrayCopy();
+ return array_pop($items);
+ }
+
+ /**
+ * @param int $index
+ * @return array|null
+ */
+ public function itemAt($index) {
+ $length = $index < 0 ? 0 - $index : $index + 1;
+ if ($length > count($this)) {
+ return NULL;
+ }
+ return array_slice(array_values($this->getArrayCopy()), $index, 1)[0];
+ }
+
+ /**
+ * Re-index the results array (which by default is non-associative)
+ *
+ * Drops any item from the results that does not contain the specified key
+ *
+ * @param string $key
+ * @return $this
+ * @throws \API_Exception
+ */
+ public function indexBy($key) {
+ if (count($this)) {
+ $newResults = [];
+ foreach ($this as $values) {
+ if (isset($values[$key])) {
+ $newResults[$values[$key]] = $values;
+ }
+ }
+ if (!$newResults) {
+ throw new \API_Exception("Key $key not found in api results");
+ }
+ $this->exchangeArray($newResults);
+ }
+ return $this;
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/Traits/ArrayQueryActionTrait.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/Traits/ArrayQueryActionTrait.php
new file mode 100644
index 00000000..1d223f1b
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/Traits/ArrayQueryActionTrait.php
@@ -0,0 +1,197 @@
+<?php
+
+namespace Civi\Api4\Generic\Traits;
+use Civi\API\Exception\NotImplementedException;
+
+/**
+ * Helper functions for performing api queries on arrays of data.
+ *
+ * @package Civi\Api4\Generic
+ */
+trait ArrayQueryActionTrait {
+
+ /**
+ * @param array $values
+ * List of all rows
+ * @return array
+ * Filtered list of rows
+ */
+ protected function queryArray($values) {
+ $values = $this->filterArray($values);
+ $values = $this->sortArray($values);
+ $values = $this->selectArray($values);
+ $values = $this->limitArray($values);
+ return $values;
+ }
+
+ /**
+ * @param array $values
+ * @return array
+ */
+ protected function filterArray($values) {
+ if ($this->getWhere()) {
+ $values = array_filter($values, [$this, 'evaluateFilters']);
+ }
+ return array_values($values);
+ }
+
+ /**
+ * @param array $row
+ * @return bool
+ */
+ private function evaluateFilters($row) {
+ $where = $this->getWhere();
+ $allConditions = in_array($where[0], ['AND', 'OR', 'NOT']) ? $where : ['AND', $where];
+ return $this->walkFilters($row, $allConditions);
+ }
+
+ /**
+ * @param array $row
+ * @param array $filters
+ * @return bool
+ * @throws \Civi\API\Exception\NotImplementedException
+ */
+ private function walkFilters($row, $filters) {
+ switch ($filters[0]) {
+ case 'AND':
+ case 'NOT':
+ $result = TRUE;
+ foreach ($filters[1] as $filter) {
+ if (!$this->walkFilters($row, $filter)) {
+ $result = FALSE;
+ break;
+ }
+ }
+ return $result == ($filters[0] == 'AND');
+
+ case 'OR':
+ $result = !count($filters[1]);
+ foreach ($filters[1] as $filter) {
+ if ($this->walkFilters($row, $filter)) {
+ return TRUE;
+ }
+ }
+ return $result;
+
+ default:
+ return $this->filterCompare($row, $filters);
+ }
+ }
+
+ /**
+ * @param array $row
+ * @param array $condition
+ * @return bool
+ * @throws \Civi\API\Exception\NotImplementedException
+ */
+ private function filterCompare($row, $condition) {
+ if (!is_array($condition)) {
+ throw new NotImplementedException('Unexpected where syntax; expecting array.');
+ }
+ $value = isset($row[$condition[0]]) ? $row[$condition[0]] : NULL;
+ $operator = $condition[1];
+ $expected = isset($condition[2]) ? $condition[2] : NULL;
+ switch ($operator) {
+ case '=':
+ case '!=':
+ case '<>':
+ $equal = $value == $expected;
+ // PHP is too imprecise about comparing the number 0
+ if ($expected === 0 || $expected === '0') {
+ $equal = ($value === 0 || $value === '0');
+ }
+ // PHP is too imprecise about comparing empty strings
+ if ($expected === '') {
+ $equal = ($value === '');
+ }
+ return $equal == ($operator == '=');
+
+ case 'IS NULL':
+ case 'IS NOT NULL':
+ return is_null($value) == ($operator == 'IS NULL');
+
+ case '>':
+ return $value > $expected;
+
+ case '>=':
+ return $value >= $expected;
+
+ case '<':
+ return $value < $expected;
+
+ case '<=':
+ return $value <= $expected;
+
+ case 'BETWEEN':
+ case 'NOT BETWEEN':
+ $between = ($value >= $expected[0] && $value <= $expected[1]);
+ return $between == ($operator == 'BETWEEN');
+
+ case 'LIKE':
+ case 'NOT LIKE':
+ $pattern = '/^' . str_replace('%', '.*', preg_quote($expected, '/')) . '$/i';
+ return !preg_match($pattern, $value) == ($operator != 'LIKE');
+
+ case 'IN':
+ return in_array($value, $expected);
+
+ case 'NOT IN':
+ return !in_array($value, $expected);
+
+ default:
+ throw new NotImplementedException("Unsupported operator: '$operator' cannot be used with array data");
+ }
+ }
+
+ /**
+ * @param $values
+ * @return array
+ */
+ protected function sortArray($values) {
+ if ($this->getOrderBy()) {
+ usort($values, [$this, 'sortCompare']);
+ }
+ return $values;
+ }
+
+ private function sortCompare($a, $b) {
+ foreach ($this->getOrderBy() as $field => $dir) {
+ $modifier = $dir == 'ASC' ? 1 : -1;
+ if (isset($a[$field]) && isset($b[$field])) {
+ if ($a[$field] == $b[$field]) {
+ continue;
+ }
+ return (strnatcasecmp($a[$field], $b[$field]) * $modifier);
+ }
+ elseif (isset($a[$field]) || isset($b[$field])) {
+ return ((isset($a[$field]) ? 1 : -1) * $modifier);
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * @param $values
+ * @return array
+ */
+ protected function selectArray($values) {
+ if ($this->getSelect()) {
+ foreach ($values as &$value) {
+ $value = array_intersect_key($value, array_flip($this->getSelect()));
+ }
+ }
+ return $values;
+ }
+
+ /**
+ * @param $values
+ * @return array
+ */
+ protected function limitArray($values) {
+ if ($this->getOffset() || $this->getLimit()) {
+ $values = array_slice($values, $this->getOffset() ?: 0, $this->getLimit() ?: NULL);
+ }
+ return $values;
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/Traits/CustomValueActionTrait.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/Traits/CustomValueActionTrait.php
new file mode 100644
index 00000000..6a765b40
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/Traits/CustomValueActionTrait.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace Civi\Api4\Generic\Traits;
+
+use Civi\Api4\Utils\FormattingUtil;
+use Civi\Api4\Utils\CoreUtil;
+
+/**
+ * Helper functions for working with custom values
+ *
+ * @package Civi\Api4\Generic
+ */
+trait CustomValueActionTrait {
+
+ function __construct($customGroup, $actionName) {
+ $this->customGroup = $customGroup;
+ parent::__construct('CustomValue', $actionName, ['id', 'entity_id']);
+ }
+
+ /**
+ * Custom Group name if this is a CustomValue pseudo-entity.
+ *
+ * @var string
+ */
+ private $customGroup;
+
+ /**
+ * @inheritDoc
+ */
+ public function getEntityName() {
+ return 'Custom_' . $this->getCustomGroup();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function writeObjects($items) {
+ $result = [];
+ foreach ($items as $item) {
+ FormattingUtil::formatWriteParams($item, $this->getEntityName(), $this->getEntityFields());
+
+ $result[] = \CRM_Core_BAO_CustomValueTable::setValues($item);
+ }
+ return $result;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function deleteObjects($items) {
+ $customTable = CoreUtil::getCustomTableByName($this->getCustomGroup());
+ $ids = [];
+ foreach ($items as $item) {
+ \CRM_Utils_Hook::pre('delete', $this->getEntityName(), $item['id'], \CRM_Core_DAO::$_nullArray);
+ \CRM_Utils_SQL_Delete::from($customTable)
+ ->where('id = #value')
+ ->param('#value', $item['id'])
+ ->execute();
+ \CRM_Utils_Hook::post('delete', $this->getEntityName(), $item['id'], \CRM_Core_DAO::$_nullArray);
+ $ids[] = $item['id'];
+ }
+ return $ids;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function fillDefaults(&$params) {
+ foreach ($this->getEntityFields() as $name => $field) {
+ if (!isset($params[$name]) && isset($field['default_value'])) {
+ $params[$name] = $field['default_value'];
+ }
+ }
+ }
+
+ /**
+ * @return string
+ */
+ public function getCustomGroup() {
+ return $this->customGroup;
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/Traits/DAOActionTrait.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/Traits/DAOActionTrait.php
new file mode 100644
index 00000000..1c92906b
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Generic/Traits/DAOActionTrait.php
@@ -0,0 +1,229 @@
+<?php
+namespace Civi\Api4\Generic\Traits;
+
+use CRM_Utils_Array as UtilsArray;
+use Civi\Api4\Utils\FormattingUtil;
+use Civi\Api4\Query\Api4SelectQuery;
+
+trait DAOActionTrait {
+
+ /**
+ * @return \CRM_Core_DAO|string
+ */
+ protected function getBaoName() {
+ require_once 'api/v3/utils.php';
+ return \_civicrm_api3_get_BAO($this->getEntityName());
+ }
+
+ /**
+ * Extract the true fields from a BAO
+ *
+ * (Used by create and update actions)
+ * @param object $bao
+ * @return array
+ */
+ public static function baoToArray($bao) {
+ $fields = $bao->fields();
+ $values = [];
+ foreach ($fields as $key => $field) {
+ $name = $field['name'];
+ if (property_exists($bao, $name)) {
+ $values[$name] = $bao->$name;
+ }
+ }
+ return $values;
+ }
+
+ /**
+ * @return array|int
+ */
+ protected function getObjects() {
+ $query = new Api4SelectQuery($this->getEntityName(), $this->getCheckPermissions());
+ $query->select = $this->getSelect();
+ $query->where = $this->getWhere();
+ $query->orderBy = $this->getOrderBy();
+ $query->limit = $this->getLimit();
+ $query->offset = $this->getOffset();
+ return $query->run();
+ }
+
+ /**
+ * Write a bao object as part of a create/update action.
+ *
+ * @param array $items
+ * The record to write to the DB.
+ * @return array
+ * The record after being written to the DB (e.g. including newly assigned "id").
+ * @throws \API_Exception
+ */
+ protected function writeObjects($items) {
+ $baoName = $this->getBaoName();
+
+ // Some BAOs are weird and don't support a straightforward "create" method.
+ $oddballs = [
+ 'Address' => 'add',
+ 'GroupContact' => 'add',
+ 'Website' => 'add',
+ ];
+ $method = UtilsArray::value($this->getEntityName(), $oddballs, 'create');
+ if (!method_exists($baoName, $method)) {
+ $method = 'add';
+ }
+
+ $result = [];
+
+ foreach ($items as $item) {
+ $entityId = UtilsArray::value('id', $item);
+ FormattingUtil::formatWriteParams($item, $this->getEntityName(), $this->getEntityFields());
+ $this->formatCustomParams($item, $entityId);
+ $item['check_permissions'] = $this->getCheckPermissions();
+
+ $apiKeyPermission = $this->getEntityName() != 'Contact' || !$this->getCheckPermissions() || array_key_exists('api_key', $this->getEntityFields())
+ || ($entityId && \CRM_Core_Permission::check('edit own api keys') && \CRM_Core_Session::getLoggedInContactID() == $entityId);
+
+ if (!$apiKeyPermission && array_key_exists('api_key', $item)) {
+ throw new \Civi\API\Exception\UnauthorizedException('Permission denied to modify api key');
+ }
+
+ // For some reason the contact bao requires this
+ if ($entityId && $this->getEntityName() == 'Contact') {
+ $item['contact_id'] = $entityId;
+ }
+
+ if ($this->getCheckPermissions() && $entityId) {
+ $this->checkContactPermissions($baoName, $item);
+ }
+
+ if (method_exists($baoName, $method)) {
+ $createResult = $baoName::$method($item);
+ }
+ else {
+ $createResult = $this->genericCreateMethod($item);
+ }
+
+ if (!$createResult) {
+ $errMessage = sprintf('%s write operation failed', $this->getEntityName());
+ throw new \API_Exception($errMessage);
+ }
+
+ if (!empty($this->reload) && is_a($createResult, 'CRM_Core_DAO')) {
+ $createResult->find(TRUE);
+ }
+
+ // trim back the junk and just get the array:
+ $resultArray = $this->baoToArray($createResult);
+
+ if (!$apiKeyPermission && array_key_exists('api_key', $resultArray)) {
+ unset($resultArray['api_key']);
+ }
+
+ $result[] = $resultArray;
+ }
+ return $result;
+ }
+
+ /**
+ * Fallback when a BAO does not contain create or add functions
+ *
+ * @param $params
+ * @return mixed
+ */
+ private function genericCreateMethod($params) {
+ $baoName = $this->getBaoName();
+ $hook = empty($params['id']) ? 'create' : 'edit';
+
+ \CRM_Utils_Hook::pre($hook, $this->getEntityName(), UtilsArray::value('id', $params), $params);
+ /** @var \CRM_Core_DAO $instance */
+ $instance = new $baoName();
+ $instance->copyValues($params, TRUE);
+ $instance->save();
+ \CRM_Utils_Hook::post($hook, $this->getEntityName(), $instance->id, $instance);
+
+ return $instance;
+ }
+
+ /**
+ * @param array $params
+ * @param int $entityId
+ * @return mixed
+ */
+ private function formatCustomParams(&$params, $entityId) {
+ $customParams = [];
+
+ // $customValueID is the ID of the custom value in the custom table for this
+ // entity (i guess this assumes it's not a multi value entity)
+ foreach ($params as $name => $value) {
+ if (strpos($name, '.') === FALSE) {
+ continue;
+ }
+
+ list($customGroup, $customField) = explode('.', $name);
+
+ $customFieldId = \CRM_Core_BAO_CustomField::getFieldValue(
+ \CRM_Core_DAO_CustomField::class,
+ $customField,
+ 'id',
+ 'name'
+ );
+ $customFieldType = \CRM_Core_BAO_CustomField::getFieldValue(
+ \CRM_Core_DAO_CustomField::class,
+ $customField,
+ 'html_type',
+ 'name'
+ );
+ $customFieldExtends = \CRM_Core_BAO_CustomGroup::getFieldValue(
+ \CRM_Core_DAO_CustomGroup::class,
+ $customGroup,
+ 'extends',
+ 'name'
+ );
+
+ // todo are we sure we don't want to allow setting to NULL? need to test
+ if ($customFieldId && NULL !== $value) {
+
+ if ($customFieldType == 'CheckBox') {
+ // this function should be part of a class
+ formatCheckBoxField($value, 'custom_' . $customFieldId, $this->getEntityName());
+ }
+
+ \CRM_Core_BAO_CustomField::formatCustomField(
+ $customFieldId,
+ $customParams,
+ $value,
+ $customFieldExtends,
+ NULL, // todo check when this is needed
+ $entityId,
+ FALSE,
+ FALSE,
+ TRUE
+ );
+ }
+ }
+
+ if ($customParams) {
+ $params['custom'] = $customParams;
+ }
+ }
+
+ /**
+ * Check edit/delete permissions for contacts and related entities.
+ *
+ * @param $baoName
+ * @param $item
+ * @throws \Civi\API\Exception\UnauthorizedException
+ */
+ protected function checkContactPermissions($baoName, $item) {
+ if ($baoName == 'CRM_Contact_BAO_Contact') {
+ $permission = $this->getActionName() == 'delete' ? \CRM_Core_Permission::DELETE : \CRM_Core_Permission::EDIT;
+ if (!\CRM_Contact_BAO_Contact_Permission::allow($item['id'], $permission)) {
+ throw new \Civi\API\Exception\UnauthorizedException('Permission denied to modify contact record');
+ }
+ }
+ else {
+ // Fixme: decouple from v3
+ require_once 'api/v3/utils.php';
+ _civicrm_api3_check_edit_permissions($baoName, ['check_permissions' => 1] + $item);
+ }
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Group.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Group.php
new file mode 100644
index 00000000..b82fa982
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Group.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace Civi\Api4;
+
+/**
+ * Group entity.
+ *
+ * @package Civi\Api4
+ */
+class Group extends Generic\DAOEntity {
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/GroupContact.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/GroupContact.php
new file mode 100644
index 00000000..8901cd3b
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/GroupContact.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace Civi\Api4;
+
+/**
+ * GroupContact entity - link between groups and contacts.
+ *
+ * A contact can either be "Added" "Removed" or "Pending" in a group.
+ * CiviCRM only considers them to be "in" a group if their status is "Added".
+ *
+ * @package Civi\Api4
+ */
+class GroupContact extends Generic\DAOEntity {
+
+ /**
+ * @return Action\GroupContact\Create
+ */
+ public static function create() {
+ return new Action\GroupContact\Create(__CLASS__, __FUNCTION__);
+ }
+
+ /**
+ * @return Action\GroupContact\Update
+ */
+ public static function update() {
+ return new Action\GroupContact\Update(__CLASS__, __FUNCTION__);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/IM.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/IM.php
new file mode 100644
index 00000000..514f39a8
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/IM.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace Civi\Api4;
+
+/**
+ * IM entity.
+ *
+ * @package Civi\Api4
+ */
+class IM extends Generic\DAOEntity {
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Navigation.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Navigation.php
new file mode 100644
index 00000000..31f2a911
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Navigation.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Civi\Api4;
+
+/**
+ * Navigation entity.
+ *
+ * @package Civi\Api4
+ */
+class Navigation extends Generic\DAOEntity {
+
+ /**
+ * @return Action\Navigation\Get
+ */
+ public static function get() {
+ return new Action\Navigation\Get(__CLASS__, __FUNCTION__);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Note.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Note.php
new file mode 100644
index 00000000..55f6b7e6
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Note.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace Civi\Api4;
+
+/**
+ * Note entity.
+ *
+ * @package Civi\Api4
+ */
+class Note extends Generic\DAOEntity {
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/OpenID.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/OpenID.php
new file mode 100644
index 00000000..a0c146aa
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/OpenID.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace Civi\Api4;
+
+/**
+ * OpenID entity.
+ *
+ * @package Civi\Api4
+ */
+class OpenID extends Generic\DAOEntity {
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/OptionGroup.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/OptionGroup.php
new file mode 100644
index 00000000..4821348a
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/OptionGroup.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace Civi\Api4;
+
+/**
+ * OptionGroup entity.
+ *
+ * @package Civi\Api4
+ */
+class OptionGroup extends Generic\DAOEntity {
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/OptionValue.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/OptionValue.php
new file mode 100644
index 00000000..16e9706c
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/OptionValue.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace Civi\Api4;
+
+/**
+ * OptionValue entity.
+ *
+ * @package Civi\Api4
+ */
+class OptionValue extends Generic\DAOEntity {
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Participant.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Participant.php
new file mode 100644
index 00000000..0e09c797
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Participant.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Civi\Api4;
+
+/**
+ * Participant entity.
+ *
+ * @package Civi\Api4
+ */
+class Participant extends Generic\DAOEntity {
+
+ /**
+ * @return Action\Participant\Get
+ */
+ public static function get() {
+ return new Action\Participant\Get(__CLASS__, __FUNCTION__);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Phone.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Phone.php
new file mode 100644
index 00000000..a02cd7cd
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Phone.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Civi\Api4;
+
+/**
+ * Phone entity.
+ *
+ * This entity allows user to add, update, retrieve or delete phone number(s) of a contact.
+ *
+ * Creating a new phone of a contact, requires at minimum a contact's ID and phone number
+ *
+ * @package Civi\Api4
+ */
+class Phone extends Generic\DAOEntity {
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Provider/ActionObjectProvider.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Provider/ActionObjectProvider.php
new file mode 100644
index 00000000..60596d92
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Provider/ActionObjectProvider.php
@@ -0,0 +1,155 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 4.7 |
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC (c) 2004-2015 |
+ +--------------------------------------------------------------------+
+ | This file is a part of CiviCRM. |
+ | |
+ | CiviCRM is free software; you can copy, modify, and distribute it |
+ | under the terms of the GNU Affero General Public License |
+ | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
+ | |
+ | CiviCRM is distributed in the hope that it will be useful, but |
+ | WITHOUT ANY WARRANTY; without even the implied warranty of |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
+ | See the GNU Affero General Public License for more details. |
+ | |
+ | You should have received a copy of the GNU Affero General Public |
+ | License and the CiviCRM Licensing Exception along |
+ | with this program; if not, contact CiviCRM LLC |
+ | at info[AT]civicrm[DOT]org. If you have questions about the |
+ | GNU Affero General Public License or the licensing of CiviCRM, |
+ | see the CiviCRM license FAQ at http://civicrm.org/licensing |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Api4\Provider;
+
+use Civi\API\Event\ResolveEvent;
+use Civi\API\Provider\ProviderInterface;
+use Civi\Api4\Generic\AbstractAction;
+use Civi\API\Events;
+use Civi\Api4\Generic\Result;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Accept $apiRequests based on \Civi\API\Action
+ */
+class ActionObjectProvider implements EventSubscriberInterface, ProviderInterface {
+
+ /**
+ * @return array
+ */
+ public static function getSubscribedEvents() {
+ // Using a high priority allows adhoc implementations
+ // to override standard implementations -- which is
+ // handy for testing/mocking.
+ return [
+ Events::RESOLVE => [
+ ['onApiResolve', Events::W_EARLY],
+ ],
+ ];
+ }
+ /**
+ * @param ResolveEvent $event
+ * API resolution event.
+ */
+ public function onApiResolve(ResolveEvent $event) {
+ $apiRequest = $event->getApiRequest();
+ if ($apiRequest instanceof AbstractAction) {
+ $event->setApiRequest($apiRequest);
+ $event->setApiProvider($this);
+ $event->stopPropagation();
+ }
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @param AbstractAction $action
+ *
+ * @return array|mixed
+ */
+ public function invoke($action) {
+ $result = new Result();
+ $result->action = $action->getActionName();
+ $result->entity = $action->getEntityName();
+ $action->_run($result);
+ $this->handleChains($action, $result);
+ return $result;
+ }
+
+ /**
+ * Run each chained action once per row
+ *
+ * @param AbstractAction $action
+ * @param Result $result
+ */
+ protected function handleChains($action, $result) {
+ foreach ($action->getChain() as $name => $request) {
+ $request += [NULL, NULL, [], NULL];
+ $request[2]['checkPermissions'] = $action->getCheckPermissions();
+ foreach ($result as &$row) {
+ $row[$name] = $this->runChain($request, $row);
+ }
+ }
+ }
+
+ /**
+ * Run a chained action
+ *
+ * @param $request
+ * @param $row
+ * @return array|\Civi\Api4\Generic\Result|null
+ * @throws \API_Exception
+ */
+ protected function runChain($request, $row) {
+ list($entity, $action, $params, $index) = $request;
+ // Swap out variables in $entity, $action & $params
+ $this->resolveChainLinks($entity, $row);
+ $this->resolveChainLinks($action, $row);
+ $this->resolveChainLinks($params, $row);
+ return (array) civicrm_api4($entity, $action, $params, $index);
+ }
+
+ /**
+ * Swap out variable names
+ *
+ * @param mixed $val
+ * @param array $result
+ */
+ protected function resolveChainLinks(&$val, $result) {
+ if (is_array($val)) {
+ foreach ($val as &$v) {
+ $this->resolveChainLinks($v, $result);
+ }
+ }
+ elseif (is_string($val) && strlen($val) > 1 && substr($val, 0, 1) === '$') {
+ $val = \CRM_Utils_Array::pathGet($result, explode('.', substr($val, 1)));
+ }
+ }
+
+ /**
+ * @inheritDoc
+ * @param int $version
+ * @return array
+ */
+ public function getEntityNames($version) {
+ /** FIXME */
+ return [];
+ }
+
+ /**
+ * @inheritDoc
+ * @param int $version
+ * @param string $entity
+ * @return array
+ */
+ public function getActionNames($version, $entity) {
+ /** FIXME Civi\API\V4\Action\GetActions */
+ return [];
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Query/Api4SelectQuery.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Query/Api4SelectQuery.php
new file mode 100644
index 00000000..a7a912da
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Query/Api4SelectQuery.php
@@ -0,0 +1,535 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 4.7 |
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC (c) 2004-2015 |
+ +--------------------------------------------------------------------+
+ | This file is a part of CiviCRM. |
+ | |
+ | CiviCRM is free software; you can copy, modify, and distribute it |
+ | under the terms of the GNU Affero General Public License |
+ | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
+ | |
+ | CiviCRM is distributed in the hope that it will be useful, but |
+ | WITHOUT ANY WARRANTY; without even the implied warranty of |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
+ | See the GNU Affero General Public License for more details. |
+ | |
+ | You should have received a copy of the GNU Affero General Public |
+ | License and the CiviCRM Licensing Exception along |
+ | with this program; if not, contact CiviCRM LLC |
+ | at info[AT]civicrm[DOT]org. If you have questions about the |
+ | GNU Affero General Public License or the licensing of CiviCRM, |
+ | see the CiviCRM license FAQ at http://civicrm.org/licensing |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Api4\Query;
+
+use Civi\API\SelectQuery;
+use Civi\Api4\Event\Events;
+use Civi\Api4\Event\PostSelectQueryEvent;
+use Civi\Api4\Service\Schema\Joinable\CustomGroupJoinable;
+use Civi\Api4\Service\Schema\Joinable\Joinable;
+use Civi\Api4\Utils\FormattingUtil;
+use Civi\Api4\Utils\CoreUtil;
+use CRM_Core_DAO_AllCoreTables as TableHelper;
+use CRM_Core_DAO_CustomField as CustomFieldDAO;
+use CRM_Utils_Array as UtilsArray;
+
+/**
+ * A query `node` may be in one of three formats:
+ *
+ * * leaf: [$fieldName, $operator, $criteria]
+ * * negated: ['NOT', $node]
+ * * branch: ['OR|NOT', [$node, $node, ...]]
+ *
+ * Leaf operators are one of:
+ *
+ * * '=', '<=', '>=', '>', '<', 'LIKE', "<>", "!=",
+ * * "NOT LIKE", 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN',
+ * * 'IS NOT NULL', or 'IS NULL'.
+ */
+class Api4SelectQuery extends SelectQuery {
+
+ /**
+ * @var int
+ */
+ protected $apiVersion = 4;
+
+ /**
+ * @var array
+ * Maps select fields to [<table_alias>, <column_alias>]
+ */
+ protected $fkSelectAliases = [];
+
+ /**
+ * @var Joinable[]
+ * The joinable tables that have been joined so far
+ */
+ protected $joinedTables = [];
+
+ /**
+ * @param string $entity
+ * @param bool $checkPermissions
+ */
+ public function __construct($entity, $checkPermissions) {
+ require_once 'api/v3/utils.php';
+ $this->entity = $entity;
+ $this->checkPermissions = $checkPermissions;
+
+ $baoName = CoreUtil::getDAOFromApiName($entity);
+ $bao = new $baoName();
+
+ $this->entityFieldNames = _civicrm_api3_field_names(_civicrm_api3_build_fields_array($bao));
+ $this->apiFieldSpec = $this->getFields();
+
+ \CRM_Utils_SQL_Select::from($this->getTableName($baoName) . ' ' . self::MAIN_TABLE_ALIAS);
+
+ // Add ACLs first to avoid redundant subclauses
+ $this->query->where($this->getAclClause(self::MAIN_TABLE_ALIAS, $baoName));
+ }
+
+ /**
+ * Why walk when you can
+ *
+ * @return array|int
+ */
+ public function run() {
+ $this->preRun();
+ $baseResults = parent::run();
+ $event = new PostSelectQueryEvent($baseResults, $this);
+ \Civi::dispatcher()->dispatch(Events::POST_SELECT_QUERY, $event);
+
+ return $event->getResults();
+ }
+
+ /**
+ * Gets all FK fields and does the required joins
+ */
+ protected function preRun() {
+ $allFields = array_merge($this->select, array_keys($this->orderBy));
+ $recurse = function($clauses) use (&$allFields, &$recurse) {
+ foreach ($clauses as $clause) {
+ if ($clause[0] === 'NOT' && is_string($clause[1][0])) {
+ $recurse($clause[1][1]);
+ }
+ elseif (in_array($clause[0], ['AND', 'OR', 'NOT'])) {
+ $recurse($clause[1]);
+ }
+ elseif (is_array($clause[0])) {
+ array_walk($clause, $recurse);
+ }
+ else {
+ $allFields[] = $clause[0];
+ }
+ }
+ };
+ $recurse($this->where);
+ $dotFields = array_unique(array_filter($allFields, function ($field) {
+ return strpos($field, '.') !== FALSE;
+ }));
+
+ foreach ($dotFields as $dotField) {
+ $this->joinFK($dotField);
+ }
+ }
+
+ /**
+ * Populate $this->selectFields
+ *
+ * @throws \Civi\API\Exception\UnauthorizedException
+ */
+ protected function buildSelectFields() {
+ $return_all_fields = (empty($this->select) || !is_array($this->select));
+ $return = $return_all_fields ? $this->entityFieldNames : $this->select;
+ if ($return_all_fields || in_array('custom', $this->select)) {
+ foreach (array_keys($this->apiFieldSpec) as $fieldName) {
+ if (strpos($fieldName, 'custom_') === 0) {
+ $return[] = $fieldName;
+ }
+ }
+ }
+
+ // Always select the ID if the table has one.
+ if (array_key_exists('id', $this->apiFieldSpec) || strstr($this->entity, 'Custom_')) {
+ $this->selectFields[self::MAIN_TABLE_ALIAS . ".id"] = "id";
+ }
+
+ // core return fields
+ foreach ($return as $fieldName) {
+ $field = $this->getField($fieldName);
+ if ($field && in_array($field['name'], $this->entityFieldNames)) {
+ $this->selectFields[self::MAIN_TABLE_ALIAS . "." . UtilsArray::value('column_name', $field, $field['name'])] = $field['name'];
+ }
+ elseif (strpos($fieldName, '.')) {
+ $fkField = $this->addFkField($fieldName, 'LEFT');
+ if ($fkField) {
+ $this->selectFields[implode('.', $fkField)] = $fieldName;
+ }
+ }
+ elseif ($field && strpos($fieldName, 'custom_') === 0) {
+ list($table_name, $column_name) = $this->addCustomField($field, 'LEFT');
+
+ if ($field['data_type'] != 'ContactReference') {
+ // 'ordinary' custom field. We will select the value as custom_XX.
+ $this->selectFields["$table_name.$column_name"] = $fieldName;
+ }
+ else {
+ // contact reference custom field. The ID will be stored in custom_XX_id.
+ // custom_XX will contain the sort name of the contact.
+ $this->query->join("c_$fieldName", "LEFT JOIN civicrm_contact c_$fieldName ON c_$fieldName.id = `$table_name`.`$column_name`");
+ $this->selectFields["$table_name.$column_name"] = $fieldName . "_id";
+ // We will call the contact table for the join c_XX.
+ $this->selectFields["c_$fieldName.sort_name"] = $fieldName;
+ }
+ }
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function buildWhereClause() {
+ foreach ($this->where as $clause) {
+ $sql_clause = $this->treeWalkWhereClause($clause);
+ $this->query->where($sql_clause);
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function buildOrderBy() {
+ foreach ($this->orderBy as $field => $dir) {
+ if ($dir !== 'ASC' && $dir !== 'DESC') {
+ throw new \API_Exception("Invalid sort direction. Cannot order by $field $dir");
+ }
+ if ($this->getField($field)) {
+ $this->query->orderBy(self::MAIN_TABLE_ALIAS . '.' . $field . " $dir");
+ }
+ // TODO: Handle joined fields, custom fields, etc.
+ else {
+ throw new \API_Exception("Invalid sort field. Cannot order by $field $dir");
+ }
+ }
+ }
+
+ /**
+ * Recursively validate and transform a branch or leaf clause array to SQL.
+ *
+ * @param array $clause
+ * @return string SQL where clause
+ *
+ * @uses validateClauseAndComposeSql() to generate the SQL etc.
+ * @todo if an 'and' is nested within and 'and' (or or-in-or) then should
+ * flatten that to be a single list of clauses.
+ */
+ protected function treeWalkWhereClause($clause) {
+ switch ($clause[0]) {
+ case 'OR':
+ case 'AND':
+ // handle branches
+ if (count($clause[1]) === 1) {
+ // a single set so AND|OR is immaterial
+ return $this->treeWalkWhereClause($clause[1][0]);
+ }
+ else {
+ $sql_subclauses = [];
+ foreach ($clause[1] as $subclause) {
+ $sql_subclauses[] = $this->treeWalkWhereClause($subclause);
+ }
+ return '(' . implode("\n" . $clause[0], $sql_subclauses) . ')';
+ }
+
+ case 'NOT':
+ // If we get a group of clauses with no operator, assume AND
+ if (!is_string($clause[1][0])) {
+ $clause[1] = ['AND', $clause[1]];
+ }
+ return 'NOT (' . $this->treeWalkWhereClause($clause[1]) . ')';
+
+ default:
+ return $this->validateClauseAndComposeSql($clause);
+ }
+ }
+
+ /**
+ * Validate and transform a leaf clause array to SQL.
+ * @param array $clause [$fieldName, $operator, $criteria]
+ * @return string SQL
+ * @throws \API_Exception
+ * @throws \Exception
+ */
+ protected function validateClauseAndComposeSql($clause) {
+ // Pad array for unary operators
+ list($key, $operator, $value) = array_pad($clause, 3, NULL);
+ $fieldSpec = $this->getField($key);
+ // derive table and column:
+ $table_name = NULL;
+ $column_name = NULL;
+ if (in_array($key, $this->entityFieldNames)) {
+ $table_name = self::MAIN_TABLE_ALIAS;
+ $column_name = $key;
+ }
+ elseif (strpos($key, '.') && isset($this->fkSelectAliases[$key])) {
+ list($table_name, $column_name) = explode('.', $this->fkSelectAliases[$key]);
+ }
+
+ if (!$table_name || !$column_name) {
+ throw new \API_Exception("Invalid field '$key' in where clause.");
+ }
+
+ FormattingUtil::formatValue($value, $fieldSpec, $this->getEntity());
+
+ $sql_clause = \CRM_Core_DAO::createSQLFilter("`$table_name`.`$column_name`", [$operator => $value]);
+ if ($sql_clause === NULL) {
+ throw new \API_Exception("Invalid value in where clause for field '$key'");
+ }
+ return $sql_clause;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function getFields() {
+ $fields = civicrm_api4($this->entity, 'getFields', ['action' => 'get', 'checkPermissions' => $this->checkPermissions, 'includeCustom' => FALSE])->indexBy('name');
+ return (array) $fields;
+ }
+
+ /**
+ * Fetch a field from the getFields list
+ *
+ * @param string $fieldName
+ *
+ * @return string|null
+ */
+ protected function getField($fieldName) {
+ if ($fieldName) {
+ $fieldPath = explode('.', $fieldName);
+ if (count($fieldPath) > 1) {
+ $fieldName = implode('.', array_slice($fieldPath, -2));
+ }
+ return UtilsArray::value($fieldName, $this->apiFieldSpec);
+ }
+ return NULL;
+ }
+
+ /**
+ * @param $key
+ * @throws \API_Exception
+ */
+ protected function joinFK($key) {
+ $pathArray = explode('.', $key);
+
+ if (count($pathArray) < 2) {
+ return;
+ }
+
+ /** @var \Civi\Api4\Service\Schema\Joiner $joiner */
+ $joiner = \Civi::container()->get('joiner');
+ $field = array_pop($pathArray);
+ $pathString = implode('.', $pathArray);
+
+ if (!$joiner->canJoin($this, $pathString)) {
+ return;
+ }
+
+ $joinPath = $joiner->join($this, $pathString);
+ /** @var Joinable $lastLink */
+ $lastLink = array_pop($joinPath);
+
+ // Cache field info for retrieval by $this->getField()
+ $prefix = array_pop($pathArray) . '.';
+ if (!isset($this->apiFieldSpec[$prefix . $field])) {
+ $joinEntity = $lastLink->getEntity();
+ // Custom fields are already prefixed
+ if ($lastLink instanceof CustomGroupJoinable) {
+ $prefix = '';
+ }
+ foreach ($lastLink->getEntityFields() as $fieldObject) {
+ $this->apiFieldSpec[$prefix . $fieldObject->getName()] = $fieldObject->toArray() + ['entity' => $joinEntity];
+ }
+ }
+
+ if (!$lastLink->getField($field)) {
+ throw new \API_Exception('Invalid join');
+ }
+
+ // custom groups use aliases for field names
+ if ($lastLink instanceof CustomGroupJoinable) {
+ $field = $lastLink->getSqlColumn($field);
+ }
+
+ $this->fkSelectAliases[$key] = sprintf('%s.%s', $lastLink->getAlias(), $field);
+ }
+
+ /**
+ * @param Joinable $joinable
+ *
+ * @return $this
+ */
+ public function addJoinedTable(Joinable $joinable) {
+ $this->joinedTables[] = $joinable;
+
+ return $this;
+ }
+
+ /**
+ * @return FALSE|string
+ */
+ public function getFrom() {
+ return TableHelper::getTableForClass(TableHelper::getFullName($this->entity));
+ }
+
+ /**
+ * @return string
+ */
+ public function getEntity() {
+ return $this->entity;
+ }
+
+ /**
+ * @return array
+ */
+ public function getSelect() {
+ return $this->select;
+ }
+
+ /**
+ * @return array
+ */
+ public function getWhere() {
+ return $this->where;
+ }
+
+ /**
+ * @return array
+ */
+ public function getOrderBy() {
+ return $this->orderBy;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getLimit() {
+ return $this->limit;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getOffset() {
+ return $this->offset;
+ }
+
+ /**
+ * @return array
+ */
+ public function getSelectFields() {
+ return $this->selectFields;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isFillUniqueFields() {
+ return $this->isFillUniqueFields;
+ }
+
+ /**
+ * @return \CRM_Utils_SQL_Select
+ */
+ public function getQuery() {
+ return $this->query;
+ }
+
+ /**
+ * @return array
+ */
+ public function getJoins() {
+ return $this->joins;
+ }
+
+ /**
+ * @return array
+ */
+ public function getApiFieldSpec() {
+ return $this->apiFieldSpec;
+ }
+
+ /**
+ * @return array
+ */
+ public function getEntityFieldNames() {
+ return $this->entityFieldNames;
+ }
+
+ /**
+ * @return array
+ */
+ public function getAclFields() {
+ return $this->aclFields;
+ }
+
+ /**
+ * @return bool|string
+ */
+ public function getCheckPermissions() {
+ return $this->checkPermissions;
+ }
+
+ /**
+ * @return int
+ */
+ public function getApiVersion() {
+ return $this->apiVersion;
+ }
+
+ /**
+ * @return array
+ */
+ public function getFkSelectAliases() {
+ return $this->fkSelectAliases;
+ }
+
+ /**
+ * @return Joinable[]
+ */
+ public function getJoinedTables() {
+ return $this->joinedTables;
+ }
+
+ /**
+ * @return Joinable
+ */
+ public function getJoinedTable($alias) {
+ foreach ($this->joinedTables as $join) {
+ if ($join->getAlias() == $alias) {
+ return $join;
+ }
+ }
+ }
+
+ /**
+ * Get table name on basis of entity
+ *
+ * @param string $baoName
+ *
+ * @return void
+ */
+ public function getTableName($baoName) {
+ if (strstr($this->entity, 'Custom_')) {
+ $this->query = \CRM_Utils_SQL_Select::from(CoreUtil::getCustomTableByName(str_replace('Custom_', '', $this->entity)) . ' ' . self::MAIN_TABLE_ALIAS);
+ $this->entityFieldNames = array_keys($this->apiFieldSpec);
+ }
+ else {
+ $bao = new $baoName();
+ $this->query = \CRM_Utils_SQL_Select::from($bao->tableName() . ' ' . self::MAIN_TABLE_ALIAS);
+ $bao->free();
+ }
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Relationship.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Relationship.php
new file mode 100644
index 00000000..5161f05f
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Relationship.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace Civi\Api4;
+
+/**
+ * Relationship entity.
+ *
+ * @package Civi\Api4
+ */
+class Relationship extends Generic\DAOEntity {
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/RelationshipType.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/RelationshipType.php
new file mode 100644
index 00000000..1cd335cd
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/RelationshipType.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace Civi\Api4;
+
+/**
+ * RelationshipType entity.
+ *
+ * @package Civi\Api4
+ */
+class RelationshipType extends Generic\DAOEntity {
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/Joinable/ActivityToActivityContactAssigneesJoinable.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/Joinable/ActivityToActivityContactAssigneesJoinable.php
new file mode 100644
index 00000000..191f4389
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/Joinable/ActivityToActivityContactAssigneesJoinable.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace Civi\Api4\Service\Schema\Joinable;
+
+class ActivityToActivityContactAssigneesJoinable extends Joinable {
+ /**
+ * @var string
+ */
+ protected $baseTable = 'civicrm_activity';
+
+ /**
+ * @var string
+ */
+ protected $baseColumn = 'id';
+
+ /**
+ * @param $alias
+ */
+ public function __construct($alias) {
+ $optionValueTable = 'civicrm_option_value';
+ $optionGroupTable = 'civicrm_option_group';
+
+ $subSubSelect = sprintf(
+ 'SELECT id FROM %s WHERE name = "%s"',
+ $optionGroupTable,
+ 'activity_contacts'
+ );
+
+ $subSelect = sprintf(
+ 'SELECT value FROM %s WHERE name = "%s" AND option_group_id = (%s)',
+ $optionValueTable,
+ 'Activity Assignees',
+ $subSubSelect
+ );
+
+ $this->addCondition(sprintf('%s.record_type_id = (%s)', $alias, $subSelect));
+ parent::__construct('civicrm_activity_contact', 'activity_id', $alias);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/Joinable/BridgeJoinable.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/Joinable/BridgeJoinable.php
new file mode 100644
index 00000000..370c5898
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/Joinable/BridgeJoinable.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Civi\Api4\Service\Schema\Joinable;
+
+class BridgeJoinable extends Joinable {
+ /**
+ * @var Joinable
+ */
+ protected $middleLink;
+
+ public function __construct($targetTable, $targetColumn, $alias, Joinable $middleLink) {
+ parent::__construct($targetTable, $targetColumn, $alias);
+ $this->middleLink = $middleLink;
+ }
+
+ /**
+ * @return Joinable
+ */
+ public function getMiddleLink() {
+ return $this->middleLink;
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/Joinable/CustomGroupJoinable.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/Joinable/CustomGroupJoinable.php
new file mode 100644
index 00000000..a1dd1a1d
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/Joinable/CustomGroupJoinable.php
@@ -0,0 +1,71 @@
+<?php
+
+namespace Civi\Api4\Service\Schema\Joinable;
+
+use Civi\Api4\CustomField;
+
+class CustomGroupJoinable extends Joinable {
+
+ /**
+ * @var string
+ */
+ protected $joinSide = self::JOIN_SIDE_LEFT;
+
+ /**
+ * @var string
+ *
+ * Name of the custom field column.
+ */
+ protected $columns;
+
+ /**
+ * @param $targetTable
+ * @param $alias
+ * @param bool $isMultiRecord
+ * @param null $entity
+ */
+ public function __construct($targetTable, $alias, $isMultiRecord, $entity, $columns) {
+ $this->entity = $entity;
+ $this->columns = $columns;
+ parent::__construct($targetTable, 'entity_id', $alias);
+ $this->joinType = $isMultiRecord ?
+ self::JOIN_TYPE_ONE_TO_MANY : self::JOIN_TYPE_ONE_TO_ONE;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getEntityFields() {
+ if (!$this->entityFields) {
+ $fields = CustomField::get()
+ ->setSelect(['custom_group.name', 'custom_group_id', 'name', 'label', 'data_type', 'html_type', 'is_required', 'is_searchable', 'is_search_range', 'weight', 'is_active', 'is_view', 'option_group_id', 'default_value'])
+ ->addWhere('custom_group.table_name', '=', $this->getTargetTable())
+ ->execute();
+ foreach ($fields as $field) {
+ $this->entityFields[] = \Civi\Api4\Service\Spec\SpecFormatter::arrayToField($field, $this->getEntity());
+ }
+ }
+ return $this->entityFields;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getField($fieldName) {
+ foreach ($this->getEntityFields() as $field) {
+ $name = $field->getName();
+ if ($name === $fieldName || strrpos($name, '.' . $fieldName) === strlen($name) - strlen($fieldName) - 1) {
+ return $field;
+ }
+ }
+ return NULL;
+ }
+
+ /**
+ * @return string
+ */
+ public function getSqlColumn($fieldName) {
+ return $this->columns[$fieldName];
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/Joinable/Joinable.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/Joinable/Joinable.php
new file mode 100644
index 00000000..0e92e3ab
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/Joinable/Joinable.php
@@ -0,0 +1,277 @@
+<?php
+
+namespace Civi\Api4\Service\Schema\Joinable;
+
+use Civi\Api4\Service\Spec\FieldSpec;
+use CRM_Core_DAO_AllCoreTables as Tables;
+
+class Joinable {
+
+ const JOIN_SIDE_LEFT = 'LEFT';
+ const JOIN_SIDE_INNER = 'INNER';
+
+ const JOIN_TYPE_ONE_TO_ONE = '1_to_1';
+ const JOIN_TYPE_MANY_TO_ONE = 'n_to_1';
+ const JOIN_TYPE_ONE_TO_MANY = '1_to_n';
+
+ /**
+ * @var string
+ */
+ protected $baseTable;
+
+ /**
+ * @var string
+ */
+ protected $baseColumn;
+
+ /**
+ * @var string
+ */
+ protected $targetTable;
+
+ /**
+ * @var string
+ *
+ * Name (or alias) of the target column)
+ */
+ protected $targetColumn;
+
+ /**
+ * @var string
+ */
+ protected $alias;
+
+ /**
+ * @var array
+ */
+ protected $conditions = [];
+
+ /**
+ * @var string
+ */
+ protected $joinSide = self::JOIN_SIDE_LEFT;
+
+ /**
+ * @var int
+ */
+ protected $joinType = self::JOIN_TYPE_ONE_TO_ONE;
+
+ /**
+ * @var string
+ */
+ protected $entity;
+
+ /**
+ * @var array
+ */
+ protected $entityFields;
+
+ /**
+ * @param $targetTable
+ * @param $targetColumn
+ * @param string|null $alias
+ */
+ public function __construct($targetTable, $targetColumn, $alias = NULL) {
+ $this->targetTable = $targetTable;
+ $this->targetColumn = $targetColumn;
+ if (!$this->entity) {
+ $this->entity = Tables::getBriefName(Tables::getClassForTable($targetTable));
+ }
+ $this->alias = $alias ?: str_replace('civicrm_', '', $targetTable);
+ }
+
+ /**
+ * Gets conditions required when joining to a base table
+ *
+ * @param string|null $baseTableAlias
+ * Name of the base table, if aliased.
+ *
+ * @return array
+ */
+ public function getConditionsForJoin($baseTableAlias = NULL) {
+ $baseCondition = sprintf(
+ '%s.%s = %s.%s',
+ $baseTableAlias ?: $this->baseTable,
+ $this->baseColumn,
+ $this->getAlias(),
+ $this->targetColumn
+ );
+
+ return array_merge([$baseCondition], $this->conditions);
+ }
+
+ /**
+ * @return string
+ */
+ public function getBaseTable() {
+ return $this->baseTable;
+ }
+
+ /**
+ * @param string $baseTable
+ *
+ * @return $this
+ */
+ public function setBaseTable($baseTable) {
+ $this->baseTable = $baseTable;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getBaseColumn() {
+ return $this->baseColumn;
+ }
+
+ /**
+ * @param string $baseColumn
+ *
+ * @return $this
+ */
+ public function setBaseColumn($baseColumn) {
+ $this->baseColumn = $baseColumn;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getAlias() {
+ return $this->alias;
+ }
+
+ /**
+ * @param string $alias
+ *
+ * @return $this
+ */
+ public function setAlias($alias) {
+ $this->alias = $alias;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getTargetTable() {
+ return $this->targetTable;
+ }
+
+ /**
+ * @return string
+ */
+ public function getTargetColumn() {
+ return $this->targetColumn;
+ }
+
+ /**
+ * @return string
+ */
+ public function getEntity() {
+ return $this->entity;
+ }
+
+ /**
+ * @param $condition
+ *
+ * @return $this
+ */
+ public function addCondition($condition) {
+ $this->conditions[] = $condition;
+
+ return $this;
+ }
+
+ /**
+ * @return array
+ */
+ public function getExtraJoinConditions() {
+ return $this->conditions;
+ }
+
+ /**
+ * @param array $conditions
+ *
+ * @return $this
+ */
+ public function setConditions($conditions) {
+ $this->conditions = $conditions;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getJoinSide() {
+ return $this->joinSide;
+ }
+
+ /**
+ * @param string $joinSide
+ *
+ * @return $this
+ */
+ public function setJoinSide($joinSide) {
+ $this->joinSide = $joinSide;
+
+ return $this;
+ }
+
+ /**
+ * @return int
+ */
+ public function getJoinType() {
+ return $this->joinType;
+ }
+
+ /**
+ * @param int $joinType
+ *
+ * @return $this
+ */
+ public function setJoinType($joinType) {
+ $this->joinType = $joinType;
+
+ return $this;
+ }
+
+ /**
+ * @return array
+ */
+ public function toArray() {
+ return get_object_vars($this);
+ }
+
+ /**
+ * @return \Civi\Api4\Service\Spec\FieldSpec[]
+ */
+ public function getEntityFields() {
+ if (!$this->entityFields) {
+ $bao = Tables::getClassForTable($this->getTargetTable());
+ if ($bao) {
+ foreach ($bao::fields() as $field) {
+ $this->entityFields[] = \Civi\Api4\Service\Spec\SpecFormatter::arrayToField($field, $this->getEntity());
+ }
+ }
+ }
+ return $this->entityFields;
+ }
+
+ /**
+ * @return \Civi\Api4\Service\Spec\FieldSpec|NULL
+ */
+ public function getField($fieldName) {
+ foreach ($this->getEntityFields() as $field) {
+ if ($field->getName() === $fieldName) {
+ return $field;
+ }
+ }
+ return NULL;
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/Joinable/OptionValueJoinable.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/Joinable/OptionValueJoinable.php
new file mode 100644
index 00000000..96f65488
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/Joinable/OptionValueJoinable.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace Civi\Api4\Service\Schema\Joinable;
+
+class OptionValueJoinable extends Joinable {
+ /**
+ * @var string
+ */
+ protected $optionGroupName;
+
+ /**
+ * @param string $optionGroup
+ * Can be either the option group name or ID
+ * @param string|null $alias
+ * The join alias
+ * @param string $keyColumn
+ * Which column to use to join, defaults to "value"
+ */
+ public function __construct($optionGroup, $alias = NULL, $keyColumn = 'value') {
+ $this->optionGroupName = $optionGroup;
+ $optionValueTable = 'civicrm_option_value';
+
+ // default join alias to option group name, e.g. activity_type
+ if (!$alias && !is_numeric($optionGroup)) {
+ $alias = $optionGroup;
+ }
+
+ parent::__construct($optionValueTable, $keyColumn, $alias);
+
+ if (!is_numeric($optionGroup)) {
+ $subSelect = 'SELECT id FROM civicrm_option_group WHERE name = "%s"';
+ $subQuery = sprintf($subSelect, $optionGroup);
+ $condition = sprintf('%s.option_group_id = (%s)', $alias, $subQuery);
+ }
+ else {
+ $condition = sprintf('%s.option_group_id = %d', $alias, $optionGroup);
+ }
+
+ $this->addCondition($condition);
+ }
+
+ /**
+ * The existing condition must also be re-aliased
+ *
+ * @param string $alias
+ *
+ * @return $this
+ */
+ public function setAlias($alias) {
+ foreach ($this->conditions as $index => $condition) {
+ $search = $this->alias . '.';
+ $replace = $alias . '.';
+ $this->conditions[$index] = str_replace($search, $replace, $condition);
+ }
+
+ parent::setAlias($alias);
+
+ return $this;
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/Joiner.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/Joiner.php
new file mode 100644
index 00000000..cb30ab57
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/Joiner.php
@@ -0,0 +1,98 @@
+<?php
+
+namespace Civi\Api4\Service\Schema;
+
+use Civi\Api4\Query\Api4SelectQuery;
+use Civi\Api4\Service\Schema\Joinable\Joinable;
+
+class Joiner {
+ /**
+ * @var SchemaMap
+ */
+ protected $schemaMap;
+
+ /**
+ * @var Joinable[][]
+ */
+ protected $cache = [];
+
+ /**
+ * @param SchemaMap $schemaMap
+ */
+ public function __construct(SchemaMap $schemaMap) {
+ $this->schemaMap = $schemaMap;
+ }
+
+ /**
+ * @param Api4SelectQuery $query
+ * The query object to do the joins on
+ * @param string $joinPath
+ * A path of aliases in dot notation, e.g. contact.phone
+ * @param string $side
+ * Can be LEFT or INNER
+ *
+ * @throws \Exception
+ * @return Joinable[]
+ * The path used to make the join
+ */
+ public function join(Api4SelectQuery $query, $joinPath, $side = 'LEFT') {
+ $fullPath = $this->getPath($query->getFrom(), $joinPath);
+ $baseTable = $query::MAIN_TABLE_ALIAS;
+
+ foreach ($fullPath as $link) {
+ $target = $link->getTargetTable();
+ $alias = $link->getAlias();
+ $conditions = $link->getConditionsForJoin($baseTable);
+
+ $query->join($side, $target, $alias, $conditions);
+ $query->addJoinedTable($link);
+
+ $baseTable = $link->getAlias();
+ }
+
+ return $fullPath;
+ }
+
+ /**
+ * @param Api4SelectQuery $query
+ * @param $joinPath
+ *
+ * @return bool
+ */
+ public function canJoin(Api4SelectQuery $query, $joinPath) {
+ return !empty($this->getPath($query->getFrom(), $joinPath));
+ }
+
+ /**
+ * @param string $baseTable
+ * @param string $joinPath
+ *
+ * @return array
+ * @throws \Exception
+ */
+ protected function getPath($baseTable, $joinPath) {
+ $cacheKey = sprintf('%s.%s', $baseTable, $joinPath);
+ if (!isset($this->cache[$cacheKey])) {
+ $stack = explode('.', $joinPath);
+ $fullPath = [];
+
+ foreach ($stack as $key => $targetAlias) {
+ $links = $this->schemaMap->getPath($baseTable, $targetAlias);
+
+ if (empty($links)) {
+ throw new \Exception(sprintf('Cannot join %s to %s', $baseTable, $targetAlias));
+ }
+ else {
+ $fullPath = array_merge($fullPath, $links);
+ $lastLink = end($links);
+ $baseTable = $lastLink->getTargetTable();
+ }
+ }
+
+ $this->cache[$cacheKey] = $fullPath;
+ }
+
+ return $this->cache[$cacheKey];
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/SchemaMap.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/SchemaMap.php
new file mode 100644
index 00000000..3989afeb
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/SchemaMap.php
@@ -0,0 +1,140 @@
+<?php
+
+namespace Civi\Api4\Service\Schema;
+
+use Civi\Api4\Service\Schema\Joinable\BridgeJoinable;
+use Civi\Api4\Service\Schema\Joinable\Joinable;
+
+class SchemaMap {
+
+ const MAX_JOIN_DEPTH = 3;
+
+ /**
+ * @var Table[]
+ */
+ protected $tables = [];
+
+ /**
+ * @param $baseTableName
+ * @param $targetTableAlias
+ *
+ * @return Joinable[]
+ * Array of links to the target table, empty if no path found
+ */
+ public function getPath($baseTableName, $targetTableAlias) {
+ $table = $this->getTableByName($baseTableName);
+ $path = [];
+
+ if (!$table) {
+ return $path;
+ }
+
+ $this->findPaths($table, $targetTableAlias, 1, $path);
+
+ foreach ($path as $index => $pathLink) {
+ if ($pathLink instanceof BridgeJoinable) {
+ $start = array_slice($path, 0, $index);
+ $middle = [$pathLink->getMiddleLink()];
+ $end = array_slice($path, $index, count($path) - $index);
+ $path = array_merge($start, $middle, $end);
+ }
+ }
+
+ return $path;
+ }
+
+ /**
+ * @return Table[]
+ */
+ public function getTables() {
+ return $this->tables;
+ }
+
+ /**
+ * @param $name
+ *
+ * @return Table|null
+ */
+ public function getTableByName($name) {
+ foreach ($this->tables as $table) {
+ if ($table->getName() === $name) {
+ return $table;
+ }
+ }
+
+ return NULL;
+ }
+
+ /**
+ * Adds a table to the schema map if it has not already been added
+ *
+ * @param Table $table
+ *
+ * @return $this
+ */
+ public function addTable(Table $table) {
+ if (!$this->getTableByName($table->getName())) {
+ $this->tables[] = $table;
+ }
+
+ return $this;
+ }
+
+ /**
+ * @param array $tables
+ */
+ public function addTables(array $tables) {
+ foreach ($tables as $table) {
+ $this->addTable($table);
+ }
+ }
+
+ /**
+ * Recursive function to traverse the schema looking for a path
+ *
+ * @param Table $table
+ * The current table to base fromm
+ * @param string $target
+ * The target joinable table alias
+ * @param int $depth
+ * The current level of recursion which reflects the number of joins needed
+ * @param Joinable[] $path
+ * (By-reference) The possible paths to the target table
+ * @param Joinable[] $currentPath
+ * For internal use only to track the path to reach the target table
+ */
+ private function findPaths(Table $table, $target, $depth, &$path, $currentPath = []
+ ) {
+ static $visited = [];
+
+ // reset if new call
+ if ($depth === 1) {
+ $visited = [];
+ }
+
+ $canBeShorter = empty($path) || count($currentPath) + 1 < count($path);
+ $tooFar = $depth > self::MAX_JOIN_DEPTH;
+ $beenHere = in_array($table->getName(), $visited);
+
+ if ($tooFar || $beenHere || !$canBeShorter) {
+ return;
+ }
+
+ // prevent circular reference
+ $visited[] = $table->getName();
+
+ foreach ($table->getExternalLinks() as $link) {
+ if ($link->getAlias() === $target) {
+ $path = array_merge($currentPath, [$link]);
+ }
+ else {
+ $linkTable = $this->getTableByName($link->getTargetTable());
+ if ($linkTable) {
+ $nextStep = array_merge($currentPath, [$link]);
+ $this->findPaths($linkTable, $target, $depth + 1, $path, $nextStep);
+ }
+ }
+ }
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/SchemaMapBuilder.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/SchemaMapBuilder.php
new file mode 100644
index 00000000..b578b73a
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/SchemaMapBuilder.php
@@ -0,0 +1,217 @@
+<?php
+
+namespace Civi\Api4\Service\Schema;
+
+use Civi\Api4\Entity;
+use Civi\Api4\Event\Events;
+use Civi\Api4\Event\SchemaMapBuildEvent;
+use Civi\Api4\Service\Schema\Joinable\CustomGroupJoinable;
+use Civi\Api4\Service\Schema\Joinable\Joinable;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Civi\Api4\Service\Schema\Joinable\OptionValueJoinable;
+use CRM_Core_DAO_AllCoreTables as TableHelper;
+use CRM_Utils_Array as UtilsArray;
+
+class SchemaMapBuilder {
+ /**
+ * @var EventDispatcherInterface
+ */
+ protected $dispatcher;
+ /**
+ * @var array
+ */
+ protected $apiEntities;
+
+ /**
+ * @param EventDispatcherInterface $dispatcher
+ */
+ public function __construct(EventDispatcherInterface $dispatcher) {
+ $this->dispatcher = $dispatcher;
+ $this->apiEntities = array_keys((array) Entity::get()->setCheckPermissions(FALSE)->addSelect('name')->execute()->indexBy('name'));
+ }
+
+ /**
+ * @return SchemaMap
+ */
+ public function build() {
+ $map = new SchemaMap();
+ $this->loadTables($map);
+
+ $event = new SchemaMapBuildEvent($map);
+ $this->dispatcher->dispatch(Events::SCHEMA_MAP_BUILD, $event);
+
+ return $map;
+ }
+
+ /**
+ * Add all tables and joins
+ *
+ * @param SchemaMap $map
+ */
+ private function loadTables(SchemaMap $map) {
+ /** @var \CRM_Core_DAO $daoName */
+ foreach (TableHelper::get() as $daoName => $data) {
+ $table = new Table($data['table']);
+ foreach ($daoName::fields() as $field => $fieldData) {
+ $this->addJoins($table, $field, $fieldData);
+ }
+ $map->addTable($table);
+ if (in_array($data['name'], $this->apiEntities)) {
+ $this->addCustomFields($map, $table, $data['name']);
+ }
+ }
+
+ $this->addBackReferences($map);
+ }
+
+ /**
+ * @param Table $table
+ * @param string $field
+ * @param array $data
+ */
+ private function addJoins(Table $table, $field, array $data) {
+ $fkClass = UtilsArray::value('FKClassName', $data);
+
+ // can there be multiple methods e.g. pseudoconstant and fkclass
+ if ($fkClass) {
+ $tableName = TableHelper::getTableForClass($fkClass);
+ $fkKey = UtilsArray::value('FKKeyColumn', $data, 'id');
+ $alias = str_replace('_id', '', $field);
+ $joinable = new Joinable($tableName, $fkKey, $alias);
+ $joinable->setJoinType($joinable::JOIN_TYPE_MANY_TO_ONE);
+ $table->addTableLink($field, $joinable);
+ }
+ elseif (UtilsArray::value('pseudoconstant', $data)) {
+ $this->addPseudoConstantJoin($table, $field, $data);
+ }
+ }
+
+ /**
+ * @param Table $table
+ * @param string $field
+ * @param array $data
+ */
+ private function addPseudoConstantJoin(Table $table, $field, array $data) {
+ $pseudoConstant = UtilsArray::value('pseudoconstant', $data);
+ $tableName = UtilsArray::value('table', $pseudoConstant);
+ $optionGroupName = UtilsArray::value('optionGroupName', $pseudoConstant);
+ $keyColumn = UtilsArray::value('keyColumn', $pseudoConstant, 'id');
+
+ if ($tableName) {
+ $alias = str_replace('civicrm_', '', $tableName);
+ $joinable = new Joinable($tableName, $keyColumn, $alias);
+ $condition = UtilsArray::value('condition', $pseudoConstant);
+ if ($condition) {
+ $joinable->addCondition($condition);
+ }
+ $table->addTableLink($field, $joinable);
+ }
+ elseif ($optionGroupName) {
+ $keyColumn = UtilsArray::value('keyColumn', $pseudoConstant, 'value');
+ $joinable = new OptionValueJoinable($optionGroupName, NULL, $keyColumn);
+
+ if (!empty($data['serialize'])) {
+ $joinable->setJoinType($joinable::JOIN_TYPE_ONE_TO_MANY);
+ }
+
+ $table->addTableLink($field, $joinable);
+ }
+ }
+
+ /**
+ * Loop through existing links and provide link from the other side
+ *
+ * @param SchemaMap $map
+ */
+ private function addBackReferences(SchemaMap $map) {
+ foreach ($map->getTables() as $table) {
+ foreach ($table->getTableLinks() as $link) {
+ // there are too many possible joins from option value so skip
+ if ($link instanceof OptionValueJoinable) {
+ continue;
+ }
+
+ $target = $map->getTableByName($link->getTargetTable());
+ $tableName = $link->getBaseTable();
+ $plural = str_replace('civicrm_', '', $this->getPlural($tableName));
+ $joinable = new Joinable($tableName, $link->getBaseColumn(), $plural);
+ $joinable->setJoinType($joinable::JOIN_TYPE_ONE_TO_MANY);
+ $target->addTableLink($link->getTargetColumn(), $joinable);
+ }
+ }
+ }
+
+ /**
+ * Simple implementation of pluralization.
+ * Could be replaced with symfony/inflector
+ *
+ * @param string $singular
+ *
+ * @return string
+ */
+ private function getPlural($singular) {
+ $last_letter = substr($singular, -1);
+ switch ($last_letter) {
+ case 'y':
+ return substr($singular, 0, -1) . 'ies';
+
+ case 's':
+ return $singular . 'es';
+
+ default:
+ return $singular . 's';
+ }
+ }
+
+ /**
+ * @param \Civi\Api4\Service\Schema\SchemaMap $map
+ * @param \Civi\Api4\Service\Schema\Table $baseTable
+ * @param string $entity
+ */
+ private function addCustomFields(SchemaMap $map, Table $baseTable, $entity) {
+ // Don't be silly
+ if (!array_key_exists($entity, \CRM_Core_SelectValues::customGroupExtends())) {
+ return;
+ }
+ $queryEntity = (array) $entity;
+ if ($entity == 'Contact') {
+ $queryEntity = ['Contact', 'Individual', 'Organization', 'Household'];
+ }
+ $fieldData = \CRM_Utils_SQL_Select::from('civicrm_custom_field f')
+ ->join('custom_group', 'INNER JOIN civicrm_custom_group g ON g.id = f.custom_group_id')
+ ->select(['g.name as custom_group_name', 'g.table_name', 'g.is_multiple', 'f.name', 'label', 'column_name', 'option_group_id'])
+ ->where('g.extends IN (@entity)', ['@entity' => $queryEntity])
+ ->where('g.is_active')
+ ->where('f.is_active')
+ ->execute();
+
+ $links = [];
+
+ while ($fieldData->fetch()) {
+ $tableName = $fieldData->table_name;
+
+ $customTable = $map->getTableByName($tableName);
+ if (!$customTable) {
+ $customTable = new Table($tableName);
+ }
+
+ if (!empty($fieldData->option_group_id)) {
+ $optionValueJoinable = new OptionValueJoinable($fieldData->option_group_id, $fieldData->label);
+ $customTable->addTableLink($fieldData->column_name, $optionValueJoinable);
+ }
+
+ $map->addTable($customTable);
+
+ $alias = $fieldData->custom_group_name;
+ $links[$alias]['tableName'] = $tableName;
+ $links[$alias]['isMultiple'] = !empty($fieldData->is_multiple);
+ $links[$alias]['columns'][$fieldData->name] = $fieldData->column_name;
+ }
+
+ foreach ($links as $alias => $link) {
+ $joinable = new CustomGroupJoinable($link['tableName'], $alias, $link['isMultiple'], $entity, $link['columns']);
+ $baseTable->addTableLink('id', $joinable);
+ }
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/Table.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/Table.php
new file mode 100644
index 00000000..1f464a45
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Schema/Table.php
@@ -0,0 +1,128 @@
+<?php
+
+namespace Civi\Api4\Service\Schema;
+
+use Civi\Api4\Service\Schema\Joinable\Joinable;
+
+class Table {
+
+ /**
+ * @var string
+ */
+ protected $name;
+
+ /**
+ * @var Joinable[]
+ * Array of links to other tables
+ */
+ protected $tableLinks = [];
+
+ /**
+ * @param $name
+ */
+ public function __construct($name) {
+ $this->name = $name;
+ }
+
+ /**
+ * @return string
+ */
+ public function getName() {
+ return $this->name;
+ }
+
+ /**
+ * @param string $name
+ *
+ * @return $this
+ */
+ public function setName($name) {
+ $this->name = $name;
+
+ return $this;
+ }
+
+ /**
+ * @return Joinable[]
+ */
+ public function getTableLinks() {
+ return $this->tableLinks;
+ }
+
+ /**
+ * @return Joinable[]
+ * Only those links that are not joining the table to itself
+ */
+ public function getExternalLinks() {
+ return array_filter($this->tableLinks, function (Joinable $joinable) {
+ return $joinable->getTargetTable() !== $this->getName();
+ });
+ }
+
+ /**
+ * @param Joinable $linkToRemove
+ */
+ public function removeLink(Joinable $linkToRemove) {
+ foreach ($this->tableLinks as $index => $link) {
+ if ($link === $linkToRemove) {
+ unset($this->tableLinks[$index]);
+ }
+ }
+ }
+
+ /**
+ * @param string $baseColumn
+ * @param Joinable $joinable
+ *
+ * @return $this
+ */
+ public function addTableLink($baseColumn, Joinable $joinable) {
+ $target = $joinable->getTargetTable();
+ $targetCol = $joinable->getTargetColumn();
+ $alias = $joinable->getAlias();
+
+ if (!$this->hasLink($target, $targetCol, $alias)) {
+ if (!$joinable->getBaseTable()) {
+ $joinable->setBaseTable($this->getName());
+ }
+ if (!$joinable->getBaseColumn()) {
+ $joinable->setBaseColumn($baseColumn);
+ }
+ $this->tableLinks[] = $joinable;
+ }
+
+ return $this;
+ }
+
+ /**
+ * @param mixed $tableLinks
+ *
+ * @return $this
+ */
+ public function setTableLinks($tableLinks) {
+ $this->tableLinks = $tableLinks;
+
+ return $this;
+ }
+
+ /**
+ * @param $target
+ * @param $targetCol
+ * @param $alias
+ *
+ * @return bool
+ */
+ private function hasLink($target, $targetCol, $alias) {
+ foreach ($this->tableLinks as $link) {
+ if ($link->getTargetTable() === $target
+ && $link->getTargetColumn() === $targetCol
+ && $link->getAlias() === $alias
+ ) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/CustomFieldSpec.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/CustomFieldSpec.php
new file mode 100644
index 00000000..2c689344
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/CustomFieldSpec.php
@@ -0,0 +1,118 @@
+<?php
+
+namespace Civi\Api4\Service\Spec;
+
+class CustomFieldSpec extends FieldSpec {
+ /**
+ * @var int
+ */
+ protected $customFieldId;
+
+ /**
+ * @var int
+ */
+ protected $customGroup;
+
+ /**
+ * @var string
+ */
+ protected $tableName;
+
+ /**
+ * @var string
+ */
+ protected $columnName;
+
+ /**
+ * @inheritDoc
+ */
+ public function setDataType($dataType) {
+ switch ($dataType) {
+ case 'ContactReference':
+ $this->setFkEntity('Contact');
+ $dataType = 'Integer';
+ break;
+
+ case 'File':
+ case 'StateProvince':
+ case 'Country':
+ $this->setFkEntity($dataType);
+ $dataType = 'Integer';
+ break;
+ }
+ return parent::setDataType($dataType);
+ }
+
+ /**
+ * @return int
+ */
+ public function getCustomFieldId() {
+ return $this->customFieldId;
+ }
+
+ /**
+ * @param int $customFieldId
+ *
+ * @return $this
+ */
+ public function setCustomFieldId($customFieldId) {
+ $this->customFieldId = $customFieldId;
+
+ return $this;
+ }
+
+ /**
+ * @return int
+ */
+ public function getCustomGroupName() {
+ return $this->customGroup;
+ }
+
+ /**
+ * @param string $customGroupName
+ *
+ * @return $this
+ */
+ public function setCustomGroupName($customGroupName) {
+ $this->customGroup = $customGroupName;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getCustomTableName() {
+ return $this->tableName;
+ }
+
+ /**
+ * @param string $customFieldColumnName
+ *
+ * @return $this
+ */
+ public function setCustomTableName($customFieldColumnName) {
+ $this->tableName = $customFieldColumnName;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getCustomFieldColumnName() {
+ return $this->columnName;
+ }
+
+ /**
+ * @param string $customFieldColumnName
+ *
+ * @return $this
+ */
+ public function setCustomFieldColumnName($customFieldColumnName) {
+ $this->columnName = $customFieldColumnName;
+
+ return $this;
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/FieldSpec.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/FieldSpec.php
new file mode 100644
index 00000000..1db2941e
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/FieldSpec.php
@@ -0,0 +1,320 @@
+<?php
+
+namespace Civi\Api4\Service\Spec;
+
+use Civi\Api4\Utils\CoreUtil;
+
+class FieldSpec {
+ /**
+ * @var mixed
+ */
+ protected $defaultValue;
+
+ /**
+ * @var string
+ */
+ protected $name;
+
+ /**
+ * @var string
+ */
+ protected $title;
+
+ /**
+ * @var string
+ */
+ protected $entity;
+
+ /**
+ * @var string
+ */
+ protected $description;
+
+ /**
+ * @var bool
+ */
+ protected $required = FALSE;
+
+ /**
+ * @var bool
+ */
+ protected $requiredIf;
+
+ /**
+ * @var array|boolean
+ */
+ protected $options;
+
+ /**
+ * @var string
+ */
+ protected $dataType;
+
+ /**
+ * @var string
+ */
+ protected $fkEntity;
+
+ /**
+ * @var int
+ */
+ protected $serialize;
+
+ /**
+ * Aliases for the valid data types
+ *
+ * @var array
+ */
+ public static $typeAliases = [
+ 'Int' => 'Integer',
+ 'Link' => 'Url',
+ 'Memo' => 'Text',
+ ];
+
+ /**
+ * @param string $name
+ * @param string $entity
+ * @param string $dataType
+ */
+ public function __construct($name, $entity, $dataType = 'String') {
+ $this->entity = $entity;
+ $this->setName($name);
+ $this->setDataType($dataType);
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getDefaultValue() {
+ return $this->defaultValue;
+ }
+
+ /**
+ * @param mixed $defaultValue
+ *
+ * @return $this
+ */
+ public function setDefaultValue($defaultValue) {
+ $this->defaultValue = $defaultValue;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getName() {
+ return $this->name;
+ }
+
+ /**
+ * @param string $name
+ *
+ * @return $this
+ */
+ public function setName($name) {
+ $this->name = $name;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getTitle() {
+ return $this->title;
+ }
+
+ /**
+ * @param string $title
+ *
+ * @return $this
+ */
+ public function setTitle($title) {
+ $this->title = $title;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getEntity() {
+ return $this->entity;
+ }
+
+ /**
+ * @return string
+ */
+ public function getDescription() {
+ return $this->description;
+ }
+
+ /**
+ * @param string $description
+ *
+ * @return $this
+ */
+ public function setDescription($description) {
+ $this->description = $description;
+
+ return $this;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isRequired() {
+ return $this->required;
+ }
+
+ /**
+ * @param bool $required
+ *
+ * @return $this
+ */
+ public function setRequired($required) {
+ $this->required = $required;
+
+ return $this;
+ }
+
+ /**
+ * @return bool
+ */
+ public function getRequiredIf() {
+ return $this->requiredIf;
+ }
+
+ /**
+ * @param bool $required
+ *
+ * @return $this
+ */
+ public function setRequiredIf($requiredIf) {
+ $this->requiredIf = $requiredIf;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getDataType() {
+ return $this->dataType;
+ }
+
+ /**
+ * @param $dataType
+ *
+ * @return $this
+ * @throws \Exception
+ */
+ public function setDataType($dataType) {
+ if (array_key_exists($dataType, self::$typeAliases)) {
+ $dataType = self::$typeAliases[$dataType];
+ }
+
+ if (!in_array($dataType, $this->getValidDataTypes())) {
+ throw new \Exception(sprintf('Invalid data type "%s', $dataType));
+ }
+
+ $this->dataType = $dataType;
+
+ return $this;
+ }
+
+ /**
+ * @return int
+ */
+ public function getSerialize() {
+ return $this->serialize;
+ }
+
+ /**
+ * @param int|null $serialize
+ */
+ public function setSerialize($serialize) {
+ $this->serialize = $serialize;
+ }
+
+ /**
+ * Add valid types that are not not part of \CRM_Utils_Type::dataTypes
+ *
+ * @return array
+ */
+ private function getValidDataTypes() {
+ $extraTypes = ['Boolean', 'Text', 'Float', 'Url'];
+ $extraTypes = array_combine($extraTypes, $extraTypes);
+
+ return array_merge(\CRM_Utils_Type::dataTypes(), $extraTypes);
+ }
+
+ /**
+ * @return array
+ */
+ public function getOptions() {
+ if (!isset($this->options) || $this->options === TRUE) {
+ $fieldName = $this->getName();
+
+ if ($this instanceof CustomFieldSpec) {
+ // buildOptions relies on the custom_* type of field names
+ $fieldName = sprintf('custom_%d', $this->getCustomFieldId());
+ }
+
+ $dao = CoreUtil::getDAOFromApiName($this->getEntity());
+ $options = $dao::buildOptions($fieldName);
+
+ if (!is_array($options) || !$options) {
+ $options = FALSE;
+ }
+
+ $this->setOptions($options);
+ }
+ return $this->options;
+ }
+
+ /**
+ * @param array|bool $options
+ *
+ * @return $this
+ */
+ public function setOptions($options) {
+ $this->options = $options;
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getFkEntity() {
+ return $this->fkEntity;
+ }
+
+ /**
+ * @param string $fkEntity
+ *
+ * @return $this
+ */
+ public function setFkEntity($fkEntity) {
+ $this->fkEntity = $fkEntity;
+
+ return $this;
+ }
+
+ /**
+ * @param array $values
+ * @return array
+ */
+ public function toArray($values = []) {
+ $ret = [];
+ foreach (get_object_vars($this) as $key => $val) {
+ $key = strtolower(preg_replace('/(?=[A-Z])/', '_$0', $key));
+ if (!$values || in_array($key, $values)) {
+ $ret[$key] = $val;
+ }
+ }
+ return $ret;
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/ActionScheduleCreationSpecProvider.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/ActionScheduleCreationSpecProvider.php
new file mode 100644
index 00000000..660bfec9
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/ActionScheduleCreationSpecProvider.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace Civi\Api4\Service\Spec\Provider;
+
+use Civi\Api4\Service\Spec\FieldSpec;
+use Civi\Api4\Service\Spec\RequestSpec;
+
+class ActionScheduleCreationSpecProvider implements SpecProviderInterface {
+ /**
+ * @inheritDoc
+ */
+ public function modifySpec(RequestSpec $spec) {
+ $spec->getFieldByName('title')->setRequired(TRUE);
+ $spec->getFieldByName('mapping_id')->setRequired(TRUE);
+ $spec->getFieldByName('entity_value')->setRequired(TRUE);
+ $spec->getFieldByName('start_action_date')->setRequiredIf('empty($values.absolute_date)');
+ $spec->getFieldByName('absolute_date')->setRequiredIf('empty($values.start_action_date)');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function applies($entity, $action) {
+ return $entity === 'ActionSchedule' && $action === 'create';
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/ActivityCreationSpecProvider.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/ActivityCreationSpecProvider.php
new file mode 100644
index 00000000..dc254342
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/ActivityCreationSpecProvider.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace Civi\Api4\Service\Spec\Provider;
+
+use Civi\Api4\Service\Spec\FieldSpec;
+use Civi\Api4\Service\Spec\RequestSpec;
+
+class ActivityCreationSpecProvider implements SpecProviderInterface {
+ /**
+ * @inheritDoc
+ */
+ public function modifySpec(RequestSpec $spec) {
+ $sourceContactField = new FieldSpec('source_contact_id', 'Activity', 'Integer');
+ $sourceContactField->setRequired(TRUE);
+ $sourceContactField->setFkEntity('Contact');
+
+ $spec->addFieldSpec($sourceContactField);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function applies($entity, $action) {
+ return $entity === 'Activity' && $action === 'create';
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/AddressCreationSpecProvider.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/AddressCreationSpecProvider.php
new file mode 100644
index 00000000..afba9c79
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/AddressCreationSpecProvider.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace Civi\Api4\Service\Spec\Provider;
+
+use Civi\Api4\Service\Spec\FieldSpec;
+use Civi\Api4\Service\Spec\RequestSpec;
+
+
+class AddressCreationSpecProvider implements SpecProviderInterface {
+
+ /**
+ * @param RequestSpec $spec
+ */
+ public function modifySpec(RequestSpec $spec) {
+ $spec->getFieldByName('contact_id')->setRequired(TRUE);
+ $spec->getFieldByName('location_type_id')->setRequired(TRUE);
+ }
+
+ /**
+ * @param string $entity
+ * @param string $action
+ *
+ * @return bool
+ */
+ public function applies($entity, $action) {
+ return $entity === 'Address' && $action === 'create';
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/ContactCreationSpecProvider.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/ContactCreationSpecProvider.php
new file mode 100644
index 00000000..94c68d9d
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/ContactCreationSpecProvider.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace Civi\Api4\Service\Spec\Provider;
+
+use Civi\Api4\Service\Spec\RequestSpec;
+
+class ContactCreationSpecProvider implements SpecProviderInterface {
+
+ /**
+ * @param RequestSpec $spec
+ */
+ public function modifySpec(RequestSpec $spec) {
+ $spec->getFieldByName('contact_type')
+ ->setRequired(TRUE)
+ ->setDefaultValue('Individual');
+
+ $spec->getFieldByName('is_opt_out')->setRequired(FALSE);
+ $spec->getFieldByName('is_deleted')->setRequired(FALSE);
+
+ }
+
+ /**
+ * @param string $entity
+ * @param string $action
+ *
+ * @return bool
+ */
+ public function applies($entity, $action) {
+ return $entity === 'Contact' && $action === 'create';
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/ContactTypeCreationSpecProvider.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/ContactTypeCreationSpecProvider.php
new file mode 100644
index 00000000..f55deb1c
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/ContactTypeCreationSpecProvider.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace Civi\Api4\Service\Spec\Provider;
+
+use Civi\Api4\Service\Spec\RequestSpec;
+
+class ContactTypeCreationSpecProvider implements SpecProviderInterface {
+
+ /**
+ * @param RequestSpec $spec
+ */
+ public function modifySpec(RequestSpec $spec) {
+ $spec->getFieldByName('label')->setRequired(TRUE);
+ $spec->getFieldByName('name')->setRequired(TRUE);
+ $spec->getFieldByName('parent_id')->setRequired(TRUE);
+
+ }
+
+ /**
+ * @param string $entity
+ * @param string $action
+ *
+ * @return bool
+ */
+ public function applies($entity, $action) {
+ return $entity === 'ContactType' && $action === 'create';
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/ContributionCreationSpecProvider.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/ContributionCreationSpecProvider.php
new file mode 100644
index 00000000..14861871
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/ContributionCreationSpecProvider.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Civi\Api4\Service\Spec\Provider;
+
+use Civi\Api4\Service\Spec\RequestSpec;
+
+class ContributionCreationSpecProvider implements SpecProviderInterface {
+ /**
+ * @inheritDoc
+ */
+ public function modifySpec(RequestSpec $spec) {
+ $spec->getFieldByName('financial_type_id')->setRequired(TRUE);
+ $spec->getFieldByName('receive_date')->setDefaultValue('now');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function applies($entity, $action) {
+ return $entity === 'Contribution' && $action === 'create';
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/CustomGroupCreationSpecProvider.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/CustomGroupCreationSpecProvider.php
new file mode 100644
index 00000000..cd033754
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/CustomGroupCreationSpecProvider.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Civi\Api4\Service\Spec\Provider;
+
+use Civi\Api4\Service\Spec\RequestSpec;
+
+class CustomGroupCreationSpecProvider implements SpecProviderInterface {
+ /**
+ * @inheritDoc
+ */
+ public function modifySpec(RequestSpec $spec) {
+ return $spec->getFieldByName('extends')->setRequired(TRUE);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function applies($entity, $action) {
+ return $entity === 'CustomGroup' && $action === 'create';
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/CustomValueSpecProvider.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/CustomValueSpecProvider.php
new file mode 100644
index 00000000..cd82d438
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/CustomValueSpecProvider.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace Civi\Api4\Service\Spec\Provider;
+
+use Civi\Api4\Service\Spec\FieldSpec;
+use Civi\Api4\Service\Spec\RequestSpec;
+
+class CustomValueSpecProvider implements SpecProviderInterface {
+
+ /**
+ * @inheritDoc
+ */
+ public function modifySpec(RequestSpec $spec) {
+ $action = $spec->getAction();
+ if ($action !== 'create') {
+ $idField = new FieldSpec('id', $spec->getEntity(), 'Integer');
+ $idField->setTitle(ts('Custom Value ID'));
+ $spec->addFieldSpec($idField);
+ }
+ $entityField = new FieldSpec('entity_id', $spec->getEntity(), 'Integer');
+ $entityField->setTitle(ts('Entity ID'));
+ $entityField->setRequired($action === 'create');
+ $entityField->setFkEntity('Contact');
+ $spec->addFieldSpec($entityField);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function applies($entity, $action) {
+ return strstr($entity, 'Custom_');
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/EmailCreationSpecProvider.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/EmailCreationSpecProvider.php
new file mode 100644
index 00000000..136b0e54
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/EmailCreationSpecProvider.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace Civi\Api4\Service\Spec\Provider;
+
+use Civi\Api4\Service\Spec\RequestSpec;
+
+class EmailCreationSpecProvider implements SpecProviderInterface {
+ /**
+ * @inheritDoc
+ */
+ public function modifySpec(RequestSpec $spec) {
+ $spec->getFieldByName('contact_id')->setRequired(TRUE);
+ $spec->getFieldByName('email')->setRequired(TRUE);
+ $spec->getFieldByName('on_hold')->setRequired(FALSE);
+ $spec->getFieldByName('is_bulkmail')->setRequired(FALSE);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function applies($entity, $action) {
+ return $entity === 'Email' && $action === 'create';
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/EventCreationSpecProvider.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/EventCreationSpecProvider.php
new file mode 100644
index 00000000..42b74a6f
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/EventCreationSpecProvider.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Civi\Api4\Service\Spec\Provider;
+
+use Civi\Api4\Service\Spec\RequestSpec;
+
+class EventCreationSpecProvider implements SpecProviderInterface {
+ /**
+ * @inheritDoc
+ */
+ public function modifySpec(RequestSpec $spec) {
+ $spec->getFieldByName('is_template')->setRequired(FALSE);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function applies($entity, $action) {
+ return $entity === 'Event' && $action === 'create';
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/GroupCreationSpecProvider.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/GroupCreationSpecProvider.php
new file mode 100644
index 00000000..8af69a0a
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/GroupCreationSpecProvider.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Civi\Api4\Service\Spec\Provider;
+
+use Civi\Api4\Service\Spec\RequestSpec;
+
+class GroupCreationSpecProvider implements SpecProviderInterface {
+ /**
+ * @inheritDoc
+ */
+ public function modifySpec(RequestSpec $spec) {
+ $spec->getFieldByName('title')->setRequired(TRUE);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function applies($entity, $action) {
+ return $entity === 'Group' && $action === 'create';
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/NavigationCreationSpecProvider.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/NavigationCreationSpecProvider.php
new file mode 100644
index 00000000..7d5fc270
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/NavigationCreationSpecProvider.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Civi\Api4\Service\Spec\Provider;
+
+use Civi\Api4\Service\Spec\RequestSpec;
+
+class NavigationCreationSpecProvider implements SpecProviderInterface {
+ /**
+ * @inheritDoc
+ */
+ public function modifySpec(RequestSpec $spec) {
+ $spec->getFieldByName('domain_id')->setRequired(FALSE);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function applies($entity, $action) {
+ return $entity === 'Navigation' && $action === 'create';
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/NoteCreationSpecProvider.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/NoteCreationSpecProvider.php
new file mode 100644
index 00000000..f12e592c
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/NoteCreationSpecProvider.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace Civi\Api4\Service\Spec\Provider;
+
+use Civi\Api4\Service\Spec\RequestSpec;
+
+
+class NoteCreationSpecProvider implements SpecProviderInterface {
+
+ /**
+ * @param RequestSpec $spec
+ */
+ public function modifySpec(RequestSpec $spec) {
+ $spec->getFieldByName('note')->setRequired(TRUE);
+ $spec->getFieldByName('entity_table')->setDefaultValue('civicrm_contact');
+ }
+
+ /**
+ * @param string $entity
+ * @param string $action
+ *
+ * @return bool
+ */
+ public function applies($entity, $action) {
+ return $entity === 'Note' && $action === 'create';
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/OptionValueCreationSpecProvider.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/OptionValueCreationSpecProvider.php
new file mode 100644
index 00000000..4ea634c1
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/OptionValueCreationSpecProvider.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Civi\Api4\Service\Spec\Provider;
+
+use Civi\Api4\Service\Spec\RequestSpec;
+
+class OptionValueCreationSpecProvider implements SpecProviderInterface {
+ /**
+ * @inheritDoc
+ */
+ public function modifySpec(RequestSpec $spec) {
+ $spec->getFieldByName('weight')->setRequired(FALSE);
+ $spec->getFieldByName('value')->setRequired(FALSE);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function applies($entity, $action) {
+ return $entity === 'OptionValue' && $action === 'create';
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/PhoneCreationSpecProvider.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/PhoneCreationSpecProvider.php
new file mode 100644
index 00000000..bb757d43
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/PhoneCreationSpecProvider.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace Civi\Api4\Service\Spec\Provider;
+
+use Civi\Api4\Service\Spec\RequestSpec;
+
+class PhoneCreationSpecProvider implements SpecProviderInterface {
+ /**
+ * @inheritDoc
+ */
+ public function modifySpec(RequestSpec $spec) {
+ $spec->getFieldByName('contact_id')->setRequired(TRUE);
+ $spec->getFieldByName('location_type_id')->setRequired(TRUE);
+ $spec->getFieldByName('phone')->setRequired(TRUE);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function applies($entity, $action) {
+ return $entity === 'Phone' && $action === 'create';
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/SpecProviderInterface.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/SpecProviderInterface.php
new file mode 100644
index 00000000..8be77e68
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/Provider/SpecProviderInterface.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Civi\Api4\Service\Spec\Provider;
+
+use Civi\Api4\Service\Spec\RequestSpec;
+
+interface SpecProviderInterface {
+ /**
+ * @param RequestSpec $spec
+ *
+ * @return void
+ */
+ public function modifySpec(RequestSpec $spec);
+
+ /**
+ * @param string $entity
+ * @param string $action
+ *
+ * @return bool
+ */
+ public function applies($entity, $action);
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/RequestSpec.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/RequestSpec.php
new file mode 100644
index 00000000..9437d930
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/RequestSpec.php
@@ -0,0 +1,110 @@
+<?php
+
+namespace Civi\Api4\Service\Spec;
+
+class RequestSpec {
+
+ /**
+ * @var string
+ */
+ protected $entity;
+
+ /**
+ * @var string
+ */
+ protected $action;
+
+ /**
+ * @var FieldSpec[]
+ */
+ protected $fields = [];
+
+ /**
+ * @param string $entity
+ * @param string $action
+ */
+ public function __construct($entity, $action) {
+ $this->entity = $entity;
+ $this->action = $action;
+ }
+
+ public function addFieldSpec(FieldSpec $field) {
+ $this->fields[] = $field;
+ }
+
+ /**
+ * @param $name
+ *
+ * @return FieldSpec|null
+ */
+ public function getFieldByName($name) {
+ foreach ($this->fields as $field) {
+ if ($field->getName() === $name) {
+ return $field;
+ }
+ }
+
+ return NULL;
+ }
+
+ /**
+ * @return array
+ * Gets all the field names currently part of the specification
+ */
+ public function getFieldNames() {
+ return array_map(function(FieldSpec $field) {
+ return $field->getName();
+ }, $this->fields);
+ }
+
+ /**
+ * @return array|FieldSpec[]
+ */
+ public function getRequiredFields() {
+ return array_filter($this->fields, function (FieldSpec $field) {
+ return $field->isRequired();
+ });
+ }
+
+ /**
+ * @return array|FieldSpec[]
+ */
+ public function getConditionalRequiredFields() {
+ return array_filter($this->fields, function (FieldSpec $field) {
+ return $field->getRequiredIf();
+ });
+ }
+
+ /**
+ * @param array $fieldNames
+ * Optional array of fields to return
+ * @return FieldSpec[]
+ */
+ public function getFields($fieldNames = NULL) {
+ if (!$fieldNames) {
+ return $this->fields;
+ }
+ $fields = [];
+ foreach ($this->fields as $field) {
+ if (in_array($field->getName(), $fieldNames)) {
+ $fields[] = $field;
+ }
+ }
+ return $fields;
+ }
+
+ /**
+ * @return string
+ */
+ public function getEntity() {
+ return $this->entity;
+ }
+
+ /**
+ * @return string
+ */
+ public function getAction() {
+ return $this->action;
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/SpecFormatter.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/SpecFormatter.php
new file mode 100644
index 00000000..c8e4d3da
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/SpecFormatter.php
@@ -0,0 +1,117 @@
+<?php
+
+namespace Civi\Api4\Service\Spec;
+
+use CRM_Utils_Array as ArrayHelper;
+use CRM_Core_DAO_AllCoreTables as TableHelper;
+
+class SpecFormatter {
+ /**
+ * @param FieldSpec[] $fields
+ * @param array $return
+ * @param bool $includeFieldOptions
+ *
+ * @return array
+ */
+ public static function specToArray($fields, $return = [], $includeFieldOptions = FALSE) {
+ $fieldArray = [];
+
+ foreach ($fields as $field) {
+ if ($includeFieldOptions || in_array('options', $return)) {
+ $field->getOptions();
+ }
+ $fieldArray[$field->getName()] = $field->toArray($return);
+ }
+
+ return $fieldArray;
+ }
+
+ /**
+ * @param array $data
+ * @param string $entity
+ *
+ * @return FieldSpec
+ */
+ public static function arrayToField(array $data, $entity) {
+ $dataTypeName = self::getDataType($data);
+
+ if (!empty($data['custom_group_id'])) {
+ $field = new CustomFieldSpec($data['name'], $entity, $dataTypeName);
+ if (strpos($entity, 'Custom_') !== 0) {
+ $field->setName($data['custom_group']['name'] . '.' . $data['name']);
+ }
+ else {
+ $field->setCustomTableName($data['custom_group']['table_name']);
+ $field->setCustomFieldColumnName($data['column_name']);
+ }
+ $field->setCustomFieldId(ArrayHelper::value('id', $data));
+ $field->setCustomGroupName($data['custom_group']['name']);
+ $field->setTitle(ArrayHelper::value('label', $data));
+ $field->setOptions(self::customFieldHasOptions($data));
+ if (\CRM_Core_BAO_CustomField::isSerialized($data)) {
+ $field->setSerialize(\CRM_Core_DAO::SERIALIZE_SEPARATOR_BOOKEND);
+ }
+ }
+ else {
+ $name = ArrayHelper::value('name', $data);
+ $field = new FieldSpec($name, $entity, $dataTypeName);
+ $field->setRequired((bool) ArrayHelper::value('required', $data, FALSE));
+ $field->setTitle(ArrayHelper::value('title', $data));
+ $field->setOptions(!empty($data['pseudoconstant']));
+ $field->setSerialize(ArrayHelper::value('serialize', $data));
+ }
+
+ $field->setDefaultValue(ArrayHelper::value('default', $data));
+ $field->setDescription(ArrayHelper::value('description', $data));
+
+ $fkAPIName = ArrayHelper::value('FKApiName', $data);
+ $fkClassName = ArrayHelper::value('FKClassName', $data);
+ if ($fkAPIName || $fkClassName) {
+ $field->setFkEntity($fkAPIName ?: TableHelper::getBriefName($fkClassName));
+ }
+
+ return $field;
+ }
+
+ /**
+ * Does this custom field have options
+ *
+ * @param array $field
+ * @return bool
+ */
+ private static function customFieldHasOptions($field) {
+ // This will include boolean fields with Yes/No options.
+ if (in_array($field['html_type'], ['Radio', 'CheckBox'])) {
+ return TRUE;
+ }
+ // Do this before the "Select" string search because date fields have a "Select Date" html_type
+ // and contactRef fields have an "Autocomplete-Select" html_type - contacts are an FK not an option list.
+ if (in_array($field['data_type'], ['ContactReference', 'Date'])) {
+ return FALSE;
+ }
+ if (strpos($field['html_type'], 'Select')) {
+ return TRUE;
+ }
+ return !empty($field['option_group_id']);
+ }
+
+ /**
+ * Get the data type from an array. Defaults to 'data_type' with fallback to
+ * mapping for the integer value 'type'
+ *
+ * @param array $data
+ *
+ * @return string
+ */
+ private static function getDataType(array $data) {
+ if (isset($data['data_type'])) {
+ return $data['data_type'];
+ }
+
+ $dataTypeInt = ArrayHelper::value('type', $data);
+ $dataTypeName = \CRM_Utils_Type::typeToString($dataTypeInt);
+
+ return $dataTypeName;
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/SpecGatherer.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/SpecGatherer.php
new file mode 100644
index 00000000..b1c83c89
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Service/Spec/SpecGatherer.php
@@ -0,0 +1,131 @@
+<?php
+
+namespace Civi\Api4\Service\Spec;
+
+use Civi\Api4\CustomField;
+use Civi\Api4\Service\Spec\Provider\SpecProviderInterface;
+use Civi\Api4\Utils\CoreUtil;
+
+class SpecGatherer {
+
+ /**
+ * @var SpecProviderInterface[]
+ */
+ protected $specProviders = [];
+
+ /**
+ * A cache of DAOs based on entity
+ *
+ * @var \CRM_Core_DAO[]
+ */
+ protected $DAONames;
+
+ /**
+ * Returns a RequestSpec with all the fields available. Uses spec providers
+ * to add or modify field specifications.
+ * For an example @see CustomFieldSpecProvider.
+ *
+ * @param string $entity
+ * @param string $action
+ * @param $includeCustom
+ *
+ * @return \Civi\Api4\Service\Spec\RequestSpec
+ */
+ public function getSpec($entity, $action, $includeCustom) {
+ $specification = new RequestSpec($entity, $action);
+
+ // Real entities
+ if (strpos($entity, 'Custom_') !== 0) {
+ $this->addDAOFields($entity, $action, $specification);
+ if ($includeCustom && array_key_exists($entity, \CRM_Core_SelectValues::customGroupExtends())) {
+ $this->addCustomFields($entity, $specification);
+ }
+ }
+ // Custom pseudo-entities
+ else {
+ $this->getCustomGroupFields(substr($entity, 7), $specification);
+ }
+
+ foreach ($this->specProviders as $provider) {
+ if ($provider->applies($entity, $action)) {
+ $provider->modifySpec($specification);
+ }
+ }
+
+ return $specification;
+ }
+
+ /**
+ * @param SpecProviderInterface $provider
+ */
+ public function addSpecProvider(SpecProviderInterface $provider) {
+ $this->specProviders[] = $provider;
+ }
+
+ /**
+ * @param string $entity
+ * @param RequestSpec $specification
+ */
+ private function addDAOFields($entity, $action, RequestSpec $specification) {
+ $DAOFields = $this->getDAOFields($entity);
+
+ foreach ($DAOFields as $DAOField) {
+ if ($DAOField['name'] == 'id' && $action == 'create') {
+ continue;
+ }
+ if ($action !== 'create' || isset($DAOField['default'])) {
+ $DAOField['required'] = FALSE;
+ }
+ if ($DAOField['name'] == 'is_active' && empty($DAOField['default'])) {
+ $DAOField['default'] = '1';
+ }
+ $field = SpecFormatter::arrayToField($DAOField, $entity);
+ $specification->addFieldSpec($field);
+ }
+ }
+
+ /**
+ * @param string $entity
+ * @param RequestSpec $specification
+ */
+ private function addCustomFields($entity, RequestSpec $specification) {
+ $extends = ($entity == 'Contact') ? ['Contact', 'Individual', 'Organization', 'Household'] : [$entity];
+ $customFields = CustomField::get()
+ ->addWhere('custom_group.extends', 'IN', $extends)
+ ->setSelect(['custom_group.name', 'custom_group_id', 'name', 'label', 'data_type', 'html_type', 'is_searchable', 'is_search_range', 'weight', 'is_active', 'is_view', 'option_group_id', 'default_value'])
+ ->execute();
+
+ foreach ($customFields as $fieldArray) {
+ $field = SpecFormatter::arrayToField($fieldArray, $entity);
+ $specification->addFieldSpec($field);
+ }
+ }
+
+ /**
+ * @param string $customGroup
+ * @param RequestSpec $specification
+ */
+ private function getCustomGroupFields($customGroup, RequestSpec $specification) {
+ $customFields = CustomField::get()
+ ->addWhere('custom_group.name', '=', $customGroup)
+ ->setSelect(['custom_group.name', 'custom_group_id', 'name', 'label', 'data_type', 'html_type', 'is_searchable', 'is_search_range', 'weight', 'is_active', 'is_view', 'option_group_id', 'default_value', 'custom_group.table_name', 'column_name'])
+ ->execute();
+
+ foreach ($customFields as $fieldArray) {
+ $field = SpecFormatter::arrayToField($fieldArray, 'Custom_' . $customGroup);
+ $specification->addFieldSpec($field);
+ }
+ }
+
+ /**
+ * @param string $entityName
+ *
+ * @return array
+ */
+ private function getDAOFields($entityName) {
+ $dao = CoreUtil::getDAOFromApiName($entityName);
+
+ return $dao::fields();
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/UFGroup.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/UFGroup.php
new file mode 100644
index 00000000..aeea02c1
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/UFGroup.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace Civi\Api4;
+
+/**
+ * UFGroup entity - AKA profiles.
+ *
+ * @package Civi\Api4
+ */
+class UFGroup extends Generic\DAOEntity {
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/UFJoin.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/UFJoin.php
new file mode 100644
index 00000000..4d68fc7a
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/UFJoin.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace Civi\Api4;
+
+/**
+ * UFJoin entity - links profiles to the components/extensions they are used for.
+ *
+ * @package Civi\Api4
+ */
+class UFJoin extends Generic\DAOEntity {
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Utils/ActionUtil.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Utils/ActionUtil.php
new file mode 100644
index 00000000..628bc6fa
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Utils/ActionUtil.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace Civi\Api4\Utils;
+
+class ActionUtil {
+
+ /**
+ * @param $entityName
+ * @param $actionName
+ * @return \Civi\Api4\Generic\AbstractAction
+ * @throws \Civi\API\Exception\NotImplementedException
+ */
+ public static function getAction($entityName, $actionName) {
+ // For custom pseudo-entities
+ if (strpos($entityName, 'Custom_') === 0) {
+ return \Civi\Api4\CustomValue::$actionName(substr($entityName, 7));
+ }
+ else {
+ $callable = ["\\Civi\\Api4\\$entityName", $actionName];
+ if (!is_callable($callable)) {
+ throw new \Civi\API\Exception\NotImplementedException("API ($entityName, $actionName) does not exist (join the API team and implement it!)");
+ }
+ return call_user_func($callable);
+ }
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Utils/ArrayInsertionUtil.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Utils/ArrayInsertionUtil.php
new file mode 100644
index 00000000..54e3944b
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Utils/ArrayInsertionUtil.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace Civi\Api4\Utils;
+
+use CRM_Utils_Array as UtilsArray;
+
+class ArrayInsertionUtil {
+ /**
+ * If the values to be inserted contain a key _parent_id they will only be
+ * inserted if the parent node ID matches their ID
+ *
+ * @param $array
+ * The array to insert the value in
+ * @param array $parts
+ * Path to insertion point with structure:
+ * [[ name => is_multiple ], ..]
+ * @param mixed $values
+ * The value to be inserted
+ */
+ public static function insert(&$array, $parts, $values) {
+ $key = key($parts);
+ $isMulti = array_shift($parts);
+ if (!isset($array[$key])) {
+ $array[$key] = $isMulti ? [] : NULL;
+ }
+ if (empty($parts)) {
+ $values = self::filterValues($array, $isMulti, $values);
+ $array[$key] = $values;
+ }
+ else {
+ if ($isMulti) {
+ foreach ($array[$key] as &$subArray) {
+ self::insert($subArray, $parts, $values);
+ }
+ }
+ else {
+ self::insert($array[$key], $parts, $values);
+ }
+ }
+ }
+
+ /**
+ * @param $parentArray
+ * @param $isMulti
+ * @param $values
+ *
+ * @return array|mixed
+ */
+ private static function filterValues($parentArray, $isMulti, $values) {
+ $parentID = UtilsArray::value('id', $parentArray);
+
+ if ($parentID) {
+ $values = array_filter($values, function ($value) use ($parentID) {
+ return UtilsArray::value('_parent_id', $value) == $parentID;
+ });
+ }
+
+ $unsets = ['_parent_id', '_base_id'];
+ array_walk($values, function (&$value) use ($unsets) {
+ foreach ($unsets as $unset) {
+ if (isset($value[$unset])) {
+ unset($value[$unset]);
+ }
+ }
+ });
+
+ if (!$isMulti) {
+ $values = array_shift($values);
+ }
+ return $values;
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Utils/CoreUtil.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Utils/CoreUtil.php
new file mode 100644
index 00000000..b43e62ca
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Utils/CoreUtil.php
@@ -0,0 +1,42 @@
+<?php
+
+namespace Civi\Api4\Utils;
+
+use Civi\Api4\CustomGroup;
+
+require_once 'api/v3/utils.php';
+
+class CoreUtil {
+
+ /**
+ * todo this class should not rely on api3 code
+ *
+ * @param $entityName
+ *
+ * @return \CRM_Core_DAO|string
+ * The DAO name for use in static calls. Return doc block is hacked to allow
+ * auto-completion of static methods
+ */
+ public static function getDAOFromApiName($entityName) {
+ if ($entityName === 'CustomValue' || strpos($entityName, 'Custom_') === 0) {
+ return 'CRM_Contact_BAO_Contact';
+ }
+ return \_civicrm_api3_get_DAO($entityName);
+ }
+
+ /**
+ * Get table name of given Custom group
+ *
+ * @param string $customGroupName
+ *
+ * @return string
+ */
+ public static function getCustomTableByName($customGroupName) {
+ return CustomGroup::get()
+ ->addSelect('table_name')
+ ->addWhere('name', '=', $customGroupName)
+ ->execute()
+ ->first()['table_name'];
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Utils/FormattingUtil.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Utils/FormattingUtil.php
new file mode 100644
index 00000000..3a7cdae5
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Utils/FormattingUtil.php
@@ -0,0 +1,106 @@
+<?php
+
+namespace Civi\Api4\Utils;
+
+use CRM_Utils_Array as UtilsArray;
+
+require_once 'api/v3/utils.php';
+
+class FormattingUtil {
+
+ /**
+ * Massage values into the format the BAO expects for a write operation
+ *
+ * @param $params
+ * @param $entity
+ * @param $fields
+ * @throws \API_Exception
+ */
+ public static function formatWriteParams(&$params, $entity, $fields) {
+ foreach ($fields as $name => $field) {
+ if (!empty($params[$name])) {
+ $value =& $params[$name];
+ // Hack for null values -- see comment below
+ if ($value === 'null') {
+ $value = 'Null';
+ }
+ FormattingUtil::formatValue($value, $field, $entity);
+ // Ensure we have an array for serialized fields
+ if (!empty($field['serialize'] && !is_array($value))) {
+ $value = (array) $value;
+ }
+ }
+ /*
+ * Because of the wacky way that database values are saved we need to format
+ * some of the values here. In this strange world the string 'null' is used to
+ * unset values. Hence if we encounter true null we change it to string 'null'.
+ *
+ * If we encounter the string 'null' then we assume the user actually wants to
+ * set the value to string null. However since the string null is reserved for
+ * unsetting values we must change it. Another quirk of the DB_DataObject is
+ * that it allows 'Null' to be set, but any other variation of string 'null'
+ * will be converted to true null, e.g. 'nuLL', 'NUlL' etc. so we change it to
+ * 'Null'.
+ */
+ elseif (array_key_exists($name, $params) && $params[$name] === NULL) {
+ $params[$name] = 'null';
+ }
+
+ if (strstr($entity, 'Custom_')) {
+ if ($name == 'entity_id') {
+ $params['entityID'] = $params['entity_id'];
+ unset($params['entity_id']);
+ }
+ elseif (!empty($field['custom_field_id'])) {
+ $params['custom_' . $field['custom_field_id']] = $params[$name];
+ unset($params[$name]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Transform raw api input to appropriate format for use in a SQL query.
+ *
+ * This is used by read AND write actions (Get, Create, Update, Replace)
+ *
+ * @param $value
+ * @param $fieldSpec
+ * @throws \API_Exception
+ */
+ public static function formatValue(&$value, $fieldSpec, $entity) {
+ if (is_array($value)) {
+ foreach ($value as &$val) {
+ self::formatValue($val, $fieldSpec, $entity);
+ }
+ return;
+ }
+ $fk = UtilsArray::value('fk_entity', $fieldSpec);
+ if ($fieldSpec['name'] == 'id') {
+ $fk = $entity;
+ }
+ $dataType = UtilsArray::value('data_type', $fieldSpec);
+
+ if ($fk === 'Domain' && $value === 'current_domain') {
+ $value = \CRM_Core_Config::domainID();
+ }
+
+ if ($fk === 'Contact' && !is_numeric($value)) {
+ $value = \_civicrm_api3_resolve_contactID($value);
+ if ('unknown-user' === $value) {
+ throw new \API_Exception("\"{$fieldSpec['name']}\" \"{$value}\" cannot be resolved to a contact ID", 2002, ['error_field' => $fieldSpec['name'], "type" => "integer"]);
+ }
+ }
+
+ switch ($dataType) {
+ case 'Timestamp':
+ $value = date('Y-m-d H:i:s', strtotime($value));
+ break;
+
+ case 'Date':
+ $value = date('Ymd', strtotime($value));
+ break;
+ }
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Utils/ReflectionUtils.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Utils/ReflectionUtils.php
new file mode 100644
index 00000000..76662647
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Utils/ReflectionUtils.php
@@ -0,0 +1,119 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 4.7 |
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC (c) 2004-2015 |
+ +--------------------------------------------------------------------+
+ | This file is a part of CiviCRM. |
+ | |
+ | CiviCRM is free software; you can copy, modify, and distribute it |
+ | under the terms of the GNU Affero General Public License |
+ | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
+ | |
+ | CiviCRM is distributed in the hope that it will be useful, but |
+ | WITHOUT ANY WARRANTY; without even the implied warranty of |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
+ | See the GNU Affero General Public License for more details. |
+ | |
+ | You should have received a copy of the GNU Affero General Public |
+ | License and the CiviCRM Licensing Exception along |
+ | with this program; if not, contact CiviCRM LLC |
+ | at info[AT]civicrm[DOT]org. If you have questions about the |
+ | GNU Affero General Public License or the licensing of CiviCRM, |
+ | see the CiviCRM license FAQ at http://civicrm.org/licensing |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Api4\Utils;
+
+/**
+ * Just another place to put static functions...
+ */
+class ReflectionUtils {
+ /**
+ * @param \Reflector|\ReflectionClass $reflection
+ * @param string $type
+ * If we are not reflecting the class itself, specify "Method", "Property", etc.
+ *
+ * @return array
+ */
+ public static function getCodeDocs($reflection, $type = NULL) {
+ $docs = self::parseDocBlock($reflection->getDocComment());
+
+ // Recurse into parent functions
+ if (isset($docs['inheritDoc']) || isset($docs['inheritdoc'])) {
+ unset($docs['inheritDoc'], $docs['inheritdoc']);
+ $newReflection = NULL;
+ try {
+ if ($type) {
+ $name = $reflection->getName();
+ $reflectionClass = $reflection->getDeclaringClass()->getParentClass();
+ if ($reflectionClass) {
+ $getItem = "get$type";
+ $newReflection = $reflectionClass->$getItem($name);
+ }
+ }
+ else {
+ $newReflection = $reflection->getParentClass();
+ }
+ }
+ catch (\ReflectionException $e) {}
+ if ($newReflection) {
+ // Mix in
+ $additionalDocs = self::getCodeDocs($newReflection, $type);
+ if (!empty($docs['comment']) && !empty($additionalDocs['comment'])) {
+ $docs['comment'] .= "\n\n" . $additionalDocs['comment'];
+ }
+ $docs += $additionalDocs;
+ }
+ }
+ return $docs;
+ }
+
+ /**
+ * @param string $comment
+ * @return array
+ */
+ public static function parseDocBlock($comment) {
+ $info = [];
+ foreach (preg_split("/((\r?\n)|(\r\n?))/", $comment) as $num => $line) {
+ if (!$num || strpos($line, '*/') !== FALSE) {
+ continue;
+ }
+ $line = ltrim(trim($line), '* ');
+ if (strpos($line, '@') === 0) {
+ $words = explode(' ', $line);
+ $key = substr($words[0], 1);
+ if ($key == 'var') {
+ $info['type'] = explode('|', $words[1]);
+ }
+ elseif ($key == 'options') {
+ $val = str_replace(', ', ',', implode(' ', array_slice($words, 1)));
+ $info['options'] = explode(',', $val);
+ }
+ else {
+ // Unrecognized annotation, but we'll duly add it to the info array
+ $val = implode(' ', array_slice($words, 1));
+ $info[$key] = strlen($val) ? $val : TRUE;
+ }
+ }
+ elseif ($num == 1) {
+ $info['description'] = $line;
+ }
+ elseif (!$line) {
+ if (isset($info['comment'])) {
+ $info['comment'] .= "\n";
+ }
+ }
+ else {
+ $info['comment'] = isset($info['comment']) ? "{$info['comment']}\n$line" : $line;
+ }
+ }
+ if (isset($info['comment'])) {
+ $info['comment'] = trim($info['comment']);
+ }
+ return $info;
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Website.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Website.php
new file mode 100644
index 00000000..fb890a0f
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/Civi/Api4/Website.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace Civi\Api4;
+
+/**
+ * Website entity.
+ *
+ * @package Civi\Api4
+ */
+class Website extends Generic\DAOEntity {
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/LICENSE.txt b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/LICENSE.txt
new file mode 100644
index 00000000..6a5653f4
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/LICENSE.txt
@@ -0,0 +1,667 @@
+Package: org.civicrm.api4
+Copyright (C) 2016, Coleman Watts <coleman@civicrm.org>
+Licensed under the GNU Affero Public License 3.0 (below).
+
+-------------------------------------------------------------------------------
+
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<http://www.gnu.org/licenses/>.
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4.ang.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4.ang.php
new file mode 100644
index 00000000..f7d69b39
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4.ang.php
@@ -0,0 +1,12 @@
+<?php
+// Autoloader data for Api4 angular module.
+return [
+ 'js' => [
+ 'ang/api4.js',
+ 'ang/api4/*.js',
+ 'ang/api4/*/*.js',
+ ],
+ 'css' => [],
+ 'partials' => [],
+ 'requires' => [],
+];
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4.js b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4.js
new file mode 100644
index 00000000..d1116fc4
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4.js
@@ -0,0 +1,4 @@
+(function(angular, $, _) {
+ // Declare a list of dependencies.
+ angular.module('api4', CRM.angRequires('api4'));
+})(angular, CRM.$, CRM._);
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4/crmApi4.js b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4/crmApi4.js
new file mode 100644
index 00000000..743b3591
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4/crmApi4.js
@@ -0,0 +1,37 @@
+(function(angular, $, _) {
+
+ angular.module('api4').factory('crmApi4', function($q) {
+ var crmApi4 = function(entity, action, params, index) {
+ // JSON serialization in CRM.api4 is not aware of Angular metadata like $$hash, so use angular.toJson()
+ var deferred = $q.defer();
+ var p;
+ var backend = crmApi4.backend || CRM.api4;
+ if (_.isObject(entity)) {
+ // eval content is locally generated.
+ /*jshint -W061 */
+ p = backend(eval('('+angular.toJson(entity)+')'), action);
+ } else {
+ // eval content is locally generated.
+ /*jshint -W061 */
+ p = backend(entity, action, eval('('+angular.toJson(params)+')'), index);
+ }
+ p.then(
+ function(result) {
+ deferred.resolve(result);
+ },
+ function(error) {
+ deferred.reject(error);
+ }
+ );
+ return deferred.promise;
+ };
+ crmApi4.backend = null;
+ crmApi4.val = function(value) {
+ var d = $.Deferred();
+ d.resolve(value);
+ return d.promise();
+ };
+ return crmApi4;
+ });
+
+})(angular, CRM.$, CRM._);
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4Explorer.ang.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4Explorer.ang.php
new file mode 100644
index 00000000..6583f277
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4Explorer.ang.php
@@ -0,0 +1,18 @@
+<?php
+// Autoloader data for Api4 explorer.
+return [
+ 'js' => [
+ 'ang/api4Explorer.js',
+ 'ang/api4Explorer/*.js',
+ 'ang/api4Explorer/*/*.js',
+ 'lib/*.js',
+ ],
+ 'css' => [
+ 'css/explorer.css',
+ ],
+ 'partials' => [
+ 'ang/api4Explorer',
+ ],
+ 'basePages' => [],
+ 'requires' => ['crmUi', 'crmUtil', 'ngRoute', 'crmRouteBinder', 'ui.sortable', 'api4'],
+];
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4Explorer.js b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4Explorer.js
new file mode 100644
index 00000000..85e10c46
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4Explorer.js
@@ -0,0 +1,4 @@
+(function(angular, $, _) {
+ // Declare a list of dependencies.
+ angular.module('api4Explorer', CRM.angRequires('api4Explorer'));
+})(angular, CRM.$, CRM._);
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4Explorer/Chain.html b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4Explorer/Chain.html
new file mode 100644
index 00000000..257efdec
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4Explorer/Chain.html
@@ -0,0 +1,4 @@
+<input class="form-control" ng-model="chain[1][0]" crm-ui-select="{data: entities, allowClear: true, placeholder: 'None'}" />
+<select class="form-control api4-chain-action" ng-model="chain[1][1]" ng-options="a for a in actions" ></select>
+<input class="form-control api4-chain-params" ng-model="chain[1][2]" placeholder="{{ ts('Params') }}" />
+<input class="form-control api4-chain-index" ng-model="chain[1][3]" placeholder="{{ ts('Index') }}" />
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4Explorer/Explorer.html b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4Explorer/Explorer.html
new file mode 100644
index 00000000..6cea301f
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4Explorer/Explorer.html
@@ -0,0 +1,137 @@
+<div id="bootstrap-theme" class="api4-explorer-page">
+ <div crm-ui-debug="availableParams"></div>
+
+ <h1 crm-page-title>
+ {{ ts('CiviCRM API v4') }}{{ entity ? (' (' + entity + '::' + action + ')') : '' }}
+ </h1>
+
+ <!--This warning will show if bootstrap is unavailable. Normally it will be hidden by the bootstrap .collapse class.-->
+ <div class="messages warning no-popup collapse">
+ <p>
+ <i class="crm-i fa-exclamation-triangle"></i>
+ <strong>{{ ts('Bootstrap theme not found.') }}</strong>
+ </p>
+ <p>{{ ts('This screen may not work correctly without a bootstrap-based theme such as Shoreditch installed.') }}</p>
+ </div>
+
+ <div class="api4-explorer-row">
+ <form name="api4-explorer" class="panel panel-default explorer-params-panel">
+ <div class="panel-heading">
+ <div class="form-inline">
+ <input class="collapsible-optgroups form-control" ng-model="entity" ng-disabled="!entities.length" ng-class="{loading: !entities.length}" crm-ui-select="{placeholder: ts('Entity'), data: entities}" />
+ <input class="collapsible-optgroups form-control" ng-model="action" ng-disabled="!entity || !actions.length" ng-class="{loading: entity && !actions.length}" crm-ui-select="{placeholder: ts('Action'), data: actions}" />
+ <input class="form-control api4-index" ng-model="index" ng-mouseenter="help('index', indexHelp)" ng-mouseleave="help()" placeholder="{{ ts('Index') }}" />
+ <button class="btn btn-success pull-right" crm-icon="fa-bolt" ng-disabled="!entity || !action || loading" ng-click="execute()">{{ ts('Execute') }}</button>
+ </div>
+ </div>
+ <div class="panel-body">
+ <div class="api4-input form-inline">
+ <div class="form-control" ng-mouseenter="help(name, param)" ng-mouseleave="help()" ng-class="{'api4-option-selected': params[name]}" ng-repeat="(name, param) in availableParams" ng-if="!isSpecial(name) && param.type[0] === 'bool'">
+ <input type="checkbox" id="api4-param-{{ name }}" ng-model="params[name]"/>
+ <label for="api4-param-{{ name }}">{{ name }}<span class="crm-marker" ng-if="param.required"> *</span></label>
+ </div>
+ </div>
+ <div class="api4-input form-inline" ng-mouseenter="help('select', availableParams.select)" ng-mouseleave="help()" ng-if="availableParams.select">
+ <label for="api4-param-select">select<span class="crm-marker" ng-if="availableParams.select.required"> *</span></label>
+ <input class="collapsible-optgroups form-control" ng-list crm-ui-select="{data: fieldsAndJoins, multiple: true}" id="api4-param-select" ng-model="params.select" style="width: 85%;"/>
+ </div>
+ <div class="api4-input form-inline" ng-mouseenter="help('fields', availableParams.fields)" ng-mouseleave="help()"ng-if="availableParams.fields">
+ <label for="api4-param-fields">fields<span class="crm-marker" ng-if="availableParams.fields.required"> *</span></label>
+ <input class="form-control" ng-list crm-ui-select="{data: fields, multiple: true}" id="api4-param-fields" ng-model="params.fields" style="width: 85%;"/>
+ </div>
+ <div class="api4-input form-inline" ng-mouseenter="help('action', availableParams.action)" ng-mouseleave="help()"ng-if="availableParams.action">
+ <label for="api4-param-action">action<span class="crm-marker" ng-if="availableParams.action.required"> *</span></label>
+ <input class="form-control" crm-ui-select="{data: actions, allowClear: true, placeholder: 'None'}" id="api4-param-action" ng-model="params.action"/>
+ </div>
+ <div class="api4-input form-inline" ng-mouseenter="help(name, param)" ng-mouseleave="help()" ng-repeat="(name, param) in availableParams" ng-if="!isSpecial(name) && (param.type[0] === 'string' || param.type[0] === 'int')">
+ <label for="api4-param-{{ name }}">{{ name }}<span class="crm-marker" ng-if="param.required"> *</span></label>
+ <input class="form-control" type="{{ param.type[0] === 'int' && param.type.length === 1 ? 'number' : 'text' }}" id="api4-param-{{ name }}" ng-model="params[name]"/>
+ <a href class="crm-hover-button" title="Clear" ng-click="clearParam(name)" ng-show="!!params[name]"><i class="crm-i fa-times"></i></a>
+ </div>
+ <div class="api4-input" ng-mouseenter="help(name, param)" ng-mouseleave="help()" ng-repeat="(name, param) in availableParams" ng-if="!isSpecial(name) && param.type[0] === 'array'">
+ <label for="api4-param-{{ name }}">{{ name }}<span class="crm-marker" ng-if="param.required"> *</span></label>
+ <textarea class="form-control" type="{{ param.type[0] === 'int' && param.type.length === 1 ? 'number' : 'text' }}" id="api4-param-{{ name }}" ng-model="params[name]">
+ </textarea>
+ </div>
+ <fieldset ng-if="availableParams.where" class="api4-where-fieldset" ng-mouseenter="help('where', availableParams.where)" ng-mouseleave="help()" crm-api4-where-clause="{where: params.where, required: availableParams.where.required, op: 'AND', label: 'where', fields: fieldsAndJoins}">
+ </fieldset>
+ <fieldset ng-if="availableParams.values" ng-mouseenter="help('values', availableParams.values)" ng-mouseleave="help()">
+ <legend>values<span class="crm-marker" ng-if="availableParams.values.required"> *</span></legend>
+ <div class="api4-input form-inline" ng-repeat="clause in params.values">
+ <input class="collapsible-optgroups form-control" ng-model="clause[0]" crm-ui-select="{formatResult: formatSelect2Item, formatSelection: formatSelect2Item, data: valuesFields, allowClear: true, placeholder: 'Field'}" />
+ <input class="form-control" ng-model="clause[1]" api4-exp-value="{field: clause[0]}" />
+ </div>
+ <div class="api4-input form-inline">
+ <input class="collapsible-optgroups form-control" ng-model="controls.values" crm-ui-select="{formatResult: formatSelect2Item, formatSelection: formatSelect2Item, data: valuesFields}" placeholder="Add value" />
+ </div>
+ </fieldset>
+ <fieldset ng-if="availableParams.orderBy" ng-mouseenter="help('orderBy', availableParams.orderBy)" ng-mouseleave="help()">
+ <legend>orderBy<span class="crm-marker" ng-if="availableParams.orderBy.required"> *</span></legend>
+ <div class="api4-input form-inline" ng-repeat="clause in params.orderBy">
+ <input class="collapsible-optgroups form-control" ng-model="clause[0]" crm-ui-select="{data: fieldsAndJoins, allowClear: true, placeholder: 'Field'}" />
+ <select class="form-control" ng-model="clause[1]">
+ <option value="ASC">ASC</option>
+ <option value="DESC">DESC</option>
+ </select>
+ </div>
+ <div class="api4-input form-inline">
+ <input class="collapsible-optgroups form-control" ng-model="controls.orderBy" crm-ui-select="{data: fieldsAndJoins}" placeholder="Add orderBy" />
+ </div>
+ </fieldset>
+ <fieldset ng-if="availableParams.chain" ng-mouseenter="help('chain', availableParams.chain)" ng-mouseleave="help()">
+ <legend>chain</legend>
+ <div class="api4-input form-inline" ng-repeat="clause in params.chain" api4-exp-chain="clause" entities="entities" main-entity="entity" >
+ </div>
+ <div class="api4-input form-inline">
+ <input class="form-control" ng-model="controls.chain" crm-ui-select="{data: entities}" placeholder="Add chain" />
+ </div>
+ </fieldset>
+ </div>
+ </form>
+ <div class="panel panel-info explorer-help-panel">
+ <div class="panel-heading">
+ <h3 class="panel-title" crm-icon="fa-info-circle">{{ helpTitle }}</h3>
+ </div>
+ <div class="panel-body">
+ <h4>{{ helpContent.description }}</h4>
+ <div ng-if="helpContent.comment">
+ <p ng-repeat='text in helpContent.comment.split("\n\n")'>{{ text }}</p>
+ </div>
+ <p ng-repeat="(key, item) in helpContent" ng-if="key !== 'description' && key !== 'comment'">
+ <strong>{{ key }}:</strong> {{ item }}
+ </p>
+ </div>
+ </div>
+ </div>
+ <div class="api4-explorer-row">
+ <div class="panel panel-warning explorer-code-panel">
+ <div class="panel-heading">
+ <h3 class="panel-title" crm-icon="fa-code">{{ ts('Code') }}</h3>
+ </div>
+ <div class="panel-body">
+ <table>
+ <tr ng-repeat="(type, item) in code">
+ <td>{{ type }}</td>
+ <td><pre>{{ item }}</pre></td>
+ </tr>
+ </table>
+ </div>
+ </div>
+ <div class="panel explorer-result-panel panel-{{ status }}" >
+ <div class="panel-heading">
+ <h3 class="panel-title">
+ <i class="fa fa-circle-o" ng-if="status === 'default'"></i>
+ <i class="fa fa-check-circle" ng-if="status === 'success'"></i>
+ <i class="fa fa-minus-circle" ng-if="status === 'danger'"></i>
+ <i class="fa fa-spinner fa-pulse" ng-if="status === 'warning'"></i>
+ {{ ts('Result') }}
+ </h3>
+ </div>
+ <div class="panel-body">
+ <pre ng-repeat="code in result">{{ code }}</pre>
+ </div>
+ </div>
+ </div>
+
+
+</div>
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4Explorer/Explorer.js b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4Explorer/Explorer.js
new file mode 100644
index 00000000..10391793
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4Explorer/Explorer.js
@@ -0,0 +1,789 @@
+(function(angular, $, _, undefined) {
+
+ // Cache schema metadata
+ var schema = [];
+ // Cache fk schema data
+ var links = [];
+ // Cache list of entities
+ var entities = [];
+ // Cache list of actions
+ var actions = [];
+ // Field options
+ var fieldOptions = {};
+
+
+ angular.module('api4Explorer').config(function($routeProvider) {
+ $routeProvider.when('/explorer/:api4entity?/:api4action?', {
+ controller: 'Api4Explorer',
+ templateUrl: '~/api4Explorer/Explorer.html',
+ reloadOnSearch: false
+ });
+ });
+
+ angular.module('api4Explorer').controller('Api4Explorer', function($scope, $routeParams, $location, $timeout, crmUiHelp, crmApi4) {
+ var ts = $scope.ts = CRM.ts('api4');
+ $scope.entities = entities;
+ $scope.actions = actions;
+ $scope.fields = [];
+ $scope.fieldsAndJoins = [];
+ $scope.availableParams = {};
+ $scope.params = {};
+ $scope.index = '';
+ var getMetaParams = schema.length ? {} : {
+ schema: ['Entity', 'get', {chain: {fields: ['$name', 'getFields']}}],
+ links: ['Entity', 'getLinks']
+ },
+ objectParams = {orderBy: 'ASC', values: '', chain: ['Entity', '', '{}']},
+ helpTitle = '',
+ helpContent = {};
+ $scope.helpTitle = '';
+ $scope.helpContent = {};
+ $scope.entity = $routeParams.api4entity;
+ $scope.result = [];
+ $scope.status = 'default';
+ $scope.loading = false;
+ $scope.controls = {};
+ $scope.code = {
+ php: '',
+ javascript: '',
+ cli: ''
+ };
+
+ $scope.$bindToRoute({
+ expr: 'index',
+ param: 'index',
+ default: ''
+ });
+
+ function ucfirst(str) {
+ return str[0].toUpperCase() + str.slice(1);
+ }
+
+ function lcfirst(str) {
+ return str[0].toLowerCase() + str.slice(1);
+ }
+
+ function pluralize(str) {
+ switch (str[str.length-1]) {
+ case 's':
+ return str + 'es';
+ case 'y':
+ return str.slice(0, -1) + 'ies';
+ default:
+ return str + 's';
+ }
+ }
+
+ // Turn a flat array into a select2 array
+ function arrayToSelect2(array) {
+ var out = [];
+ _.each(array, function(item) {
+ out.push({id: item, text: item});
+ });
+ return out;
+ }
+
+ // Reformat an existing array of objects for compatibility with select2
+ function formatForSelect2(input, container, key, extra, prefix) {
+ _.each(input, function(item) {
+ var id = (prefix || '') + item[key];
+ var formatted = {id: id, text: id};
+ if (extra) {
+ _.merge(formatted, _.pick(item, extra));
+ }
+ container.push(formatted);
+ });
+ return container;
+ }
+
+ function getFieldList(source) {
+ var fields = [],
+ fieldInfo = _.findWhere(getEntity().actions, {name: $scope.action}).fields;
+ formatForSelect2(fieldInfo, fields, 'name', ['description', 'required', 'default_value']);
+ return fields;
+ }
+
+ function addJoins(fieldList) {
+ var fields = _.cloneDeep(fieldList),
+ fks = _.findWhere(links, {entity: $scope.entity}) || {};
+ _.each(fks.links, function(link) {
+ var linkFields = entityFields(link.entity);
+ if (linkFields) {
+ fields.push({
+ text: link.alias,
+ description: 'Join to ' + link.entity,
+ children: formatForSelect2(linkFields, [], 'name', ['description'], link.alias + '.')
+ });
+ }
+ });
+ return fields;
+ }
+
+ $scope.help = function(title, param) {
+ if (!param) {
+ $scope.helpTitle = helpTitle;
+ $scope.helpContent = helpContent;
+ } else {
+ $scope.helpTitle = title;
+ $scope.helpContent = param;
+ }
+ };
+
+ $scope.valuesFields = function() {
+ var fields = _.cloneDeep($scope.fields);
+ // Disable fields that are already in use
+ _.each($scope.params.values || [], function(val) {
+ (_.findWhere(fields, {id: val[0]}) || {}).disabled = true;
+ });
+ return {results: fields};
+ };
+
+ $scope.formatSelect2Item = function(row) {
+ return _.escape(row.text) +
+ (row.required ? '<span class="crm-marker"> *</span>' : '') +
+ (row.description ? '<div class="crm-select2-row-description"><p>' + _.escape(row.description) + '</p></div>' : '');
+ };
+
+ $scope.clearParam = function(name) {
+ $scope.params[name] = $scope.availableParams[name].default;
+ };
+
+ $scope.isSpecial = function(name) {
+ var specialParams = ['select', 'fields', 'action', 'where', 'values', 'orderBy', 'chain'];
+ return _.contains(specialParams, name);
+ };
+
+ function getEntity(entityName) {
+ return _.findWhere(schema, {name: entityName || $scope.entity});
+ }
+
+ // Get all params that have been set
+ function getParams() {
+ var params = {};
+ _.each($scope.params, function(param, key) {
+ if (param != $scope.availableParams[key].default && !(typeof param === 'object' && _.isEmpty(param))) {
+ if (_.contains($scope.availableParams[key].type, 'array') && (typeof objectParams[key] === 'undefined')) {
+ params[key] = parseYaml(_.cloneDeep(param));
+ } else {
+ params[key] = param;
+ }
+ }
+ });
+ _.each(objectParams, function(defaultVal, key) {
+ if (params[key]) {
+ var newParam = {};
+ _.each(params[key], function(item) {
+ newParam[item[0]] = parseYaml(_.cloneDeep(item[1]));
+ });
+ params[key] = newParam;
+ }
+ });
+ if (params.where) {
+ formatWhereClause(params.where);
+ }
+ return params;
+ }
+
+ // Coerce value to an array when the operator is IN or NOT IN
+ // Note this has already been passed through parseYaml once
+ function formatWhereClause(where) {
+ _.each(where, function(clause) {
+ if (_.isArray(clause)) {
+ if (clause.length === 3) {
+ if (_.contains(['IN', 'NOT IN'], clause[1]) && (_.isNumber(clause[2]) || (_.isString(clause[2]) && clause[2].length))) {
+ clause[2] = parseYaml('[' + clause[2] + ']');
+ }
+ } else {
+ formatWhereClause(clause);
+ }
+ }
+ });
+ }
+
+ function parseYaml(input) {
+ if (typeof input === 'undefined') {
+ return undefined;
+ }
+ if (_.isObject(input) || _.isArray(input)) {
+ _.each(input, function(item, index) {
+ input[index] = parseYaml(item);
+ });
+ return input;
+ }
+ try {
+ return input === '>' ? '>' : jsyaml.safeLoad(input);
+ } catch (e) {
+ return input;
+ }
+ }
+
+ function selectAction() {
+ $scope.action = $routeParams.api4action;
+ $scope.fieldsAndJoins = [];
+ formatForSelect2(getEntity().actions, actions, 'name', ['description', 'params']);
+ if ($scope.action) {
+ var actionInfo = _.findWhere(actions, {id: $scope.action});
+ $scope.fields = getFieldList();
+ if (_.contains(['get', 'update', 'delete', 'replace'], $scope.action)) {
+ $scope.fieldsAndJoins = addJoins($scope.fields);
+ } else {
+ $scope.fieldsAndJoins = $scope.fields;
+ }
+ _.each(actionInfo.params, function (param, name) {
+ var format,
+ defaultVal = _.cloneDeep(param.default);
+ if (param.type) {
+ switch (param.type[0]) {
+ case 'int':
+ case 'bool':
+ format = param.type[0];
+ break;
+
+ case 'array':
+ case 'object':
+ format = 'json';
+ break;
+
+ default:
+ format = 'raw';
+ }
+ if (name == 'limit') {
+ defaultVal = 25;
+ }
+ if (name === 'values') {
+ defaultVal = defaultValues(defaultVal);
+ }
+ $scope.$bindToRoute({
+ expr: 'params["' + name + '"]',
+ param: name,
+ format: format,
+ default: defaultVal,
+ deep: format === 'json'
+ });
+ }
+ if (typeof objectParams[name] !== 'undefined') {
+ $scope.$watch('params.' + name, function(values) {
+ // Remove empty values
+ _.each(values, function(clause, index) {
+ if (!clause || !clause[0]) {
+ $scope.params[name].splice(index, 1);
+ }
+ });
+ }, true);
+ $scope.$watch('controls.' + name, function(value) {
+ var field = value;
+ $timeout(function() {
+ if (field) {
+ var defaultOp = _.cloneDeep(objectParams[name]);
+ if (name === 'chain') {
+ var num = $scope.params.chain.length;
+ defaultOp[0] = field;
+ field = 'name_me_' + num;
+ }
+ $scope.params[name].push([field, defaultOp]);
+ $scope.controls[name] = null;
+ }
+ });
+ });
+ }
+ });
+ $scope.availableParams = actionInfo.params;
+ }
+ writeCode();
+ }
+
+ function defaultValues(defaultVal) {
+ _.each($scope.fields, function(field) {
+ if (field.required) {
+ defaultVal.push([field.id, '']);
+ }
+ });
+ return defaultVal;
+ }
+
+ function stringify(value, trim) {
+ if (typeof value === 'undefined') {
+ return '';
+ }
+ var str = JSON.stringify(value).replace(/,/g, ', ');
+ if (trim) {
+ str = str.slice(1, -1);
+ }
+ return str.trim();
+ }
+
+ function writeCode() {
+ var code = {
+ php: ts('Select an entity and action'),
+ javascript: '',
+ cli: ''
+ },
+ entity = $scope.entity,
+ action = $scope.action,
+ params = getParams(),
+ index = isInt($scope.index) ? +$scope.index : $scope.index,
+ result = 'result';
+ if ($scope.entity && $scope.action) {
+ if (action.slice(0, 3) === 'get') {
+ result = entity.substr(0, 7) === 'Custom_' ? _.camelCase(entity.substr(7)) : entity;
+ result = lcfirst(action.replace(/s$/, '').slice(3) || result);
+ }
+ var results = lcfirst(_.isNumber(index) ? result : pluralize(result)),
+ paramCount = _.size(params),
+ i = 0;
+
+ // Write javascript
+ code.javascript = "CRM.api4('" + entity + "', '" + action + "', {";
+ _.each(params, function(param, key) {
+ code.javascript += "\n " + key + ': ' + stringify(param) +
+ (++i < paramCount ? ',' : '');
+ if (key === 'checkPermissions') {
+ code.javascript += ' // IGNORED: permissions are always enforced from client-side requests';
+ }
+ });
+ code.javascript += "\n}";
+ if (index || index === 0) {
+ code.javascript += ', ' + JSON.stringify(index);
+ }
+ code.javascript += ").then(function(" + results + ") {\n // do something with " + results + " array\n}, function(failure) {\n // handle failure\n});";
+
+ // Write php code
+ if (entity.substr(0, 7) !== 'Custom_') {
+ code.php = '$' + results + " = \\Civi\\Api4\\" + entity + '::' + action + '()';
+ } else {
+ code.php = '$' + results + " = \\Civi\\Api4\\CustomValue::" + action + "('" + entity.substr(7) + "')";
+ }
+ _.each(params, function(param, key) {
+ var val = '';
+ if (typeof objectParams[key] !== 'undefined' && key !== 'chain') {
+ _.each(param, function(item, index) {
+ val = phpFormat(index) + ', ' + phpFormat(item, 4);
+ code.php += "\n ->add" + ucfirst(key).replace(/s$/, '') + '(' + val + ')';
+ });
+ } else if (key === 'where') {
+ _.each(param, function (clause) {
+ if (clause[0] === 'AND' || clause[0] === 'OR' || clause[0] === 'NOT') {
+ code.php += "\n ->addClause(" + phpFormat(clause[0]) + ", " + phpFormat(clause[1]).slice(1, -1) + ')';
+ } else {
+ code.php += "\n ->addWhere(" + phpFormat(clause).slice(1, -1) + ")";
+ }
+ });
+ } else {
+ code.php += "\n ->set" + ucfirst(key) + '(' + phpFormat(param, 4) + ')';
+ }
+ });
+ code.php += "\n ->execute()";
+ if (_.isNumber(index)) {
+ code.php += !index ? '\n ->first()' : (index === -1 ? '\n ->last()' : '\n ->itemAt(' + index + ')');
+ } else if (index) {
+ code.php += "\n ->indexBy('" + index + "')";
+ }
+ code.php += ";\n";
+ if (!_.isNumber(index)) {
+ code.php += "foreach ($" + results + ' as $' + ((_.isString(index) && index) ? index + ' => $' : '') + result + ') {\n // do something\n}';
+ }
+
+ // Write cli code
+ code.cli = 'cv api4 ' + entity + '.' + action + " '" + stringify(params) + "'";
+ }
+ $scope.code = code;
+ }
+
+ function isInt(value) {
+ if (_.isNumber(value)) {
+ return true;
+ }
+ if (!_.isString(value)) {
+ return false;
+ }
+ return /^-{0,1}\d+$/.test(value);
+ }
+
+ $scope.execute = function() {
+ $scope.status = 'warning';
+ $scope.loading = true;
+ crmApi4($scope.entity, $scope.action, getParams(), $scope.index)
+ .then(function(data) {
+ var meta = {length: _.size(data)},
+ result = JSON.stringify(data, null, 2);
+ if (_.isArray(data)) {
+ data.length = 0;
+ _.assign(meta, data);
+ }
+ $scope.loading = false;
+ $scope.status = 'success';
+ $scope.result = [JSON.stringify(meta).replace('{', '').replace(/}$/, ''), result];
+ }, function(data) {
+ $scope.loading = false;
+ $scope.status = 'danger';
+ $scope.result = [JSON.stringify(data, null, 2)];
+ });
+ };
+
+ /**
+ * Format value to look like php code
+ */
+ function phpFormat(val, indent) {
+ if (typeof val === 'undefined') {
+ return '';
+ }
+ indent = (typeof indent === 'number') ? _.repeat(' ', indent) : (indent || '');
+ var ret = '',
+ baseLine = indent ? indent.slice(0, -2) : '',
+ newLine = indent ? '\n' : '';
+ if ($.isPlainObject(val)) {
+ $.each(val, function(k, v) {
+ ret += (ret ? ', ' : '') + newLine + indent + "'" + k + "' => " + phpFormat(v);
+ });
+ return '[' + ret + newLine + baseLine + ']';
+ }
+ if ($.isArray(val)) {
+ $.each(val, function(k, v) {
+ ret += (ret ? ', ' : '') + newLine + indent + phpFormat(v);
+ });
+ return '[' + ret + newLine + baseLine + ']';
+ }
+ if (_.isString(val) && !_.contains(val, "'")) {
+ return "'" + val + "'";
+ }
+ return JSON.stringify(val).replace(/\$/g, '\\$');
+ }
+
+ function fetchMeta() {
+ crmApi4(getMetaParams)
+ .then(function(data) {
+ if (data.schema) {
+ schema = data.schema;
+ entities.length = 0;
+ formatForSelect2(schema, entities, 'name', ['description']);
+ if ($scope.entity && !$scope.action) {
+ showEntityHelp($scope.entity);
+ }
+ }
+ if (data.links) {
+ links = data.links;
+ }
+ if (data.actions) {
+ getEntity().actions = data.actions;
+ selectAction();
+ }
+ });
+ }
+
+ // Help for an entity with no action selected
+ function showEntityHelp(entityName) {
+ var entityInfo = getEntity(entityName);
+ $scope.helpTitle = helpTitle = $scope.entity;
+ $scope.helpContent = helpContent = {
+ description: entityInfo.description,
+ comment: entityInfo.comment
+ };
+ }
+
+ if (!$scope.entity) {
+ $scope.helpTitle = helpTitle = ts('Help');
+ $scope.helpContent = helpContent = {description: ts('Welcome to the api explorer.'), comment: ts('Select an entity to begin.')};
+ if (getMetaParams.schema) {
+ fetchMeta();
+ }
+ } else if (!actions.length && (!schema.length || !getEntity().actions)) {
+ if (getMetaParams.schema) {
+ entities.push({id: $scope.entity, text: $scope.entity});
+ }
+ getMetaParams.actions = [$scope.entity, 'getActions', {chain: {fields: [$scope.entity, 'getFields', {action: '$name'}]}}];
+ fetchMeta();
+ } else {
+ selectAction();
+ }
+
+ if ($scope.entity && schema.length) {
+ showEntityHelp($scope.entity);
+ }
+
+ // Update route when changing entity
+ $scope.$watch('entity', function(newVal, oldVal) {
+ if (oldVal !== newVal) {
+ // Flush actions cache to re-fetch for new entity
+ actions = [];
+ $location.url('/explorer/' + newVal);
+ }
+ });
+
+ // Update route when changing actions
+ $scope.$watch('action', function(newVal, oldVal) {
+ if ($scope.entity && $routeParams.api4action !== newVal && !_.isUndefined(newVal)) {
+ $location.url('/explorer/' + $scope.entity + '/' + newVal);
+ } else if (newVal) {
+ $scope.helpTitle = helpTitle = $scope.entity + '::' + newVal;
+ $scope.helpContent = helpContent = _.pick(_.findWhere(getEntity().actions, {name: newVal}), ['description', 'comment']);
+ }
+ });
+
+ $scope.indexHelp = {
+ description: ts('(string|int) Index results or select by index.'),
+ comment: ts('Pass a string to index the results by a field value. E.g. index: "name" will return an associative array with names as keys.') + '\n\n' +
+ ts('Pass an integer to return a single result; e.g. index: 0 will return the first result, 1 will return the second, and -1 will return the last.')
+ };
+
+ $scope.$watch('params', writeCode, true);
+ $scope.$watch('index', writeCode);
+ writeCode();
+
+ });
+
+ angular.module('api4Explorer').directive('crmApi4WhereClause', function($timeout) {
+ return {
+ scope: {
+ data: '=crmApi4WhereClause'
+ },
+ templateUrl: '~/api4Explorer/WhereClause.html',
+ link: function (scope, element, attrs) {
+ var ts = scope.ts = CRM.ts('api4');
+ scope.newClause = '';
+ scope.conjunctions = ['AND', 'OR', 'NOT'];
+ scope.operators = CRM.vars.api4.operators;
+
+ scope.addGroup = function(op) {
+ scope.data.where.push([op, []]);
+ };
+
+ scope.removeGroup = function() {
+ scope.data.groupParent.splice(scope.data.groupIndex, 1);
+ };
+
+ scope.onSort = function(event, ui) {
+ $('.api4-where-fieldset').toggleClass('api4-sorting', event.type === 'sortstart');
+ $('.api4-input.form-inline').css('margin-left', '');
+ };
+
+ // Indent clause while dragging between nested groups
+ scope.onSortOver = function(event, ui) {
+ var offset = 0;
+ if (ui.sender) {
+ offset = $(ui.placeholder).offset().left - $(ui.sender).offset().left;
+ }
+ $('.api4-input.form-inline.ui-sortable-helper').css('margin-left', '' + offset + 'px');
+ };
+
+ scope.$watch('newClause', function(value) {
+ var field = value;
+ $timeout(function() {
+ if (field) {
+ scope.data.where.push([field, '=', '']);
+ scope.newClause = null;
+ }
+ });
+ });
+ scope.$watch('data.where', function(values) {
+ // Remove empty values
+ _.each(values, function(clause, index) {
+ if (typeof clause !== 'undefined' && !clause[0]) {
+ values.splice(index, 1);
+ }
+ });
+ }, true);
+ }
+ };
+ });
+
+ angular.module('api4Explorer').directive('api4ExpValue', function($routeParams, crmApi4) {
+ return {
+ scope: {
+ data: '=api4ExpValue'
+ },
+ link: function (scope, element, attrs) {
+ var ts = scope.ts = CRM.ts('api4'),
+ entity = $routeParams.api4entity;
+
+ function getField(fieldName) {
+ var fieldNames = fieldName.split('.');
+ return get(entity, fieldNames);
+
+ function get(entity, fieldNames) {
+ if (fieldNames.length === 1) {
+ return _.findWhere(entityFields(entity), {name: fieldNames[0]});
+ }
+ var comboName = _.findWhere(entityFields(entity), {name: fieldNames[0] + '.' + fieldNames[1]});
+ if (comboName) {
+ return comboName;
+ }
+ var linkName = fieldNames.shift(),
+ entityLinks = _.findWhere(links, {entity: entity}).links,
+ newEntity = _.findWhere(entityLinks, {alias: linkName}).entity;
+ return get(newEntity, fieldNames);
+ }
+ }
+
+ function destroyWidget() {
+ var $el = $(element);
+ if ($el.is('.crm-form-date-wrapper .crm-hidden-date')) {
+ $el.crmDatepicker('destroy');
+ }
+ if ($el.is('.select2-container + input')) {
+ $el.crmEntityRef('destroy');
+ }
+ $(element).removeData().removeAttr('type').removeAttr('placeholder').show();
+ }
+
+ function makeWidget(field, op) {
+ var $el = $(element),
+ dataType = field.data_type,
+ multi = _.includes(['IN', 'NOT IN'], op);
+ if (op === 'IS NULL' || op === 'IS NOT NULL') {
+ $el.hide();
+ return;
+ }
+ if (dataType === 'Timestamp' || dataType === 'Date') {
+ if (_.includes(['=', '!=', '<>', '<', '>=', '<', '<='], op)) {
+ $el.crmDatepicker({time: dataType === 'Timestamp'});
+ }
+ } else if (_.includes(['=', '!=', '<>', 'IN', 'NOT IN'], op)) {
+ if (field.fk_entity) {
+ $el.crmEntityRef({entity: field.fk_entity, select:{multiple: multi}});
+ } else if (field.options) {
+ $el.addClass('loading').attr('placeholder', ts('- select -')).crmSelect2({multiple: multi, data: [{id: '', text: ''}]});
+ loadFieldOptions(field.entity).then(function(data) {
+ var options = [];
+ _.each(_.findWhere(data, {name: field.name}).options, function(val, key) {
+ options.push({id: key, text: val});
+ });
+ $el.removeClass('loading').select2({data: options, multiple: multi});
+ });
+ } else if (dataType === 'Boolean') {
+ $el.attr('placeholder', ts('- select -')).crmSelect2({allowClear: false, multiple: multi, placeholder: ts('- select -'), data: [
+ {id: '1', text: ts('Yes')},
+ {id: '0', text: ts('No')}
+ ]});
+ }
+ }
+ }
+
+ function loadFieldOptions(entity) {
+ var action = $routeParams.api4action;
+ if (!fieldOptions[entity + action]) {
+ fieldOptions[entity + action] = crmApi4(entity, 'getFields', {
+ loadOptions: true,
+ action: action,
+ where: [["options", "!=", false]],
+ select: ["name", "options"]
+ });
+ }
+ return fieldOptions[entity + action];
+ }
+
+ scope.$watchCollection('data', function(data) {
+ destroyWidget();
+ var field = getField(data.field);
+ if (field) {
+ makeWidget(field, data.op || '=');
+ }
+ });
+ }
+ };
+ });
+
+
+ angular.module('api4Explorer').directive('api4ExpChain', function(crmApi4) {
+ return {
+ scope: {
+ chain: '=api4ExpChain',
+ mainEntity: '=',
+ entities: '='
+ },
+ templateUrl: '~/api4Explorer/Chain.html',
+ link: function (scope, element, attrs) {
+ var ts = scope.ts = CRM.ts('api4');
+
+ function changeEntity(newEntity, oldEntity) {
+ // When clearing entity remove this chain
+ if (!newEntity) {
+ scope.chain[0] = '';
+ return;
+ }
+ // Reset action && index
+ if (newEntity !== oldEntity) {
+ scope.chain[1][1] = scope.chain[1][2] = '';
+ }
+ if (getEntity(newEntity).actions) {
+ setActions();
+ } else {
+ crmApi4(newEntity, 'getActions', {chain: {fields: [newEntity, 'getFields', {action: '$name'}]}})
+ .then(function(data) {
+ getEntity(data.entity).actions = data;
+ if (data.entity === scope.chain[1][0]) {
+ setActions();
+ }
+ });
+ }
+ }
+
+ function setActions() {
+ scope.actions = [''].concat(_.pluck(getEntity(scope.chain[1][0]).actions, 'name'));
+ }
+
+ // Set default params when choosing action
+ function changeAction(newAction, oldAction) {
+ var link;
+ // Prepopulate links
+ if (newAction && newAction !== oldAction) {
+ // Clear index
+ scope.chain[1][3] = '';
+ // Look for links back to main entity
+ _.each(entityFields(scope.chain[1][0]), function(field) {
+ if (field.fk_entity === scope.mainEntity) {
+ link = [field.name, '$id'];
+ }
+ });
+ // Look for links from main entity
+ if (!link && newAction !== 'create') {
+ _.each(entityFields(scope.mainEntity), function(field) {
+ if (field.fk_entity === scope.chain[1][0]) {
+ link = ['id', '$' + field.name];
+ // Since we're specifying the id, set index to getsingle
+ scope.chain[1][3] = '0';
+ }
+ });
+ }
+ if (link && _.contains(['get', 'update', 'replace', 'delete'], newAction)) {
+ scope.chain[1][2] = '{where: [[' + link[0] + ', =, ' + link[1] + ']]}';
+ }
+ else if (link && _.contains(['create'], newAction)) {
+ scope.chain[1][2] = '{values: {' + link[0] + ': ' + link[1] + '}}';
+ } else {
+ scope.chain[1][2] = '{}';
+ }
+ }
+ }
+
+ scope.$watch("chain[1][0]", changeEntity);
+ scope.$watch("chain[1][1]", changeAction);
+ }
+ };
+ });
+
+ function getEntity(entityName) {
+ return _.findWhere(schema, {name: entityName});
+ }
+
+ function entityFields(entityName) {
+ return _.result(getEntity(entityName), 'fields');
+ }
+
+ // Collapsible optgroups for select2
+ $(function() {
+ $('body')
+ .on('select2-open', function(e) {
+ if ($(e.target).hasClass('collapsible-optgroups')) {
+ $('#select2-drop')
+ .off('.collapseOptionGroup')
+ .addClass('collapsible-optgroups-enabled')
+ .on('click.collapseOptionGroup', '.select2-result-with-children > .select2-result-label', function() {
+ $(this).parent().toggleClass('optgroup-expanded');
+ });
+ }
+ })
+ .on('select2-close', function() {
+ $('#select2-drop').off('.collapseOptionGroup').removeClass('collapsible-optgroups-enabled');
+ });
+ });
+})(angular, CRM.$, CRM._);
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4Explorer/WhereClause.html b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4Explorer/WhereClause.html
new file mode 100644
index 00000000..d36480f9
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4Explorer/WhereClause.html
@@ -0,0 +1,39 @@
+<legend>{{ data.label || data.op + ' group' }}<span class="crm-marker" ng-if="data.required"> *</span></legend>
+<div class="btn-group btn-group-xs" ng-if="data.groupParent">
+ <button class="btn btn-danger-outline" ng-click="removeGroup()" title="{{ ts('Remove group') }}">
+ <i class="crm-i fa-trash"></i>
+ </button>
+</div>
+<div class="api4-where-group-sortable" ng-model="data.where" ui-sortable="{axis: 'y', connectWith: '.api4-where-group-sortable', containment: '.api4-where-fieldset', over: onSortOver, start: onSort, stop: onSort}">
+ <div class="api4-input form-inline clearfix" ng-repeat="(index, clause) in data.where">
+ <div class="api4-clause-badge" title="{{ ts('Drag to reposition') }}">
+ <span class="badge badge-info">
+ <span ng-if="!index && !data.groupParent">Where</span>
+ <span ng-if="index || data.groupParent">{{ data.op }}</span>
+ <i class="crm-i fa-arrows"></i>
+ </span>
+ </div>
+ <div ng-if="clause[0] !== 'AND' && clause[0] !== 'OR' && clause[0] !== 'NOT'" class="api4-input-group">
+ <input class="collapsible-optgroups form-control" ng-model="clause[0]" crm-ui-select="{data: data.fields, allowClear: true, placeholder: 'Field'}" />
+ <select class="form-control api4-operator" ng-model="clause[1]" ng-options="o for o in operators" ></select>
+ <input class="form-control" ng-model="clause[2]" api4-exp-value="{field: clause[0], op: clause[1]}" />
+ </div>
+ <fieldset class="clearfix" ng-if="clause[0] === 'AND' || clause[0] === 'OR' || clause[0] === 'NOT'" crm-api4-where-clause="{where: clause[1], op: clause[0], fields: data.fields, operators: data.operators, groupParent: data.where, groupIndex: index}">
+ </fieldset>
+ </div>
+</div>
+<div class="api4-input form-inline">
+ <div class="api4-clause-badge">
+ <div class="btn-group btn-group-xs" title="{{ data.groupParent ? ts('Add a subgroup of clauses') : ts('Add a group of clauses') }}">
+ <button type="button" class="btn btn-info dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+ {{ data.op }} <span class="caret"></span>
+ </button>
+ <ul class="dropdown-menu api4-add-where-group-menu">
+ <li ng-repeat="con in conjunctions" ng-if="data.op !== con">
+ <a href ng-click="addGroup(con)">{{ con }}</a>
+ </li>
+ </ul>
+ </div>
+ </div>
+ <input class="collapsible-optgroups form-control" ng-model="newClause" title="Add a single clause" crm-ui-select="{data: data.fields, placeholder: 'Add clause'}" />
+</div> \ No newline at end of file
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/api4.civix.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/api4.civix.php
new file mode 100644
index 00000000..4bd0b6be
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/api4.civix.php
@@ -0,0 +1,460 @@
+<?php
+
+// AUTO-GENERATED FILE -- Civix may overwrite any changes made to this file
+
+/**
+ * The ExtensionUtil class provides small stubs for accessing resources of this
+ * extension.
+ */
+class CRM_Api4_ExtensionUtil {
+ const SHORT_NAME = "api4";
+ const LONG_NAME = "org.civicrm.api4";
+ const CLASS_PREFIX = "CRM_Api4";
+
+ /**
+ * Translate a string using the extension's domain.
+ *
+ * If the extension doesn't have a specific translation
+ * for the string, fallback to the default translations.
+ *
+ * @param string $text
+ * Canonical message text (generally en_US).
+ * @param array $params
+ * @return string
+ * Translated text.
+ * @see ts
+ */
+ public static function ts($text, $params = array()) {
+ if (!array_key_exists('domain', $params)) {
+ $params['domain'] = array(self::LONG_NAME, NULL);
+ }
+ return ts($text, $params);
+ }
+
+ /**
+ * Get the URL of a resource file (in this extension).
+ *
+ * @param string|NULL $file
+ * Ex: NULL.
+ * Ex: 'css/foo.css'.
+ * @return string
+ * Ex: 'http://example.org/sites/default/ext/org.example.foo'.
+ * Ex: 'http://example.org/sites/default/ext/org.example.foo/css/foo.css'.
+ */
+ public static function url($file = NULL) {
+ if ($file === NULL) {
+ return rtrim(CRM_Core_Resources::singleton()->getUrl(self::LONG_NAME), '/');
+ }
+ return CRM_Core_Resources::singleton()->getUrl(self::LONG_NAME, $file);
+ }
+
+ /**
+ * Get the path of a resource file (in this extension).
+ *
+ * @param string|NULL $file
+ * Ex: NULL.
+ * Ex: 'css/foo.css'.
+ * @return string
+ * Ex: '/var/www/example.org/sites/default/ext/org.example.foo'.
+ * Ex: '/var/www/example.org/sites/default/ext/org.example.foo/css/foo.css'.
+ */
+ public static function path($file = NULL) {
+ // return CRM_Core_Resources::singleton()->getPath(self::LONG_NAME, $file);
+ return __DIR__ . ($file === NULL ? '' : (DIRECTORY_SEPARATOR . $file));
+ }
+
+ /**
+ * Get the name of a class within this extension.
+ *
+ * @param string $suffix
+ * Ex: 'Page_HelloWorld' or 'Page\\HelloWorld'.
+ * @return string
+ * Ex: 'CRM_Foo_Page_HelloWorld'.
+ */
+ public static function findClass($suffix) {
+ return self::CLASS_PREFIX . '_' . str_replace('\\', '_', $suffix);
+ }
+
+}
+
+use CRM_Api4_ExtensionUtil as E;
+
+/**
+ * (Delegated) Implements hook_civicrm_config().
+ *
+ * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_config
+ */
+function _api4_civix_civicrm_config(&$config = NULL) {
+ static $configured = FALSE;
+ if ($configured) {
+ return;
+ }
+ $configured = TRUE;
+
+ $template =& CRM_Core_Smarty::singleton();
+
+ $extRoot = dirname(__FILE__) . DIRECTORY_SEPARATOR;
+ $extDir = $extRoot . 'templates';
+
+ if (is_array($template->template_dir)) {
+ array_unshift($template->template_dir, $extDir);
+ }
+ else {
+ $template->template_dir = array($extDir, $template->template_dir);
+ }
+
+ $include_path = $extRoot . PATH_SEPARATOR . get_include_path();
+ set_include_path($include_path);
+}
+
+/**
+ * (Delegated) Implements hook_civicrm_xmlMenu().
+ *
+ * @param $files array(string)
+ *
+ * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_xmlMenu
+ */
+function _api4_civix_civicrm_xmlMenu(&$files) {
+ foreach (_api4_civix_glob(__DIR__ . '/xml/Menu/*.xml') as $file) {
+ $files[] = $file;
+ }
+}
+
+/**
+ * Implements hook_civicrm_install().
+ *
+ * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_install
+ */
+function _api4_civix_civicrm_install() {
+ _api4_civix_civicrm_config();
+ if ($upgrader = _api4_civix_upgrader()) {
+ $upgrader->onInstall();
+ }
+}
+
+/**
+ * Implements hook_civicrm_postInstall().
+ *
+ * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_postInstall
+ */
+function _api4_civix_civicrm_postInstall() {
+ _api4_civix_civicrm_config();
+ if ($upgrader = _api4_civix_upgrader()) {
+ if (is_callable(array($upgrader, 'onPostInstall'))) {
+ $upgrader->onPostInstall();
+ }
+ }
+}
+
+/**
+ * Implements hook_civicrm_uninstall().
+ *
+ * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_uninstall
+ */
+function _api4_civix_civicrm_uninstall() {
+ _api4_civix_civicrm_config();
+ if ($upgrader = _api4_civix_upgrader()) {
+ $upgrader->onUninstall();
+ }
+}
+
+/**
+ * (Delegated) Implements hook_civicrm_enable().
+ *
+ * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_enable
+ */
+function _api4_civix_civicrm_enable() {
+ _api4_civix_civicrm_config();
+ if ($upgrader = _api4_civix_upgrader()) {
+ if (is_callable(array($upgrader, 'onEnable'))) {
+ $upgrader->onEnable();
+ }
+ }
+}
+
+/**
+ * (Delegated) Implements hook_civicrm_disable().
+ *
+ * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_disable
+ * @return mixed
+ */
+function _api4_civix_civicrm_disable() {
+ _api4_civix_civicrm_config();
+ if ($upgrader = _api4_civix_upgrader()) {
+ if (is_callable(array($upgrader, 'onDisable'))) {
+ $upgrader->onDisable();
+ }
+ }
+}
+
+/**
+ * (Delegated) Implements hook_civicrm_upgrade().
+ *
+ * @param $op string, the type of operation being performed; 'check' or 'enqueue'
+ * @param $queue CRM_Queue_Queue, (for 'enqueue') the modifiable list of pending up upgrade tasks
+ *
+ * @return mixed based on op. for 'check', returns array(boolean) (TRUE if upgrades are pending)
+ * for 'enqueue', returns void
+ *
+ * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_upgrade
+ */
+function _api4_civix_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
+ if ($upgrader = _api4_civix_upgrader()) {
+ return $upgrader->onUpgrade($op, $queue);
+ }
+}
+
+/**
+ * @return CRM_Api4_Upgrader
+ */
+function _api4_civix_upgrader() {
+ if (!file_exists(__DIR__ . '/CRM/Api4/Upgrader.php')) {
+ return NULL;
+ }
+ else {
+ return CRM_Api4_Upgrader_Base::instance();
+ }
+}
+
+/**
+ * Search directory tree for files which match a glob pattern
+ *
+ * Note: Dot-directories (like "..", ".git", or ".svn") will be ignored.
+ * Note: In Civi 4.3+, delegate to CRM_Utils_File::findFiles()
+ *
+ * @param $dir string, base dir
+ * @param $pattern string, glob pattern, eg "*.txt"
+ * @return array(string)
+ */
+function _api4_civix_find_files($dir, $pattern) {
+ if (is_callable(array('CRM_Utils_File', 'findFiles'))) {
+ return CRM_Utils_File::findFiles($dir, $pattern);
+ }
+
+ $todos = array($dir);
+ $result = array();
+ while (!empty($todos)) {
+ $subdir = array_shift($todos);
+ foreach (_api4_civix_glob("$subdir/$pattern") as $match) {
+ if (!is_dir($match)) {
+ $result[] = $match;
+ }
+ }
+ if ($dh = opendir($subdir)) {
+ while (FALSE !== ($entry = readdir($dh))) {
+ $path = $subdir . DIRECTORY_SEPARATOR . $entry;
+ if ($entry{0} == '.') {
+ }
+ elseif (is_dir($path)) {
+ $todos[] = $path;
+ }
+ }
+ closedir($dh);
+ }
+ }
+ return $result;
+}
+/**
+ * (Delegated) Implements hook_civicrm_managed().
+ *
+ * Find any *.mgd.php files, merge their content, and return.
+ *
+ * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_managed
+ */
+function _api4_civix_civicrm_managed(&$entities) {
+ $mgdFiles = _api4_civix_find_files(__DIR__, '*.mgd.php');
+ foreach ($mgdFiles as $file) {
+ $es = include $file;
+ foreach ($es as $e) {
+ if (empty($e['module'])) {
+ $e['module'] = E::LONG_NAME;
+ }
+ $entities[] = $e;
+ if (empty($e['params']['version'])) {
+ $e['params']['version'] = '3';
+ }
+ }
+ }
+}
+
+/**
+ * (Delegated) Implements hook_civicrm_caseTypes().
+ *
+ * Find any and return any files matching "xml/case/*.xml"
+ *
+ * Note: This hook only runs in CiviCRM 4.4+.
+ *
+ * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_caseTypes
+ */
+function _api4_civix_civicrm_caseTypes(&$caseTypes) {
+ if (!is_dir(__DIR__ . '/xml/case')) {
+ return;
+ }
+
+ foreach (_api4_civix_glob(__DIR__ . '/xml/case/*.xml') as $file) {
+ $name = preg_replace('/\.xml$/', '', basename($file));
+ if ($name != CRM_Case_XMLProcessor::mungeCaseType($name)) {
+ $errorMessage = sprintf("Case-type file name is malformed (%s vs %s)", $name, CRM_Case_XMLProcessor::mungeCaseType($name));
+ CRM_Core_Error::fatal($errorMessage);
+ // throw new CRM_Core_Exception($errorMessage);
+ }
+ $caseTypes[$name] = array(
+ 'module' => E::LONG_NAME,
+ 'name' => $name,
+ 'file' => $file,
+ );
+ }
+}
+
+/**
+ * (Delegated) Implements hook_civicrm_angularModules().
+ *
+ * Find any and return any files matching "ang/*.ang.php"
+ *
+ * Note: This hook only runs in CiviCRM 4.5+.
+ *
+ * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_angularModules
+ */
+function _api4_civix_civicrm_angularModules(&$angularModules) {
+ if (!is_dir(__DIR__ . '/ang')) {
+ return;
+ }
+
+ $files = _api4_civix_glob(__DIR__ . '/ang/*.ang.php');
+ foreach ($files as $file) {
+ $name = preg_replace(':\.ang\.php$:', '', basename($file));
+ $module = include $file;
+ if (empty($module['ext'])) {
+ $module['ext'] = E::LONG_NAME;
+ }
+ $angularModules[$name] = $module;
+ }
+}
+
+/**
+ * Glob wrapper which is guaranteed to return an array.
+ *
+ * The documentation for glob() says, "On some systems it is impossible to
+ * distinguish between empty match and an error." Anecdotally, the return
+ * result for an empty match is sometimes array() and sometimes FALSE.
+ * This wrapper provides consistency.
+ *
+ * @link http://php.net/glob
+ * @param string $pattern
+ * @return array, possibly empty
+ */
+function _api4_civix_glob($pattern) {
+ $result = glob($pattern);
+ return is_array($result) ? $result : array();
+}
+
+/**
+ * Inserts a navigation menu item at a given place in the hierarchy.
+ *
+ * @param array $menu - menu hierarchy
+ * @param string $path - path to parent of this item, e.g. 'my_extension/submenu'
+ * 'Mailing', or 'Administer/System Settings'
+ * @param array $item - the item to insert (parent/child attributes will be
+ * filled for you)
+ */
+function _api4_civix_insert_navigation_menu(&$menu, $path, $item) {
+ // If we are done going down the path, insert menu
+ if (empty($path)) {
+ $menu[] = array(
+ 'attributes' => array_merge(array(
+ 'label' => CRM_Utils_Array::value('name', $item),
+ 'active' => 1,
+ ), $item),
+ );
+ return TRUE;
+ }
+ else {
+ // Find an recurse into the next level down
+ $found = FALSE;
+ $path = explode('/', $path);
+ $first = array_shift($path);
+ foreach ($menu as $key => &$entry) {
+ if ($entry['attributes']['name'] == $first) {
+ if (!isset($entry['child'])) {
+ $entry['child'] = array();
+ }
+ $found = _api4_civix_insert_navigation_menu($entry['child'], implode('/', $path), $item, $key);
+ }
+ }
+ return $found;
+ }
+}
+
+/**
+ * (Delegated) Implements hook_civicrm_navigationMenu().
+ */
+function _api4_civix_navigationMenu(&$nodes) {
+ if (!is_callable(array('CRM_Core_BAO_Navigation', 'fixNavigationMenu'))) {
+ _api4_civix_fixNavigationMenu($nodes);
+ }
+}
+
+/**
+ * Given a navigation menu, generate navIDs for any items which are
+ * missing them.
+ */
+function _api4_civix_fixNavigationMenu(&$nodes) {
+ $maxNavID = 1;
+ array_walk_recursive($nodes, function($item, $key) use (&$maxNavID) {
+ if ($key === 'navID') {
+ $maxNavID = max($maxNavID, $item);
+ }
+ });
+ _api4_civix_fixNavigationMenuItems($nodes, $maxNavID, NULL);
+}
+
+function _api4_civix_fixNavigationMenuItems(&$nodes, &$maxNavID, $parentID) {
+ $origKeys = array_keys($nodes);
+ foreach ($origKeys as $origKey) {
+ if (!isset($nodes[$origKey]['attributes']['parentID']) && $parentID !== NULL) {
+ $nodes[$origKey]['attributes']['parentID'] = $parentID;
+ }
+ // If no navID, then assign navID and fix key.
+ if (!isset($nodes[$origKey]['attributes']['navID'])) {
+ $newKey = ++$maxNavID;
+ $nodes[$origKey]['attributes']['navID'] = $newKey;
+ $nodes[$newKey] = $nodes[$origKey];
+ unset($nodes[$origKey]);
+ $origKey = $newKey;
+ }
+ if (isset($nodes[$origKey]['child']) && is_array($nodes[$origKey]['child'])) {
+ _api4_civix_fixNavigationMenuItems($nodes[$origKey]['child'], $maxNavID, $nodes[$origKey]['attributes']['navID']);
+ }
+ }
+}
+
+/**
+ * (Delegated) Implements hook_civicrm_alterSettingsFolders().
+ *
+ * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_alterSettingsFolders
+ */
+function _api4_civix_civicrm_alterSettingsFolders(&$metaDataFolders = NULL) {
+ static $configured = FALSE;
+ if ($configured) {
+ return;
+ }
+ $configured = TRUE;
+
+ $settingsDir = __DIR__ . DIRECTORY_SEPARATOR . 'settings';
+ if (is_dir($settingsDir) && !in_array($settingsDir, $metaDataFolders)) {
+ $metaDataFolders[] = $settingsDir;
+ }
+}
+
+/**
+ * (Delegated) Implements hook_civicrm_entityTypes().
+ *
+ * Find any *.entityType.php files, merge their content, and return.
+ *
+ * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_entityTypes
+ */
+
+function _api4_civix_civicrm_entityTypes(&$entityTypes) {
+ $entityTypes = array_merge($entityTypes, array (
+ ));
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/api4.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/api4.php
new file mode 100644
index 00000000..9c05d773
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/api4.php
@@ -0,0 +1,193 @@
+<?php
+
+require_once 'api4.civix.php';
+require_once 'api/Exception.php';
+
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
+use Symfony\Component\Config\FileLocator;
+
+/**
+ * Procedural wrapper for the OO api version 4.
+ *
+ * @param string $entity
+ * @param string $action
+ * @param array $params
+ * @param string|int $index
+ * If $index is a string, the results array will be indexed by that key.
+ * If $index is an integer, only the result at that index will be returned.
+ *
+ * @return \Civi\Api4\Generic\Result
+ * @throws \API_Exception
+ * @throws \Civi\API\Exception\NotImplementedException
+ */
+function civicrm_api4($entity, $action, $params = [], $index = NULL) {
+ $apiCall = \Civi\Api4\Utils\ActionUtil::getAction($entity, $action);
+ foreach ($params as $name => $param) {
+ $setter = 'set' . ucfirst($name);
+ $apiCall->$setter($param);
+ }
+ $result = $apiCall->execute();
+
+ // Index results by key
+ if ($index && is_string($index) && !CRM_Utils_Rule::integer($index)) {
+ $result->indexBy($index);
+ }
+ // Return result at index
+ if (CRM_Utils_Rule::integer($index)) {
+ $item = $result->itemAt($index);
+ if (is_null($item)) {
+ throw new \API_Exception("Index $index not found in api results");
+ }
+ $result->exchangeArray($item);
+
+ }
+ return $result;
+}
+
+/**
+ * @param ContainerBuilder $container
+ */
+function api4_civicrm_container($container) {
+ $loader = new XmlFileLoader($container, new FileLocator(__DIR__));
+ $loader->load('services.xml');
+
+ $container->getDefinition('civi_api_kernel')->addMethodCall(
+ 'registerApiProvider',
+ [new Reference('action_object_provider')]
+ );
+
+ // add event subscribers$container->get(
+ $dispatcher = $container->getDefinition('dispatcher');
+ $subscribers = $container->findTaggedServiceIds('event_subscriber');
+
+ foreach (array_keys($subscribers) as $subscriber) {
+ $dispatcher->addMethodCall(
+ 'addSubscriber',
+ [new Reference($subscriber)]
+ );
+ }
+
+ // add spec providers
+ $providers = $container->findTaggedServiceIds('spec_provider');
+ $gatherer = $container->getDefinition('spec_gatherer');
+
+ foreach (array_keys($providers) as $provider) {
+ $gatherer->addMethodCall(
+ 'addSpecProvider',
+ [new Reference($provider)]
+ );
+ }
+
+ if (defined('CIVICRM_UF') && CIVICRM_UF === 'UnitTests') {
+ $loader->load('tests/services.xml');
+ }
+}
+
+/**
+ * Implements hook_civicrm_coreResourceList().
+ */
+function api4_civicrm_coreResourceList(&$list, $region) {
+ if ($region == 'html-header') {
+ Civi::resources()->addScriptFile('org.civicrm.api4', 'js/api4.js', -9000, $region);
+ }
+}
+
+/**
+ * Implements hook_civicrm_config().
+ *
+ * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_config
+ */
+function api4_civicrm_config(&$config) {
+ _api4_civix_civicrm_config($config);
+}
+
+/**
+ * Implements hook_civicrm_xmlMenu().
+ *
+ * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_xmlMenu
+ */
+function api4_civicrm_xmlMenu(&$files) {
+ _api4_civix_civicrm_xmlMenu($files);
+}
+
+/**
+ * Implements hook_civicrm_install().
+ *
+ * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_install
+ */
+function api4_civicrm_install() {
+ _api4_civix_civicrm_install();
+}
+
+/**
+ * Implements hook_civicrm_uninstall().
+ *
+ * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_uninstall
+ */
+function api4_civicrm_uninstall() {
+ _api4_civix_civicrm_uninstall();
+}
+
+/**
+ * Implements hook_civicrm_enable().
+ *
+ * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_enable
+ */
+function api4_civicrm_enable() {
+ _api4_civix_civicrm_enable();
+}
+
+/**
+ * Implements hook_civicrm_disable().
+ *
+ * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_disable
+ */
+function api4_civicrm_disable() {
+ _api4_civix_civicrm_disable();
+}
+
+/**
+ * Implements hook_civicrm_upgrade().
+ *
+ * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_upgrade
+ */
+function api4_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
+ return _api4_civix_civicrm_upgrade($op, $queue);
+}
+
+/**
+ * Implements hook_civicrm_managed().
+ *
+ * Generate a list of entities to create/deactivate/delete when this module
+ * is installed, disabled, uninstalled.
+ *
+ * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_managed
+ */
+function api4_civicrm_managed(&$entities) {
+ _api4_civix_civicrm_managed($entities);
+}
+
+/**
+ * Implements hook_civicrm_angularModules().
+ *
+ * Generate a list of Angular modules.
+ *
+ * Note: This hook only runs in CiviCRM 4.5+. It may
+ * use features only available in v4.6+.
+ *
+ * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_caseTypes
+ */
+function api4_civicrm_angularModules(&$angularModules) {
+ _api4_civix_civicrm_angularModules($angularModules);
+}
+
+/**
+ * Implements hook_civicrm_alterSettingsFolders().
+ *
+ * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_alterSettingsFolders
+ */
+function api4_civicrm_alterSettingsFolders(&$metaDataFolders = NULL) {
+ _api4_civix_civicrm_alterSettingsFolders($metaDataFolders);
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/css/explorer.css b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/css/explorer.css
new file mode 100644
index 00000000..0a5f5d17
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/css/explorer.css
@@ -0,0 +1,191 @@
+/* Style rules for Api4 Explorer */
+
+#bootstrap-theme.api4-explorer-page .panel-heading {
+ height: 50px;
+}
+#bootstrap-theme.api4-explorer-page .panel-body {
+ min-height: calc( 100% - 50px);
+}
+#bootstrap-theme.api4-explorer-page .explorer-params-panel .panel-heading {
+ padding-top: 12px;
+}
+#bootstrap-theme.api4-explorer-page .explorer-params-panel .panel-heading button {
+ position: relative;
+ top: -5px;
+}
+#bootstrap-theme .explorer-params-panel .panel-heading .form-inline > .select2-container {
+ max-width: 25% !important;
+}
+#bootstrap-theme.api4-explorer-page .api4-explorer-row {
+ display: flex;
+}
+#bootstrap-theme.api4-explorer-page > div > .panel {
+ flex: 1;
+ margin: 10px;
+ min-height: 400px;
+}
+#bootstrap-theme.api4-explorer-page > div > form.panel {
+ flex: 2;
+}
+/* Fix weird shorditch style */
+#bootstrap-theme.api4-explorer-page .api4-explorer-row .panel .panel-heading {
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+ margin-bottom: 0;
+}
+#bootstrap-theme.api4-explorer-page .explorer-code-panel table td:first-child {
+ width: 5em;
+}
+
+#bootstrap-theme.api4-explorer-page .explorer-params-panel > .panel-body > div.api4-input {
+ margin-bottom: 10px;
+}
+
+#bootstrap-theme.api4-explorer-page .explorer-help-panel .panel-body {
+ word-break: break-word;
+}
+
+#bootstrap-theme.api4-explorer-page form label {
+ text-transform: capitalize;
+}
+
+#bootstrap-theme.api4-explorer-page fieldset {
+ padding: 6px;
+ border: 1px solid lightgrey;
+ margin-bottom: 10px;
+ position: relative;
+}
+
+#bootstrap-theme.api4-explorer-page fieldset legend {
+ background-color: white;
+ font-size: 13px;
+ margin: 0;
+ width: auto;
+ border: 0 none;
+ padding: 2px 5px;
+ text-transform: capitalize;
+}
+#bootstrap-theme.api4-explorer-page fieldset > .btn-group {
+ position: absolute;
+ right: 0;
+ top: 11px;
+}
+#bootstrap-theme.api4-explorer-page fieldset > .btn-group .btn {
+ border: 0 none;
+}
+
+#bootstrap-theme.api4-explorer-page fieldset div.api4-input {
+ margin-bottom: 10px;
+}
+
+#bootstrap-theme.api4-explorer-page fieldset div.api4-input.ui-sortable-helper {
+ background-color: rgba(255, 255, 255, .9);
+}
+
+#bootstrap-theme.api4-explorer-page fieldset div.api4-input.ui-sortable-helper {
+ background-color: rgba(255, 255, 255, .9);
+}
+
+#bootstrap-theme.api4-explorer-page div.api4-input.form-inline .form-control {
+ margin-right: 6px;
+}
+
+#bootstrap-theme.api4-explorer-page div.api4-input.form-inline .form-control:not(.api4-option-selected) {
+ transition: none;
+ box-shadow: none;
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+}
+
+#bootstrap-theme.api4-explorer-page div.api4-input.form-inline .form-control label {
+ font-weight: normal;
+ position: relative;
+ top: -2px;
+}
+
+#bootstrap-theme.api4-explorer-page .api4-where-fieldset fieldset {
+ float: right;
+ width: calc(100% - 58px);
+ margin-top: -8px;
+}
+
+#bootstrap-theme.api4-explorer-page .api4-where-fieldset.api4-sorting fieldset .api4-where-group-sortable {
+ min-height: 3.5em;
+}
+
+#bootstrap-theme.api4-explorer-page .api4-input-group {
+ display: inline-block;
+}
+
+#bootstrap-theme.api4-explorer-page .api4-clause-badge {
+ width: 55px;
+ display: inline-block;
+ cursor: move;
+}
+#bootstrap-theme.api4-explorer-page .api4-clause-badge .badge {
+ opacity: .5;
+ position: relative;
+}
+#bootstrap-theme.api4-explorer-page .api4-clause-badge .caret {
+ margin: 0;
+}
+#bootstrap-theme.api4-explorer-page .api4-clause-badge .crm-i {
+ display: none;
+ padding: 0 6px;
+}
+#bootstrap-theme.api4-explorer-page .ui-sortable-helper .api4-clause-badge .badge span {
+ display: none;
+}
+#bootstrap-theme.api4-explorer-page .ui-sortable-helper .api4-clause-badge .crm-i {
+ display: inline-block;
+}
+
+#bootstrap-theme.api4-explorer-page .api4-operator,
+#bootstrap-theme.api4-explorer-page .api4-chain-index,
+#bootstrap-theme.api4-explorer-page .api4-index,
+#bootstrap-theme.api4-explorer-page .api4-chain-action {
+ width: 70px;
+}
+#bootstrap-theme.api4-explorer-page .api4-chain-params {
+ width: calc( 100% - 346px);
+}
+
+#bootstrap-theme.api4-explorer-page .api4-add-where-group-menu {
+ min-width: 80px;
+ background-color: rgba(186, 225, 251, 0.94);
+}
+#bootstrap-theme.api4-explorer-page .api4-add-where-group-menu a {
+ padding: 5px 10px;
+}
+
+/* Collapsible optgroups for select2 */
+div.select2-drop.collapsible-optgroups-enabled .select2-result-with-children:not(.optgroup-expanded) > .select2-result-sub > li.select2-result {
+ display: none;
+}
+div.select2-drop.collapsible-optgroups-enabled .select2-result-with-children > .select2-result-label:before {
+ font-family: FontAwesome;
+ content: "\f0da";
+ display: inline-block;
+ padding-right: 3px;
+ vertical-align: middle;
+ font-weight: normal;
+}
+div.select2-drop.collapsible-optgroups-enabled .select2-result-with-children.optgroup-expanded > .select2-result-label:before {
+ content: "\f0d7";
+}
+
+/**
+ * Shims so the UI isn't completely broken when a Bootstrap theme is not installed
+ */
+#bootstrap-theme.api4-explorer-page * {
+ box-sizing: border-box;
+}
+.api4-explorer-page.panel {
+ border: 1px solid grey;
+ background-color: white;
+}
+.api4-explorer-page.panel-heading {
+ padding: 10px 20px;
+ color: #464354;
+ background-color: #f3f6f7;
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/images/ApiExplorer.png b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/images/ApiExplorer.png
new file mode 100644
index 00000000..1eb25124
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/images/ApiExplorer.png
Binary files differ
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/info.xml b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/info.xml
new file mode 100644
index 00000000..4e04b8ef
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/info.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+<extension key="org.civicrm.api4" type="module">
+ <file>api4</file>
+ <name>API v4</name>
+ <description>Makes version 4 of the CiviCRM API available to other extensions.</description>
+ <license>AGPL-3.0</license>
+ <maintainer>
+ <author>Coleman Watts</author>
+ <email>coleman@civicrm.org</email>
+ </maintainer>
+ <urls>
+ <url desc="Main Extension Page">https://github.com/civicrm/api4</url>
+ <url desc="Documentation">https://wiki.civicrm.org/confluence/display/CRM/API+v4+Spec</url>
+ <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
+ </urls>
+ <releaseDate>2019-05-22</releaseDate>
+ <version>4.4.2</version>
+ <develStage>stable</develStage>
+ <compatibility>
+ <ver>5.13</ver>
+ </compatibility>
+ <comments>This extension does nothing on its own. Install it if other extensions require you to do so.</comments>
+ <classloader>
+ <psr4 prefix="Civi\" path="Civi" />
+ </classloader>
+ <civix>
+ <namespace>CRM/Api4</namespace>
+ </civix>
+</extension>
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/js/api4.js b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/js/api4.js
new file mode 100644
index 00000000..7306168a
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/js/api4.js
@@ -0,0 +1,43 @@
+(function($, _) {
+
+ // result is an array, but in js, an array is also an object
+ // Assign all the metadata properties to it, mirroring the results arrayObject in php
+ function arrayObject(data) {
+ var result = data.values || [];
+ if (_.isArray(result)) {
+ delete(data.values);
+ _.assign(result, data);
+ }
+ return result;
+ }
+
+ CRM.api4 = function(entity, action, params, index) {
+ return new Promise(function(resolve, reject) {
+ if (typeof entity === 'string') {
+ $.post(CRM.url('civicrm/ajax/api4/' + entity + '/' + action), {
+ params: JSON.stringify(params),
+ index: index
+ })
+ .done(function (data) {
+ resolve(arrayObject(data));
+ })
+ .fail(function (data) {
+ reject(data.responseJSON);
+ });
+ } else {
+ $.post(CRM.url('civicrm/ajax/api4'), {
+ calls: JSON.stringify(entity)
+ })
+ .done(function(data) {
+ _.each(data, function(item, key) {
+ data[key] = arrayObject(item);
+ });
+ resolve(data);
+ })
+ .fail(function (data) {
+ reject(data.responseJSON);
+ });
+ }
+ });
+ };
+})(CRM.$, CRM._); \ No newline at end of file
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/js/load-bootstrap.js b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/js/load-bootstrap.js
new file mode 100644
index 00000000..aff280a0
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/js/load-bootstrap.js
@@ -0,0 +1,7 @@
+// Loads a copy of shoreditch's bootstrap if bootstrap is missing
+CRM.$(function($) {
+ if (!$.isFunction($.fn.dropdown)) {
+ CRM.loadScript(CRM.vars.api4.basePath + 'lib/shoreditch/dropdown.js');
+ $('head').append('<link type="text/css" rel="stylesheet" href="' + CRM.vars.api4.basePath + 'lib/shoreditch/bootstrap.css" />');
+ }
+}); \ No newline at end of file
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/lib/js-yaml.js b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/lib/js-yaml.js
new file mode 100644
index 00000000..0f8df899
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/lib/js-yaml.js
@@ -0,0 +1,3919 @@
+/* js-yaml 3.12.1 https://github.com/nodeca/js-yaml */(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.jsyaml = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
+'use strict';
+
+
+var loader = require('./js-yaml/loader');
+var dumper = require('./js-yaml/dumper');
+
+
+function deprecated(name) {
+ return function () {
+ throw new Error('Function ' + name + ' is deprecated and cannot be used.');
+ };
+}
+
+
+module.exports.Type = require('./js-yaml/type');
+module.exports.Schema = require('./js-yaml/schema');
+module.exports.FAILSAFE_SCHEMA = require('./js-yaml/schema/failsafe');
+module.exports.JSON_SCHEMA = require('./js-yaml/schema/json');
+module.exports.CORE_SCHEMA = require('./js-yaml/schema/core');
+module.exports.DEFAULT_SAFE_SCHEMA = require('./js-yaml/schema/default_safe');
+module.exports.DEFAULT_FULL_SCHEMA = require('./js-yaml/schema/default_full');
+module.exports.load = loader.load;
+module.exports.loadAll = loader.loadAll;
+module.exports.safeLoad = loader.safeLoad;
+module.exports.safeLoadAll = loader.safeLoadAll;
+module.exports.dump = dumper.dump;
+module.exports.safeDump = dumper.safeDump;
+module.exports.YAMLException = require('./js-yaml/exception');
+
+// Deprecated schema names from JS-YAML 2.0.x
+module.exports.MINIMAL_SCHEMA = require('./js-yaml/schema/failsafe');
+module.exports.SAFE_SCHEMA = require('./js-yaml/schema/default_safe');
+module.exports.DEFAULT_SCHEMA = require('./js-yaml/schema/default_full');
+
+// Deprecated functions from JS-YAML 1.x.x
+module.exports.scan = deprecated('scan');
+module.exports.parse = deprecated('parse');
+module.exports.compose = deprecated('compose');
+module.exports.addConstructor = deprecated('addConstructor');
+
+},{"./js-yaml/dumper":3,"./js-yaml/exception":4,"./js-yaml/loader":5,"./js-yaml/schema":7,"./js-yaml/schema/core":8,"./js-yaml/schema/default_full":9,"./js-yaml/schema/default_safe":10,"./js-yaml/schema/failsafe":11,"./js-yaml/schema/json":12,"./js-yaml/type":13}],2:[function(require,module,exports){
+'use strict';
+
+
+function isNothing(subject) {
+ return (typeof subject === 'undefined') || (subject === null);
+}
+
+
+function isObject(subject) {
+ return (typeof subject === 'object') && (subject !== null);
+}
+
+
+function toArray(sequence) {
+ if (Array.isArray(sequence)) return sequence;
+ else if (isNothing(sequence)) return [];
+
+ return [ sequence ];
+}
+
+
+function extend(target, source) {
+ var index, length, key, sourceKeys;
+
+ if (source) {
+ sourceKeys = Object.keys(source);
+
+ for (index = 0, length = sourceKeys.length; index < length; index += 1) {
+ key = sourceKeys[index];
+ target[key] = source[key];
+ }
+ }
+
+ return target;
+}
+
+
+function repeat(string, count) {
+ var result = '', cycle;
+
+ for (cycle = 0; cycle < count; cycle += 1) {
+ result += string;
+ }
+
+ return result;
+}
+
+
+function isNegativeZero(number) {
+ return (number === 0) && (Number.NEGATIVE_INFINITY === 1 / number);
+}
+
+
+module.exports.isNothing = isNothing;
+module.exports.isObject = isObject;
+module.exports.toArray = toArray;
+module.exports.repeat = repeat;
+module.exports.isNegativeZero = isNegativeZero;
+module.exports.extend = extend;
+
+},{}],3:[function(require,module,exports){
+'use strict';
+
+/*eslint-disable no-use-before-define*/
+
+var common = require('./common');
+var YAMLException = require('./exception');
+var DEFAULT_FULL_SCHEMA = require('./schema/default_full');
+var DEFAULT_SAFE_SCHEMA = require('./schema/default_safe');
+
+var _toString = Object.prototype.toString;
+var _hasOwnProperty = Object.prototype.hasOwnProperty;
+
+var CHAR_TAB = 0x09; /* Tab */
+var CHAR_LINE_FEED = 0x0A; /* LF */
+var CHAR_SPACE = 0x20; /* Space */
+var CHAR_EXCLAMATION = 0x21; /* ! */
+var CHAR_DOUBLE_QUOTE = 0x22; /* " */
+var CHAR_SHARP = 0x23; /* # */
+var CHAR_PERCENT = 0x25; /* % */
+var CHAR_AMPERSAND = 0x26; /* & */
+var CHAR_SINGLE_QUOTE = 0x27; /* ' */
+var CHAR_ASTERISK = 0x2A; /* * */
+var CHAR_COMMA = 0x2C; /* , */
+var CHAR_MINUS = 0x2D; /* - */
+var CHAR_COLON = 0x3A; /* : */
+var CHAR_GREATER_THAN = 0x3E; /* > */
+var CHAR_QUESTION = 0x3F; /* ? */
+var CHAR_COMMERCIAL_AT = 0x40; /* @ */
+var CHAR_LEFT_SQUARE_BRACKET = 0x5B; /* [ */
+var CHAR_RIGHT_SQUARE_BRACKET = 0x5D; /* ] */
+var CHAR_GRAVE_ACCENT = 0x60; /* ` */
+var CHAR_LEFT_CURLY_BRACKET = 0x7B; /* { */
+var CHAR_VERTICAL_LINE = 0x7C; /* | */
+var CHAR_RIGHT_CURLY_BRACKET = 0x7D; /* } */
+
+var ESCAPE_SEQUENCES = {};
+
+ESCAPE_SEQUENCES[0x00] = '\\0';
+ESCAPE_SEQUENCES[0x07] = '\\a';
+ESCAPE_SEQUENCES[0x08] = '\\b';
+ESCAPE_SEQUENCES[0x09] = '\\t';
+ESCAPE_SEQUENCES[0x0A] = '\\n';
+ESCAPE_SEQUENCES[0x0B] = '\\v';
+ESCAPE_SEQUENCES[0x0C] = '\\f';
+ESCAPE_SEQUENCES[0x0D] = '\\r';
+ESCAPE_SEQUENCES[0x1B] = '\\e';
+ESCAPE_SEQUENCES[0x22] = '\\"';
+ESCAPE_SEQUENCES[0x5C] = '\\\\';
+ESCAPE_SEQUENCES[0x85] = '\\N';
+ESCAPE_SEQUENCES[0xA0] = '\\_';
+ESCAPE_SEQUENCES[0x2028] = '\\L';
+ESCAPE_SEQUENCES[0x2029] = '\\P';
+
+var DEPRECATED_BOOLEANS_SYNTAX = [
+ 'y', 'Y', 'yes', 'Yes', 'YES', 'on', 'On', 'ON',
+ 'n', 'N', 'no', 'No', 'NO', 'off', 'Off', 'OFF'
+];
+
+function compileStyleMap(schema, map) {
+ var result, keys, index, length, tag, style, type;
+
+ if (map === null) return {};
+
+ result = {};
+ keys = Object.keys(map);
+
+ for (index = 0, length = keys.length; index < length; index += 1) {
+ tag = keys[index];
+ style = String(map[tag]);
+
+ if (tag.slice(0, 2) === '!!') {
+ tag = 'tag:yaml.org,2002:' + tag.slice(2);
+ }
+ type = schema.compiledTypeMap['fallback'][tag];
+
+ if (type && _hasOwnProperty.call(type.styleAliases, style)) {
+ style = type.styleAliases[style];
+ }
+
+ result[tag] = style;
+ }
+
+ return result;
+}
+
+function encodeHex(character) {
+ var string, handle, length;
+
+ string = character.toString(16).toUpperCase();
+
+ if (character <= 0xFF) {
+ handle = 'x';
+ length = 2;
+ } else if (character <= 0xFFFF) {
+ handle = 'u';
+ length = 4;
+ } else if (character <= 0xFFFFFFFF) {
+ handle = 'U';
+ length = 8;
+ } else {
+ throw new YAMLException('code point within a string may not be greater than 0xFFFFFFFF');
+ }
+
+ return '\\' + handle + common.repeat('0', length - string.length) + string;
+}
+
+function State(options) {
+ this.schema = options['schema'] || DEFAULT_FULL_SCHEMA;
+ this.indent = Math.max(1, (options['indent'] || 2));
+ this.noArrayIndent = options['noArrayIndent'] || false;
+ this.skipInvalid = options['skipInvalid'] || false;
+ this.flowLevel = (common.isNothing(options['flowLevel']) ? -1 : options['flowLevel']);
+ this.styleMap = compileStyleMap(this.schema, options['styles'] || null);
+ this.sortKeys = options['sortKeys'] || false;
+ this.lineWidth = options['lineWidth'] || 80;
+ this.noRefs = options['noRefs'] || false;
+ this.noCompatMode = options['noCompatMode'] || false;
+ this.condenseFlow = options['condenseFlow'] || false;
+
+ this.implicitTypes = this.schema.compiledImplicit;
+ this.explicitTypes = this.schema.compiledExplicit;
+
+ this.tag = null;
+ this.result = '';
+
+ this.duplicates = [];
+ this.usedDuplicates = null;
+}
+
+// Indents every line in a string. Empty lines (\n only) are not indented.
+function indentString(string, spaces) {
+ var ind = common.repeat(' ', spaces),
+ position = 0,
+ next = -1,
+ result = '',
+ line,
+ length = string.length;
+
+ while (position < length) {
+ next = string.indexOf('\n', position);
+ if (next === -1) {
+ line = string.slice(position);
+ position = length;
+ } else {
+ line = string.slice(position, next + 1);
+ position = next + 1;
+ }
+
+ if (line.length && line !== '\n') result += ind;
+
+ result += line;
+ }
+
+ return result;
+}
+
+function generateNextLine(state, level) {
+ return '\n' + common.repeat(' ', state.indent * level);
+}
+
+function testImplicitResolving(state, str) {
+ var index, length, type;
+
+ for (index = 0, length = state.implicitTypes.length; index < length; index += 1) {
+ type = state.implicitTypes[index];
+
+ if (type.resolve(str)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// [33] s-white ::= s-space | s-tab
+function isWhitespace(c) {
+ return c === CHAR_SPACE || c === CHAR_TAB;
+}
+
+// Returns true if the character can be printed without escaping.
+// From YAML 1.2: "any allowed characters known to be non-printable
+// should also be escaped. [However,] This isn’t mandatory"
+// Derived from nb-char - \t - #x85 - #xA0 - #x2028 - #x2029.
+function isPrintable(c) {
+ return (0x00020 <= c && c <= 0x00007E)
+ || ((0x000A1 <= c && c <= 0x00D7FF) && c !== 0x2028 && c !== 0x2029)
+ || ((0x0E000 <= c && c <= 0x00FFFD) && c !== 0xFEFF /* BOM */)
+ || (0x10000 <= c && c <= 0x10FFFF);
+}
+
+// Simplified test for values allowed after the first character in plain style.
+function isPlainSafe(c) {
+ // Uses a subset of nb-char - c-flow-indicator - ":" - "#"
+ // where nb-char ::= c-printable - b-char - c-byte-order-mark.
+ return isPrintable(c) && c !== 0xFEFF
+ // - c-flow-indicator
+ && c !== CHAR_COMMA
+ && c !== CHAR_LEFT_SQUARE_BRACKET
+ && c !== CHAR_RIGHT_SQUARE_BRACKET
+ && c !== CHAR_LEFT_CURLY_BRACKET
+ && c !== CHAR_RIGHT_CURLY_BRACKET
+ // - ":" - "#"
+ && c !== CHAR_COLON
+ && c !== CHAR_SHARP;
+}
+
+// Simplified test for values allowed as the first character in plain style.
+function isPlainSafeFirst(c) {
+ // Uses a subset of ns-char - c-indicator
+ // where ns-char = nb-char - s-white.
+ return isPrintable(c) && c !== 0xFEFF
+ && !isWhitespace(c) // - s-white
+ // - (c-indicator ::=
+ // “-” | “?” | “:” | “,” | “[” | “]” | “{” | “}”
+ && c !== CHAR_MINUS
+ && c !== CHAR_QUESTION
+ && c !== CHAR_COLON
+ && c !== CHAR_COMMA
+ && c !== CHAR_LEFT_SQUARE_BRACKET
+ && c !== CHAR_RIGHT_SQUARE_BRACKET
+ && c !== CHAR_LEFT_CURLY_BRACKET
+ && c !== CHAR_RIGHT_CURLY_BRACKET
+ // | “#” | “&” | “*” | “!” | “|” | “>” | “'” | “"”
+ && c !== CHAR_SHARP
+ && c !== CHAR_AMPERSAND
+ && c !== CHAR_ASTERISK
+ && c !== CHAR_EXCLAMATION
+ && c !== CHAR_VERTICAL_LINE
+ && c !== CHAR_GREATER_THAN
+ && c !== CHAR_SINGLE_QUOTE
+ && c !== CHAR_DOUBLE_QUOTE
+ // | “%” | “@” | “`”)
+ && c !== CHAR_PERCENT
+ && c !== CHAR_COMMERCIAL_AT
+ && c !== CHAR_GRAVE_ACCENT;
+}
+
+// Determines whether block indentation indicator is required.
+function needIndentIndicator(string) {
+ var leadingSpaceRe = /^\n* /;
+ return leadingSpaceRe.test(string);
+}
+
+var STYLE_PLAIN = 1,
+ STYLE_SINGLE = 2,
+ STYLE_LITERAL = 3,
+ STYLE_FOLDED = 4,
+ STYLE_DOUBLE = 5;
+
+// Determines which scalar styles are possible and returns the preferred style.
+// lineWidth = -1 => no limit.
+// Pre-conditions: str.length > 0.
+// Post-conditions:
+// STYLE_PLAIN or STYLE_SINGLE => no \n are in the string.
+// STYLE_LITERAL => no lines are suitable for folding (or lineWidth is -1).
+// STYLE_FOLDED => a line > lineWidth and can be folded (and lineWidth != -1).
+function chooseScalarStyle(string, singleLineOnly, indentPerLevel, lineWidth, testAmbiguousType) {
+ var i;
+ var char;
+ var hasLineBreak = false;
+ var hasFoldableLine = false; // only checked if shouldTrackWidth
+ var shouldTrackWidth = lineWidth !== -1;
+ var previousLineBreak = -1; // count the first line correctly
+ var plain = isPlainSafeFirst(string.charCodeAt(0))
+ && !isWhitespace(string.charCodeAt(string.length - 1));
+
+ if (singleLineOnly) {
+ // Case: no block styles.
+ // Check for disallowed characters to rule out plain and single.
+ for (i = 0; i < string.length; i++) {
+ char = string.charCodeAt(i);
+ if (!isPrintable(char)) {
+ return STYLE_DOUBLE;
+ }
+ plain = plain && isPlainSafe(char);
+ }
+ } else {
+ // Case: block styles permitted.
+ for (i = 0; i < string.length; i++) {
+ char = string.charCodeAt(i);
+ if (char === CHAR_LINE_FEED) {
+ hasLineBreak = true;
+ // Check if any line can be folded.
+ if (shouldTrackWidth) {
+ hasFoldableLine = hasFoldableLine ||
+ // Foldable line = too long, and not more-indented.
+ (i - previousLineBreak - 1 > lineWidth &&
+ string[previousLineBreak + 1] !== ' ');
+ previousLineBreak = i;
+ }
+ } else if (!isPrintable(char)) {
+ return STYLE_DOUBLE;
+ }
+ plain = plain && isPlainSafe(char);
+ }
+ // in case the end is missing a \n
+ hasFoldableLine = hasFoldableLine || (shouldTrackWidth &&
+ (i - previousLineBreak - 1 > lineWidth &&
+ string[previousLineBreak + 1] !== ' '));
+ }
+ // Although every style can represent \n without escaping, prefer block styles
+ // for multiline, since they're more readable and they don't add empty lines.
+ // Also prefer folding a super-long line.
+ if (!hasLineBreak && !hasFoldableLine) {
+ // Strings interpretable as another type have to be quoted;
+ // e.g. the string 'true' vs. the boolean true.
+ return plain && !testAmbiguousType(string)
+ ? STYLE_PLAIN : STYLE_SINGLE;
+ }
+ // Edge case: block indentation indicator can only have one digit.
+ if (indentPerLevel > 9 && needIndentIndicator(string)) {
+ return STYLE_DOUBLE;
+ }
+ // At this point we know block styles are valid.
+ // Prefer literal style unless we want to fold.
+ return hasFoldableLine ? STYLE_FOLDED : STYLE_LITERAL;
+}
+
+// Note: line breaking/folding is implemented for only the folded style.
+// NB. We drop the last trailing newline (if any) of a returned block scalar
+// since the dumper adds its own newline. This always works:
+// • No ending newline => unaffected; already using strip "-" chomping.
+// • Ending newline => removed then restored.
+// Importantly, this keeps the "+" chomp indicator from gaining an extra line.
+function writeScalar(state, string, level, iskey) {
+ state.dump = (function () {
+ if (string.length === 0) {
+ return "''";
+ }
+ if (!state.noCompatMode &&
+ DEPRECATED_BOOLEANS_SYNTAX.indexOf(string) !== -1) {
+ return "'" + string + "'";
+ }
+
+ var indent = state.indent * Math.max(1, level); // no 0-indent scalars
+ // As indentation gets deeper, let the width decrease monotonically
+ // to the lower bound min(state.lineWidth, 40).
+ // Note that this implies
+ // state.lineWidth ≤ 40 + state.indent: width is fixed at the lower bound.
+ // state.lineWidth > 40 + state.indent: width decreases until the lower bound.
+ // This behaves better than a constant minimum width which disallows narrower options,
+ // or an indent threshold which causes the width to suddenly increase.
+ var lineWidth = state.lineWidth === -1
+ ? -1 : Math.max(Math.min(state.lineWidth, 40), state.lineWidth - indent);
+
+ // Without knowing if keys are implicit/explicit, assume implicit for safety.
+ var singleLineOnly = iskey
+ // No block styles in flow mode.
+ || (state.flowLevel > -1 && level >= state.flowLevel);
+ function testAmbiguity(string) {
+ return testImplicitResolving(state, string);
+ }
+
+ switch (chooseScalarStyle(string, singleLineOnly, state.indent, lineWidth, testAmbiguity)) {
+ case STYLE_PLAIN:
+ return string;
+ case STYLE_SINGLE:
+ return "'" + string.replace(/'/g, "''") + "'";
+ case STYLE_LITERAL:
+ return '|' + blockHeader(string, state.indent)
+ + dropEndingNewline(indentString(string, indent));
+ case STYLE_FOLDED:
+ return '>' + blockHeader(string, state.indent)
+ + dropEndingNewline(indentString(foldString(string, lineWidth), indent));
+ case STYLE_DOUBLE:
+ return '"' + escapeString(string, lineWidth) + '"';
+ default:
+ throw new YAMLException('impossible error: invalid scalar style');
+ }
+ }());
+}
+
+// Pre-conditions: string is valid for a block scalar, 1 <= indentPerLevel <= 9.
+function blockHeader(string, indentPerLevel) {
+ var indentIndicator = needIndentIndicator(string) ? String(indentPerLevel) : '';
+
+ // note the special case: the string '\n' counts as a "trailing" empty line.
+ var clip = string[string.length - 1] === '\n';
+ var keep = clip && (string[string.length - 2] === '\n' || string === '\n');
+ var chomp = keep ? '+' : (clip ? '' : '-');
+
+ return indentIndicator + chomp + '\n';
+}
+
+// (See the note for writeScalar.)
+function dropEndingNewline(string) {
+ return string[string.length - 1] === '\n' ? string.slice(0, -1) : string;
+}
+
+// Note: a long line without a suitable break point will exceed the width limit.
+// Pre-conditions: every char in str isPrintable, str.length > 0, width > 0.
+function foldString(string, width) {
+ // In folded style, $k$ consecutive newlines output as $k+1$ newlines—
+ // unless they're before or after a more-indented line, or at the very
+ // beginning or end, in which case $k$ maps to $k$.
+ // Therefore, parse each chunk as newline(s) followed by a content line.
+ var lineRe = /(\n+)([^\n]*)/g;
+
+ // first line (possibly an empty line)
+ var result = (function () {
+ var nextLF = string.indexOf('\n');
+ nextLF = nextLF !== -1 ? nextLF : string.length;
+ lineRe.lastIndex = nextLF;
+ return foldLine(string.slice(0, nextLF), width);
+ }());
+ // If we haven't reached the first content line yet, don't add an extra \n.
+ var prevMoreIndented = string[0] === '\n' || string[0] === ' ';
+ var moreIndented;
+
+ // rest of the lines
+ var match;
+ while ((match = lineRe.exec(string))) {
+ var prefix = match[1], line = match[2];
+ moreIndented = (line[0] === ' ');
+ result += prefix
+ + (!prevMoreIndented && !moreIndented && line !== ''
+ ? '\n' : '')
+ + foldLine(line, width);
+ prevMoreIndented = moreIndented;
+ }
+
+ return result;
+}
+
+// Greedy line breaking.
+// Picks the longest line under the limit each time,
+// otherwise settles for the shortest line over the limit.
+// NB. More-indented lines *cannot* be folded, as that would add an extra \n.
+function foldLine(line, width) {
+ if (line === '' || line[0] === ' ') return line;
+
+ // Since a more-indented line adds a \n, breaks can't be followed by a space.
+ var breakRe = / [^ ]/g; // note: the match index will always be <= length-2.
+ var match;
+ // start is an inclusive index. end, curr, and next are exclusive.
+ var start = 0, end, curr = 0, next = 0;
+ var result = '';
+
+ // Invariants: 0 <= start <= length-1.
+ // 0 <= curr <= next <= max(0, length-2). curr - start <= width.
+ // Inside the loop:
+ // A match implies length >= 2, so curr and next are <= length-2.
+ while ((match = breakRe.exec(line))) {
+ next = match.index;
+ // maintain invariant: curr - start <= width
+ if (next - start > width) {
+ end = (curr > start) ? curr : next; // derive end <= length-2
+ result += '\n' + line.slice(start, end);
+ // skip the space that was output as \n
+ start = end + 1; // derive start <= length-1
+ }
+ curr = next;
+ }
+
+ // By the invariants, start <= length-1, so there is something left over.
+ // It is either the whole string or a part starting from non-whitespace.
+ result += '\n';
+ // Insert a break if the remainder is too long and there is a break available.
+ if (line.length - start > width && curr > start) {
+ result += line.slice(start, curr) + '\n' + line.slice(curr + 1);
+ } else {
+ result += line.slice(start);
+ }
+
+ return result.slice(1); // drop extra \n joiner
+}
+
+// Escapes a double-quoted string.
+function escapeString(string) {
+ var result = '';
+ var char, nextChar;
+ var escapeSeq;
+
+ for (var i = 0; i < string.length; i++) {
+ char = string.charCodeAt(i);
+ // Check for surrogate pairs (reference Unicode 3.0 section "3.7 Surrogates").
+ if (char >= 0xD800 && char <= 0xDBFF/* high surrogate */) {
+ nextChar = string.charCodeAt(i + 1);
+ if (nextChar >= 0xDC00 && nextChar <= 0xDFFF/* low surrogate */) {
+ // Combine the surrogate pair and store it escaped.
+ result += encodeHex((char - 0xD800) * 0x400 + nextChar - 0xDC00 + 0x10000);
+ // Advance index one extra since we already used that char here.
+ i++; continue;
+ }
+ }
+ escapeSeq = ESCAPE_SEQUENCES[char];
+ result += !escapeSeq && isPrintable(char)
+ ? string[i]
+ : escapeSeq || encodeHex(char);
+ }
+
+ return result;
+}
+
+function writeFlowSequence(state, level, object) {
+ var _result = '',
+ _tag = state.tag,
+ index,
+ length;
+
+ for (index = 0, length = object.length; index < length; index += 1) {
+ // Write only valid elements.
+ if (writeNode(state, level, object[index], false, false)) {
+ if (index !== 0) _result += ',' + (!state.condenseFlow ? ' ' : '');
+ _result += state.dump;
+ }
+ }
+
+ state.tag = _tag;
+ state.dump = '[' + _result + ']';
+}
+
+function writeBlockSequence(state, level, object, compact) {
+ var _result = '',
+ _tag = state.tag,
+ index,
+ length;
+
+ for (index = 0, length = object.length; index < length; index += 1) {
+ // Write only valid elements.
+ if (writeNode(state, level + 1, object[index], true, true)) {
+ if (!compact || index !== 0) {
+ _result += generateNextLine(state, level);
+ }
+
+ if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) {
+ _result += '-';
+ } else {
+ _result += '- ';
+ }
+
+ _result += state.dump;
+ }
+ }
+
+ state.tag = _tag;
+ state.dump = _result || '[]'; // Empty sequence if no valid values.
+}
+
+function writeFlowMapping(state, level, object) {
+ var _result = '',
+ _tag = state.tag,
+ objectKeyList = Object.keys(object),
+ index,
+ length,
+ objectKey,
+ objectValue,
+ pairBuffer;
+
+ for (index = 0, length = objectKeyList.length; index < length; index += 1) {
+ pairBuffer = state.condenseFlow ? '"' : '';
+
+ if (index !== 0) pairBuffer += ', ';
+
+ objectKey = objectKeyList[index];
+ objectValue = object[objectKey];
+
+ if (!writeNode(state, level, objectKey, false, false)) {
+ continue; // Skip this pair because of invalid key;
+ }
+
+ if (state.dump.length > 1024) pairBuffer += '? ';
+
+ pairBuffer += state.dump + (state.condenseFlow ? '"' : '') + ':' + (state.condenseFlow ? '' : ' ');
+
+ if (!writeNode(state, level, objectValue, false, false)) {
+ continue; // Skip this pair because of invalid value.
+ }
+
+ pairBuffer += state.dump;
+
+ // Both key and value are valid.
+ _result += pairBuffer;
+ }
+
+ state.tag = _tag;
+ state.dump = '{' + _result + '}';
+}
+
+function writeBlockMapping(state, level, object, compact) {
+ var _result = '',
+ _tag = state.tag,
+ objectKeyList = Object.keys(object),
+ index,
+ length,
+ objectKey,
+ objectValue,
+ explicitPair,
+ pairBuffer;
+
+ // Allow sorting keys so that the output file is deterministic
+ if (state.sortKeys === true) {
+ // Default sorting
+ objectKeyList.sort();
+ } else if (typeof state.sortKeys === 'function') {
+ // Custom sort function
+ objectKeyList.sort(state.sortKeys);
+ } else if (state.sortKeys) {
+ // Something is wrong
+ throw new YAMLException('sortKeys must be a boolean or a function');
+ }
+
+ for (index = 0, length = objectKeyList.length; index < length; index += 1) {
+ pairBuffer = '';
+
+ if (!compact || index !== 0) {
+ pairBuffer += generateNextLine(state, level);
+ }
+
+ objectKey = objectKeyList[index];
+ objectValue = object[objectKey];
+
+ if (!writeNode(state, level + 1, objectKey, true, true, true)) {
+ continue; // Skip this pair because of invalid key.
+ }
+
+ explicitPair = (state.tag !== null && state.tag !== '?') ||
+ (state.dump && state.dump.length > 1024);
+
+ if (explicitPair) {
+ if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) {
+ pairBuffer += '?';
+ } else {
+ pairBuffer += '? ';
+ }
+ }
+
+ pairBuffer += state.dump;
+
+ if (explicitPair) {
+ pairBuffer += generateNextLine(state, level);
+ }
+
+ if (!writeNode(state, level + 1, objectValue, true, explicitPair)) {
+ continue; // Skip this pair because of invalid value.
+ }
+
+ if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) {
+ pairBuffer += ':';
+ } else {
+ pairBuffer += ': ';
+ }
+
+ pairBuffer += state.dump;
+
+ // Both key and value are valid.
+ _result += pairBuffer;
+ }
+
+ state.tag = _tag;
+ state.dump = _result || '{}'; // Empty mapping if no valid pairs.
+}
+
+function detectType(state, object, explicit) {
+ var _result, typeList, index, length, type, style;
+
+ typeList = explicit ? state.explicitTypes : state.implicitTypes;
+
+ for (index = 0, length = typeList.length; index < length; index += 1) {
+ type = typeList[index];
+
+ if ((type.instanceOf || type.predicate) &&
+ (!type.instanceOf || ((typeof object === 'object') && (object instanceof type.instanceOf))) &&
+ (!type.predicate || type.predicate(object))) {
+
+ state.tag = explicit ? type.tag : '?';
+
+ if (type.represent) {
+ style = state.styleMap[type.tag] || type.defaultStyle;
+
+ if (_toString.call(type.represent) === '[object Function]') {
+ _result = type.represent(object, style);
+ } else if (_hasOwnProperty.call(type.represent, style)) {
+ _result = type.represent[style](object, style);
+ } else {
+ throw new YAMLException('!<' + type.tag + '> tag resolver accepts not "' + style + '" style');
+ }
+
+ state.dump = _result;
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// Serializes `object` and writes it to global `result`.
+// Returns true on success, or false on invalid object.
+//
+function writeNode(state, level, object, block, compact, iskey) {
+ state.tag = null;
+ state.dump = object;
+
+ if (!detectType(state, object, false)) {
+ detectType(state, object, true);
+ }
+
+ var type = _toString.call(state.dump);
+
+ if (block) {
+ block = (state.flowLevel < 0 || state.flowLevel > level);
+ }
+
+ var objectOrArray = type === '[object Object]' || type === '[object Array]',
+ duplicateIndex,
+ duplicate;
+
+ if (objectOrArray) {
+ duplicateIndex = state.duplicates.indexOf(object);
+ duplicate = duplicateIndex !== -1;
+ }
+
+ if ((state.tag !== null && state.tag !== '?') || duplicate || (state.indent !== 2 && level > 0)) {
+ compact = false;
+ }
+
+ if (duplicate && state.usedDuplicates[duplicateIndex]) {
+ state.dump = '*ref_' + duplicateIndex;
+ } else {
+ if (objectOrArray && duplicate && !state.usedDuplicates[duplicateIndex]) {
+ state.usedDuplicates[duplicateIndex] = true;
+ }
+ if (type === '[object Object]') {
+ if (block && (Object.keys(state.dump).length !== 0)) {
+ writeBlockMapping(state, level, state.dump, compact);
+ if (duplicate) {
+ state.dump = '&ref_' + duplicateIndex + state.dump;
+ }
+ } else {
+ writeFlowMapping(state, level, state.dump);
+ if (duplicate) {
+ state.dump = '&ref_' + duplicateIndex + ' ' + state.dump;
+ }
+ }
+ } else if (type === '[object Array]') {
+ var arrayLevel = (state.noArrayIndent) ? level - 1 : level;
+ if (block && (state.dump.length !== 0)) {
+ writeBlockSequence(state, arrayLevel, state.dump, compact);
+ if (duplicate) {
+ state.dump = '&ref_' + duplicateIndex + state.dump;
+ }
+ } else {
+ writeFlowSequence(state, arrayLevel, state.dump);
+ if (duplicate) {
+ state.dump = '&ref_' + duplicateIndex + ' ' + state.dump;
+ }
+ }
+ } else if (type === '[object String]') {
+ if (state.tag !== '?') {
+ writeScalar(state, state.dump, level, iskey);
+ }
+ } else {
+ if (state.skipInvalid) return false;
+ throw new YAMLException('unacceptable kind of an object to dump ' + type);
+ }
+
+ if (state.tag !== null && state.tag !== '?') {
+ state.dump = '!<' + state.tag + '> ' + state.dump;
+ }
+ }
+
+ return true;
+}
+
+function getDuplicateReferences(object, state) {
+ var objects = [],
+ duplicatesIndexes = [],
+ index,
+ length;
+
+ inspectNode(object, objects, duplicatesIndexes);
+
+ for (index = 0, length = duplicatesIndexes.length; index < length; index += 1) {
+ state.duplicates.push(objects[duplicatesIndexes[index]]);
+ }
+ state.usedDuplicates = new Array(length);
+}
+
+function inspectNode(object, objects, duplicatesIndexes) {
+ var objectKeyList,
+ index,
+ length;
+
+ if (object !== null && typeof object === 'object') {
+ index = objects.indexOf(object);
+ if (index !== -1) {
+ if (duplicatesIndexes.indexOf(index) === -1) {
+ duplicatesIndexes.push(index);
+ }
+ } else {
+ objects.push(object);
+
+ if (Array.isArray(object)) {
+ for (index = 0, length = object.length; index < length; index += 1) {
+ inspectNode(object[index], objects, duplicatesIndexes);
+ }
+ } else {
+ objectKeyList = Object.keys(object);
+
+ for (index = 0, length = objectKeyList.length; index < length; index += 1) {
+ inspectNode(object[objectKeyList[index]], objects, duplicatesIndexes);
+ }
+ }
+ }
+ }
+}
+
+function dump(input, options) {
+ options = options || {};
+
+ var state = new State(options);
+
+ if (!state.noRefs) getDuplicateReferences(input, state);
+
+ if (writeNode(state, 0, input, true, true)) return state.dump + '\n';
+
+ return '';
+}
+
+function safeDump(input, options) {
+ return dump(input, common.extend({ schema: DEFAULT_SAFE_SCHEMA }, options));
+}
+
+module.exports.dump = dump;
+module.exports.safeDump = safeDump;
+
+},{"./common":2,"./exception":4,"./schema/default_full":9,"./schema/default_safe":10}],4:[function(require,module,exports){
+// YAML error class. http://stackoverflow.com/questions/8458984
+//
+'use strict';
+
+function YAMLException(reason, mark) {
+ // Super constructor
+ Error.call(this);
+
+ this.name = 'YAMLException';
+ this.reason = reason;
+ this.mark = mark;
+ this.message = (this.reason || '(unknown reason)') + (this.mark ? ' ' + this.mark.toString() : '');
+
+ // Include stack trace in error object
+ if (Error.captureStackTrace) {
+ // Chrome and NodeJS
+ Error.captureStackTrace(this, this.constructor);
+ } else {
+ // FF, IE 10+ and Safari 6+. Fallback for others
+ this.stack = (new Error()).stack || '';
+ }
+}
+
+
+// Inherit from Error
+YAMLException.prototype = Object.create(Error.prototype);
+YAMLException.prototype.constructor = YAMLException;
+
+
+YAMLException.prototype.toString = function toString(compact) {
+ var result = this.name + ': ';
+
+ result += this.reason || '(unknown reason)';
+
+ if (!compact && this.mark) {
+ result += ' ' + this.mark.toString();
+ }
+
+ return result;
+};
+
+
+module.exports = YAMLException;
+
+},{}],5:[function(require,module,exports){
+'use strict';
+
+/*eslint-disable max-len,no-use-before-define*/
+
+var common = require('./common');
+var YAMLException = require('./exception');
+var Mark = require('./mark');
+var DEFAULT_SAFE_SCHEMA = require('./schema/default_safe');
+var DEFAULT_FULL_SCHEMA = require('./schema/default_full');
+
+
+var _hasOwnProperty = Object.prototype.hasOwnProperty;
+
+
+var CONTEXT_FLOW_IN = 1;
+var CONTEXT_FLOW_OUT = 2;
+var CONTEXT_BLOCK_IN = 3;
+var CONTEXT_BLOCK_OUT = 4;
+
+
+var CHOMPING_CLIP = 1;
+var CHOMPING_STRIP = 2;
+var CHOMPING_KEEP = 3;
+
+
+var PATTERN_NON_PRINTABLE = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/;
+var PATTERN_NON_ASCII_LINE_BREAKS = /[\x85\u2028\u2029]/;
+var PATTERN_FLOW_INDICATORS = /[,\[\]\{\}]/;
+var PATTERN_TAG_HANDLE = /^(?:!|!!|![a-z\-]+!)$/i;
+var PATTERN_TAG_URI = /^(?:!|[^,\[\]\{\}])(?:%[0-9a-f]{2}|[0-9a-z\-#;\/\?:@&=\+\$,_\.!~\*'\(\)\[\]])*$/i;
+
+
+function is_EOL(c) {
+ return (c === 0x0A/* LF */) || (c === 0x0D/* CR */);
+}
+
+function is_WHITE_SPACE(c) {
+ return (c === 0x09/* Tab */) || (c === 0x20/* Space */);
+}
+
+function is_WS_OR_EOL(c) {
+ return (c === 0x09/* Tab */) ||
+ (c === 0x20/* Space */) ||
+ (c === 0x0A/* LF */) ||
+ (c === 0x0D/* CR */);
+}
+
+function is_FLOW_INDICATOR(c) {
+ return c === 0x2C/* , */ ||
+ c === 0x5B/* [ */ ||
+ c === 0x5D/* ] */ ||
+ c === 0x7B/* { */ ||
+ c === 0x7D/* } */;
+}
+
+function fromHexCode(c) {
+ var lc;
+
+ if ((0x30/* 0 */ <= c) && (c <= 0x39/* 9 */)) {
+ return c - 0x30;
+ }
+
+ /*eslint-disable no-bitwise*/
+ lc = c | 0x20;
+
+ if ((0x61/* a */ <= lc) && (lc <= 0x66/* f */)) {
+ return lc - 0x61 + 10;
+ }
+
+ return -1;
+}
+
+function escapedHexLen(c) {
+ if (c === 0x78/* x */) { return 2; }
+ if (c === 0x75/* u */) { return 4; }
+ if (c === 0x55/* U */) { return 8; }
+ return 0;
+}
+
+function fromDecimalCode(c) {
+ if ((0x30/* 0 */ <= c) && (c <= 0x39/* 9 */)) {
+ return c - 0x30;
+ }
+
+ return -1;
+}
+
+function simpleEscapeSequence(c) {
+ /* eslint-disable indent */
+ return (c === 0x30/* 0 */) ? '\x00' :
+ (c === 0x61/* a */) ? '\x07' :
+ (c === 0x62/* b */) ? '\x08' :
+ (c === 0x74/* t */) ? '\x09' :
+ (c === 0x09/* Tab */) ? '\x09' :
+ (c === 0x6E/* n */) ? '\x0A' :
+ (c === 0x76/* v */) ? '\x0B' :
+ (c === 0x66/* f */) ? '\x0C' :
+ (c === 0x72/* r */) ? '\x0D' :
+ (c === 0x65/* e */) ? '\x1B' :
+ (c === 0x20/* Space */) ? ' ' :
+ (c === 0x22/* " */) ? '\x22' :
+ (c === 0x2F/* / */) ? '/' :
+ (c === 0x5C/* \ */) ? '\x5C' :
+ (c === 0x4E/* N */) ? '\x85' :
+ (c === 0x5F/* _ */) ? '\xA0' :
+ (c === 0x4C/* L */) ? '\u2028' :
+ (c === 0x50/* P */) ? '\u2029' : '';
+}
+
+function charFromCodepoint(c) {
+ if (c <= 0xFFFF) {
+ return String.fromCharCode(c);
+ }
+ // Encode UTF-16 surrogate pair
+ // https://en.wikipedia.org/wiki/UTF-16#Code_points_U.2B010000_to_U.2B10FFFF
+ return String.fromCharCode(
+ ((c - 0x010000) >> 10) + 0xD800,
+ ((c - 0x010000) & 0x03FF) + 0xDC00
+ );
+}
+
+var simpleEscapeCheck = new Array(256); // integer, for fast access
+var simpleEscapeMap = new Array(256);
+for (var i = 0; i < 256; i++) {
+ simpleEscapeCheck[i] = simpleEscapeSequence(i) ? 1 : 0;
+ simpleEscapeMap[i] = simpleEscapeSequence(i);
+}
+
+
+function State(input, options) {
+ this.input = input;
+
+ this.filename = options['filename'] || null;
+ this.schema = options['schema'] || DEFAULT_FULL_SCHEMA;
+ this.onWarning = options['onWarning'] || null;
+ this.legacy = options['legacy'] || false;
+ this.json = options['json'] || false;
+ this.listener = options['listener'] || null;
+
+ this.implicitTypes = this.schema.compiledImplicit;
+ this.typeMap = this.schema.compiledTypeMap;
+
+ this.length = input.length;
+ this.position = 0;
+ this.line = 0;
+ this.lineStart = 0;
+ this.lineIndent = 0;
+
+ this.documents = [];
+
+ /*
+ this.version;
+ this.checkLineBreaks;
+ this.tagMap;
+ this.anchorMap;
+ this.tag;
+ this.anchor;
+ this.kind;
+ this.result;*/
+
+}
+
+
+function generateError(state, message) {
+ return new YAMLException(
+ message,
+ new Mark(state.filename, state.input, state.position, state.line, (state.position - state.lineStart)));
+}
+
+function throwError(state, message) {
+ throw generateError(state, message);
+}
+
+function throwWarning(state, message) {
+ if (state.onWarning) {
+ state.onWarning.call(null, generateError(state, message));
+ }
+}
+
+
+var directiveHandlers = {
+
+ YAML: function handleYamlDirective(state, name, args) {
+
+ var match, major, minor;
+
+ if (state.version !== null) {
+ throwError(state, 'duplication of %YAML directive');
+ }
+
+ if (args.length !== 1) {
+ throwError(state, 'YAML directive accepts exactly one argument');
+ }
+
+ match = /^([0-9]+)\.([0-9]+)$/.exec(args[0]);
+
+ if (match === null) {
+ throwError(state, 'ill-formed argument of the YAML directive');
+ }
+
+ major = parseInt(match[1], 10);
+ minor = parseInt(match[2], 10);
+
+ if (major !== 1) {
+ throwError(state, 'unacceptable YAML version of the document');
+ }
+
+ state.version = args[0];
+ state.checkLineBreaks = (minor < 2);
+
+ if (minor !== 1 && minor !== 2) {
+ throwWarning(state, 'unsupported YAML version of the document');
+ }
+ },
+
+ TAG: function handleTagDirective(state, name, args) {
+
+ var handle, prefix;
+
+ if (args.length !== 2) {
+ throwError(state, 'TAG directive accepts exactly two arguments');
+ }
+
+ handle = args[0];
+ prefix = args[1];
+
+ if (!PATTERN_TAG_HANDLE.test(handle)) {
+ throwError(state, 'ill-formed tag handle (first argument) of the TAG directive');
+ }
+
+ if (_hasOwnProperty.call(state.tagMap, handle)) {
+ throwError(state, 'there is a previously declared suffix for "' + handle + '" tag handle');
+ }
+
+ if (!PATTERN_TAG_URI.test(prefix)) {
+ throwError(state, 'ill-formed tag prefix (second argument) of the TAG directive');
+ }
+
+ state.tagMap[handle] = prefix;
+ }
+};
+
+
+function captureSegment(state, start, end, checkJson) {
+ var _position, _length, _character, _result;
+
+ if (start < end) {
+ _result = state.input.slice(start, end);
+
+ if (checkJson) {
+ for (_position = 0, _length = _result.length; _position < _length; _position += 1) {
+ _character = _result.charCodeAt(_position);
+ if (!(_character === 0x09 ||
+ (0x20 <= _character && _character <= 0x10FFFF))) {
+ throwError(state, 'expected valid JSON character');
+ }
+ }
+ } else if (PATTERN_NON_PRINTABLE.test(_result)) {
+ throwError(state, 'the stream contains non-printable characters');
+ }
+
+ state.result += _result;
+ }
+}
+
+function mergeMappings(state, destination, source, overridableKeys) {
+ var sourceKeys, key, index, quantity;
+
+ if (!common.isObject(source)) {
+ throwError(state, 'cannot merge mappings; the provided source object is unacceptable');
+ }
+
+ sourceKeys = Object.keys(source);
+
+ for (index = 0, quantity = sourceKeys.length; index < quantity; index += 1) {
+ key = sourceKeys[index];
+
+ if (!_hasOwnProperty.call(destination, key)) {
+ destination[key] = source[key];
+ overridableKeys[key] = true;
+ }
+ }
+}
+
+function storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode, startLine, startPos) {
+ var index, quantity;
+
+ keyNode = String(keyNode);
+
+ if (_result === null) {
+ _result = {};
+ }
+
+ if (keyTag === 'tag:yaml.org,2002:merge') {
+ if (Array.isArray(valueNode)) {
+ for (index = 0, quantity = valueNode.length; index < quantity; index += 1) {
+ mergeMappings(state, _result, valueNode[index], overridableKeys);
+ }
+ } else {
+ mergeMappings(state, _result, valueNode, overridableKeys);
+ }
+ } else {
+ if (!state.json &&
+ !_hasOwnProperty.call(overridableKeys, keyNode) &&
+ _hasOwnProperty.call(_result, keyNode)) {
+ state.line = startLine || state.line;
+ state.position = startPos || state.position;
+ throwError(state, 'duplicated mapping key');
+ }
+ _result[keyNode] = valueNode;
+ delete overridableKeys[keyNode];
+ }
+
+ return _result;
+}
+
+function readLineBreak(state) {
+ var ch;
+
+ ch = state.input.charCodeAt(state.position);
+
+ if (ch === 0x0A/* LF */) {
+ state.position++;
+ } else if (ch === 0x0D/* CR */) {
+ state.position++;
+ if (state.input.charCodeAt(state.position) === 0x0A/* LF */) {
+ state.position++;
+ }
+ } else {
+ throwError(state, 'a line break is expected');
+ }
+
+ state.line += 1;
+ state.lineStart = state.position;
+}
+
+function skipSeparationSpace(state, allowComments, checkIndent) {
+ var lineBreaks = 0,
+ ch = state.input.charCodeAt(state.position);
+
+ while (ch !== 0) {
+ while (is_WHITE_SPACE(ch)) {
+ ch = state.input.charCodeAt(++state.position);
+ }
+
+ if (allowComments && ch === 0x23/* # */) {
+ do {
+ ch = state.input.charCodeAt(++state.position);
+ } while (ch !== 0x0A/* LF */ && ch !== 0x0D/* CR */ && ch !== 0);
+ }
+
+ if (is_EOL(ch)) {
+ readLineBreak(state);
+
+ ch = state.input.charCodeAt(state.position);
+ lineBreaks++;
+ state.lineIndent = 0;
+
+ while (ch === 0x20/* Space */) {
+ state.lineIndent++;
+ ch = state.input.charCodeAt(++state.position);
+ }
+ } else {
+ break;
+ }
+ }
+
+ if (checkIndent !== -1 && lineBreaks !== 0 && state.lineIndent < checkIndent) {
+ throwWarning(state, 'deficient indentation');
+ }
+
+ return lineBreaks;
+}
+
+function testDocumentSeparator(state) {
+ var _position = state.position,
+ ch;
+
+ ch = state.input.charCodeAt(_position);
+
+ // Condition state.position === state.lineStart is tested
+ // in parent on each call, for efficiency. No needs to test here again.
+ if ((ch === 0x2D/* - */ || ch === 0x2E/* . */) &&
+ ch === state.input.charCodeAt(_position + 1) &&
+ ch === state.input.charCodeAt(_position + 2)) {
+
+ _position += 3;
+
+ ch = state.input.charCodeAt(_position);
+
+ if (ch === 0 || is_WS_OR_EOL(ch)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+function writeFoldedLines(state, count) {
+ if (count === 1) {
+ state.result += ' ';
+ } else if (count > 1) {
+ state.result += common.repeat('\n', count - 1);
+ }
+}
+
+
+function readPlainScalar(state, nodeIndent, withinFlowCollection) {
+ var preceding,
+ following,
+ captureStart,
+ captureEnd,
+ hasPendingContent,
+ _line,
+ _lineStart,
+ _lineIndent,
+ _kind = state.kind,
+ _result = state.result,
+ ch;
+
+ ch = state.input.charCodeAt(state.position);
+
+ if (is_WS_OR_EOL(ch) ||
+ is_FLOW_INDICATOR(ch) ||
+ ch === 0x23/* # */ ||
+ ch === 0x26/* & */ ||
+ ch === 0x2A/* * */ ||
+ ch === 0x21/* ! */ ||
+ ch === 0x7C/* | */ ||
+ ch === 0x3E/* > */ ||
+ ch === 0x27/* ' */ ||
+ ch === 0x22/* " */ ||
+ ch === 0x25/* % */ ||
+ ch === 0x40/* @ */ ||
+ ch === 0x60/* ` */) {
+ return false;
+ }
+
+ if (ch === 0x3F/* ? */ || ch === 0x2D/* - */) {
+ following = state.input.charCodeAt(state.position + 1);
+
+ if (is_WS_OR_EOL(following) ||
+ withinFlowCollection && is_FLOW_INDICATOR(following)) {
+ return false;
+ }
+ }
+
+ state.kind = 'scalar';
+ state.result = '';
+ captureStart = captureEnd = state.position;
+ hasPendingContent = false;
+
+ while (ch !== 0) {
+ if (ch === 0x3A/* : */) {
+ following = state.input.charCodeAt(state.position + 1);
+
+ if (is_WS_OR_EOL(following) ||
+ withinFlowCollection && is_FLOW_INDICATOR(following)) {
+ break;
+ }
+
+ } else if (ch === 0x23/* # */) {
+ preceding = state.input.charCodeAt(state.position - 1);
+
+ if (is_WS_OR_EOL(preceding)) {
+ break;
+ }
+
+ } else if ((state.position === state.lineStart && testDocumentSeparator(state)) ||
+ withinFlowCollection && is_FLOW_INDICATOR(ch)) {
+ break;
+
+ } else if (is_EOL(ch)) {
+ _line = state.line;
+ _lineStart = state.lineStart;
+ _lineIndent = state.lineIndent;
+ skipSeparationSpace(state, false, -1);
+
+ if (state.lineIndent >= nodeIndent) {
+ hasPendingContent = true;
+ ch = state.input.charCodeAt(state.position);
+ continue;
+ } else {
+ state.position = captureEnd;
+ state.line = _line;
+ state.lineStart = _lineStart;
+ state.lineIndent = _lineIndent;
+ break;
+ }
+ }
+
+ if (hasPendingContent) {
+ captureSegment(state, captureStart, captureEnd, false);
+ writeFoldedLines(state, state.line - _line);
+ captureStart = captureEnd = state.position;
+ hasPendingContent = false;
+ }
+
+ if (!is_WHITE_SPACE(ch)) {
+ captureEnd = state.position + 1;
+ }
+
+ ch = state.input.charCodeAt(++state.position);
+ }
+
+ captureSegment(state, captureStart, captureEnd, false);
+
+ if (state.result) {
+ return true;
+ }
+
+ state.kind = _kind;
+ state.result = _result;
+ return false;
+}
+
+function readSingleQuotedScalar(state, nodeIndent) {
+ var ch,
+ captureStart, captureEnd;
+
+ ch = state.input.charCodeAt(state.position);
+
+ if (ch !== 0x27/* ' */) {
+ return false;
+ }
+
+ state.kind = 'scalar';
+ state.result = '';
+ state.position++;
+ captureStart = captureEnd = state.position;
+
+ while ((ch = state.input.charCodeAt(state.position)) !== 0) {
+ if (ch === 0x27/* ' */) {
+ captureSegment(state, captureStart, state.position, true);
+ ch = state.input.charCodeAt(++state.position);
+
+ if (ch === 0x27/* ' */) {
+ captureStart = state.position;
+ state.position++;
+ captureEnd = state.position;
+ } else {
+ return true;
+ }
+
+ } else if (is_EOL(ch)) {
+ captureSegment(state, captureStart, captureEnd, true);
+ writeFoldedLines(state, skipSeparationSpace(state, false, nodeIndent));
+ captureStart = captureEnd = state.position;
+
+ } else if (state.position === state.lineStart && testDocumentSeparator(state)) {
+ throwError(state, 'unexpected end of the document within a single quoted scalar');
+
+ } else {
+ state.position++;
+ captureEnd = state.position;
+ }
+ }
+
+ throwError(state, 'unexpected end of the stream within a single quoted scalar');
+}
+
+function readDoubleQuotedScalar(state, nodeIndent) {
+ var captureStart,
+ captureEnd,
+ hexLength,
+ hexResult,
+ tmp,
+ ch;
+
+ ch = state.input.charCodeAt(state.position);
+
+ if (ch !== 0x22/* " */) {
+ return false;
+ }
+
+ state.kind = 'scalar';
+ state.result = '';
+ state.position++;
+ captureStart = captureEnd = state.position;
+
+ while ((ch = state.input.charCodeAt(state.position)) !== 0) {
+ if (ch === 0x22/* " */) {
+ captureSegment(state, captureStart, state.position, true);
+ state.position++;
+ return true;
+
+ } else if (ch === 0x5C/* \ */) {
+ captureSegment(state, captureStart, state.position, true);
+ ch = state.input.charCodeAt(++state.position);
+
+ if (is_EOL(ch)) {
+ skipSeparationSpace(state, false, nodeIndent);
+
+ // TODO: rework to inline fn with no type cast?
+ } else if (ch < 256 && simpleEscapeCheck[ch]) {
+ state.result += simpleEscapeMap[ch];
+ state.position++;
+
+ } else if ((tmp = escapedHexLen(ch)) > 0) {
+ hexLength = tmp;
+ hexResult = 0;
+
+ for (; hexLength > 0; hexLength--) {
+ ch = state.input.charCodeAt(++state.position);
+
+ if ((tmp = fromHexCode(ch)) >= 0) {
+ hexResult = (hexResult << 4) + tmp;
+
+ } else {
+ throwError(state, 'expected hexadecimal character');
+ }
+ }
+
+ state.result += charFromCodepoint(hexResult);
+
+ state.position++;
+
+ } else {
+ throwError(state, 'unknown escape sequence');
+ }
+
+ captureStart = captureEnd = state.position;
+
+ } else if (is_EOL(ch)) {
+ captureSegment(state, captureStart, captureEnd, true);
+ writeFoldedLines(state, skipSeparationSpace(state, false, nodeIndent));
+ captureStart = captureEnd = state.position;
+
+ } else if (state.position === state.lineStart && testDocumentSeparator(state)) {
+ throwError(state, 'unexpected end of the document within a double quoted scalar');
+
+ } else {
+ state.position++;
+ captureEnd = state.position;
+ }
+ }
+
+ throwError(state, 'unexpected end of the stream within a double quoted scalar');
+}
+
+function readFlowCollection(state, nodeIndent) {
+ var readNext = true,
+ _line,
+ _tag = state.tag,
+ _result,
+ _anchor = state.anchor,
+ following,
+ terminator,
+ isPair,
+ isExplicitPair,
+ isMapping,
+ overridableKeys = {},
+ keyNode,
+ keyTag,
+ valueNode,
+ ch;
+
+ ch = state.input.charCodeAt(state.position);
+
+ if (ch === 0x5B/* [ */) {
+ terminator = 0x5D;/* ] */
+ isMapping = false;
+ _result = [];
+ } else if (ch === 0x7B/* { */) {
+ terminator = 0x7D;/* } */
+ isMapping = true;
+ _result = {};
+ } else {
+ return false;
+ }
+
+ if (state.anchor !== null) {
+ state.anchorMap[state.anchor] = _result;
+ }
+
+ ch = state.input.charCodeAt(++state.position);
+
+ while (ch !== 0) {
+ skipSeparationSpace(state, true, nodeIndent);
+
+ ch = state.input.charCodeAt(state.position);
+
+ if (ch === terminator) {
+ state.position++;
+ state.tag = _tag;
+ state.anchor = _anchor;
+ state.kind = isMapping ? 'mapping' : 'sequence';
+ state.result = _result;
+ return true;
+ } else if (!readNext) {
+ throwError(state, 'missed comma between flow collection entries');
+ }
+
+ keyTag = keyNode = valueNode = null;
+ isPair = isExplicitPair = false;
+
+ if (ch === 0x3F/* ? */) {
+ following = state.input.charCodeAt(state.position + 1);
+
+ if (is_WS_OR_EOL(following)) {
+ isPair = isExplicitPair = true;
+ state.position++;
+ skipSeparationSpace(state, true, nodeIndent);
+ }
+ }
+
+ _line = state.line;
+ composeNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true);
+ keyTag = state.tag;
+ keyNode = state.result;
+ skipSeparationSpace(state, true, nodeIndent);
+
+ ch = state.input.charCodeAt(state.position);
+
+ if ((isExplicitPair || state.line === _line) && ch === 0x3A/* : */) {
+ isPair = true;
+ ch = state.input.charCodeAt(++state.position);
+ skipSeparationSpace(state, true, nodeIndent);
+ composeNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true);
+ valueNode = state.result;
+ }
+
+ if (isMapping) {
+ storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode);
+ } else if (isPair) {
+ _result.push(storeMappingPair(state, null, overridableKeys, keyTag, keyNode, valueNode));
+ } else {
+ _result.push(keyNode);
+ }
+
+ skipSeparationSpace(state, true, nodeIndent);
+
+ ch = state.input.charCodeAt(state.position);
+
+ if (ch === 0x2C/* , */) {
+ readNext = true;
+ ch = state.input.charCodeAt(++state.position);
+ } else {
+ readNext = false;
+ }
+ }
+
+ throwError(state, 'unexpected end of the stream within a flow collection');
+}
+
+function readBlockScalar(state, nodeIndent) {
+ var captureStart,
+ folding,
+ chomping = CHOMPING_CLIP,
+ didReadContent = false,
+ detectedIndent = false,
+ textIndent = nodeIndent,
+ emptyLines = 0,
+ atMoreIndented = false,
+ tmp,
+ ch;
+
+ ch = state.input.charCodeAt(state.position);
+
+ if (ch === 0x7C/* | */) {
+ folding = false;
+ } else if (ch === 0x3E/* > */) {
+ folding = true;
+ } else {
+ return false;
+ }
+
+ state.kind = 'scalar';
+ state.result = '';
+
+ while (ch !== 0) {
+ ch = state.input.charCodeAt(++state.position);
+
+ if (ch === 0x2B/* + */ || ch === 0x2D/* - */) {
+ if (CHOMPING_CLIP === chomping) {
+ chomping = (ch === 0x2B/* + */) ? CHOMPING_KEEP : CHOMPING_STRIP;
+ } else {
+ throwError(state, 'repeat of a chomping mode identifier');
+ }
+
+ } else if ((tmp = fromDecimalCode(ch)) >= 0) {
+ if (tmp === 0) {
+ throwError(state, 'bad explicit indentation width of a block scalar; it cannot be less than one');
+ } else if (!detectedIndent) {
+ textIndent = nodeIndent + tmp - 1;
+ detectedIndent = true;
+ } else {
+ throwError(state, 'repeat of an indentation width identifier');
+ }
+
+ } else {
+ break;
+ }
+ }
+
+ if (is_WHITE_SPACE(ch)) {
+ do { ch = state.input.charCodeAt(++state.position); }
+ while (is_WHITE_SPACE(ch));
+
+ if (ch === 0x23/* # */) {
+ do { ch = state.input.charCodeAt(++state.position); }
+ while (!is_EOL(ch) && (ch !== 0));
+ }
+ }
+
+ while (ch !== 0) {
+ readLineBreak(state);
+ state.lineIndent = 0;
+
+ ch = state.input.charCodeAt(state.position);
+
+ while ((!detectedIndent || state.lineIndent < textIndent) &&
+ (ch === 0x20/* Space */)) {
+ state.lineIndent++;
+ ch = state.input.charCodeAt(++state.position);
+ }
+
+ if (!detectedIndent && state.lineIndent > textIndent) {
+ textIndent = state.lineIndent;
+ }
+
+ if (is_EOL(ch)) {
+ emptyLines++;
+ continue;
+ }
+
+ // End of the scalar.
+ if (state.lineIndent < textIndent) {
+
+ // Perform the chomping.
+ if (chomping === CHOMPING_KEEP) {
+ state.result += common.repeat('\n', didReadContent ? 1 + emptyLines : emptyLines);
+ } else if (chomping === CHOMPING_CLIP) {
+ if (didReadContent) { // i.e. only if the scalar is not empty.
+ state.result += '\n';
+ }
+ }
+
+ // Break this `while` cycle and go to the funciton's epilogue.
+ break;
+ }
+
+ // Folded style: use fancy rules to handle line breaks.
+ if (folding) {
+
+ // Lines starting with white space characters (more-indented lines) are not folded.
+ if (is_WHITE_SPACE(ch)) {
+ atMoreIndented = true;
+ // except for the first content line (cf. Example 8.1)
+ state.result += common.repeat('\n', didReadContent ? 1 + emptyLines : emptyLines);
+
+ // End of more-indented block.
+ } else if (atMoreIndented) {
+ atMoreIndented = false;
+ state.result += common.repeat('\n', emptyLines + 1);
+
+ // Just one line break - perceive as the same line.
+ } else if (emptyLines === 0) {
+ if (didReadContent) { // i.e. only if we have already read some scalar content.
+ state.result += ' ';
+ }
+
+ // Several line breaks - perceive as different lines.
+ } else {
+ state.result += common.repeat('\n', emptyLines);
+ }
+
+ // Literal style: just add exact number of line breaks between content lines.
+ } else {
+ // Keep all line breaks except the header line break.
+ state.result += common.repeat('\n', didReadContent ? 1 + emptyLines : emptyLines);
+ }
+
+ didReadContent = true;
+ detectedIndent = true;
+ emptyLines = 0;
+ captureStart = state.position;
+
+ while (!is_EOL(ch) && (ch !== 0)) {
+ ch = state.input.charCodeAt(++state.position);
+ }
+
+ captureSegment(state, captureStart, state.position, false);
+ }
+
+ return true;
+}
+
+function readBlockSequence(state, nodeIndent) {
+ var _line,
+ _tag = state.tag,
+ _anchor = state.anchor,
+ _result = [],
+ following,
+ detected = false,
+ ch;
+
+ if (state.anchor !== null) {
+ state.anchorMap[state.anchor] = _result;
+ }
+
+ ch = state.input.charCodeAt(state.position);
+
+ while (ch !== 0) {
+
+ if (ch !== 0x2D/* - */) {
+ break;
+ }
+
+ following = state.input.charCodeAt(state.position + 1);
+
+ if (!is_WS_OR_EOL(following)) {
+ break;
+ }
+
+ detected = true;
+ state.position++;
+
+ if (skipSeparationSpace(state, true, -1)) {
+ if (state.lineIndent <= nodeIndent) {
+ _result.push(null);
+ ch = state.input.charCodeAt(state.position);
+ continue;
+ }
+ }
+
+ _line = state.line;
+ composeNode(state, nodeIndent, CONTEXT_BLOCK_IN, false, true);
+ _result.push(state.result);
+ skipSeparationSpace(state, true, -1);
+
+ ch = state.input.charCodeAt(state.position);
+
+ if ((state.line === _line || state.lineIndent > nodeIndent) && (ch !== 0)) {
+ throwError(state, 'bad indentation of a sequence entry');
+ } else if (state.lineIndent < nodeIndent) {
+ break;
+ }
+ }
+
+ if (detected) {
+ state.tag = _tag;
+ state.anchor = _anchor;
+ state.kind = 'sequence';
+ state.result = _result;
+ return true;
+ }
+ return false;
+}
+
+function readBlockMapping(state, nodeIndent, flowIndent) {
+ var following,
+ allowCompact,
+ _line,
+ _pos,
+ _tag = state.tag,
+ _anchor = state.anchor,
+ _result = {},
+ overridableKeys = {},
+ keyTag = null,
+ keyNode = null,
+ valueNode = null,
+ atExplicitKey = false,
+ detected = false,
+ ch;
+
+ if (state.anchor !== null) {
+ state.anchorMap[state.anchor] = _result;
+ }
+
+ ch = state.input.charCodeAt(state.position);
+
+ while (ch !== 0) {
+ following = state.input.charCodeAt(state.position + 1);
+ _line = state.line; // Save the current line.
+ _pos = state.position;
+
+ //
+ // Explicit notation case. There are two separate blocks:
+ // first for the key (denoted by "?") and second for the value (denoted by ":")
+ //
+ if ((ch === 0x3F/* ? */ || ch === 0x3A/* : */) && is_WS_OR_EOL(following)) {
+
+ if (ch === 0x3F/* ? */) {
+ if (atExplicitKey) {
+ storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null);
+ keyTag = keyNode = valueNode = null;
+ }
+
+ detected = true;
+ atExplicitKey = true;
+ allowCompact = true;
+
+ } else if (atExplicitKey) {
+ // i.e. 0x3A/* : */ === character after the explicit key.
+ atExplicitKey = false;
+ allowCompact = true;
+
+ } else {
+ throwError(state, 'incomplete explicit mapping pair; a key node is missed; or followed by a non-tabulated empty line');
+ }
+
+ state.position += 1;
+ ch = following;
+
+ //
+ // Implicit notation case. Flow-style node as the key first, then ":", and the value.
+ //
+ } else if (composeNode(state, flowIndent, CONTEXT_FLOW_OUT, false, true)) {
+
+ if (state.line === _line) {
+ ch = state.input.charCodeAt(state.position);
+
+ while (is_WHITE_SPACE(ch)) {
+ ch = state.input.charCodeAt(++state.position);
+ }
+
+ if (ch === 0x3A/* : */) {
+ ch = state.input.charCodeAt(++state.position);
+
+ if (!is_WS_OR_EOL(ch)) {
+ throwError(state, 'a whitespace character is expected after the key-value separator within a block mapping');
+ }
+
+ if (atExplicitKey) {
+ storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null);
+ keyTag = keyNode = valueNode = null;
+ }
+
+ detected = true;
+ atExplicitKey = false;
+ allowCompact = false;
+ keyTag = state.tag;
+ keyNode = state.result;
+
+ } else if (detected) {
+ throwError(state, 'can not read an implicit mapping pair; a colon is missed');
+
+ } else {
+ state.tag = _tag;
+ state.anchor = _anchor;
+ return true; // Keep the result of `composeNode`.
+ }
+
+ } else if (detected) {
+ throwError(state, 'can not read a block mapping entry; a multiline key may not be an implicit key');
+
+ } else {
+ state.tag = _tag;
+ state.anchor = _anchor;
+ return true; // Keep the result of `composeNode`.
+ }
+
+ } else {
+ break; // Reading is done. Go to the epilogue.
+ }
+
+ //
+ // Common reading code for both explicit and implicit notations.
+ //
+ if (state.line === _line || state.lineIndent > nodeIndent) {
+ if (composeNode(state, nodeIndent, CONTEXT_BLOCK_OUT, true, allowCompact)) {
+ if (atExplicitKey) {
+ keyNode = state.result;
+ } else {
+ valueNode = state.result;
+ }
+ }
+
+ if (!atExplicitKey) {
+ storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode, _line, _pos);
+ keyTag = keyNode = valueNode = null;
+ }
+
+ skipSeparationSpace(state, true, -1);
+ ch = state.input.charCodeAt(state.position);
+ }
+
+ if (state.lineIndent > nodeIndent && (ch !== 0)) {
+ throwError(state, 'bad indentation of a mapping entry');
+ } else if (state.lineIndent < nodeIndent) {
+ break;
+ }
+ }
+
+ //
+ // Epilogue.
+ //
+
+ // Special case: last mapping's node contains only the key in explicit notation.
+ if (atExplicitKey) {
+ storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null);
+ }
+
+ // Expose the resulting mapping.
+ if (detected) {
+ state.tag = _tag;
+ state.anchor = _anchor;
+ state.kind = 'mapping';
+ state.result = _result;
+ }
+
+ return detected;
+}
+
+function readTagProperty(state) {
+ var _position,
+ isVerbatim = false,
+ isNamed = false,
+ tagHandle,
+ tagName,
+ ch;
+
+ ch = state.input.charCodeAt(state.position);
+
+ if (ch !== 0x21/* ! */) return false;
+
+ if (state.tag !== null) {
+ throwError(state, 'duplication of a tag property');
+ }
+
+ ch = state.input.charCodeAt(++state.position);
+
+ if (ch === 0x3C/* < */) {
+ isVerbatim = true;
+ ch = state.input.charCodeAt(++state.position);
+
+ } else if (ch === 0x21/* ! */) {
+ isNamed = true;
+ tagHandle = '!!';
+ ch = state.input.charCodeAt(++state.position);
+
+ } else {
+ tagHandle = '!';
+ }
+
+ _position = state.position;
+
+ if (isVerbatim) {
+ do { ch = state.input.charCodeAt(++state.position); }
+ while (ch !== 0 && ch !== 0x3E/* > */);
+
+ if (state.position < state.length) {
+ tagName = state.input.slice(_position, state.position);
+ ch = state.input.charCodeAt(++state.position);
+ } else {
+ throwError(state, 'unexpected end of the stream within a verbatim tag');
+ }
+ } else {
+ while (ch !== 0 && !is_WS_OR_EOL(ch)) {
+
+ if (ch === 0x21/* ! */) {
+ if (!isNamed) {
+ tagHandle = state.input.slice(_position - 1, state.position + 1);
+
+ if (!PATTERN_TAG_HANDLE.test(tagHandle)) {
+ throwError(state, 'named tag handle cannot contain such characters');
+ }
+
+ isNamed = true;
+ _position = state.position + 1;
+ } else {
+ throwError(state, 'tag suffix cannot contain exclamation marks');
+ }
+ }
+
+ ch = state.input.charCodeAt(++state.position);
+ }
+
+ tagName = state.input.slice(_position, state.position);
+
+ if (PATTERN_FLOW_INDICATORS.test(tagName)) {
+ throwError(state, 'tag suffix cannot contain flow indicator characters');
+ }
+ }
+
+ if (tagName && !PATTERN_TAG_URI.test(tagName)) {
+ throwError(state, 'tag name cannot contain such characters: ' + tagName);
+ }
+
+ if (isVerbatim) {
+ state.tag = tagName;
+
+ } else if (_hasOwnProperty.call(state.tagMap, tagHandle)) {
+ state.tag = state.tagMap[tagHandle] + tagName;
+
+ } else if (tagHandle === '!') {
+ state.tag = '!' + tagName;
+
+ } else if (tagHandle === '!!') {
+ state.tag = 'tag:yaml.org,2002:' + tagName;
+
+ } else {
+ throwError(state, 'undeclared tag handle "' + tagHandle + '"');
+ }
+
+ return true;
+}
+
+function readAnchorProperty(state) {
+ var _position,
+ ch;
+
+ ch = state.input.charCodeAt(state.position);
+
+ if (ch !== 0x26/* & */) return false;
+
+ if (state.anchor !== null) {
+ throwError(state, 'duplication of an anchor property');
+ }
+
+ ch = state.input.charCodeAt(++state.position);
+ _position = state.position;
+
+ while (ch !== 0 && !is_WS_OR_EOL(ch) && !is_FLOW_INDICATOR(ch)) {
+ ch = state.input.charCodeAt(++state.position);
+ }
+
+ if (state.position === _position) {
+ throwError(state, 'name of an anchor node must contain at least one character');
+ }
+
+ state.anchor = state.input.slice(_position, state.position);
+ return true;
+}
+
+function readAlias(state) {
+ var _position, alias,
+ ch;
+
+ ch = state.input.charCodeAt(state.position);
+
+ if (ch !== 0x2A/* * */) return false;
+
+ ch = state.input.charCodeAt(++state.position);
+ _position = state.position;
+
+ while (ch !== 0 && !is_WS_OR_EOL(ch) && !is_FLOW_INDICATOR(ch)) {
+ ch = state.input.charCodeAt(++state.position);
+ }
+
+ if (state.position === _position) {
+ throwError(state, 'name of an alias node must contain at least one character');
+ }
+
+ alias = state.input.slice(_position, state.position);
+
+ if (!state.anchorMap.hasOwnProperty(alias)) {
+ throwError(state, 'unidentified alias "' + alias + '"');
+ }
+
+ state.result = state.anchorMap[alias];
+ skipSeparationSpace(state, true, -1);
+ return true;
+}
+
+function composeNode(state, parentIndent, nodeContext, allowToSeek, allowCompact) {
+ var allowBlockStyles,
+ allowBlockScalars,
+ allowBlockCollections,
+ indentStatus = 1, // 1: this>parent, 0: this=parent, -1: this<parent
+ atNewLine = false,
+ hasContent = false,
+ typeIndex,
+ typeQuantity,
+ type,
+ flowIndent,
+ blockIndent;
+
+ if (state.listener !== null) {
+ state.listener('open', state);
+ }
+
+ state.tag = null;
+ state.anchor = null;
+ state.kind = null;
+ state.result = null;
+
+ allowBlockStyles = allowBlockScalars = allowBlockCollections =
+ CONTEXT_BLOCK_OUT === nodeContext ||
+ CONTEXT_BLOCK_IN === nodeContext;
+
+ if (allowToSeek) {
+ if (skipSeparationSpace(state, true, -1)) {
+ atNewLine = true;
+
+ if (state.lineIndent > parentIndent) {
+ indentStatus = 1;
+ } else if (state.lineIndent === parentIndent) {
+ indentStatus = 0;
+ } else if (state.lineIndent < parentIndent) {
+ indentStatus = -1;
+ }
+ }
+ }
+
+ if (indentStatus === 1) {
+ while (readTagProperty(state) || readAnchorProperty(state)) {
+ if (skipSeparationSpace(state, true, -1)) {
+ atNewLine = true;
+ allowBlockCollections = allowBlockStyles;
+
+ if (state.lineIndent > parentIndent) {
+ indentStatus = 1;
+ } else if (state.lineIndent === parentIndent) {
+ indentStatus = 0;
+ } else if (state.lineIndent < parentIndent) {
+ indentStatus = -1;
+ }
+ } else {
+ allowBlockCollections = false;
+ }
+ }
+ }
+
+ if (allowBlockCollections) {
+ allowBlockCollections = atNewLine || allowCompact;
+ }
+
+ if (indentStatus === 1 || CONTEXT_BLOCK_OUT === nodeContext) {
+ if (CONTEXT_FLOW_IN === nodeContext || CONTEXT_FLOW_OUT === nodeContext) {
+ flowIndent = parentIndent;
+ } else {
+ flowIndent = parentIndent + 1;
+ }
+
+ blockIndent = state.position - state.lineStart;
+
+ if (indentStatus === 1) {
+ if (allowBlockCollections &&
+ (readBlockSequence(state, blockIndent) ||
+ readBlockMapping(state, blockIndent, flowIndent)) ||
+ readFlowCollection(state, flowIndent)) {
+ hasContent = true;
+ } else {
+ if ((allowBlockScalars && readBlockScalar(state, flowIndent)) ||
+ readSingleQuotedScalar(state, flowIndent) ||
+ readDoubleQuotedScalar(state, flowIndent)) {
+ hasContent = true;
+
+ } else if (readAlias(state)) {
+ hasContent = true;
+
+ if (state.tag !== null || state.anchor !== null) {
+ throwError(state, 'alias node should not have any properties');
+ }
+
+ } else if (readPlainScalar(state, flowIndent, CONTEXT_FLOW_IN === nodeContext)) {
+ hasContent = true;
+
+ if (state.tag === null) {
+ state.tag = '?';
+ }
+ }
+
+ if (state.anchor !== null) {
+ state.anchorMap[state.anchor] = state.result;
+ }
+ }
+ } else if (indentStatus === 0) {
+ // Special case: block sequences are allowed to have same indentation level as the parent.
+ // http://www.yaml.org/spec/1.2/spec.html#id2799784
+ hasContent = allowBlockCollections && readBlockSequence(state, blockIndent);
+ }
+ }
+
+ if (state.tag !== null && state.tag !== '!') {
+ if (state.tag === '?') {
+ for (typeIndex = 0, typeQuantity = state.implicitTypes.length; typeIndex < typeQuantity; typeIndex += 1) {
+ type = state.implicitTypes[typeIndex];
+
+ // Implicit resolving is not allowed for non-scalar types, and '?'
+ // non-specific tag is only assigned to plain scalars. So, it isn't
+ // needed to check for 'kind' conformity.
+
+ if (type.resolve(state.result)) { // `state.result` updated in resolver if matched
+ state.result = type.construct(state.result);
+ state.tag = type.tag;
+ if (state.anchor !== null) {
+ state.anchorMap[state.anchor] = state.result;
+ }
+ break;
+ }
+ }
+ } else if (_hasOwnProperty.call(state.typeMap[state.kind || 'fallback'], state.tag)) {
+ type = state.typeMap[state.kind || 'fallback'][state.tag];
+
+ if (state.result !== null && type.kind !== state.kind) {
+ throwError(state, 'unacceptable node kind for !<' + state.tag + '> tag; it should be "' + type.kind + '", not "' + state.kind + '"');
+ }
+
+ if (!type.resolve(state.result)) { // `state.result` updated in resolver if matched
+ throwError(state, 'cannot resolve a node with !<' + state.tag + '> explicit tag');
+ } else {
+ state.result = type.construct(state.result);
+ if (state.anchor !== null) {
+ state.anchorMap[state.anchor] = state.result;
+ }
+ }
+ } else {
+ throwError(state, 'unknown tag !<' + state.tag + '>');
+ }
+ }
+
+ if (state.listener !== null) {
+ state.listener('close', state);
+ }
+ return state.tag !== null || state.anchor !== null || hasContent;
+}
+
+function readDocument(state) {
+ var documentStart = state.position,
+ _position,
+ directiveName,
+ directiveArgs,
+ hasDirectives = false,
+ ch;
+
+ state.version = null;
+ state.checkLineBreaks = state.legacy;
+ state.tagMap = {};
+ state.anchorMap = {};
+
+ while ((ch = state.input.charCodeAt(state.position)) !== 0) {
+ skipSeparationSpace(state, true, -1);
+
+ ch = state.input.charCodeAt(state.position);
+
+ if (state.lineIndent > 0 || ch !== 0x25/* % */) {
+ break;
+ }
+
+ hasDirectives = true;
+ ch = state.input.charCodeAt(++state.position);
+ _position = state.position;
+
+ while (ch !== 0 && !is_WS_OR_EOL(ch)) {
+ ch = state.input.charCodeAt(++state.position);
+ }
+
+ directiveName = state.input.slice(_position, state.position);
+ directiveArgs = [];
+
+ if (directiveName.length < 1) {
+ throwError(state, 'directive name must not be less than one character in length');
+ }
+
+ while (ch !== 0) {
+ while (is_WHITE_SPACE(ch)) {
+ ch = state.input.charCodeAt(++state.position);
+ }
+
+ if (ch === 0x23/* # */) {
+ do { ch = state.input.charCodeAt(++state.position); }
+ while (ch !== 0 && !is_EOL(ch));
+ break;
+ }
+
+ if (is_EOL(ch)) break;
+
+ _position = state.position;
+
+ while (ch !== 0 && !is_WS_OR_EOL(ch)) {
+ ch = state.input.charCodeAt(++state.position);
+ }
+
+ directiveArgs.push(state.input.slice(_position, state.position));
+ }
+
+ if (ch !== 0) readLineBreak(state);
+
+ if (_hasOwnProperty.call(directiveHandlers, directiveName)) {
+ directiveHandlers[directiveName](state, directiveName, directiveArgs);
+ } else {
+ throwWarning(state, 'unknown document directive "' + directiveName + '"');
+ }
+ }
+
+ skipSeparationSpace(state, true, -1);
+
+ if (state.lineIndent === 0 &&
+ state.input.charCodeAt(state.position) === 0x2D/* - */ &&
+ state.input.charCodeAt(state.position + 1) === 0x2D/* - */ &&
+ state.input.charCodeAt(state.position + 2) === 0x2D/* - */) {
+ state.position += 3;
+ skipSeparationSpace(state, true, -1);
+
+ } else if (hasDirectives) {
+ throwError(state, 'directives end mark is expected');
+ }
+
+ composeNode(state, state.lineIndent - 1, CONTEXT_BLOCK_OUT, false, true);
+ skipSeparationSpace(state, true, -1);
+
+ if (state.checkLineBreaks &&
+ PATTERN_NON_ASCII_LINE_BREAKS.test(state.input.slice(documentStart, state.position))) {
+ throwWarning(state, 'non-ASCII line breaks are interpreted as content');
+ }
+
+ state.documents.push(state.result);
+
+ if (state.position === state.lineStart && testDocumentSeparator(state)) {
+
+ if (state.input.charCodeAt(state.position) === 0x2E/* . */) {
+ state.position += 3;
+ skipSeparationSpace(state, true, -1);
+ }
+ return;
+ }
+
+ if (state.position < (state.length - 1)) {
+ throwError(state, 'end of the stream or a document separator is expected');
+ } else {
+ return;
+ }
+}
+
+
+function loadDocuments(input, options) {
+ input = String(input);
+ options = options || {};
+
+ if (input.length !== 0) {
+
+ // Add tailing `\n` if not exists
+ if (input.charCodeAt(input.length - 1) !== 0x0A/* LF */ &&
+ input.charCodeAt(input.length - 1) !== 0x0D/* CR */) {
+ input += '\n';
+ }
+
+ // Strip BOM
+ if (input.charCodeAt(0) === 0xFEFF) {
+ input = input.slice(1);
+ }
+ }
+
+ var state = new State(input, options);
+
+ // Use 0 as string terminator. That significantly simplifies bounds check.
+ state.input += '\0';
+
+ while (state.input.charCodeAt(state.position) === 0x20/* Space */) {
+ state.lineIndent += 1;
+ state.position += 1;
+ }
+
+ while (state.position < (state.length - 1)) {
+ readDocument(state);
+ }
+
+ return state.documents;
+}
+
+
+function loadAll(input, iterator, options) {
+ var documents = loadDocuments(input, options), index, length;
+
+ if (typeof iterator !== 'function') {
+ return documents;
+ }
+
+ for (index = 0, length = documents.length; index < length; index += 1) {
+ iterator(documents[index]);
+ }
+}
+
+
+function load(input, options) {
+ var documents = loadDocuments(input, options);
+
+ if (documents.length === 0) {
+ /*eslint-disable no-undefined*/
+ return undefined;
+ } else if (documents.length === 1) {
+ return documents[0];
+ }
+ throw new YAMLException('expected a single document in the stream, but found more');
+}
+
+
+function safeLoadAll(input, output, options) {
+ if (typeof output === 'function') {
+ loadAll(input, output, common.extend({ schema: DEFAULT_SAFE_SCHEMA }, options));
+ } else {
+ return loadAll(input, common.extend({ schema: DEFAULT_SAFE_SCHEMA }, options));
+ }
+}
+
+
+function safeLoad(input, options) {
+ return load(input, common.extend({ schema: DEFAULT_SAFE_SCHEMA }, options));
+}
+
+
+module.exports.loadAll = loadAll;
+module.exports.load = load;
+module.exports.safeLoadAll = safeLoadAll;
+module.exports.safeLoad = safeLoad;
+
+},{"./common":2,"./exception":4,"./mark":6,"./schema/default_full":9,"./schema/default_safe":10}],6:[function(require,module,exports){
+'use strict';
+
+
+var common = require('./common');
+
+
+function Mark(name, buffer, position, line, column) {
+ this.name = name;
+ this.buffer = buffer;
+ this.position = position;
+ this.line = line;
+ this.column = column;
+}
+
+
+Mark.prototype.getSnippet = function getSnippet(indent, maxLength) {
+ var head, start, tail, end, snippet;
+
+ if (!this.buffer) return null;
+
+ indent = indent || 4;
+ maxLength = maxLength || 75;
+
+ head = '';
+ start = this.position;
+
+ while (start > 0 && '\x00\r\n\x85\u2028\u2029'.indexOf(this.buffer.charAt(start - 1)) === -1) {
+ start -= 1;
+ if (this.position - start > (maxLength / 2 - 1)) {
+ head = ' ... ';
+ start += 5;
+ break;
+ }
+ }
+
+ tail = '';
+ end = this.position;
+
+ while (end < this.buffer.length && '\x00\r\n\x85\u2028\u2029'.indexOf(this.buffer.charAt(end)) === -1) {
+ end += 1;
+ if (end - this.position > (maxLength / 2 - 1)) {
+ tail = ' ... ';
+ end -= 5;
+ break;
+ }
+ }
+
+ snippet = this.buffer.slice(start, end);
+
+ return common.repeat(' ', indent) + head + snippet + tail + '\n' +
+ common.repeat(' ', indent + this.position - start + head.length) + '^';
+};
+
+
+Mark.prototype.toString = function toString(compact) {
+ var snippet, where = '';
+
+ if (this.name) {
+ where += 'in "' + this.name + '" ';
+ }
+
+ where += 'at line ' + (this.line + 1) + ', column ' + (this.column + 1);
+
+ if (!compact) {
+ snippet = this.getSnippet();
+
+ if (snippet) {
+ where += ':\n' + snippet;
+ }
+ }
+
+ return where;
+};
+
+
+module.exports = Mark;
+
+},{"./common":2}],7:[function(require,module,exports){
+'use strict';
+
+/*eslint-disable max-len*/
+
+var common = require('./common');
+var YAMLException = require('./exception');
+var Type = require('./type');
+
+
+function compileList(schema, name, result) {
+ var exclude = [];
+
+ schema.include.forEach(function (includedSchema) {
+ result = compileList(includedSchema, name, result);
+ });
+
+ schema[name].forEach(function (currentType) {
+ result.forEach(function (previousType, previousIndex) {
+ if (previousType.tag === currentType.tag && previousType.kind === currentType.kind) {
+ exclude.push(previousIndex);
+ }
+ });
+
+ result.push(currentType);
+ });
+
+ return result.filter(function (type, index) {
+ return exclude.indexOf(index) === -1;
+ });
+}
+
+
+function compileMap(/* lists... */) {
+ var result = {
+ scalar: {},
+ sequence: {},
+ mapping: {},
+ fallback: {}
+ }, index, length;
+
+ function collectType(type) {
+ result[type.kind][type.tag] = result['fallback'][type.tag] = type;
+ }
+
+ for (index = 0, length = arguments.length; index < length; index += 1) {
+ arguments[index].forEach(collectType);
+ }
+ return result;
+}
+
+
+function Schema(definition) {
+ this.include = definition.include || [];
+ this.implicit = definition.implicit || [];
+ this.explicit = definition.explicit || [];
+
+ this.implicit.forEach(function (type) {
+ if (type.loadKind && type.loadKind !== 'scalar') {
+ throw new YAMLException('There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.');
+ }
+ });
+
+ this.compiledImplicit = compileList(this, 'implicit', []);
+ this.compiledExplicit = compileList(this, 'explicit', []);
+ this.compiledTypeMap = compileMap(this.compiledImplicit, this.compiledExplicit);
+}
+
+
+Schema.DEFAULT = null;
+
+
+Schema.create = function createSchema() {
+ var schemas, types;
+
+ switch (arguments.length) {
+ case 1:
+ schemas = Schema.DEFAULT;
+ types = arguments[0];
+ break;
+
+ case 2:
+ schemas = arguments[0];
+ types = arguments[1];
+ break;
+
+ default:
+ throw new YAMLException('Wrong number of arguments for Schema.create function');
+ }
+
+ schemas = common.toArray(schemas);
+ types = common.toArray(types);
+
+ if (!schemas.every(function (schema) { return schema instanceof Schema; })) {
+ throw new YAMLException('Specified list of super schemas (or a single Schema object) contains a non-Schema object.');
+ }
+
+ if (!types.every(function (type) { return type instanceof Type; })) {
+ throw new YAMLException('Specified list of YAML types (or a single Type object) contains a non-Type object.');
+ }
+
+ return new Schema({
+ include: schemas,
+ explicit: types
+ });
+};
+
+
+module.exports = Schema;
+
+},{"./common":2,"./exception":4,"./type":13}],8:[function(require,module,exports){
+// Standard YAML's Core schema.
+// http://www.yaml.org/spec/1.2/spec.html#id2804923
+//
+// NOTE: JS-YAML does not support schema-specific tag resolution restrictions.
+// So, Core schema has no distinctions from JSON schema is JS-YAML.
+
+
+'use strict';
+
+
+var Schema = require('../schema');
+
+
+module.exports = new Schema({
+ include: [
+ require('./json')
+ ]
+});
+
+},{"../schema":7,"./json":12}],9:[function(require,module,exports){
+// JS-YAML's default schema for `load` function.
+// It is not described in the YAML specification.
+//
+// This schema is based on JS-YAML's default safe schema and includes
+// JavaScript-specific types: !!js/undefined, !!js/regexp and !!js/function.
+//
+// Also this schema is used as default base schema at `Schema.create` function.
+
+
+'use strict';
+
+
+var Schema = require('../schema');
+
+
+module.exports = Schema.DEFAULT = new Schema({
+ include: [
+ require('./default_safe')
+ ],
+ explicit: [
+ require('../type/js/undefined'),
+ require('../type/js/regexp'),
+ require('../type/js/function')
+ ]
+});
+
+},{"../schema":7,"../type/js/function":18,"../type/js/regexp":19,"../type/js/undefined":20,"./default_safe":10}],10:[function(require,module,exports){
+// JS-YAML's default schema for `safeLoad` function.
+// It is not described in the YAML specification.
+//
+// This schema is based on standard YAML's Core schema and includes most of
+// extra types described at YAML tag repository. (http://yaml.org/type/)
+
+
+'use strict';
+
+
+var Schema = require('../schema');
+
+
+module.exports = new Schema({
+ include: [
+ require('./core')
+ ],
+ implicit: [
+ require('../type/timestamp'),
+ require('../type/merge')
+ ],
+ explicit: [
+ require('../type/binary'),
+ require('../type/omap'),
+ require('../type/pairs'),
+ require('../type/set')
+ ]
+});
+
+},{"../schema":7,"../type/binary":14,"../type/merge":22,"../type/omap":24,"../type/pairs":25,"../type/set":27,"../type/timestamp":29,"./core":8}],11:[function(require,module,exports){
+// Standard YAML's Failsafe schema.
+// http://www.yaml.org/spec/1.2/spec.html#id2802346
+
+
+'use strict';
+
+
+var Schema = require('../schema');
+
+
+module.exports = new Schema({
+ explicit: [
+ require('../type/str'),
+ require('../type/seq'),
+ require('../type/map')
+ ]
+});
+
+},{"../schema":7,"../type/map":21,"../type/seq":26,"../type/str":28}],12:[function(require,module,exports){
+// Standard YAML's JSON schema.
+// http://www.yaml.org/spec/1.2/spec.html#id2803231
+//
+// NOTE: JS-YAML does not support schema-specific tag resolution restrictions.
+// So, this schema is not such strict as defined in the YAML specification.
+// It allows numbers in binary notaion, use `Null` and `NULL` as `null`, etc.
+
+
+'use strict';
+
+
+var Schema = require('../schema');
+
+
+module.exports = new Schema({
+ include: [
+ require('./failsafe')
+ ],
+ implicit: [
+ require('../type/null'),
+ require('../type/bool'),
+ require('../type/int'),
+ require('../type/float')
+ ]
+});
+
+},{"../schema":7,"../type/bool":15,"../type/float":16,"../type/int":17,"../type/null":23,"./failsafe":11}],13:[function(require,module,exports){
+'use strict';
+
+var YAMLException = require('./exception');
+
+var TYPE_CONSTRUCTOR_OPTIONS = [
+ 'kind',
+ 'resolve',
+ 'construct',
+ 'instanceOf',
+ 'predicate',
+ 'represent',
+ 'defaultStyle',
+ 'styleAliases'
+];
+
+var YAML_NODE_KINDS = [
+ 'scalar',
+ 'sequence',
+ 'mapping'
+];
+
+function compileStyleAliases(map) {
+ var result = {};
+
+ if (map !== null) {
+ Object.keys(map).forEach(function (style) {
+ map[style].forEach(function (alias) {
+ result[String(alias)] = style;
+ });
+ });
+ }
+
+ return result;
+}
+
+function Type(tag, options) {
+ options = options || {};
+
+ Object.keys(options).forEach(function (name) {
+ if (TYPE_CONSTRUCTOR_OPTIONS.indexOf(name) === -1) {
+ throw new YAMLException('Unknown option "' + name + '" is met in definition of "' + tag + '" YAML type.');
+ }
+ });
+
+ // TODO: Add tag format check.
+ this.tag = tag;
+ this.kind = options['kind'] || null;
+ this.resolve = options['resolve'] || function () { return true; };
+ this.construct = options['construct'] || function (data) { return data; };
+ this.instanceOf = options['instanceOf'] || null;
+ this.predicate = options['predicate'] || null;
+ this.represent = options['represent'] || null;
+ this.defaultStyle = options['defaultStyle'] || null;
+ this.styleAliases = compileStyleAliases(options['styleAliases'] || null);
+
+ if (YAML_NODE_KINDS.indexOf(this.kind) === -1) {
+ throw new YAMLException('Unknown kind "' + this.kind + '" is specified for "' + tag + '" YAML type.');
+ }
+}
+
+module.exports = Type;
+
+},{"./exception":4}],14:[function(require,module,exports){
+'use strict';
+
+/*eslint-disable no-bitwise*/
+
+var NodeBuffer;
+
+try {
+ // A trick for browserified version, to not include `Buffer` shim
+ var _require = require;
+ NodeBuffer = _require('buffer').Buffer;
+} catch (__) {}
+
+var Type = require('../type');
+
+
+// [ 64, 65, 66 ] -> [ padding, CR, LF ]
+var BASE64_MAP = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n\r';
+
+
+function resolveYamlBinary(data) {
+ if (data === null) return false;
+
+ var code, idx, bitlen = 0, max = data.length, map = BASE64_MAP;
+
+ // Convert one by one.
+ for (idx = 0; idx < max; idx++) {
+ code = map.indexOf(data.charAt(idx));
+
+ // Skip CR/LF
+ if (code > 64) continue;
+
+ // Fail on illegal characters
+ if (code < 0) return false;
+
+ bitlen += 6;
+ }
+
+ // If there are any bits left, source was corrupted
+ return (bitlen % 8) === 0;
+}
+
+function constructYamlBinary(data) {
+ var idx, tailbits,
+ input = data.replace(/[\r\n=]/g, ''), // remove CR/LF & padding to simplify scan
+ max = input.length,
+ map = BASE64_MAP,
+ bits = 0,
+ result = [];
+
+ // Collect by 6*4 bits (3 bytes)
+
+ for (idx = 0; idx < max; idx++) {
+ if ((idx % 4 === 0) && idx) {
+ result.push((bits >> 16) & 0xFF);
+ result.push((bits >> 8) & 0xFF);
+ result.push(bits & 0xFF);
+ }
+
+ bits = (bits << 6) | map.indexOf(input.charAt(idx));
+ }
+
+ // Dump tail
+
+ tailbits = (max % 4) * 6;
+
+ if (tailbits === 0) {
+ result.push((bits >> 16) & 0xFF);
+ result.push((bits >> 8) & 0xFF);
+ result.push(bits & 0xFF);
+ } else if (tailbits === 18) {
+ result.push((bits >> 10) & 0xFF);
+ result.push((bits >> 2) & 0xFF);
+ } else if (tailbits === 12) {
+ result.push((bits >> 4) & 0xFF);
+ }
+
+ // Wrap into Buffer for NodeJS and leave Array for browser
+ if (NodeBuffer) {
+ // Support node 6.+ Buffer API when available
+ return NodeBuffer.from ? NodeBuffer.from(result) : new NodeBuffer(result);
+ }
+
+ return result;
+}
+
+function representYamlBinary(object /*, style*/) {
+ var result = '', bits = 0, idx, tail,
+ max = object.length,
+ map = BASE64_MAP;
+
+ // Convert every three bytes to 4 ASCII characters.
+
+ for (idx = 0; idx < max; idx++) {
+ if ((idx % 3 === 0) && idx) {
+ result += map[(bits >> 18) & 0x3F];
+ result += map[(bits >> 12) & 0x3F];
+ result += map[(bits >> 6) & 0x3F];
+ result += map[bits & 0x3F];
+ }
+
+ bits = (bits << 8) + object[idx];
+ }
+
+ // Dump tail
+
+ tail = max % 3;
+
+ if (tail === 0) {
+ result += map[(bits >> 18) & 0x3F];
+ result += map[(bits >> 12) & 0x3F];
+ result += map[(bits >> 6) & 0x3F];
+ result += map[bits & 0x3F];
+ } else if (tail === 2) {
+ result += map[(bits >> 10) & 0x3F];
+ result += map[(bits >> 4) & 0x3F];
+ result += map[(bits << 2) & 0x3F];
+ result += map[64];
+ } else if (tail === 1) {
+ result += map[(bits >> 2) & 0x3F];
+ result += map[(bits << 4) & 0x3F];
+ result += map[64];
+ result += map[64];
+ }
+
+ return result;
+}
+
+function isBinary(object) {
+ return NodeBuffer && NodeBuffer.isBuffer(object);
+}
+
+module.exports = new Type('tag:yaml.org,2002:binary', {
+ kind: 'scalar',
+ resolve: resolveYamlBinary,
+ construct: constructYamlBinary,
+ predicate: isBinary,
+ represent: representYamlBinary
+});
+
+},{"../type":13}],15:[function(require,module,exports){
+'use strict';
+
+var Type = require('../type');
+
+function resolveYamlBoolean(data) {
+ if (data === null) return false;
+
+ var max = data.length;
+
+ return (max === 4 && (data === 'true' || data === 'True' || data === 'TRUE')) ||
+ (max === 5 && (data === 'false' || data === 'False' || data === 'FALSE'));
+}
+
+function constructYamlBoolean(data) {
+ return data === 'true' ||
+ data === 'True' ||
+ data === 'TRUE';
+}
+
+function isBoolean(object) {
+ return Object.prototype.toString.call(object) === '[object Boolean]';
+}
+
+module.exports = new Type('tag:yaml.org,2002:bool', {
+ kind: 'scalar',
+ resolve: resolveYamlBoolean,
+ construct: constructYamlBoolean,
+ predicate: isBoolean,
+ represent: {
+ lowercase: function (object) { return object ? 'true' : 'false'; },
+ uppercase: function (object) { return object ? 'TRUE' : 'FALSE'; },
+ camelcase: function (object) { return object ? 'True' : 'False'; }
+ },
+ defaultStyle: 'lowercase'
+});
+
+},{"../type":13}],16:[function(require,module,exports){
+'use strict';
+
+var common = require('../common');
+var Type = require('../type');
+
+var YAML_FLOAT_PATTERN = new RegExp(
+ // 2.5e4, 2.5 and integers
+ '^(?:[-+]?(?:0|[1-9][0-9_]*)(?:\\.[0-9_]*)?(?:[eE][-+]?[0-9]+)?' +
+ // .2e4, .2
+ // special case, seems not from spec
+ '|\\.[0-9_]+(?:[eE][-+]?[0-9]+)?' +
+ // 20:59
+ '|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]*' +
+ // .inf
+ '|[-+]?\\.(?:inf|Inf|INF)' +
+ // .nan
+ '|\\.(?:nan|NaN|NAN))$');
+
+function resolveYamlFloat(data) {
+ if (data === null) return false;
+
+ if (!YAML_FLOAT_PATTERN.test(data) ||
+ // Quick hack to not allow integers end with `_`
+ // Probably should update regexp & check speed
+ data[data.length - 1] === '_') {
+ return false;
+ }
+
+ return true;
+}
+
+function constructYamlFloat(data) {
+ var value, sign, base, digits;
+
+ value = data.replace(/_/g, '').toLowerCase();
+ sign = value[0] === '-' ? -1 : 1;
+ digits = [];
+
+ if ('+-'.indexOf(value[0]) >= 0) {
+ value = value.slice(1);
+ }
+
+ if (value === '.inf') {
+ return (sign === 1) ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY;
+
+ } else if (value === '.nan') {
+ return NaN;
+
+ } else if (value.indexOf(':') >= 0) {
+ value.split(':').forEach(function (v) {
+ digits.unshift(parseFloat(v, 10));
+ });
+
+ value = 0.0;
+ base = 1;
+
+ digits.forEach(function (d) {
+ value += d * base;
+ base *= 60;
+ });
+
+ return sign * value;
+
+ }
+ return sign * parseFloat(value, 10);
+}
+
+
+var SCIENTIFIC_WITHOUT_DOT = /^[-+]?[0-9]+e/;
+
+function representYamlFloat(object, style) {
+ var res;
+
+ if (isNaN(object)) {
+ switch (style) {
+ case 'lowercase': return '.nan';
+ case 'uppercase': return '.NAN';
+ case 'camelcase': return '.NaN';
+ }
+ } else if (Number.POSITIVE_INFINITY === object) {
+ switch (style) {
+ case 'lowercase': return '.inf';
+ case 'uppercase': return '.INF';
+ case 'camelcase': return '.Inf';
+ }
+ } else if (Number.NEGATIVE_INFINITY === object) {
+ switch (style) {
+ case 'lowercase': return '-.inf';
+ case 'uppercase': return '-.INF';
+ case 'camelcase': return '-.Inf';
+ }
+ } else if (common.isNegativeZero(object)) {
+ return '-0.0';
+ }
+
+ res = object.toString(10);
+
+ // JS stringifier can build scientific format without dots: 5e-100,
+ // while YAML requres dot: 5.e-100. Fix it with simple hack
+
+ return SCIENTIFIC_WITHOUT_DOT.test(res) ? res.replace('e', '.e') : res;
+}
+
+function isFloat(object) {
+ return (Object.prototype.toString.call(object) === '[object Number]') &&
+ (object % 1 !== 0 || common.isNegativeZero(object));
+}
+
+module.exports = new Type('tag:yaml.org,2002:float', {
+ kind: 'scalar',
+ resolve: resolveYamlFloat,
+ construct: constructYamlFloat,
+ predicate: isFloat,
+ represent: representYamlFloat,
+ defaultStyle: 'lowercase'
+});
+
+},{"../common":2,"../type":13}],17:[function(require,module,exports){
+'use strict';
+
+var common = require('../common');
+var Type = require('../type');
+
+function isHexCode(c) {
+ return ((0x30/* 0 */ <= c) && (c <= 0x39/* 9 */)) ||
+ ((0x41/* A */ <= c) && (c <= 0x46/* F */)) ||
+ ((0x61/* a */ <= c) && (c <= 0x66/* f */));
+}
+
+function isOctCode(c) {
+ return ((0x30/* 0 */ <= c) && (c <= 0x37/* 7 */));
+}
+
+function isDecCode(c) {
+ return ((0x30/* 0 */ <= c) && (c <= 0x39/* 9 */));
+}
+
+function resolveYamlInteger(data) {
+ if (data === null) return false;
+
+ var max = data.length,
+ index = 0,
+ hasDigits = false,
+ ch;
+
+ if (!max) return false;
+
+ ch = data[index];
+
+ // sign
+ if (ch === '-' || ch === '+') {
+ ch = data[++index];
+ }
+
+ if (ch === '0') {
+ // 0
+ if (index + 1 === max) return true;
+ ch = data[++index];
+
+ // base 2, base 8, base 16
+
+ if (ch === 'b') {
+ // base 2
+ index++;
+
+ for (; index < max; index++) {
+ ch = data[index];
+ if (ch === '_') continue;
+ if (ch !== '0' && ch !== '1') return false;
+ hasDigits = true;
+ }
+ return hasDigits && ch !== '_';
+ }
+
+
+ if (ch === 'x') {
+ // base 16
+ index++;
+
+ for (; index < max; index++) {
+ ch = data[index];
+ if (ch === '_') continue;
+ if (!isHexCode(data.charCodeAt(index))) return false;
+ hasDigits = true;
+ }
+ return hasDigits && ch !== '_';
+ }
+
+ // base 8
+ for (; index < max; index++) {
+ ch = data[index];
+ if (ch === '_') continue;
+ if (!isOctCode(data.charCodeAt(index))) return false;
+ hasDigits = true;
+ }
+ return hasDigits && ch !== '_';
+ }
+
+ // base 10 (except 0) or base 60
+
+ // value should not start with `_`;
+ if (ch === '_') return false;
+
+ for (; index < max; index++) {
+ ch = data[index];
+ if (ch === '_') continue;
+ if (ch === ':') break;
+ if (!isDecCode(data.charCodeAt(index))) {
+ return false;
+ }
+ hasDigits = true;
+ }
+
+ // Should have digits and should not end with `_`
+ if (!hasDigits || ch === '_') return false;
+
+ // if !base60 - done;
+ if (ch !== ':') return true;
+
+ // base60 almost not used, no needs to optimize
+ return /^(:[0-5]?[0-9])+$/.test(data.slice(index));
+}
+
+function constructYamlInteger(data) {
+ var value = data, sign = 1, ch, base, digits = [];
+
+ if (value.indexOf('_') !== -1) {
+ value = value.replace(/_/g, '');
+ }
+
+ ch = value[0];
+
+ if (ch === '-' || ch === '+') {
+ if (ch === '-') sign = -1;
+ value = value.slice(1);
+ ch = value[0];
+ }
+
+ if (value === '0') return 0;
+
+ if (ch === '0') {
+ if (value[1] === 'b') return sign * parseInt(value.slice(2), 2);
+ if (value[1] === 'x') return sign * parseInt(value, 16);
+ return sign * parseInt(value, 8);
+ }
+
+ if (value.indexOf(':') !== -1) {
+ value.split(':').forEach(function (v) {
+ digits.unshift(parseInt(v, 10));
+ });
+
+ value = 0;
+ base = 1;
+
+ digits.forEach(function (d) {
+ value += (d * base);
+ base *= 60;
+ });
+
+ return sign * value;
+
+ }
+
+ return sign * parseInt(value, 10);
+}
+
+function isInteger(object) {
+ return (Object.prototype.toString.call(object)) === '[object Number]' &&
+ (object % 1 === 0 && !common.isNegativeZero(object));
+}
+
+module.exports = new Type('tag:yaml.org,2002:int', {
+ kind: 'scalar',
+ resolve: resolveYamlInteger,
+ construct: constructYamlInteger,
+ predicate: isInteger,
+ represent: {
+ binary: function (obj) { return obj >= 0 ? '0b' + obj.toString(2) : '-0b' + obj.toString(2).slice(1); },
+ octal: function (obj) { return obj >= 0 ? '0' + obj.toString(8) : '-0' + obj.toString(8).slice(1); },
+ decimal: function (obj) { return obj.toString(10); },
+ /* eslint-disable max-len */
+ hexadecimal: function (obj) { return obj >= 0 ? '0x' + obj.toString(16).toUpperCase() : '-0x' + obj.toString(16).toUpperCase().slice(1); }
+ },
+ defaultStyle: 'decimal',
+ styleAliases: {
+ binary: [ 2, 'bin' ],
+ octal: [ 8, 'oct' ],
+ decimal: [ 10, 'dec' ],
+ hexadecimal: [ 16, 'hex' ]
+ }
+});
+
+},{"../common":2,"../type":13}],18:[function(require,module,exports){
+'use strict';
+
+var esprima;
+
+// Browserified version does not have esprima
+//
+// 1. For node.js just require module as deps
+// 2. For browser try to require mudule via external AMD system.
+// If not found - try to fallback to window.esprima. If not
+// found too - then fail to parse.
+//
+try {
+ // workaround to exclude package from browserify list.
+ var _require = require;
+ esprima = _require('esprima');
+} catch (_) {
+ /*global window */
+ if (typeof window !== 'undefined') esprima = window.esprima;
+}
+
+var Type = require('../../type');
+
+function resolveJavascriptFunction(data) {
+ if (data === null) return false;
+
+ try {
+ var source = '(' + data + ')',
+ ast = esprima.parse(source, { range: true });
+
+ if (ast.type !== 'Program' ||
+ ast.body.length !== 1 ||
+ ast.body[0].type !== 'ExpressionStatement' ||
+ (ast.body[0].expression.type !== 'ArrowFunctionExpression' &&
+ ast.body[0].expression.type !== 'FunctionExpression')) {
+ return false;
+ }
+
+ return true;
+ } catch (err) {
+ return false;
+ }
+}
+
+function constructJavascriptFunction(data) {
+ /*jslint evil:true*/
+
+ var source = '(' + data + ')',
+ ast = esprima.parse(source, { range: true }),
+ params = [],
+ body;
+
+ if (ast.type !== 'Program' ||
+ ast.body.length !== 1 ||
+ ast.body[0].type !== 'ExpressionStatement' ||
+ (ast.body[0].expression.type !== 'ArrowFunctionExpression' &&
+ ast.body[0].expression.type !== 'FunctionExpression')) {
+ throw new Error('Failed to resolve function');
+ }
+
+ ast.body[0].expression.params.forEach(function (param) {
+ params.push(param.name);
+ });
+
+ body = ast.body[0].expression.body.range;
+
+ // Esprima's ranges include the first '{' and the last '}' characters on
+ // function expressions. So cut them out.
+ if (ast.body[0].expression.body.type === 'BlockStatement') {
+ /*eslint-disable no-new-func*/
+ return new Function(params, source.slice(body[0] + 1, body[1] - 1));
+ }
+ // ES6 arrow functions can omit the BlockStatement. In that case, just return
+ // the body.
+ /*eslint-disable no-new-func*/
+ return new Function(params, 'return ' + source.slice(body[0], body[1]));
+}
+
+function representJavascriptFunction(object /*, style*/) {
+ return object.toString();
+}
+
+function isFunction(object) {
+ return Object.prototype.toString.call(object) === '[object Function]';
+}
+
+module.exports = new Type('tag:yaml.org,2002:js/function', {
+ kind: 'scalar',
+ resolve: resolveJavascriptFunction,
+ construct: constructJavascriptFunction,
+ predicate: isFunction,
+ represent: representJavascriptFunction
+});
+
+},{"../../type":13}],19:[function(require,module,exports){
+'use strict';
+
+var Type = require('../../type');
+
+function resolveJavascriptRegExp(data) {
+ if (data === null) return false;
+ if (data.length === 0) return false;
+
+ var regexp = data,
+ tail = /\/([gim]*)$/.exec(data),
+ modifiers = '';
+
+ // if regexp starts with '/' it can have modifiers and must be properly closed
+ // `/foo/gim` - modifiers tail can be maximum 3 chars
+ if (regexp[0] === '/') {
+ if (tail) modifiers = tail[1];
+
+ if (modifiers.length > 3) return false;
+ // if expression starts with /, is should be properly terminated
+ if (regexp[regexp.length - modifiers.length - 1] !== '/') return false;
+ }
+
+ return true;
+}
+
+function constructJavascriptRegExp(data) {
+ var regexp = data,
+ tail = /\/([gim]*)$/.exec(data),
+ modifiers = '';
+
+ // `/foo/gim` - tail can be maximum 4 chars
+ if (regexp[0] === '/') {
+ if (tail) modifiers = tail[1];
+ regexp = regexp.slice(1, regexp.length - modifiers.length - 1);
+ }
+
+ return new RegExp(regexp, modifiers);
+}
+
+function representJavascriptRegExp(object /*, style*/) {
+ var result = '/' + object.source + '/';
+
+ if (object.global) result += 'g';
+ if (object.multiline) result += 'm';
+ if (object.ignoreCase) result += 'i';
+
+ return result;
+}
+
+function isRegExp(object) {
+ return Object.prototype.toString.call(object) === '[object RegExp]';
+}
+
+module.exports = new Type('tag:yaml.org,2002:js/regexp', {
+ kind: 'scalar',
+ resolve: resolveJavascriptRegExp,
+ construct: constructJavascriptRegExp,
+ predicate: isRegExp,
+ represent: representJavascriptRegExp
+});
+
+},{"../../type":13}],20:[function(require,module,exports){
+'use strict';
+
+var Type = require('../../type');
+
+function resolveJavascriptUndefined() {
+ return true;
+}
+
+function constructJavascriptUndefined() {
+ /*eslint-disable no-undefined*/
+ return undefined;
+}
+
+function representJavascriptUndefined() {
+ return '';
+}
+
+function isUndefined(object) {
+ return typeof object === 'undefined';
+}
+
+module.exports = new Type('tag:yaml.org,2002:js/undefined', {
+ kind: 'scalar',
+ resolve: resolveJavascriptUndefined,
+ construct: constructJavascriptUndefined,
+ predicate: isUndefined,
+ represent: representJavascriptUndefined
+});
+
+},{"../../type":13}],21:[function(require,module,exports){
+'use strict';
+
+var Type = require('../type');
+
+module.exports = new Type('tag:yaml.org,2002:map', {
+ kind: 'mapping',
+ construct: function (data) { return data !== null ? data : {}; }
+});
+
+},{"../type":13}],22:[function(require,module,exports){
+'use strict';
+
+var Type = require('../type');
+
+function resolveYamlMerge(data) {
+ return data === '<<' || data === null;
+}
+
+module.exports = new Type('tag:yaml.org,2002:merge', {
+ kind: 'scalar',
+ resolve: resolveYamlMerge
+});
+
+},{"../type":13}],23:[function(require,module,exports){
+'use strict';
+
+var Type = require('../type');
+
+function resolveYamlNull(data) {
+ if (data === null) return true;
+
+ var max = data.length;
+
+ return (max === 1 && data === '~') ||
+ (max === 4 && (data === 'null' || data === 'Null' || data === 'NULL'));
+}
+
+function constructYamlNull() {
+ return null;
+}
+
+function isNull(object) {
+ return object === null;
+}
+
+module.exports = new Type('tag:yaml.org,2002:null', {
+ kind: 'scalar',
+ resolve: resolveYamlNull,
+ construct: constructYamlNull,
+ predicate: isNull,
+ represent: {
+ canonical: function () { return '~'; },
+ lowercase: function () { return 'null'; },
+ uppercase: function () { return 'NULL'; },
+ camelcase: function () { return 'Null'; }
+ },
+ defaultStyle: 'lowercase'
+});
+
+},{"../type":13}],24:[function(require,module,exports){
+'use strict';
+
+var Type = require('../type');
+
+var _hasOwnProperty = Object.prototype.hasOwnProperty;
+var _toString = Object.prototype.toString;
+
+function resolveYamlOmap(data) {
+ if (data === null) return true;
+
+ var objectKeys = [], index, length, pair, pairKey, pairHasKey,
+ object = data;
+
+ for (index = 0, length = object.length; index < length; index += 1) {
+ pair = object[index];
+ pairHasKey = false;
+
+ if (_toString.call(pair) !== '[object Object]') return false;
+
+ for (pairKey in pair) {
+ if (_hasOwnProperty.call(pair, pairKey)) {
+ if (!pairHasKey) pairHasKey = true;
+ else return false;
+ }
+ }
+
+ if (!pairHasKey) return false;
+
+ if (objectKeys.indexOf(pairKey) === -1) objectKeys.push(pairKey);
+ else return false;
+ }
+
+ return true;
+}
+
+function constructYamlOmap(data) {
+ return data !== null ? data : [];
+}
+
+module.exports = new Type('tag:yaml.org,2002:omap', {
+ kind: 'sequence',
+ resolve: resolveYamlOmap,
+ construct: constructYamlOmap
+});
+
+},{"../type":13}],25:[function(require,module,exports){
+'use strict';
+
+var Type = require('../type');
+
+var _toString = Object.prototype.toString;
+
+function resolveYamlPairs(data) {
+ if (data === null) return true;
+
+ var index, length, pair, keys, result,
+ object = data;
+
+ result = new Array(object.length);
+
+ for (index = 0, length = object.length; index < length; index += 1) {
+ pair = object[index];
+
+ if (_toString.call(pair) !== '[object Object]') return false;
+
+ keys = Object.keys(pair);
+
+ if (keys.length !== 1) return false;
+
+ result[index] = [ keys[0], pair[keys[0]] ];
+ }
+
+ return true;
+}
+
+function constructYamlPairs(data) {
+ if (data === null) return [];
+
+ var index, length, pair, keys, result,
+ object = data;
+
+ result = new Array(object.length);
+
+ for (index = 0, length = object.length; index < length; index += 1) {
+ pair = object[index];
+
+ keys = Object.keys(pair);
+
+ result[index] = [ keys[0], pair[keys[0]] ];
+ }
+
+ return result;
+}
+
+module.exports = new Type('tag:yaml.org,2002:pairs', {
+ kind: 'sequence',
+ resolve: resolveYamlPairs,
+ construct: constructYamlPairs
+});
+
+},{"../type":13}],26:[function(require,module,exports){
+'use strict';
+
+var Type = require('../type');
+
+module.exports = new Type('tag:yaml.org,2002:seq', {
+ kind: 'sequence',
+ construct: function (data) { return data !== null ? data : []; }
+});
+
+},{"../type":13}],27:[function(require,module,exports){
+'use strict';
+
+var Type = require('../type');
+
+var _hasOwnProperty = Object.prototype.hasOwnProperty;
+
+function resolveYamlSet(data) {
+ if (data === null) return true;
+
+ var key, object = data;
+
+ for (key in object) {
+ if (_hasOwnProperty.call(object, key)) {
+ if (object[key] !== null) return false;
+ }
+ }
+
+ return true;
+}
+
+function constructYamlSet(data) {
+ return data !== null ? data : {};
+}
+
+module.exports = new Type('tag:yaml.org,2002:set', {
+ kind: 'mapping',
+ resolve: resolveYamlSet,
+ construct: constructYamlSet
+});
+
+},{"../type":13}],28:[function(require,module,exports){
+'use strict';
+
+var Type = require('../type');
+
+module.exports = new Type('tag:yaml.org,2002:str', {
+ kind: 'scalar',
+ construct: function (data) { return data !== null ? data : ''; }
+});
+
+},{"../type":13}],29:[function(require,module,exports){
+'use strict';
+
+var Type = require('../type');
+
+var YAML_DATE_REGEXP = new RegExp(
+ '^([0-9][0-9][0-9][0-9])' + // [1] year
+ '-([0-9][0-9])' + // [2] month
+ '-([0-9][0-9])$'); // [3] day
+
+var YAML_TIMESTAMP_REGEXP = new RegExp(
+ '^([0-9][0-9][0-9][0-9])' + // [1] year
+ '-([0-9][0-9]?)' + // [2] month
+ '-([0-9][0-9]?)' + // [3] day
+ '(?:[Tt]|[ \\t]+)' + // ...
+ '([0-9][0-9]?)' + // [4] hour
+ ':([0-9][0-9])' + // [5] minute
+ ':([0-9][0-9])' + // [6] second
+ '(?:\\.([0-9]*))?' + // [7] fraction
+ '(?:[ \\t]*(Z|([-+])([0-9][0-9]?)' + // [8] tz [9] tz_sign [10] tz_hour
+ '(?::([0-9][0-9]))?))?$'); // [11] tz_minute
+
+function resolveYamlTimestamp(data) {
+ if (data === null) return false;
+ if (YAML_DATE_REGEXP.exec(data) !== null) return true;
+ if (YAML_TIMESTAMP_REGEXP.exec(data) !== null) return true;
+ return false;
+}
+
+function constructYamlTimestamp(data) {
+ var match, year, month, day, hour, minute, second, fraction = 0,
+ delta = null, tz_hour, tz_minute, date;
+
+ match = YAML_DATE_REGEXP.exec(data);
+ if (match === null) match = YAML_TIMESTAMP_REGEXP.exec(data);
+
+ if (match === null) throw new Error('Date resolve error');
+
+ // match: [1] year [2] month [3] day
+
+ year = +(match[1]);
+ month = +(match[2]) - 1; // JS month starts with 0
+ day = +(match[3]);
+
+ if (!match[4]) { // no hour
+ return new Date(Date.UTC(year, month, day));
+ }
+
+ // match: [4] hour [5] minute [6] second [7] fraction
+
+ hour = +(match[4]);
+ minute = +(match[5]);
+ second = +(match[6]);
+
+ if (match[7]) {
+ fraction = match[7].slice(0, 3);
+ while (fraction.length < 3) { // milli-seconds
+ fraction += '0';
+ }
+ fraction = +fraction;
+ }
+
+ // match: [8] tz [9] tz_sign [10] tz_hour [11] tz_minute
+
+ if (match[9]) {
+ tz_hour = +(match[10]);
+ tz_minute = +(match[11] || 0);
+ delta = (tz_hour * 60 + tz_minute) * 60000; // delta in mili-seconds
+ if (match[9] === '-') delta = -delta;
+ }
+
+ date = new Date(Date.UTC(year, month, day, hour, minute, second, fraction));
+
+ if (delta) date.setTime(date.getTime() - delta);
+
+ return date;
+}
+
+function representYamlTimestamp(object /*, style*/) {
+ return object.toISOString();
+}
+
+module.exports = new Type('tag:yaml.org,2002:timestamp', {
+ kind: 'scalar',
+ resolve: resolveYamlTimestamp,
+ construct: constructYamlTimestamp,
+ instanceOf: Date,
+ represent: representYamlTimestamp
+});
+
+},{"../type":13}],"/":[function(require,module,exports){
+'use strict';
+
+
+var yaml = require('./lib/js-yaml.js');
+
+
+module.exports = yaml;
+
+},{"./lib/js-yaml.js":1}]},{},[])("/")
+});
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/lib/shoreditch/bootstrap.css b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/lib/shoreditch/bootstrap.css
new file mode 100644
index 00000000..8051b1ec
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/lib/shoreditch/bootstrap.css
@@ -0,0 +1 @@
+#bootstrap-theme .editable-wrap{display:inline-block;white-space:nowrap;margin:0}#bootstrap-theme .editable-wrap .editable-controls, #bootstrap-theme .editable-wrap .editable-error{margin-bottom:0}#bootstrap-theme .editable-wrap .editable-controls>input, #bootstrap-theme .editable-wrap .editable-controls>select, #bootstrap-theme .editable-wrap .editable-controls>textarea{margin-bottom:0}#bootstrap-theme .editable-wrap .editable-input{display:inline-block}#bootstrap-theme .editable-buttons{display:inline-block;vertical-align:top}#bootstrap-theme .editable-buttons button{margin-left:5px}#bootstrap-theme .editable-input.editable-has-buttons{width:auto}#bootstrap-theme .editable-bstime .editable-input input[type=text]{width:46px}#bootstrap-theme .editable-bstime .well-small{margin-bottom:0;padding:10px}#bootstrap-theme .editable-range output{display:inline-block;min-width:30px;vertical-align:top;text-align:center}#bootstrap-theme .editable-color input[type=color]{width:50px}#bootstrap-theme .editable-checkbox label span, #bootstrap-theme .editable-checklist label span, #bootstrap-theme .editable-radiolist label span{margin-left:7px;margin-right:10px}#bootstrap-theme .editable-hide{display:none !important}#bootstrap-theme .editable-click, #bootstrap-theme a.editable-click{text-decoration:none;color:#428bca;border-bottom:dashed 1px #428bca}#bootstrap-theme .editable-click:hover, #bootstrap-theme a.editable-click:hover{text-decoration:none;color:#2a6496;border-bottom-color:#2a6496}#bootstrap-theme .editable-empty, #bootstrap-theme .editable-empty:hover, #bootstrap-theme .editable-empty:focus, #bootstrap-theme a.editable-empty, #bootstrap-theme a.editable-empty:hover, #bootstrap-theme a.editable-empty:focus{font-style:italic;color:#D14;text-decoration:none}.ta-hidden-input{width:1px;height:1px;border:none;position:absolute;top:-10000px;left:-10000px;opacity:0;overflow:hidden;margin:0;padding:0}#bootstrap-theme .ta-root.focussed>.ta-scroll-window.form-control{outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6);border-color:#66afe9}#bootstrap-theme .ta-editor.ta-html, #bootstrap-theme .ta-scroll-window.form-control{min-height:300px;height:auto;overflow:auto;font-family:inherit;font-size:100%}#bootstrap-theme .ta-scroll-window.form-control{position:relative;padding:0}#bootstrap-theme .ta-scroll-window>.ta-bind{height:auto;min-height:300px;padding:6px 12px}#bootstrap-theme .ta-editor:focus{user-select:text}#bootstrap-theme .ta-resizer-handle-overlay{z-index:100;position:absolute;display:none}#bootstrap-theme .ta-resizer-handle-overlay>.ta-resizer-handle-info{position:absolute;bottom:16px;right:16px;border:1px solid #000;background-color:#FFF;opacity:0.7;padding:0 4px}#bootstrap-theme .ta-resizer-handle-overlay>.ta-resizer-handle-background{position:absolute;bottom:5px;right:5px;left:5px;top:5px;border:1px solid #000;background-color:rgba(0,0,0,0.2)}#bootstrap-theme .ta-resizer-handle-overlay>.ta-resizer-handle-corner{width:10px;height:10px;position:absolute}#bootstrap-theme .ta-resizer-handle-overlay>.ta-resizer-handle-corner-tl{top:0;left:0;border-left:1px solid #000;border-top:1px solid #000}#bootstrap-theme .ta-resizer-handle-overlay>.ta-resizer-handle-corner-tr{top:0;right:0;border-right:1px solid #000;border-top:1px solid #000}#bootstrap-theme .ta-resizer-handle-overlay>.ta-resizer-handle-corner-bl{bottom:0;left:0;border-left:1px solid #000;border-bottom:1px solid #000}#bootstrap-theme .ta-resizer-handle-overlay>.ta-resizer-handle-corner-br{bottom:0;right:0;border:1px solid #000;cursor:se-resize;background-color:#FFF}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;font-size:14px;font-weight:400;line-height:1.42857143;text-align:left;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);padding:1px}.popover.top{margin-top:-10px}.popover.bottom{margin-top:10px}.popover-title{font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0;margin:0;padding:8px 14px}.popover-content{padding:9px 14px}.popover>.arrow, .popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:rgba(0,0,0,0.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}#bootstrap-theme .ng-animate.item:not(.left):not(.right){-webkit-transition:0s ease-in-out left;transition:0s ease-in-out left}#bootstrap-theme .uib-datepicker .uib-title{width:100%}#bootstrap-theme .uib-day button, #bootstrap-theme .uib-month button, #bootstrap-theme .uib-year button{min-width:100%}#bootstrap-theme .uib-left, #bootstrap-theme .uib-right{width:100%}#bootstrap-theme .uib-position-measure{display:block !important;visibility:hidden !important;position:absolute !important;top:-9999px !important;left:-9999px !important}#bootstrap-theme .uib-position-scrollbar-measure{position:absolute !important;top:-9999px !important;width:50px !important;height:50px !important;overflow:scroll !important}#bootstrap-theme .uib-position-body-scrollbar-measure{overflow:scroll !important}#bootstrap-theme .uib-datepicker-popup.dropdown-menu{display:block;float:none;margin:0}#bootstrap-theme .uib-button-bar{padding:10px 9px 2px}[uib-tooltip-popup].tooltip.top-left>.tooltip-arrow, [uib-tooltip-popup].tooltip.top-right>.tooltip-arrow, [uib-tooltip-popup].tooltip.bottom-left>.tooltip-arrow, [uib-tooltip-popup].tooltip.bottom-right>.tooltip-arrow, [uib-tooltip-popup].tooltip.left-top>.tooltip-arrow, [uib-tooltip-popup].tooltip.left-bottom>.tooltip-arrow, [uib-tooltip-popup].tooltip.right-top>.tooltip-arrow, [uib-tooltip-popup].tooltip.right-bottom>.tooltip-arrow, [uib-tooltip-html-popup].tooltip.top-left>.tooltip-arrow, [uib-tooltip-html-popup].tooltip.top-right>.tooltip-arrow, [uib-tooltip-html-popup].tooltip.bottom-left>.tooltip-arrow, [uib-tooltip-html-popup].tooltip.bottom-right>.tooltip-arrow, [uib-tooltip-html-popup].tooltip.left-top>.tooltip-arrow, [uib-tooltip-html-popup].tooltip.left-bottom>.tooltip-arrow, [uib-tooltip-html-popup].tooltip.right-top>.tooltip-arrow, [uib-tooltip-html-popup].tooltip.right-bottom>.tooltip-arrow, [uib-tooltip-template-popup].tooltip.top-left>.tooltip-arrow, [uib-tooltip-template-popup].tooltip.top-right>.tooltip-arrow, [uib-tooltip-template-popup].tooltip.bottom-left>.tooltip-arrow, [uib-tooltip-template-popup].tooltip.bottom-right>.tooltip-arrow, [uib-tooltip-template-popup].tooltip.left-top>.tooltip-arrow, [uib-tooltip-template-popup].tooltip.left-bottom>.tooltip-arrow, [uib-tooltip-template-popup].tooltip.right-top>.tooltip-arrow, [uib-tooltip-template-popup].tooltip.right-bottom>.tooltip-arrow, [uib-popover-popup].popover.top-left>.arrow, [uib-popover-popup].popover.top-right>.arrow, [uib-popover-popup].popover.bottom-left>.arrow, [uib-popover-popup].popover.bottom-right>.arrow, [uib-popover-popup].popover.left-top>.arrow, [uib-popover-popup].popover.left-bottom>.arrow, [uib-popover-popup].popover.right-top>.arrow, [uib-popover-popup].popover.right-bottom>.arrow, [uib-popover-html-popup].popover.top-left>.arrow, [uib-popover-html-popup].popover.top-right>.arrow, [uib-popover-html-popup].popover.bottom-left>.arrow, [uib-popover-html-popup].popover.bottom-right>.arrow, [uib-popover-html-popup].popover.left-top>.arrow, [uib-popover-html-popup].popover.left-bottom>.arrow, [uib-popover-html-popup].popover.right-top>.arrow, [uib-popover-html-popup].popover.right-bottom>.arrow, [uib-popover-template-popup].popover.top-left>.arrow, [uib-popover-template-popup].popover.top-right>.arrow, [uib-popover-template-popup].popover.bottom-left>.arrow, [uib-popover-template-popup].popover.bottom-right>.arrow, [uib-popover-template-popup].popover.left-top>.arrow, [uib-popover-template-popup].popover.left-bottom>.arrow, [uib-popover-template-popup].popover.right-top>.arrow, [uib-popover-template-popup].popover.right-bottom>.arrow{top:auto;bottom:auto;left:auto;right:auto;margin:0}[uib-popover-popup].popover, [uib-popover-html-popup].popover, [uib-popover-template-popup].popover{display:block !important}#bootstrap-theme .uib-time input{width:50px}#bootstrap-theme [uib-typeahead-popup].dropdown-menu{display:block}#bootstrap-theme .ui-select-highlight{font-weight:bold}#bootstrap-theme .ui-select-offscreen{clip:rect(0 0 0 0) !important;width:1px !important;height:1px !important;border:0 !important;margin:0 !important;padding:0 !important;overflow:hidden !important;position:absolute !important;outline:0 !important;left:0px !important;top:0px !important}#bootstrap-theme .ui-select-choices-row:hover{background-color:#f5f5f5}#bootstrap-theme .ng-dirty.ng-invalid>a.select2-choice{border-color:#D44950}#bootstrap-theme .select2-result-single{padding-left:0}#bootstrap-theme .select2-locked>.select2-search-choice-close{display:none}#bootstrap-theme .select-locked>.ui-select-match-close{display:none}body#bootstrap-theme>.select2-container.open, #bootstrap-theme>.select2-container.open{z-index:9999}#bootstrap-theme .ui-select-container[theme="select2"].direction-up .ui-select-match, #bootstrap-theme .ui-select-container.select2.direction-up .ui-select-match{border-radius:4px;border-top-left-radius:0;border-top-right-radius:0}#bootstrap-theme .ui-select-container[theme="select2"].direction-up .ui-select-dropdown, #bootstrap-theme .ui-select-container.select2.direction-up .ui-select-dropdown{border-radius:4px;border-bottom-left-radius:0;border-bottom-right-radius:0;border-top-width:1px;border-top-style:solid;box-shadow:0 -4px 8px rgba(0,0,0,0.25);margin-top:-4px}#bootstrap-theme .ui-select-container[theme="select2"].direction-up .ui-select-dropdown .select2-search, #bootstrap-theme .ui-select-container.select2.direction-up .ui-select-dropdown .select2-search{margin-top:4px}#bootstrap-theme .ui-select-container[theme="select2"].direction-up.select2-dropdown-open .ui-select-match, #bootstrap-theme .ui-select-container.select2.direction-up.select2-dropdown-open .ui-select-match{border-bottom-color:#5897fb}#bootstrap-theme .ui-select-container[theme="select2"] .ui-select-dropdown .ui-select-search-hidden, #bootstrap-theme .ui-select-container[theme="select2"] .ui-select-dropdown .ui-select-search-hidden input{opacity:0;height:0;min-height:0;padding:0;margin:0;border:0}#bootstrap-theme .selectize-input.selectize-focus{border-color:#007FBB !important}#bootstrap-theme .selectize-control.single>.selectize-input>input{width:100%}#bootstrap-theme .selectize-control.multi>.selectize-input>input{margin:0 !important}#bootstrap-theme .selectize-control>.selectize-dropdown{width:100%}#bootstrap-theme .ng-dirty.ng-invalid>div.selectize-input{border-color:#D44950}#bootstrap-theme .ui-select-container[theme="selectize"].direction-up .ui-select-dropdown{box-shadow:0 -4px 8px rgba(0,0,0,0.25);margin-top:-2px}#bootstrap-theme .ui-select-container[theme="selectize"] input.ui-select-search-hidden{opacity:0;height:0;min-height:0;padding:0;margin:0;border:0;width:0}#bootstrap-theme .btn-default-focus{color:#333;background-color:#EBEBEB;border-color:#ADADAD;text-decoration:none;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6)}#bootstrap-theme .ui-select-bootstrap .ui-select-toggle{position:relative}#bootstrap-theme .ui-select-bootstrap .ui-select-toggle>.caret{position:absolute;height:10px;top:50%;right:10px;margin-top:-2px}#bootstrap-theme .input-group>.ui-select-bootstrap.dropdown{position:static}#bootstrap-theme .input-group>.ui-select-bootstrap>input.ui-select-search.form-control{border-radius:4px;border-top-right-radius:0;border-bottom-right-radius:0}#bootstrap-theme .input-group>.ui-select-bootstrap>input.ui-select-search.form-control.direction-up{border-radius:4px !important;border-top-right-radius:0 !important;border-bottom-right-radius:0 !important}#bootstrap-theme .ui-select-bootstrap .ui-select-search-hidden{opacity:0;height:0;min-height:0;padding:0;margin:0;border:0}#bootstrap-theme .ui-select-bootstrap>.ui-select-match>.btn{text-align:left !important}#bootstrap-theme .ui-select-bootstrap>.ui-select-match>.caret{position:absolute;top:45%;right:15px}#bootstrap-theme .ui-select-bootstrap>.ui-select-choices, #bootstrap-theme .ui-select-bootstrap>.ui-select-no-choice{width:100%;height:auto;max-height:200px;overflow-x:hidden;margin-top:-1px}body#bootstrap-theme>.ui-select-bootstrap.open, #bootstrap-theme>.ui-select-bootstrap.open{z-index:1000}#bootstrap-theme .ui-select-multiple.ui-select-bootstrap{height:auto;padding:3px 3px 0 3px}#bootstrap-theme .ui-select-multiple.ui-select-bootstrap input.ui-select-search{background-color:transparent !important;border:none;outline:none;height:1.666666em;margin-bottom:3px}#bootstrap-theme .ui-select-multiple.ui-select-bootstrap .ui-select-match .close{font-size:1.6em;line-height:0.75}#bootstrap-theme .ui-select-multiple.ui-select-bootstrap .ui-select-match-item{outline:0;margin:0 3px 3px 0}#bootstrap-theme .ui-select-multiple .ui-select-match-item{position:relative}#bootstrap-theme .ui-select-multiple .ui-select-match-item.dropping .ui-select-match-close{pointer-events:none}#bootstrap-theme .ui-select-multiple:hover .ui-select-match-item.dropping-before:before{content:"";position:absolute;top:0;right:100%;height:100%;margin-right:2px;border-left:1px solid #428bca}#bootstrap-theme .ui-select-multiple:hover .ui-select-match-item.dropping-after:after{content:"";position:absolute;top:0;left:100%;height:100%;margin-left:2px;border-right:1px solid #428bca}#bootstrap-theme .ui-select-bootstrap .ui-select-choices-row>span{cursor:pointer;display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}#bootstrap-theme .ui-select-bootstrap .ui-select-choices-row>span:hover, #bootstrap-theme .ui-select-bootstrap .ui-select-choices-row>span:focus{text-decoration:none;color:#262626;background-color:#f5f5f5}#bootstrap-theme .ui-select-bootstrap .ui-select-choices-row.active>span{color:#fff;text-decoration:none;outline:0;background-color:#428bca}#bootstrap-theme .ui-select-bootstrap .ui-select-choices-row.disabled>span, #bootstrap-theme .ui-select-bootstrap .ui-select-choices-row.active.disabled>span{color:#777;cursor:not-allowed;background-color:#fff}#bootstrap-theme .ui-select-match.ng-hide-add, #bootstrap-theme .ui-select-search.ng-hide-add{display:none !important}#bootstrap-theme .ui-select-bootstrap.ng-dirty.ng-invalid>button.btn.ui-select-match{border-color:#D44950}#bootstrap-theme .ui-select-container[theme="bootstrap"].direction-up .ui-select-dropdown{box-shadow:0 -4px 8px rgba(0,0,0,0.25)}#bootstrap-theme .ui-select-bootstrap .ui-select-match-text{width:100%;padding-right:1em}#bootstrap-theme .ui-select-bootstrap .ui-select-match-text span{display:inline-block;width:100%;overflow:hidden}#bootstrap-theme .ui-select-bootstrap .ui-select-toggle>a.btn{position:absolute;height:10px;right:10px;margin-top:-2px}#bootstrap-theme .ui-select-refreshing{position:absolute;right:0;padding:8px 27px;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased}@-webkit-keyframes ui-select-spin{#bootstrap-theme 0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}#bootstrap-theme 100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes ui-select-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}#bootstrap-theme .ui-select-spin{-webkit-animation:ui-select-spin 2s infinite linear;animation:ui-select-spin 2s infinite linear}#bootstrap-theme .ui-select-refreshing.ng-animate{-webkit-animation:none 0s}@font-face{font-family:'FontAwesome';src:url("../fonts/font-awesome/fontawesome-webfont.eot?v=4.7.0");src:url("../fonts/font-awesome/fontawesome-webfont.eot?#iefix&v=4.7.0") format("embedded-opentype"),url("../fonts/font-awesome/fontawesome-webfont.woff2?v=4.7.0") format("woff2"),url("../fonts/font-awesome/fontawesome-webfont.woff?v=4.7.0") format("woff"),url("../fonts/font-awesome/fontawesome-webfont.ttf?v=4.7.0") format("truetype"),url("../fonts/font-awesome/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular") format("svg");font-weight:normal;font-style:normal}#bootstrap-theme .fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}#bootstrap-theme .fa-lg{font-size:1.3333333333em;line-height:.75em;vertical-align:-15%}#bootstrap-theme .fa-2x{font-size:2em}#bootstrap-theme .fa-3x{font-size:3em}#bootstrap-theme .fa-4x{font-size:4em}#bootstrap-theme .fa-5x{font-size:5em}#bootstrap-theme .fa-fw{width:1.2857142857em;text-align:center}#bootstrap-theme .fa-ul{padding-left:0;margin-left:2.1428571429em;list-style-type:none}#bootstrap-theme .fa-ul>li{position:relative}#bootstrap-theme .fa-li{position:absolute;left:-2.1428571429em;width:2.1428571429em;top:.1428571429em;text-align:center}#bootstrap-theme .fa-li.fa-lg{left:-1.8571428571em}#bootstrap-theme .fa-border{padding:.2em .25em .15em;border:solid 0.08em #eee;border-radius:.1em}#bootstrap-theme .fa-pull-left{float:left}#bootstrap-theme .fa-pull-right{float:right}#bootstrap-theme .fa.fa-pull-left{margin-right:.3em}#bootstrap-theme .fa.fa-pull-right{margin-left:.3em}#bootstrap-theme .pull-right{float:right}#bootstrap-theme .pull-left{float:left}#bootstrap-theme .fa.pull-left{margin-right:.3em}#bootstrap-theme .fa.pull-right{margin-left:.3em}#bootstrap-theme .fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}#bootstrap-theme .fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{#bootstrap-theme 0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}#bootstrap-theme 100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}#bootstrap-theme .fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}#bootstrap-theme .fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}#bootstrap-theme .fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}#bootstrap-theme .fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}#bootstrap-theme .fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}#bootstrap-theme :root .fa-rotate-90, #bootstrap-theme :root .fa-rotate-180, #bootstrap-theme :root .fa-rotate-270, #bootstrap-theme :root .fa-flip-horizontal, #bootstrap-theme :root .fa-flip-vertical{filter:none}#bootstrap-theme .fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}#bootstrap-theme .fa-stack-1x, #bootstrap-theme .fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}#bootstrap-theme .fa-stack-1x{line-height:inherit}#bootstrap-theme .fa-stack-2x{font-size:2em}#bootstrap-theme .fa-inverse{color:#fff}#bootstrap-theme .fa-glass:before{content:""}#bootstrap-theme .fa-music:before{content:""}#bootstrap-theme .fa-search:before{content:""}#bootstrap-theme .fa-envelope-o:before{content:""}#bootstrap-theme .fa-heart:before{content:""}#bootstrap-theme .fa-star:before{content:""}#bootstrap-theme .fa-star-o:before{content:""}#bootstrap-theme .fa-user:before{content:""}#bootstrap-theme .fa-film:before{content:""}#bootstrap-theme .fa-th-large:before{content:""}#bootstrap-theme .fa-th:before{content:""}#bootstrap-theme .fa-th-list:before{content:""}#bootstrap-theme .fa-check:before{content:""}#bootstrap-theme .fa-remove:before, #bootstrap-theme .fa-close:before, #bootstrap-theme .fa-times:before{content:""}#bootstrap-theme .fa-search-plus:before{content:""}#bootstrap-theme .fa-search-minus:before{content:""}#bootstrap-theme .fa-power-off:before{content:""}#bootstrap-theme .fa-signal:before{content:""}#bootstrap-theme .fa-gear:before, #bootstrap-theme .fa-cog:before{content:""}#bootstrap-theme .fa-trash-o:before{content:""}#bootstrap-theme .fa-home:before{content:""}#bootstrap-theme .fa-file-o:before{content:""}#bootstrap-theme .fa-clock-o:before{content:""}#bootstrap-theme .fa-road:before{content:""}#bootstrap-theme .fa-download:before{content:""}#bootstrap-theme .fa-arrow-circle-o-down:before{content:""}#bootstrap-theme .fa-arrow-circle-o-up:before{content:""}#bootstrap-theme .fa-inbox:before{content:""}#bootstrap-theme .fa-play-circle-o:before{content:""}#bootstrap-theme .fa-rotate-right:before, #bootstrap-theme .fa-repeat:before{content:""}#bootstrap-theme .fa-refresh:before{content:""}#bootstrap-theme .fa-list-alt:before{content:""}#bootstrap-theme .fa-lock:before{content:""}#bootstrap-theme .fa-flag:before{content:""}#bootstrap-theme .fa-headphones:before{content:""}#bootstrap-theme .fa-volume-off:before{content:""}#bootstrap-theme .fa-volume-down:before{content:""}#bootstrap-theme .fa-volume-up:before{content:""}#bootstrap-theme .fa-qrcode:before{content:""}#bootstrap-theme .fa-barcode:before{content:""}#bootstrap-theme .fa-tag:before{content:""}#bootstrap-theme .fa-tags:before{content:""}#bootstrap-theme .fa-book:before{content:""}#bootstrap-theme .fa-bookmark:before{content:""}#bootstrap-theme .fa-print:before{content:""}#bootstrap-theme .fa-camera:before{content:""}#bootstrap-theme .fa-font:before{content:""}#bootstrap-theme .fa-bold:before{content:""}#bootstrap-theme .fa-italic:before{content:""}#bootstrap-theme .fa-text-height:before{content:""}#bootstrap-theme .fa-text-width:before{content:""}#bootstrap-theme .fa-align-left:before{content:""}#bootstrap-theme .fa-align-center:before{content:""}#bootstrap-theme .fa-align-right:before{content:""}#bootstrap-theme .fa-align-justify:before{content:""}#bootstrap-theme .fa-list:before{content:""}#bootstrap-theme .fa-dedent:before, #bootstrap-theme .fa-outdent:before{content:""}#bootstrap-theme .fa-indent:before{content:""}#bootstrap-theme .fa-video-camera:before{content:""}#bootstrap-theme .fa-photo:before, #bootstrap-theme .fa-image:before, #bootstrap-theme .fa-picture-o:before{content:""}#bootstrap-theme .fa-pencil:before{content:""}#bootstrap-theme .fa-map-marker:before{content:""}#bootstrap-theme .fa-adjust:before{content:""}#bootstrap-theme .fa-tint:before{content:""}#bootstrap-theme .fa-edit:before, #bootstrap-theme .fa-pencil-square-o:before{content:""}#bootstrap-theme .fa-share-square-o:before{content:""}#bootstrap-theme .fa-check-square-o:before{content:""}#bootstrap-theme .fa-arrows:before{content:""}#bootstrap-theme .fa-step-backward:before{content:""}#bootstrap-theme .fa-fast-backward:before{content:""}#bootstrap-theme .fa-backward:before{content:""}#bootstrap-theme .fa-play:before{content:""}#bootstrap-theme .fa-pause:before{content:""}#bootstrap-theme .fa-stop:before{content:""}#bootstrap-theme .fa-forward:before{content:""}#bootstrap-theme .fa-fast-forward:before{content:""}#bootstrap-theme .fa-step-forward:before{content:""}#bootstrap-theme .fa-eject:before{content:""}#bootstrap-theme .fa-chevron-left:before{content:""}#bootstrap-theme .fa-chevron-right:before{content:""}#bootstrap-theme .fa-plus-circle:before{content:""}#bootstrap-theme .fa-minus-circle:before{content:""}#bootstrap-theme .fa-times-circle:before{content:""}#bootstrap-theme .fa-check-circle:before{content:""}#bootstrap-theme .fa-question-circle:before{content:""}#bootstrap-theme .fa-info-circle:before{content:""}#bootstrap-theme .fa-crosshairs:before{content:""}#bootstrap-theme .fa-times-circle-o:before{content:""}#bootstrap-theme .fa-check-circle-o:before{content:""}#bootstrap-theme .fa-ban:before{content:""}#bootstrap-theme .fa-arrow-left:before{content:""}#bootstrap-theme .fa-arrow-right:before{content:""}#bootstrap-theme .fa-arrow-up:before{content:""}#bootstrap-theme .fa-arrow-down:before{content:""}#bootstrap-theme .fa-mail-forward:before, #bootstrap-theme .fa-share:before{content:""}#bootstrap-theme .fa-expand:before{content:""}#bootstrap-theme .fa-compress:before{content:""}#bootstrap-theme .fa-plus:before{content:""}#bootstrap-theme .fa-minus:before{content:""}#bootstrap-theme .fa-asterisk:before{content:""}#bootstrap-theme .fa-exclamation-circle:before{content:""}#bootstrap-theme .fa-gift:before{content:""}#bootstrap-theme .fa-leaf:before{content:""}#bootstrap-theme .fa-fire:before{content:""}#bootstrap-theme .fa-eye:before{content:""}#bootstrap-theme .fa-eye-slash:before{content:""}#bootstrap-theme .fa-warning:before, #bootstrap-theme .fa-exclamation-triangle:before{content:""}#bootstrap-theme .fa-plane:before{content:""}#bootstrap-theme .fa-calendar:before{content:""}#bootstrap-theme .fa-random:before{content:""}#bootstrap-theme .fa-comment:before{content:""}#bootstrap-theme .fa-magnet:before{content:""}#bootstrap-theme .fa-chevron-up:before{content:""}#bootstrap-theme .fa-chevron-down:before{content:""}#bootstrap-theme .fa-retweet:before{content:""}#bootstrap-theme .fa-shopping-cart:before{content:""}#bootstrap-theme .fa-folder:before{content:""}#bootstrap-theme .fa-folder-open:before{content:""}#bootstrap-theme .fa-arrows-v:before{content:""}#bootstrap-theme .fa-arrows-h:before{content:""}#bootstrap-theme .fa-bar-chart-o:before, #bootstrap-theme .fa-bar-chart:before{content:""}#bootstrap-theme .fa-twitter-square:before{content:""}#bootstrap-theme .fa-facebook-square:before{content:""}#bootstrap-theme .fa-camera-retro:before{content:""}#bootstrap-theme .fa-key:before{content:""}#bootstrap-theme .fa-gears:before, #bootstrap-theme .fa-cogs:before{content:""}#bootstrap-theme .fa-comments:before{content:""}#bootstrap-theme .fa-thumbs-o-up:before{content:""}#bootstrap-theme .fa-thumbs-o-down:before{content:""}#bootstrap-theme .fa-star-half:before{content:""}#bootstrap-theme .fa-heart-o:before{content:""}#bootstrap-theme .fa-sign-out:before{content:""}#bootstrap-theme .fa-linkedin-square:before{content:""}#bootstrap-theme .fa-thumb-tack:before{content:""}#bootstrap-theme .fa-external-link:before{content:""}#bootstrap-theme .fa-sign-in:before{content:""}#bootstrap-theme .fa-trophy:before{content:""}#bootstrap-theme .fa-github-square:before{content:""}#bootstrap-theme .fa-upload:before{content:""}#bootstrap-theme .fa-lemon-o:before{content:""}#bootstrap-theme .fa-phone:before{content:""}#bootstrap-theme .fa-square-o:before{content:""}#bootstrap-theme .fa-bookmark-o:before{content:""}#bootstrap-theme .fa-phone-square:before{content:""}#bootstrap-theme .fa-twitter:before{content:""}#bootstrap-theme .fa-facebook-f:before, #bootstrap-theme .fa-facebook:before{content:""}#bootstrap-theme .fa-github:before{content:""}#bootstrap-theme .fa-unlock:before{content:""}#bootstrap-theme .fa-credit-card:before{content:""}#bootstrap-theme .fa-feed:before, #bootstrap-theme .fa-rss:before{content:""}#bootstrap-theme .fa-hdd-o:before{content:""}#bootstrap-theme .fa-bullhorn:before{content:""}#bootstrap-theme .fa-bell:before{content:""}#bootstrap-theme .fa-certificate:before{content:""}#bootstrap-theme .fa-hand-o-right:before{content:""}#bootstrap-theme .fa-hand-o-left:before{content:""}#bootstrap-theme .fa-hand-o-up:before{content:""}#bootstrap-theme .fa-hand-o-down:before{content:""}#bootstrap-theme .fa-arrow-circle-left:before{content:""}#bootstrap-theme .fa-arrow-circle-right:before{content:""}#bootstrap-theme .fa-arrow-circle-up:before{content:""}#bootstrap-theme .fa-arrow-circle-down:before{content:""}#bootstrap-theme .fa-globe:before{content:""}#bootstrap-theme .fa-wrench:before{content:""}#bootstrap-theme .fa-tasks:before{content:""}#bootstrap-theme .fa-filter:before{content:""}#bootstrap-theme .fa-briefcase:before{content:""}#bootstrap-theme .fa-arrows-alt:before{content:""}#bootstrap-theme .fa-group:before, #bootstrap-theme .fa-users:before{content:""}#bootstrap-theme .fa-chain:before, #bootstrap-theme .fa-link:before{content:""}#bootstrap-theme .fa-cloud:before{content:""}#bootstrap-theme .fa-flask:before{content:""}#bootstrap-theme .fa-cut:before, #bootstrap-theme .fa-scissors:before{content:""}#bootstrap-theme .fa-copy:before, #bootstrap-theme .fa-files-o:before{content:""}#bootstrap-theme .fa-paperclip:before{content:""}#bootstrap-theme .fa-save:before, #bootstrap-theme .fa-floppy-o:before{content:""}#bootstrap-theme .fa-square:before{content:""}#bootstrap-theme .fa-navicon:before, #bootstrap-theme .fa-reorder:before, #bootstrap-theme .fa-bars:before{content:""}#bootstrap-theme .fa-list-ul:before{content:""}#bootstrap-theme .fa-list-ol:before{content:""}#bootstrap-theme .fa-strikethrough:before{content:""}#bootstrap-theme .fa-underline:before{content:""}#bootstrap-theme .fa-table:before{content:""}#bootstrap-theme .fa-magic:before{content:""}#bootstrap-theme .fa-truck:before{content:""}#bootstrap-theme .fa-pinterest:before{content:""}#bootstrap-theme .fa-pinterest-square:before{content:""}#bootstrap-theme .fa-google-plus-square:before{content:""}#bootstrap-theme .fa-google-plus:before{content:""}#bootstrap-theme .fa-money:before{content:""}#bootstrap-theme .fa-caret-down:before{content:""}#bootstrap-theme .fa-caret-up:before{content:""}#bootstrap-theme .fa-caret-left:before{content:""}#bootstrap-theme .fa-caret-right:before{content:""}#bootstrap-theme .fa-columns:before{content:""}#bootstrap-theme .fa-unsorted:before, #bootstrap-theme .fa-sort:before{content:""}#bootstrap-theme .fa-sort-down:before, #bootstrap-theme .fa-sort-desc:before{content:""}#bootstrap-theme .fa-sort-up:before, #bootstrap-theme .fa-sort-asc:before{content:""}#bootstrap-theme .fa-envelope:before{content:""}#bootstrap-theme .fa-linkedin:before{content:""}#bootstrap-theme .fa-rotate-left:before, #bootstrap-theme .fa-undo:before{content:""}#bootstrap-theme .fa-legal:before, #bootstrap-theme .fa-gavel:before{content:""}#bootstrap-theme .fa-dashboard:before, #bootstrap-theme .fa-tachometer:before{content:""}#bootstrap-theme .fa-comment-o:before{content:""}#bootstrap-theme .fa-comments-o:before{content:""}#bootstrap-theme .fa-flash:before, #bootstrap-theme .fa-bolt:before{content:""}#bootstrap-theme .fa-sitemap:before{content:""}#bootstrap-theme .fa-umbrella:before{content:""}#bootstrap-theme .fa-paste:before, #bootstrap-theme .fa-clipboard:before{content:""}#bootstrap-theme .fa-lightbulb-o:before{content:""}#bootstrap-theme .fa-exchange:before{content:""}#bootstrap-theme .fa-cloud-download:before{content:""}#bootstrap-theme .fa-cloud-upload:before{content:""}#bootstrap-theme .fa-user-md:before{content:""}#bootstrap-theme .fa-stethoscope:before{content:""}#bootstrap-theme .fa-suitcase:before{content:""}#bootstrap-theme .fa-bell-o:before{content:""}#bootstrap-theme .fa-coffee:before{content:""}#bootstrap-theme .fa-cutlery:before{content:""}#bootstrap-theme .fa-file-text-o:before{content:""}#bootstrap-theme .fa-building-o:before{content:""}#bootstrap-theme .fa-hospital-o:before{content:""}#bootstrap-theme .fa-ambulance:before{content:""}#bootstrap-theme .fa-medkit:before{content:""}#bootstrap-theme .fa-fighter-jet:before{content:""}#bootstrap-theme .fa-beer:before{content:""}#bootstrap-theme .fa-h-square:before{content:""}#bootstrap-theme .fa-plus-square:before{content:""}#bootstrap-theme .fa-angle-double-left:before{content:""}#bootstrap-theme .fa-angle-double-right:before{content:""}#bootstrap-theme .fa-angle-double-up:before{content:""}#bootstrap-theme .fa-angle-double-down:before{content:""}#bootstrap-theme .fa-angle-left:before{content:""}#bootstrap-theme .fa-angle-right:before{content:""}#bootstrap-theme .fa-angle-up:before{content:""}#bootstrap-theme .fa-angle-down:before{content:""}#bootstrap-theme .fa-desktop:before{content:""}#bootstrap-theme .fa-laptop:before{content:""}#bootstrap-theme .fa-tablet:before{content:""}#bootstrap-theme .fa-mobile-phone:before, #bootstrap-theme .fa-mobile:before{content:""}#bootstrap-theme .fa-circle-o:before{content:""}#bootstrap-theme .fa-quote-left:before{content:""}#bootstrap-theme .fa-quote-right:before{content:""}#bootstrap-theme .fa-spinner:before{content:""}#bootstrap-theme .fa-circle:before{content:""}#bootstrap-theme .fa-mail-reply:before, #bootstrap-theme .fa-reply:before{content:""}#bootstrap-theme .fa-github-alt:before{content:""}#bootstrap-theme .fa-folder-o:before{content:""}#bootstrap-theme .fa-folder-open-o:before{content:""}#bootstrap-theme .fa-smile-o:before{content:""}#bootstrap-theme .fa-frown-o:before{content:""}#bootstrap-theme .fa-meh-o:before{content:""}#bootstrap-theme .fa-gamepad:before{content:""}#bootstrap-theme .fa-keyboard-o:before{content:""}#bootstrap-theme .fa-flag-o:before{content:""}#bootstrap-theme .fa-flag-checkered:before{content:""}#bootstrap-theme .fa-terminal:before{content:""}#bootstrap-theme .fa-code:before{content:""}#bootstrap-theme .fa-mail-reply-all:before, #bootstrap-theme .fa-reply-all:before{content:""}#bootstrap-theme .fa-star-half-empty:before, #bootstrap-theme .fa-star-half-full:before, #bootstrap-theme .fa-star-half-o:before{content:""}#bootstrap-theme .fa-location-arrow:before{content:""}#bootstrap-theme .fa-crop:before{content:""}#bootstrap-theme .fa-code-fork:before{content:""}#bootstrap-theme .fa-unlink:before, #bootstrap-theme .fa-chain-broken:before{content:""}#bootstrap-theme .fa-question:before{content:""}#bootstrap-theme .fa-info:before{content:""}#bootstrap-theme .fa-exclamation:before{content:""}#bootstrap-theme .fa-superscript:before{content:""}#bootstrap-theme .fa-subscript:before{content:""}#bootstrap-theme .fa-eraser:before{content:""}#bootstrap-theme .fa-puzzle-piece:before{content:""}#bootstrap-theme .fa-microphone:before{content:""}#bootstrap-theme .fa-microphone-slash:before{content:""}#bootstrap-theme .fa-shield:before{content:""}#bootstrap-theme .fa-calendar-o:before{content:""}#bootstrap-theme .fa-fire-extinguisher:before{content:""}#bootstrap-theme .fa-rocket:before{content:""}#bootstrap-theme .fa-maxcdn:before{content:""}#bootstrap-theme .fa-chevron-circle-left:before{content:""}#bootstrap-theme .fa-chevron-circle-right:before{content:""}#bootstrap-theme .fa-chevron-circle-up:before{content:""}#bootstrap-theme .fa-chevron-circle-down:before{content:""}#bootstrap-theme .fa-html5:before{content:""}#bootstrap-theme .fa-css3:before{content:""}#bootstrap-theme .fa-anchor:before{content:""}#bootstrap-theme .fa-unlock-alt:before{content:""}#bootstrap-theme .fa-bullseye:before{content:""}#bootstrap-theme .fa-ellipsis-h:before{content:""}#bootstrap-theme .fa-ellipsis-v:before{content:""}#bootstrap-theme .fa-rss-square:before{content:""}#bootstrap-theme .fa-play-circle:before{content:""}#bootstrap-theme .fa-ticket:before{content:""}#bootstrap-theme .fa-minus-square:before{content:""}#bootstrap-theme .fa-minus-square-o:before{content:""}#bootstrap-theme .fa-level-up:before{content:""}#bootstrap-theme .fa-level-down:before{content:""}#bootstrap-theme .fa-check-square:before{content:""}#bootstrap-theme .fa-pencil-square:before{content:""}#bootstrap-theme .fa-external-link-square:before{content:""}#bootstrap-theme .fa-share-square:before{content:""}#bootstrap-theme .fa-compass:before{content:""}#bootstrap-theme .fa-toggle-down:before, #bootstrap-theme .fa-caret-square-o-down:before{content:""}#bootstrap-theme .fa-toggle-up:before, #bootstrap-theme .fa-caret-square-o-up:before{content:""}#bootstrap-theme .fa-toggle-right:before, #bootstrap-theme .fa-caret-square-o-right:before{content:""}#bootstrap-theme .fa-euro:before, #bootstrap-theme .fa-eur:before{content:""}#bootstrap-theme .fa-gbp:before{content:""}#bootstrap-theme .fa-dollar:before, #bootstrap-theme .fa-usd:before{content:""}#bootstrap-theme .fa-rupee:before, #bootstrap-theme .fa-inr:before{content:""}#bootstrap-theme .fa-cny:before, #bootstrap-theme .fa-rmb:before, #bootstrap-theme .fa-yen:before, #bootstrap-theme .fa-jpy:before{content:""}#bootstrap-theme .fa-ruble:before, #bootstrap-theme .fa-rouble:before, #bootstrap-theme .fa-rub:before{content:""}#bootstrap-theme .fa-won:before, #bootstrap-theme .fa-krw:before{content:""}#bootstrap-theme .fa-bitcoin:before, #bootstrap-theme .fa-btc:before{content:""}#bootstrap-theme .fa-file:before{content:""}#bootstrap-theme .fa-file-text:before{content:""}#bootstrap-theme .fa-sort-alpha-asc:before{content:""}#bootstrap-theme .fa-sort-alpha-desc:before{content:""}#bootstrap-theme .fa-sort-amount-asc:before{content:""}#bootstrap-theme .fa-sort-amount-desc:before{content:""}#bootstrap-theme .fa-sort-numeric-asc:before{content:""}#bootstrap-theme .fa-sort-numeric-desc:before{content:""}#bootstrap-theme .fa-thumbs-up:before{content:""}#bootstrap-theme .fa-thumbs-down:before{content:""}#bootstrap-theme .fa-youtube-square:before{content:""}#bootstrap-theme .fa-youtube:before{content:""}#bootstrap-theme .fa-xing:before{content:""}#bootstrap-theme .fa-xing-square:before{content:""}#bootstrap-theme .fa-youtube-play:before{content:""}#bootstrap-theme .fa-dropbox:before{content:""}#bootstrap-theme .fa-stack-overflow:before{content:""}#bootstrap-theme .fa-instagram:before{content:""}#bootstrap-theme .fa-flickr:before{content:""}#bootstrap-theme .fa-adn:before{content:""}#bootstrap-theme .fa-bitbucket:before{content:""}#bootstrap-theme .fa-bitbucket-square:before{content:""}#bootstrap-theme .fa-tumblr:before{content:""}#bootstrap-theme .fa-tumblr-square:before{content:""}#bootstrap-theme .fa-long-arrow-down:before{content:""}#bootstrap-theme .fa-long-arrow-up:before{content:""}#bootstrap-theme .fa-long-arrow-left:before{content:""}#bootstrap-theme .fa-long-arrow-right:before{content:""}#bootstrap-theme .fa-apple:before{content:""}#bootstrap-theme .fa-windows:before{content:""}#bootstrap-theme .fa-android:before{content:""}#bootstrap-theme .fa-linux:before{content:""}#bootstrap-theme .fa-dribbble:before{content:""}#bootstrap-theme .fa-skype:before{content:""}#bootstrap-theme .fa-foursquare:before{content:""}#bootstrap-theme .fa-trello:before{content:""}#bootstrap-theme .fa-female:before{content:""}#bootstrap-theme .fa-male:before{content:""}#bootstrap-theme .fa-gittip:before, #bootstrap-theme .fa-gratipay:before{content:""}#bootstrap-theme .fa-sun-o:before{content:""}#bootstrap-theme .fa-moon-o:before{content:""}#bootstrap-theme .fa-archive:before{content:""}#bootstrap-theme .fa-bug:before{content:""}#bootstrap-theme .fa-vk:before{content:""}#bootstrap-theme .fa-weibo:before{content:""}#bootstrap-theme .fa-renren:before{content:""}#bootstrap-theme .fa-pagelines:before{content:""}#bootstrap-theme .fa-stack-exchange:before{content:""}#bootstrap-theme .fa-arrow-circle-o-right:before{content:""}#bootstrap-theme .fa-arrow-circle-o-left:before{content:""}#bootstrap-theme .fa-toggle-left:before, #bootstrap-theme .fa-caret-square-o-left:before{content:""}#bootstrap-theme .fa-dot-circle-o:before{content:""}#bootstrap-theme .fa-wheelchair:before{content:""}#bootstrap-theme .fa-vimeo-square:before{content:""}#bootstrap-theme .fa-turkish-lira:before, #bootstrap-theme .fa-try:before{content:""}#bootstrap-theme .fa-plus-square-o:before{content:""}#bootstrap-theme .fa-space-shuttle:before{content:""}#bootstrap-theme .fa-slack:before{content:""}#bootstrap-theme .fa-envelope-square:before{content:""}#bootstrap-theme .fa-wordpress:before{content:""}#bootstrap-theme .fa-openid:before{content:""}#bootstrap-theme .fa-institution:before, #bootstrap-theme .fa-bank:before, #bootstrap-theme .fa-university:before{content:""}#bootstrap-theme .fa-mortar-board:before, #bootstrap-theme .fa-graduation-cap:before{content:""}#bootstrap-theme .fa-yahoo:before{content:""}#bootstrap-theme .fa-google:before{content:""}#bootstrap-theme .fa-reddit:before{content:""}#bootstrap-theme .fa-reddit-square:before{content:""}#bootstrap-theme .fa-stumbleupon-circle:before{content:""}#bootstrap-theme .fa-stumbleupon:before{content:""}#bootstrap-theme .fa-delicious:before{content:""}#bootstrap-theme .fa-digg:before{content:""}#bootstrap-theme .fa-pied-piper-pp:before{content:""}#bootstrap-theme .fa-pied-piper-alt:before{content:""}#bootstrap-theme .fa-drupal:before{content:""}#bootstrap-theme .fa-joomla:before{content:""}#bootstrap-theme .fa-language:before{content:""}#bootstrap-theme .fa-fax:before{content:""}#bootstrap-theme .fa-building:before{content:""}#bootstrap-theme .fa-child:before{content:""}#bootstrap-theme .fa-paw:before{content:""}#bootstrap-theme .fa-spoon:before{content:""}#bootstrap-theme .fa-cube:before{content:""}#bootstrap-theme .fa-cubes:before{content:""}#bootstrap-theme .fa-behance:before{content:""}#bootstrap-theme .fa-behance-square:before{content:""}#bootstrap-theme .fa-steam:before{content:""}#bootstrap-theme .fa-steam-square:before{content:""}#bootstrap-theme .fa-recycle:before{content:""}#bootstrap-theme .fa-automobile:before, #bootstrap-theme .fa-car:before{content:""}#bootstrap-theme .fa-cab:before, #bootstrap-theme .fa-taxi:before{content:""}#bootstrap-theme .fa-tree:before{content:""}#bootstrap-theme .fa-spotify:before{content:""}#bootstrap-theme .fa-deviantart:before{content:""}#bootstrap-theme .fa-soundcloud:before{content:""}#bootstrap-theme .fa-database:before{content:""}#bootstrap-theme .fa-file-pdf-o:before{content:""}#bootstrap-theme .fa-file-word-o:before{content:""}#bootstrap-theme .fa-file-excel-o:before{content:""}#bootstrap-theme .fa-file-powerpoint-o:before{content:""}#bootstrap-theme .fa-file-photo-o:before, #bootstrap-theme .fa-file-picture-o:before, #bootstrap-theme .fa-file-image-o:before{content:""}#bootstrap-theme .fa-file-zip-o:before, #bootstrap-theme .fa-file-archive-o:before{content:""}#bootstrap-theme .fa-file-sound-o:before, #bootstrap-theme .fa-file-audio-o:before{content:""}#bootstrap-theme .fa-file-movie-o:before, #bootstrap-theme .fa-file-video-o:before{content:""}#bootstrap-theme .fa-file-code-o:before{content:""}#bootstrap-theme .fa-vine:before{content:""}#bootstrap-theme .fa-codepen:before{content:""}#bootstrap-theme .fa-jsfiddle:before{content:""}#bootstrap-theme .fa-life-bouy:before, #bootstrap-theme .fa-life-buoy:before, #bootstrap-theme .fa-life-saver:before, #bootstrap-theme .fa-support:before, #bootstrap-theme .fa-life-ring:before{content:""}#bootstrap-theme .fa-circle-o-notch:before{content:""}#bootstrap-theme .fa-ra:before, #bootstrap-theme .fa-resistance:before, #bootstrap-theme .fa-rebel:before{content:""}#bootstrap-theme .fa-ge:before, #bootstrap-theme .fa-empire:before{content:""}#bootstrap-theme .fa-git-square:before{content:""}#bootstrap-theme .fa-git:before{content:""}#bootstrap-theme .fa-y-combinator-square:before, #bootstrap-theme .fa-yc-square:before, #bootstrap-theme .fa-hacker-news:before{content:""}#bootstrap-theme .fa-tencent-weibo:before{content:""}#bootstrap-theme .fa-qq:before{content:""}#bootstrap-theme .fa-wechat:before, #bootstrap-theme .fa-weixin:before{content:""}#bootstrap-theme .fa-send:before, #bootstrap-theme .fa-paper-plane:before{content:""}#bootstrap-theme .fa-send-o:before, #bootstrap-theme .fa-paper-plane-o:before{content:""}#bootstrap-theme .fa-history:before{content:""}#bootstrap-theme .fa-circle-thin:before{content:""}#bootstrap-theme .fa-header:before{content:""}#bootstrap-theme .fa-paragraph:before{content:""}#bootstrap-theme .fa-sliders:before{content:""}#bootstrap-theme .fa-share-alt:before{content:""}#bootstrap-theme .fa-share-alt-square:before{content:""}#bootstrap-theme .fa-bomb:before{content:""}#bootstrap-theme .fa-soccer-ball-o:before, #bootstrap-theme .fa-futbol-o:before{content:""}#bootstrap-theme .fa-tty:before{content:""}#bootstrap-theme .fa-binoculars:before{content:""}#bootstrap-theme .fa-plug:before{content:""}#bootstrap-theme .fa-slideshare:before{content:""}#bootstrap-theme .fa-twitch:before{content:""}#bootstrap-theme .fa-yelp:before{content:""}#bootstrap-theme .fa-newspaper-o:before{content:""}#bootstrap-theme .fa-wifi:before{content:""}#bootstrap-theme .fa-calculator:before{content:""}#bootstrap-theme .fa-paypal:before{content:""}#bootstrap-theme .fa-google-wallet:before{content:""}#bootstrap-theme .fa-cc-visa:before{content:""}#bootstrap-theme .fa-cc-mastercard:before{content:""}#bootstrap-theme .fa-cc-discover:before{content:""}#bootstrap-theme .fa-cc-amex:before{content:""}#bootstrap-theme .fa-cc-paypal:before{content:""}#bootstrap-theme .fa-cc-stripe:before{content:""}#bootstrap-theme .fa-bell-slash:before{content:""}#bootstrap-theme .fa-bell-slash-o:before{content:""}#bootstrap-theme .fa-trash:before{content:""}#bootstrap-theme .fa-copyright:before{content:""}#bootstrap-theme .fa-at:before{content:""}#bootstrap-theme .fa-eyedropper:before{content:""}#bootstrap-theme .fa-paint-brush:before{content:""}#bootstrap-theme .fa-birthday-cake:before{content:""}#bootstrap-theme .fa-area-chart:before{content:""}#bootstrap-theme .fa-pie-chart:before{content:""}#bootstrap-theme .fa-line-chart:before{content:""}#bootstrap-theme .fa-lastfm:before{content:""}#bootstrap-theme .fa-lastfm-square:before{content:""}#bootstrap-theme .fa-toggle-off:before{content:""}#bootstrap-theme .fa-toggle-on:before{content:""}#bootstrap-theme .fa-bicycle:before{content:""}#bootstrap-theme .fa-bus:before{content:""}#bootstrap-theme .fa-ioxhost:before{content:""}#bootstrap-theme .fa-angellist:before{content:""}#bootstrap-theme .fa-cc:before{content:""}#bootstrap-theme .fa-shekel:before, #bootstrap-theme .fa-sheqel:before, #bootstrap-theme .fa-ils:before{content:""}#bootstrap-theme .fa-meanpath:before{content:""}#bootstrap-theme .fa-buysellads:before{content:""}#bootstrap-theme .fa-connectdevelop:before{content:""}#bootstrap-theme .fa-dashcube:before{content:""}#bootstrap-theme .fa-forumbee:before{content:""}#bootstrap-theme .fa-leanpub:before{content:""}#bootstrap-theme .fa-sellsy:before{content:""}#bootstrap-theme .fa-shirtsinbulk:before{content:""}#bootstrap-theme .fa-simplybuilt:before{content:""}#bootstrap-theme .fa-skyatlas:before{content:""}#bootstrap-theme .fa-cart-plus:before{content:""}#bootstrap-theme .fa-cart-arrow-down:before{content:""}#bootstrap-theme .fa-diamond:before{content:""}#bootstrap-theme .fa-ship:before{content:""}#bootstrap-theme .fa-user-secret:before{content:""}#bootstrap-theme .fa-motorcycle:before{content:""}#bootstrap-theme .fa-street-view:before{content:""}#bootstrap-theme .fa-heartbeat:before{content:""}#bootstrap-theme .fa-venus:before{content:""}#bootstrap-theme .fa-mars:before{content:""}#bootstrap-theme .fa-mercury:before{content:""}#bootstrap-theme .fa-intersex:before, #bootstrap-theme .fa-transgender:before{content:""}#bootstrap-theme .fa-transgender-alt:before{content:""}#bootstrap-theme .fa-venus-double:before{content:""}#bootstrap-theme .fa-mars-double:before{content:""}#bootstrap-theme .fa-venus-mars:before{content:""}#bootstrap-theme .fa-mars-stroke:before{content:""}#bootstrap-theme .fa-mars-stroke-v:before{content:""}#bootstrap-theme .fa-mars-stroke-h:before{content:""}#bootstrap-theme .fa-neuter:before{content:""}#bootstrap-theme .fa-genderless:before{content:""}#bootstrap-theme .fa-facebook-official:before{content:""}#bootstrap-theme .fa-pinterest-p:before{content:""}#bootstrap-theme .fa-whatsapp:before{content:""}#bootstrap-theme .fa-server:before{content:""}#bootstrap-theme .fa-user-plus:before{content:""}#bootstrap-theme .fa-user-times:before{content:""}#bootstrap-theme .fa-hotel:before, #bootstrap-theme .fa-bed:before{content:""}#bootstrap-theme .fa-viacoin:before{content:""}#bootstrap-theme .fa-train:before{content:""}#bootstrap-theme .fa-subway:before{content:""}#bootstrap-theme .fa-medium:before{content:""}#bootstrap-theme .fa-yc:before, #bootstrap-theme .fa-y-combinator:before{content:""}#bootstrap-theme .fa-optin-monster:before{content:""}#bootstrap-theme .fa-opencart:before{content:""}#bootstrap-theme .fa-expeditedssl:before{content:""}#bootstrap-theme .fa-battery-4:before, #bootstrap-theme .fa-battery:before, #bootstrap-theme .fa-battery-full:before{content:""}#bootstrap-theme .fa-battery-3:before, #bootstrap-theme .fa-battery-three-quarters:before{content:""}#bootstrap-theme .fa-battery-2:before, #bootstrap-theme .fa-battery-half:before{content:""}#bootstrap-theme .fa-battery-1:before, #bootstrap-theme .fa-battery-quarter:before{content:""}#bootstrap-theme .fa-battery-0:before, #bootstrap-theme .fa-battery-empty:before{content:""}#bootstrap-theme .fa-mouse-pointer:before{content:""}#bootstrap-theme .fa-i-cursor:before{content:""}#bootstrap-theme .fa-object-group:before{content:""}#bootstrap-theme .fa-object-ungroup:before{content:""}#bootstrap-theme .fa-sticky-note:before{content:""}#bootstrap-theme .fa-sticky-note-o:before{content:""}#bootstrap-theme .fa-cc-jcb:before{content:""}#bootstrap-theme .fa-cc-diners-club:before{content:""}#bootstrap-theme .fa-clone:before{content:""}#bootstrap-theme .fa-balance-scale:before{content:""}#bootstrap-theme .fa-hourglass-o:before{content:""}#bootstrap-theme .fa-hourglass-1:before, #bootstrap-theme .fa-hourglass-start:before{content:""}#bootstrap-theme .fa-hourglass-2:before, #bootstrap-theme .fa-hourglass-half:before{content:""}#bootstrap-theme .fa-hourglass-3:before, #bootstrap-theme .fa-hourglass-end:before{content:""}#bootstrap-theme .fa-hourglass:before{content:""}#bootstrap-theme .fa-hand-grab-o:before, #bootstrap-theme .fa-hand-rock-o:before{content:""}#bootstrap-theme .fa-hand-stop-o:before, #bootstrap-theme .fa-hand-paper-o:before{content:""}#bootstrap-theme .fa-hand-scissors-o:before{content:""}#bootstrap-theme .fa-hand-lizard-o:before{content:""}#bootstrap-theme .fa-hand-spock-o:before{content:""}#bootstrap-theme .fa-hand-pointer-o:before{content:""}#bootstrap-theme .fa-hand-peace-o:before{content:""}#bootstrap-theme .fa-trademark:before{content:""}#bootstrap-theme .fa-registered:before{content:""}#bootstrap-theme .fa-creative-commons:before{content:""}#bootstrap-theme .fa-gg:before{content:""}#bootstrap-theme .fa-gg-circle:before{content:""}#bootstrap-theme .fa-tripadvisor:before{content:""}#bootstrap-theme .fa-odnoklassniki:before{content:""}#bootstrap-theme .fa-odnoklassniki-square:before{content:""}#bootstrap-theme .fa-get-pocket:before{content:""}#bootstrap-theme .fa-wikipedia-w:before{content:""}#bootstrap-theme .fa-safari:before{content:""}#bootstrap-theme .fa-chrome:before{content:""}#bootstrap-theme .fa-firefox:before{content:""}#bootstrap-theme .fa-opera:before{content:""}#bootstrap-theme .fa-internet-explorer:before{content:""}#bootstrap-theme .fa-tv:before, #bootstrap-theme .fa-television:before{content:""}#bootstrap-theme .fa-contao:before{content:""}#bootstrap-theme .fa-500px:before{content:""}#bootstrap-theme .fa-amazon:before{content:""}#bootstrap-theme .fa-calendar-plus-o:before{content:""}#bootstrap-theme .fa-calendar-minus-o:before{content:""}#bootstrap-theme .fa-calendar-times-o:before{content:""}#bootstrap-theme .fa-calendar-check-o:before{content:""}#bootstrap-theme .fa-industry:before{content:""}#bootstrap-theme .fa-map-pin:before{content:""}#bootstrap-theme .fa-map-signs:before{content:""}#bootstrap-theme .fa-map-o:before{content:""}#bootstrap-theme .fa-map:before{content:""}#bootstrap-theme .fa-commenting:before{content:""}#bootstrap-theme .fa-commenting-o:before{content:""}#bootstrap-theme .fa-houzz:before{content:""}#bootstrap-theme .fa-vimeo:before{content:""}#bootstrap-theme .fa-black-tie:before{content:""}#bootstrap-theme .fa-fonticons:before{content:""}#bootstrap-theme .fa-reddit-alien:before{content:""}#bootstrap-theme .fa-edge:before{content:""}#bootstrap-theme .fa-credit-card-alt:before{content:""}#bootstrap-theme .fa-codiepie:before{content:""}#bootstrap-theme .fa-modx:before{content:""}#bootstrap-theme .fa-fort-awesome:before{content:""}#bootstrap-theme .fa-usb:before{content:""}#bootstrap-theme .fa-product-hunt:before{content:""}#bootstrap-theme .fa-mixcloud:before{content:""}#bootstrap-theme .fa-scribd:before{content:""}#bootstrap-theme .fa-pause-circle:before{content:""}#bootstrap-theme .fa-pause-circle-o:before{content:""}#bootstrap-theme .fa-stop-circle:before{content:""}#bootstrap-theme .fa-stop-circle-o:before{content:""}#bootstrap-theme .fa-shopping-bag:before{content:""}#bootstrap-theme .fa-shopping-basket:before{content:""}#bootstrap-theme .fa-hashtag:before{content:""}#bootstrap-theme .fa-bluetooth:before{content:""}#bootstrap-theme .fa-bluetooth-b:before{content:""}#bootstrap-theme .fa-percent:before{content:""}#bootstrap-theme .fa-gitlab:before{content:""}#bootstrap-theme .fa-wpbeginner:before{content:""}#bootstrap-theme .fa-wpforms:before{content:""}#bootstrap-theme .fa-envira:before{content:""}#bootstrap-theme .fa-universal-access:before{content:""}#bootstrap-theme .fa-wheelchair-alt:before{content:""}#bootstrap-theme .fa-question-circle-o:before{content:""}#bootstrap-theme .fa-blind:before{content:""}#bootstrap-theme .fa-audio-description:before{content:""}#bootstrap-theme .fa-volume-control-phone:before{content:""}#bootstrap-theme .fa-braille:before{content:""}#bootstrap-theme .fa-assistive-listening-systems:before{content:""}#bootstrap-theme .fa-asl-interpreting:before, #bootstrap-theme .fa-american-sign-language-interpreting:before{content:""}#bootstrap-theme .fa-deafness:before, #bootstrap-theme .fa-hard-of-hearing:before, #bootstrap-theme .fa-deaf:before{content:""}#bootstrap-theme .fa-glide:before{content:""}#bootstrap-theme .fa-glide-g:before{content:""}#bootstrap-theme .fa-signing:before, #bootstrap-theme .fa-sign-language:before{content:""}#bootstrap-theme .fa-low-vision:before{content:""}#bootstrap-theme .fa-viadeo:before{content:""}#bootstrap-theme .fa-viadeo-square:before{content:""}#bootstrap-theme .fa-snapchat:before{content:""}#bootstrap-theme .fa-snapchat-ghost:before{content:""}#bootstrap-theme .fa-snapchat-square:before{content:""}#bootstrap-theme .fa-pied-piper:before{content:""}#bootstrap-theme .fa-first-order:before{content:""}#bootstrap-theme .fa-yoast:before{content:""}#bootstrap-theme .fa-themeisle:before{content:""}#bootstrap-theme .fa-google-plus-circle:before, #bootstrap-theme .fa-google-plus-official:before{content:""}#bootstrap-theme .fa-fa:before, #bootstrap-theme .fa-font-awesome:before{content:""}#bootstrap-theme .fa-handshake-o:before{content:""}#bootstrap-theme .fa-envelope-open:before{content:""}#bootstrap-theme .fa-envelope-open-o:before{content:""}#bootstrap-theme .fa-linode:before{content:""}#bootstrap-theme .fa-address-book:before{content:""}#bootstrap-theme .fa-address-book-o:before{content:""}#bootstrap-theme .fa-vcard:before, #bootstrap-theme .fa-address-card:before{content:""}#bootstrap-theme .fa-vcard-o:before, #bootstrap-theme .fa-address-card-o:before{content:""}#bootstrap-theme .fa-user-circle:before{content:""}#bootstrap-theme .fa-user-circle-o:before{content:""}#bootstrap-theme .fa-user-o:before{content:""}#bootstrap-theme .fa-id-badge:before{content:""}#bootstrap-theme .fa-drivers-license:before, #bootstrap-theme .fa-id-card:before{content:""}#bootstrap-theme .fa-drivers-license-o:before, #bootstrap-theme .fa-id-card-o:before{content:""}#bootstrap-theme .fa-quora:before{content:""}#bootstrap-theme .fa-free-code-camp:before{content:""}#bootstrap-theme .fa-telegram:before{content:""}#bootstrap-theme .fa-thermometer-4:before, #bootstrap-theme .fa-thermometer:before, #bootstrap-theme .fa-thermometer-full:before{content:""}#bootstrap-theme .fa-thermometer-3:before, #bootstrap-theme .fa-thermometer-three-quarters:before{content:""}#bootstrap-theme .fa-thermometer-2:before, #bootstrap-theme .fa-thermometer-half:before{content:""}#bootstrap-theme .fa-thermometer-1:before, #bootstrap-theme .fa-thermometer-quarter:before{content:""}#bootstrap-theme .fa-thermometer-0:before, #bootstrap-theme .fa-thermometer-empty:before{content:""}#bootstrap-theme .fa-shower:before{content:""}#bootstrap-theme .fa-bathtub:before, #bootstrap-theme .fa-s15:before, #bootstrap-theme .fa-bath:before{content:""}#bootstrap-theme .fa-podcast:before{content:""}#bootstrap-theme .fa-window-maximize:before{content:""}#bootstrap-theme .fa-window-minimize:before{content:""}#bootstrap-theme .fa-window-restore:before{content:""}#bootstrap-theme .fa-times-rectangle:before, #bootstrap-theme .fa-window-close:before{content:""}#bootstrap-theme .fa-times-rectangle-o:before, #bootstrap-theme .fa-window-close-o:before{content:""}#bootstrap-theme .fa-bandcamp:before{content:""}#bootstrap-theme .fa-grav:before{content:""}#bootstrap-theme .fa-etsy:before{content:""}#bootstrap-theme .fa-imdb:before{content:""}#bootstrap-theme .fa-ravelry:before{content:""}#bootstrap-theme .fa-eercast:before{content:""}#bootstrap-theme .fa-microchip:before{content:""}#bootstrap-theme .fa-snowflake-o:before{content:""}#bootstrap-theme .fa-superpowers:before{content:""}#bootstrap-theme .fa-wpexplorer:before{content:""}#bootstrap-theme .fa-meetup:before{content:""}#bootstrap-theme .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}#bootstrap-theme .sr-only-focusable:active, #bootstrap-theme .sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}#bootstrap-theme .ps-container{-ms-touch-action:none;touch-action:none;overflow:hidden !important;-ms-overflow-style:none}@supports (-ms-overflow-style: none){#bootstrap-theme .ps-container{overflow:auto !important}}@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none){#bootstrap-theme .ps-container{overflow:auto !important}}#bootstrap-theme .ps-container.ps-active-x>.ps-scrollbar-x-rail, #bootstrap-theme .ps-container.ps-active-y>.ps-scrollbar-y-rail{display:block;background-color:rgba(0,0,0,0)}#bootstrap-theme .ps-container.ps-in-scrolling{pointer-events:none}#bootstrap-theme .ps-container.ps-in-scrolling.ps-x>.ps-scrollbar-x-rail{background-color:#eee;opacity:.9}#bootstrap-theme .ps-container.ps-in-scrolling.ps-x>.ps-scrollbar-x-rail>.ps-scrollbar-x{background-color:#999}#bootstrap-theme .ps-container.ps-in-scrolling.ps-y>.ps-scrollbar-y-rail{background-color:#eee;opacity:.9}#bootstrap-theme .ps-container.ps-in-scrolling.ps-y>.ps-scrollbar-y-rail>.ps-scrollbar-y{background-color:#999}#bootstrap-theme .ps-container>.ps-scrollbar-x-rail{display:none;position:absolute;border-radius:4px;opacity:0;transition:background-color .2s linear, opacity .2s linear;bottom:3px;height:8px}#bootstrap-theme .ps-container>.ps-scrollbar-x-rail>.ps-scrollbar-x{position:absolute;background-color:#aaa;border-radius:4px;transition:background-color .2s linear;bottom:0;height:8px}#bootstrap-theme .ps-container>.ps-scrollbar-y-rail{display:none;position:absolute;border-radius:4px;opacity:0;transition:background-color .2s linear, opacity .2s linear;right:3px;width:8px}#bootstrap-theme .ps-container>.ps-scrollbar-y-rail>.ps-scrollbar-y{position:absolute;background-color:#aaa;border-radius:4px;transition:background-color .2s linear;right:0;width:8px}#bootstrap-theme .ps-container:hover.ps-in-scrolling{pointer-events:none}#bootstrap-theme .ps-container:hover.ps-in-scrolling.ps-x>.ps-scrollbar-x-rail{background-color:#eee;opacity:.9}#bootstrap-theme .ps-container:hover.ps-in-scrolling.ps-x>.ps-scrollbar-x-rail>.ps-scrollbar-x{background-color:#999}#bootstrap-theme .ps-container:hover.ps-in-scrolling.ps-y>.ps-scrollbar-y-rail{background-color:#eee;opacity:.9}#bootstrap-theme .ps-container:hover.ps-in-scrolling.ps-y>.ps-scrollbar-y-rail>.ps-scrollbar-y{background-color:#999}#bootstrap-theme .ps-container:hover>.ps-scrollbar-x-rail, #bootstrap-theme .ps-container:hover>.ps-scrollbar-y-rail{opacity:.6}#bootstrap-theme .ps-container:hover>.ps-scrollbar-x-rail:hover{background-color:#eee;opacity:.9}#bootstrap-theme .ps-container:hover>.ps-scrollbar-x-rail:hover>.ps-scrollbar-x{background-color:#999}#bootstrap-theme .ps-container:hover>.ps-scrollbar-y-rail:hover{background-color:#eee;opacity:.9}#bootstrap-theme .ps-container:hover>.ps-scrollbar-y-rail:hover>.ps-scrollbar-y{background-color:#999}#bootstrap-theme .form-control .select2-choice{border:0;border-radius:2px;text-decoration:none !important}#bootstrap-theme .form-control .select2-choice .select2-arrow{border-radius:0 2px 2px 0;background:transparent;border-left:0 none;right:4px;top:2px}#bootstrap-theme .form-control.select2-container{padding:0;height:auto}#bootstrap-theme .form-control.select2-container.select2-dropdown-open{border-color:#5897FB;border-radius:3px 3px 0 0}#bootstrap-theme .form-control .select2-container.select2-dropdown-open .select2-choices{border-radius:3px 3px 0 0}#bootstrap-theme .form-control.select2-container .select2-choices{border:0 !important;border-radius:3px;padding-left:4px;margin-bottom:0}#bootstrap-theme .control-group.warning .select2-container .select2-choice, #bootstrap-theme .control-group.warning .select2-container .select2-choices, #bootstrap-theme .control-group.warning .select2-container-active .select2-choice, #bootstrap-theme .control-group.warning .select2-container-active .select2-choices, #bootstrap-theme .control-group.warning .select2-dropdown-open.select2-drop-above .select2-choice, #bootstrap-theme .control-group.warning .select2-dropdown-open.select2-drop-above .select2-choices, #bootstrap-theme .control-group.warning .select2-container-multi.select2-container-active .select2-choices{border:1px solid #C09853 !important}#bootstrap-theme .control-group.warning .select2-container .select2-choice div{border-left:1px solid #C09853 !important;background:#FCF8E3 !important}#bootstrap-theme .control-group.error .select2-container .select2-choice, #bootstrap-theme .control-group.error .select2-container .select2-choices, #bootstrap-theme .control-group.error .select2-container-active .select2-choice, #bootstrap-theme .control-group.error .select2-container-active .select2-choices, #bootstrap-theme .control-group.error .select2-dropdown-open.select2-drop-above .select2-choice, #bootstrap-theme .control-group.error .select2-dropdown-open.select2-drop-above .select2-choices, #bootstrap-theme .control-group.error .select2-container-multi.select2-container-active .select2-choices{border:1px solid #B94A48 !important}#bootstrap-theme .control-group.error .select2-container .select2-choice div{border-left:1px solid #B94A48 !important;background:#F2DEDE !important}#bootstrap-theme .control-group.info .select2-container .select2-choice, #bootstrap-theme .control-group.info .select2-container .select2-choices, #bootstrap-theme .control-group.info .select2-container-active .select2-choice, #bootstrap-theme .control-group.info .select2-container-active .select2-choices, #bootstrap-theme .control-group.info .select2-dropdown-open.select2-drop-above .select2-choice, #bootstrap-theme .control-group.info .select2-dropdown-open.select2-drop-above .select2-choices, #bootstrap-theme .control-group.info .select2-container-multi.select2-container-active .select2-choices{border:1px solid #3A87AD !important}#bootstrap-theme .control-group.info .select2-container .select2-choice div{border-left:1px solid #3A87AD !important;background:#D9EDF7 !important}#bootstrap-theme .control-group.success .select2-container .select2-choice, #bootstrap-theme .control-group.success .select2-container .select2-choices, #bootstrap-theme .control-group.success .select2-container-active .select2-choice, #bootstrap-theme .control-group.success .select2-container-active .select2-choices, #bootstrap-theme .control-group.success .select2-dropdown-open.select2-drop-above .select2-choice, #bootstrap-theme .control-group.success .select2-dropdown-open.select2-drop-above .select2-choices, #bootstrap-theme .control-group.success .select2-container-multi.select2-container-active .select2-choices{border:1px solid #468847 !important}#bootstrap-theme .control-group.success .select2-container .select2-choice div{border-left:1px solid #468847 !important;background:#DFF0D8 !important}#bootstrap-theme .fa-0_5x{font-size:9px;font-weight:300}#bootstrap-theme .fa-1x{font-size:13px}#bootstrap-theme .fa-color-primary{color:#0071bd}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:url("../fonts/open-sans/OpenSans-Regular-webfont.eot");src:url("../fonts/open-sans/OpenSans-Regular-webfont.eot?#iefix") format("embedded-opentype"),url("../fonts/open-sans/OpenSans-Regular-webfont.woff") format("woff"),url("../fonts/open-sans/OpenSans-Regular-webfont.ttf") format("truetype"),url("../fonts/open-sans/OpenSans-Regular-webfont.svg#open_sansregular") format("svg")}@font-face{font-family:'Open Sans';font-style:normal;font-weight:600;src:url("../fonts/open-sans/OpenSans-Semibold-webfont.eot");src:url("../fonts/open-sans/OpenSans-Semibold-webfont.eot?#iefix") format("embedded-opentype"),url("../fonts/open-sans/OpenSans-Semibold-webfont.woff") format("woff"),url("../fonts/open-sans/OpenSans-Semibold-webfont.ttf") format("truetype"),url("../fonts/open-sans/OpenSans-Semibold-webfont.svg#open_sanssemibold") format("svg")}@font-face{font-family:'Open Sans';font-style:normal;font-weight:700;src:url("../fonts/open-sans/OpenSans-Bold-webfont.eot");src:url("../fonts/open-sans/OpenSans-Bold-webfont.eot?#iefix") format("embedded-opentype"),url("../fonts/open-sans/OpenSans-Bold-webfont.woff") format("woff"),url("../fonts/open-sans/OpenSans-Bold-webfont.ttf") format("truetype"),url("../fonts/open-sans/OpenSans-Bold-webfont.svg#open_sansbold") format("svg")}@font-face{font-family:'Open Sans';font-style:italic;font-weight:400;src:url("../fonts/open-sans/OpenSans-Italic-webfont.eot");src:url("../fonts/open-sans/OpenSans-Italic-webfont.eot?#iefix") format("embedded-opentype"),url("../fonts/open-sans/OpenSans-Italic-webfont.woff") format("woff"),url("../fonts/open-sans/OpenSans-Italic-webfont.ttf") format("truetype"),url("../fonts/open-sans/OpenSans-Italic-webfont.svg#open_sansitalic") format("svg")}html#bootstrap-theme.overlay-open .navbar-fixed-top, #bootstrap-theme.overlay-open .navbar-fixed-top{z-index:400}html#bootstrap-theme.js fieldset.collapsed, #bootstrap-theme.js fieldset.collapsed{height:auto}html#bootstrap-theme.js input.form-autocomplete, #bootstrap-theme.js input.form-autocomplete{background-image:none}body#bootstrap-theme, #bootstrap-theme{position:relative}body#bootstrap-theme.admin-expanded.admin-vertical.admin-nw .navbar, #bootstrap-theme.admin-expanded.admin-vertical.admin-nw .navbar, body#bootstrap-theme.admin-expanded.admin-vertical.admin-sw .navbar, #bootstrap-theme.admin-expanded.admin-vertical.admin-sw .navbar{margin-left:260px}body#bootstrap-theme.navbar-is-fixed-top, #bootstrap-theme.navbar-is-fixed-top{padding-top:64px !important}body#bootstrap-theme.navbar-is-fixed-bottom, #bootstrap-theme.navbar-is-fixed-bottom{padding-bottom:64px !important}body#bootstrap-theme.toolbar, #bootstrap-theme.toolbar{padding-top:30px !important}body#bootstrap-theme.toolbar .navbar-fixed-top, #bootstrap-theme.toolbar .navbar-fixed-top{top:30px}body#bootstrap-theme.toolbar.navbar-is-fixed-top, #bootstrap-theme.toolbar.navbar-is-fixed-top{padding-top:94px !important}body#bootstrap-theme.toolbar-drawer, #bootstrap-theme.toolbar-drawer{padding-top:64px !important}body#bootstrap-theme.toolbar-drawer .navbar-fixed-top, #bootstrap-theme.toolbar-drawer .navbar-fixed-top{top:64px}body#bootstrap-theme.toolbar-drawer.navbar-is-fixed-top, #bootstrap-theme.toolbar-drawer.navbar-is-fixed-top{padding-top:128px !important}body#bootstrap-theme.admin-menu .navbar-fixed-top, #bootstrap-theme.admin-menu .navbar-fixed-top{top:29px}body#bootstrap-theme.admin-menu.navbar-is-fixed-top, #bootstrap-theme.admin-menu.navbar-is-fixed-top{padding-top:93px !important}body#bootstrap-theme div#admin-toolbar, #bootstrap-theme div#admin-toolbar{z-index:1600}body#bootstrap-theme #toolbar, #bootstrap-theme #toolbar, body#bootstrap-theme #admin-menu, #bootstrap-theme #admin-menu, body#bootstrap-theme #admin-toolbar, #bootstrap-theme #admin-toolbar{-webkit-box-shadow:none;box-shadow:none}body#bootstrap-theme #admin-menu, #bootstrap-theme #admin-menu{margin:0;padding:0;position:fixed;z-index:1600}body#bootstrap-theme #admin-menu .dropdown li, #bootstrap-theme #admin-menu .dropdown li{line-height:normal}#bootstrap-theme dt, #bootstrap-theme dd{margin:0}#bootstrap-theme .navbar.container{margin-top:20px}@media screen and (min-width: screen-sm-min){#bootstrap-theme .navbar.container{max-width:706px}}@media screen and (min-width: screen-md-min){#bootstrap-theme .navbar.container{max-width:926px}}@media screen and (min-width: screen-lg-min){#bootstrap-theme .navbar.container{max-width:1126px}}#bootstrap-theme .navbar.container>.container{margin:0;padding:0;width:auto}#bootstrap-theme #overlay-container, #bootstrap-theme .overlay-modal-background, #bootstrap-theme .overlay-element{z-index:1500}#bootstrap-theme #toolbar{z-index:1600}#bootstrap-theme #toolbar ul{padding:0;margin:0}#bootstrap-theme #toolbar ul a, #bootstrap-theme #toolbar ul a:hover{color:#fff;text-decoration:none}#bootstrap-theme .modal{z-index:1620}#bootstrap-theme .modal-dialog{z-index:1630}#bootstrap-theme .modal-backdrop{z-index:1610}#bootstrap-theme .footer{margin-top:45px;padding-top:35px;padding-bottom:36px;border-top:1px solid #E5E5E5}#bootstrap-theme .element-invisible{margin:0;padding:0;width:1px}#bootstrap-theme .navbar .logo{margin-right:-15px;padding-left:15px;padding-right:15px}@media screen and (min-width: screen-sm-min){#bootstrap-theme .navbar .logo{margin-right:0;padding-left:0}}#bootstrap-theme ul.secondary{float:left}@media screen and (min-width: screen-sm-min){#bootstrap-theme ul.secondary{float:right}}#bootstrap-theme .page-header{margin-top:0}#bootstrap-theme .block:first-child h2.block-title{margin-top:0}#bootstrap-theme p:last-child{margin-bottom:0}#bootstrap-theme .region-help>.glyphicon{font-size:17px;float:left;margin:-0.05em 0.5em 0 0}#bootstrap-theme .region-help .block{overflow:hidden}#bootstrap-theme form#search-block-form{margin:0}#bootstrap-theme .navbar #block-search-form{float:none;margin:5px 0 5px 5px}@media screen and (min-width: screen-md-min){#bootstrap-theme .navbar #block-search-form{float:right}}#bootstrap-theme .navbar-search .control-group{margin-bottom:0px}#bootstrap-theme ul.action-links{margin:10px 0;padding:0}#bootstrap-theme ul.action-links li{display:inline;margin:0;padding:0 4px 0 0}#bootstrap-theme ul.action-links .glyphicon{padding-right:0.5em}#bootstrap-theme input, #bootstrap-theme textarea, #bootstrap-theme select, #bootstrap-theme .uneditable-input{max-width:100%;width:auto}#bootstrap-theme input.error{color:#cf3458;border-color:#cf3458}#bootstrap-theme fieldset legend.panel-heading{font-size:13px;float:left;line-height:1em;margin:0}#bootstrap-theme fieldset .panel-body{clear:both}#bootstrap-theme fieldset .panel-heading a.panel-title{color:inherit;display:block;margin:-10px -15px;padding:10px 15px}#bootstrap-theme fieldset .panel-heading a.panel-title:hover{text-decoration:none}#bootstrap-theme .form-actions{clear:both}#bootstrap-theme .resizable-textarea textarea{border-radius:2px 2px 0 0}#bootstrap-theme .radio:first-child, #bootstrap-theme .checkbox:first-child{margin-top:0}#bootstrap-theme .radio:last-child, #bootstrap-theme .checkbox:last-child{margin-bottom:0}#bootstrap-theme .help-block, #bootstrap-theme .control-group .help-inline{color:#e8eef0;font-size:12px;margin:5px 0 10px;padding:0}#bootstrap-theme .panel-heading{display:block}#bootstrap-theme a.tabledrag-handle .handle{height:auto;width:auto}#bootstrap-theme .error{color:#cf3458}#bootstrap-theme div.error, #bootstrap-theme table tr.error{background-color:#f2dede;color:#cf3458}#bootstrap-theme .control-group.error{background:none}#bootstrap-theme .control-group.error label, #bootstrap-theme .control-group.error .control-label{color:#cf3458;font-weight:600}#bootstrap-theme .control-group.error input, #bootstrap-theme .control-group.error textarea, #bootstrap-theme .control-group.error select, #bootstrap-theme .control-group.error .uneditable-input{color:#464354;border:1px solid #c2cfd8}#bootstrap-theme .control-group.error .help-block, #bootstrap-theme .control-group.error .help-inline{color:#4d4d69}#bootstrap-theme .list-inline>li.first{padding-left:0}#bootstrap-theme .nav-tabs{margin-bottom:10px}#bootstrap-theme ul{margin:initial;padding-left:40px}#bootstrap-theme ul li.collapsed, #bootstrap-theme ul li.expanded, #bootstrap-theme ul li.leaf{list-style:none;list-style-image:none}#bootstrap-theme .tabs--secondary{margin:0 0 10px}#bootstrap-theme .submitted{margin-bottom:1em;font-style:italic;font-weight:normal;color:#777}#bootstrap-theme .password-strength{width:17em;float:right;margin-top:1.4em}#bootstrap-theme .password-strength-title{display:inline}#bootstrap-theme .password-strength-text{float:right;font-weight:bold}#bootstrap-theme .password-indicator{background-color:#8e8ea0;height:0.3em;width:100%}#bootstrap-theme .password-indicator div{height:100%;width:0%;background-color:#9494a5}#bootstrap-theme input.password-confirm, #bootstrap-theme input.password-field{width:16em;margin-bottom:0.4em}#bootstrap-theme div.password-confirm{float:right;margin-top:1.5em;visibility:hidden;width:17em}#bootstrap-theme div.form-item div.password-suggestions{padding:0.2em 0.5em;margin:0.7em 0;width:38.5em;border:1px solid #B4B4B4}#bootstrap-theme div.password-suggestions ul{margin-bottom:0}#bootstrap-theme .confirm-parent, #bootstrap-theme .password-parent{clear:left;margin:0;width:36.3em}#bootstrap-theme .progress-wrapper .progress{margin-bottom:10px}#bootstrap-theme .pagination ul>li>a.progress-disabled{float:left}#bootstrap-theme .form-autocomplete .glyphicon{color:#e8eef0;font-size:120%}#bootstrap-theme .form-autocomplete .glyphicon.glyphicon-spin{color:#0071bd}#bootstrap-theme .form-autocomplete .input-group-addon{background-color:#fff}#bootstrap-theme .ajax-progress .glyphicon{font-size:90%;margin:0 -.25em 0 0.5em}#bootstrap-theme .glyphicon-spin{display:inline-block;-moz-animation:spin 1s infinite linear;-o-animation:spin 1s infinite linear;-webkit-animation:spin 1s infinite linear;animation:spin 1s infinite linear}#bootstrap-theme a .glyphicon-spin{display:inline-block;text-decoration:none}@-moz-keyframes spin{#bootstrap-theme 0%{-moz-transform:rotate(0deg)}#bootstrap-theme 100%{-moz-transform:rotate(359deg)}}@-webkit-keyframes spin{#bootstrap-theme 0%{-webkit-transform:rotate(0deg)}#bootstrap-theme 100%{-webkit-transform:rotate(359deg)}}@-o-keyframes spin{#bootstrap-theme 0%{-o-transform:rotate(0deg)}#bootstrap-theme 100%{-o-transform:rotate(359deg)}}@-ms-keyframes spin{#bootstrap-theme 0%{-ms-transform:rotate(0deg)}#bootstrap-theme 100%{-ms-transform:rotate(359deg)}}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}}#bootstrap-theme .glyphicon-refresh{-webkit-transform-origin:50% 45%;-moz-transform-origin:50% 45%;-ms-transform-origin:50% 45%;-o-transform-origin:50% 45%;transform-origin:50% 45%}#bootstrap-theme .tabbable{margin-bottom:20px}#bootstrap-theme .tabs-below>.nav-tabs, #bootstrap-theme .tabs-left>.nav-tabs, #bootstrap-theme .tabs-right>.nav-tabs{border-bottom:0}#bootstrap-theme .tabs-below>.nav-tabs .summary, #bootstrap-theme .tabs-left>.nav-tabs .summary, #bootstrap-theme .tabs-right>.nav-tabs .summary{color:#bcbcc8;font-size:12px}#bootstrap-theme .tab-pane>.panel-heading{display:none}#bootstrap-theme .tab-content>.active{display:block}#bootstrap-theme .tabs-below>.nav-tabs{border-top:1px solid #d3dee2}#bootstrap-theme .tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0}#bootstrap-theme .tabs-below>.nav-tabs>li>a{border-radius:0 0 2px 2px}#bootstrap-theme .tabs-below>.nav-tabs>li>a:hover, #bootstrap-theme .tabs-below>.nav-tabs>li>a:focus{border-top-color:#d3dee2;border-bottom-color:transparent}#bootstrap-theme .tabs-below>.nav-tabs>.active>a, #bootstrap-theme .tabs-below>.nav-tabs>.active>a:hover, #bootstrap-theme .tabs-below>.nav-tabs>.active>a:focus{border-color:transparent #d3dee2 #d3dee2 #d3dee2}#bootstrap-theme .tabs-left>.nav-tabs, #bootstrap-theme .tabs-right>.nav-tabs{padding-bottom:20px;width:220px}#bootstrap-theme .tabs-left>.nav-tabs>li, #bootstrap-theme .tabs-right>.nav-tabs>li{float:none}#bootstrap-theme .tabs-left>.nav-tabs>li:focus, #bootstrap-theme .tabs-right>.nav-tabs>li:focus{outline:0}#bootstrap-theme .tabs-left>.nav-tabs>li>a, #bootstrap-theme .tabs-right>.nav-tabs>li>a{margin-right:0;margin-bottom:3px}#bootstrap-theme .tabs-left>.nav-tabs>li>a:focus, #bootstrap-theme .tabs-right>.nav-tabs>li>a:focus{outline:0}#bootstrap-theme .tabs-left>.tab-content, #bootstrap-theme .tabs-right>.tab-content{border-radius:0 2px 2px 2px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05);border:1px solid #d3dee2;overflow:hidden;padding:10px 15px}#bootstrap-theme .tabs-left>.nav-tabs{float:left;margin-right:-1px}#bootstrap-theme .tabs-left>.nav-tabs>li>a{border-radius:2px 0 0 2px}#bootstrap-theme .tabs-left>.nav-tabs>li>a:hover, #bootstrap-theme .tabs-left>.nav-tabs>li>a:focus{border-color:#f3f6f7 #d3dee2 #f3f6f7 #f3f6f7}#bootstrap-theme .tabs-left>.nav-tabs>.active>a, #bootstrap-theme .tabs-left>.nav-tabs>.active>a:hover, #bootstrap-theme .tabs-left>.nav-tabs>.active>a:focus{border-color:#d3dee2 transparent #d3dee2 #d3dee2;-webkit-box-shadow:-1px 1px 1px rgba(0,0,0,0.05);box-shadow:-1px 1px 1px rgba(0,0,0,0.05)}#bootstrap-theme .tabs-right>.nav-tabs{float:right;margin-left:-1px}#bootstrap-theme .tabs-right>.nav-tabs>li>a{border-radius:0 2px 2px 0}#bootstrap-theme .tabs-right>.nav-tabs>li>a:hover, #bootstrap-theme .tabs-right>.nav-tabs>li>a:focus{border-color:#f3f6f7 #f3f6f7 #f3f6f7 #d3dee2;-webkit-box-shadow:1px 1px 1px rgba(0,0,0,0.05);box-shadow:1px 1px 1px rgba(0,0,0,0.05)}#bootstrap-theme .tabs-right>.nav-tabs>.active>a, #bootstrap-theme .tabs-right>.nav-tabs>.active>a:hover, #bootstrap-theme .tabs-right>.nav-tabs>.active>a:focus{border-color:#d3dee2 #d3dee2 #d3dee2 transparent}#bootstrap-theme th.checkbox, #bootstrap-theme td.checkbox, #bootstrap-theme th.radio, #bootstrap-theme td.radio{display:table-cell}#bootstrap-theme .views-display-settings .label{font-size:100%;color:#666666}#bootstrap-theme .views-display-settings .footer{padding:0;margin:4px 0 0 0}#bootstrap-theme table .radio input[type="radio"], #bootstrap-theme table .checkbox input[type="checkbox"]{max-width:inherit}#bootstrap-theme .alert a{font-weight:bold}#bootstrap-theme .alert-success a{color:#2e2c38}#bootstrap-theme .alert-info a{color:#2e2c38}#bootstrap-theme .alert-warning a{color:#2e2c38}#bootstrap-theme .alert-danger a{color:#2e2c38}#bootstrap-theme h3{background-color:rgba(0,0,0,0);background-color:initial;border:0;border:initial;border-radius:0;border-radius:initial;box-shadow:none;box-shadow:initial;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-family:initial;padding:0;padding:initial;margin:0;margin:initial;font-size:16px;font-weight:400}#bootstrap-theme table{border:0;border:initial;margin:0;margin:initial;font-size:inherit}#bootstrap-theme table td{border:0;border:initial;color:inherit}#bootstrap-theme table th{background-color:inherit;background-color:initial;border:0;border:initial;text-transform:none;text-transform:initial;color:inherit;font-size:inherit;font-weight:600}#bootstrap-theme .collapsed{background:none;background:initial;cursor:auto;cursor:initial;padding:0;padding:initial}#bootstrap-theme .panel{position:static;position:initial;padding:0;padding:initial;width:auto;width:initial;z-index:auto;z-index:initial;display:block}#bootstrap-theme .crm-button .crm-i{left:0.6em;top:0.6em}#bootstrap-theme .crm-select2{width:15em}#bootstrap-theme label .crm-error-label{padding:0;padding:initial;font-size:13px}html#bootstrap-theme, #bootstrap-theme{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body#bootstrap-theme, #bootstrap-theme{margin:0}#bootstrap-theme article, #bootstrap-theme aside, #bootstrap-theme details, #bootstrap-theme figcaption, #bootstrap-theme figure, #bootstrap-theme footer, #bootstrap-theme header, #bootstrap-theme hgroup, #bootstrap-theme main, #bootstrap-theme menu, #bootstrap-theme nav, #bootstrap-theme section, #bootstrap-theme summary{display:block}#bootstrap-theme audio, #bootstrap-theme canvas, #bootstrap-theme progress, #bootstrap-theme video{display:inline-block;vertical-align:baseline}#bootstrap-theme audio:not([controls]){display:none;height:0}#bootstrap-theme [hidden], #bootstrap-theme template{display:none}#bootstrap-theme a{background-color:transparent}#bootstrap-theme a:active, #bootstrap-theme a:hover{outline:0}#bootstrap-theme abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}#bootstrap-theme b, #bootstrap-theme strong{font-weight:bold}#bootstrap-theme dfn{font-style:italic}#bootstrap-theme h1{font-size:2em;margin:0.67em 0}#bootstrap-theme mark{background:#ff0;color:#000}#bootstrap-theme small{font-size:80%}#bootstrap-theme sub, #bootstrap-theme sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}#bootstrap-theme sup{top:-0.5em}#bootstrap-theme sub{bottom:-0.25em}#bootstrap-theme img{border:0}#bootstrap-theme svg:not(:root){overflow:hidden}#bootstrap-theme figure{margin:1em 40px}#bootstrap-theme hr{box-sizing:content-box;height:0}#bootstrap-theme pre{overflow:auto}#bootstrap-theme code, #bootstrap-theme kbd, #bootstrap-theme pre, #bootstrap-theme samp{font-family:monospace, monospace;font-size:1em}#bootstrap-theme button, #bootstrap-theme input, #bootstrap-theme optgroup, #bootstrap-theme select, #bootstrap-theme textarea{color:inherit;font:inherit;margin:0}#bootstrap-theme button{overflow:visible}#bootstrap-theme button, #bootstrap-theme select{text-transform:none}#bootstrap-theme button, html#bootstrap-theme input[type="button"], #bootstrap-theme input[type="button"], #bootstrap-theme input[type="reset"], #bootstrap-theme input[type="submit"]{-webkit-appearance:button;cursor:pointer}#bootstrap-theme button[disabled], html#bootstrap-theme input[disabled], #bootstrap-theme input[disabled]{cursor:default}#bootstrap-theme button::-moz-focus-inner, #bootstrap-theme input::-moz-focus-inner{border:0;padding:0}#bootstrap-theme input{line-height:normal}#bootstrap-theme input[type="checkbox"], #bootstrap-theme input[type="radio"]{box-sizing:border-box;padding:0}#bootstrap-theme input[type="number"]::-webkit-inner-spin-button, #bootstrap-theme input[type="number"]::-webkit-outer-spin-button{height:auto}#bootstrap-theme input[type="search"]{-webkit-appearance:textfield;box-sizing:content-box}#bootstrap-theme input[type="search"]::-webkit-search-cancel-button, #bootstrap-theme input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}#bootstrap-theme fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}#bootstrap-theme legend{border:0;padding:0}#bootstrap-theme textarea{overflow:auto}#bootstrap-theme optgroup{font-weight:bold}#bootstrap-theme table{border-collapse:collapse;border-spacing:0}#bootstrap-theme td, #bootstrap-theme th{padding:0}@media print{#bootstrap-theme *, #bootstrap-theme *:before, #bootstrap-theme *:after{color:#000 !important;text-shadow:none !important;background:transparent !important;box-shadow:none !important}#bootstrap-theme a, #bootstrap-theme a:visited{text-decoration:underline}#bootstrap-theme a[href]:after{content:" (" attr(href) ")"}#bootstrap-theme abbr[title]:after{content:" (" attr(title) ")"}#bootstrap-theme a[href^="#"]:after, #bootstrap-theme a[href^="javascript:"]:after{content:""}#bootstrap-theme pre, #bootstrap-theme blockquote{border:1px solid #999;page-break-inside:avoid}#bootstrap-theme thead{display:table-header-group}#bootstrap-theme tr, #bootstrap-theme img{page-break-inside:avoid}#bootstrap-theme img{max-width:100% !important}#bootstrap-theme p, #bootstrap-theme h2, #bootstrap-theme h3{orphans:3;widows:3}#bootstrap-theme h2, #bootstrap-theme h3{page-break-after:avoid}#bootstrap-theme .navbar{display:none}#bootstrap-theme .btn>.caret, #bootstrap-theme .dropup>.btn>.caret{border-top-color:#000 !important}#bootstrap-theme .label{border:1px solid #000}#bootstrap-theme .table{border-collapse:collapse !important}#bootstrap-theme .table td, #bootstrap-theme .table th{background-color:#fff !important}#bootstrap-theme .table-bordered th, #bootstrap-theme .table-bordered td{border:1px solid #ddd !important}}@font-face{font-family:"Glyphicons Halflings";src:url("../base/fonts/glyphicons-halflings-regular.eot");src:url("../base/fonts/glyphicons-halflings-regular.eot?#iefix") format("embedded-opentype"),url("../base/fonts/glyphicons-halflings-regular.woff2") format("woff2"),url("../base/fonts/glyphicons-halflings-regular.woff") format("woff"),url("../base/fonts/glyphicons-halflings-regular.ttf") format("truetype"),url("../base/fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular") format("svg")}#bootstrap-theme .glyphicon{position:relative;top:1px;display:inline-block;font-family:"Glyphicons Halflings";font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}#bootstrap-theme .glyphicon-asterisk:before{content:"\002a"}#bootstrap-theme .glyphicon-plus:before{content:"\002b"}#bootstrap-theme .glyphicon-euro:before, #bootstrap-theme .glyphicon-eur:before{content:"\20ac"}#bootstrap-theme .glyphicon-minus:before{content:"\2212"}#bootstrap-theme .glyphicon-cloud:before{content:"\2601"}#bootstrap-theme .glyphicon-envelope:before{content:"\2709"}#bootstrap-theme .glyphicon-pencil:before{content:"\270f"}#bootstrap-theme .glyphicon-glass:before{content:"\e001"}#bootstrap-theme .glyphicon-music:before{content:"\e002"}#bootstrap-theme .glyphicon-search:before{content:"\e003"}#bootstrap-theme .glyphicon-heart:before{content:"\e005"}#bootstrap-theme .glyphicon-star:before{content:"\e006"}#bootstrap-theme .glyphicon-star-empty:before{content:"\e007"}#bootstrap-theme .glyphicon-user:before{content:"\e008"}#bootstrap-theme .glyphicon-film:before{content:"\e009"}#bootstrap-theme .glyphicon-th-large:before{content:"\e010"}#bootstrap-theme .glyphicon-th:before{content:"\e011"}#bootstrap-theme .glyphicon-th-list:before{content:"\e012"}#bootstrap-theme .glyphicon-ok:before{content:"\e013"}#bootstrap-theme .glyphicon-remove:before{content:"\e014"}#bootstrap-theme .glyphicon-zoom-in:before{content:"\e015"}#bootstrap-theme .glyphicon-zoom-out:before{content:"\e016"}#bootstrap-theme .glyphicon-off:before{content:"\e017"}#bootstrap-theme .glyphicon-signal:before{content:"\e018"}#bootstrap-theme .glyphicon-cog:before{content:"\e019"}#bootstrap-theme .glyphicon-trash:before{content:"\e020"}#bootstrap-theme .glyphicon-home:before{content:"\e021"}#bootstrap-theme .glyphicon-file:before{content:"\e022"}#bootstrap-theme .glyphicon-time:before{content:"\e023"}#bootstrap-theme .glyphicon-road:before{content:"\e024"}#bootstrap-theme .glyphicon-download-alt:before{content:"\e025"}#bootstrap-theme .glyphicon-download:before{content:"\e026"}#bootstrap-theme .glyphicon-upload:before{content:"\e027"}#bootstrap-theme .glyphicon-inbox:before{content:"\e028"}#bootstrap-theme .glyphicon-play-circle:before{content:"\e029"}#bootstrap-theme .glyphicon-repeat:before{content:"\e030"}#bootstrap-theme .glyphicon-refresh:before{content:"\e031"}#bootstrap-theme .glyphicon-list-alt:before{content:"\e032"}#bootstrap-theme .glyphicon-lock:before{content:"\e033"}#bootstrap-theme .glyphicon-flag:before{content:"\e034"}#bootstrap-theme .glyphicon-headphones:before{content:"\e035"}#bootstrap-theme .glyphicon-volume-off:before{content:"\e036"}#bootstrap-theme .glyphicon-volume-down:before{content:"\e037"}#bootstrap-theme .glyphicon-volume-up:before{content:"\e038"}#bootstrap-theme .glyphicon-qrcode:before{content:"\e039"}#bootstrap-theme .glyphicon-barcode:before{content:"\e040"}#bootstrap-theme .glyphicon-tag:before{content:"\e041"}#bootstrap-theme .glyphicon-tags:before{content:"\e042"}#bootstrap-theme .glyphicon-book:before{content:"\e043"}#bootstrap-theme .glyphicon-bookmark:before{content:"\e044"}#bootstrap-theme .glyphicon-print:before{content:"\e045"}#bootstrap-theme .glyphicon-camera:before{content:"\e046"}#bootstrap-theme .glyphicon-font:before{content:"\e047"}#bootstrap-theme .glyphicon-bold:before{content:"\e048"}#bootstrap-theme .glyphicon-italic:before{content:"\e049"}#bootstrap-theme .glyphicon-text-height:before{content:"\e050"}#bootstrap-theme .glyphicon-text-width:before{content:"\e051"}#bootstrap-theme .glyphicon-align-left:before{content:"\e052"}#bootstrap-theme .glyphicon-align-center:before{content:"\e053"}#bootstrap-theme .glyphicon-align-right:before{content:"\e054"}#bootstrap-theme .glyphicon-align-justify:before{content:"\e055"}#bootstrap-theme .glyphicon-list:before{content:"\e056"}#bootstrap-theme .glyphicon-indent-left:before{content:"\e057"}#bootstrap-theme .glyphicon-indent-right:before{content:"\e058"}#bootstrap-theme .glyphicon-facetime-video:before{content:"\e059"}#bootstrap-theme .glyphicon-picture:before{content:"\e060"}#bootstrap-theme .glyphicon-map-marker:before{content:"\e062"}#bootstrap-theme .glyphicon-adjust:before{content:"\e063"}#bootstrap-theme .glyphicon-tint:before{content:"\e064"}#bootstrap-theme .glyphicon-edit:before{content:"\e065"}#bootstrap-theme .glyphicon-share:before{content:"\e066"}#bootstrap-theme .glyphicon-check:before{content:"\e067"}#bootstrap-theme .glyphicon-move:before{content:"\e068"}#bootstrap-theme .glyphicon-step-backward:before{content:"\e069"}#bootstrap-theme .glyphicon-fast-backward:before{content:"\e070"}#bootstrap-theme .glyphicon-backward:before{content:"\e071"}#bootstrap-theme .glyphicon-play:before{content:"\e072"}#bootstrap-theme .glyphicon-pause:before{content:"\e073"}#bootstrap-theme .glyphicon-stop:before{content:"\e074"}#bootstrap-theme .glyphicon-forward:before{content:"\e075"}#bootstrap-theme .glyphicon-fast-forward:before{content:"\e076"}#bootstrap-theme .glyphicon-step-forward:before{content:"\e077"}#bootstrap-theme .glyphicon-eject:before{content:"\e078"}#bootstrap-theme .glyphicon-chevron-left:before{content:"\e079"}#bootstrap-theme .glyphicon-chevron-right:before{content:"\e080"}#bootstrap-theme .glyphicon-plus-sign:before{content:"\e081"}#bootstrap-theme .glyphicon-minus-sign:before{content:"\e082"}#bootstrap-theme .glyphicon-remove-sign:before{content:"\e083"}#bootstrap-theme .glyphicon-ok-sign:before{content:"\e084"}#bootstrap-theme .glyphicon-question-sign:before{content:"\e085"}#bootstrap-theme .glyphicon-info-sign:before{content:"\e086"}#bootstrap-theme .glyphicon-screenshot:before{content:"\e087"}#bootstrap-theme .glyphicon-remove-circle:before{content:"\e088"}#bootstrap-theme .glyphicon-ok-circle:before{content:"\e089"}#bootstrap-theme .glyphicon-ban-circle:before{content:"\e090"}#bootstrap-theme .glyphicon-arrow-left:before{content:"\e091"}#bootstrap-theme .glyphicon-arrow-right:before{content:"\e092"}#bootstrap-theme .glyphicon-arrow-up:before{content:"\e093"}#bootstrap-theme .glyphicon-arrow-down:before{content:"\e094"}#bootstrap-theme .glyphicon-share-alt:before{content:"\e095"}#bootstrap-theme .glyphicon-resize-full:before{content:"\e096"}#bootstrap-theme .glyphicon-resize-small:before{content:"\e097"}#bootstrap-theme .glyphicon-exclamation-sign:before{content:"\e101"}#bootstrap-theme .glyphicon-gift:before{content:"\e102"}#bootstrap-theme .glyphicon-leaf:before{content:"\e103"}#bootstrap-theme .glyphicon-fire:before{content:"\e104"}#bootstrap-theme .glyphicon-eye-open:before{content:"\e105"}#bootstrap-theme .glyphicon-eye-close:before{content:"\e106"}#bootstrap-theme .glyphicon-warning-sign:before{content:"\e107"}#bootstrap-theme .glyphicon-plane:before{content:"\e108"}#bootstrap-theme .glyphicon-calendar:before{content:"\e109"}#bootstrap-theme .glyphicon-random:before{content:"\e110"}#bootstrap-theme .glyphicon-comment:before{content:"\e111"}#bootstrap-theme .glyphicon-magnet:before{content:"\e112"}#bootstrap-theme .glyphicon-chevron-up:before{content:"\e113"}#bootstrap-theme .glyphicon-chevron-down:before{content:"\e114"}#bootstrap-theme .glyphicon-retweet:before{content:"\e115"}#bootstrap-theme .glyphicon-shopping-cart:before{content:"\e116"}#bootstrap-theme .glyphicon-folder-close:before{content:"\e117"}#bootstrap-theme .glyphicon-folder-open:before{content:"\e118"}#bootstrap-theme .glyphicon-resize-vertical:before{content:"\e119"}#bootstrap-theme .glyphicon-resize-horizontal:before{content:"\e120"}#bootstrap-theme .glyphicon-hdd:before{content:"\e121"}#bootstrap-theme .glyphicon-bullhorn:before{content:"\e122"}#bootstrap-theme .glyphicon-bell:before{content:"\e123"}#bootstrap-theme .glyphicon-certificate:before{content:"\e124"}#bootstrap-theme .glyphicon-thumbs-up:before{content:"\e125"}#bootstrap-theme .glyphicon-thumbs-down:before{content:"\e126"}#bootstrap-theme .glyphicon-hand-right:before{content:"\e127"}#bootstrap-theme .glyphicon-hand-left:before{content:"\e128"}#bootstrap-theme .glyphicon-hand-up:before{content:"\e129"}#bootstrap-theme .glyphicon-hand-down:before{content:"\e130"}#bootstrap-theme .glyphicon-circle-arrow-right:before{content:"\e131"}#bootstrap-theme .glyphicon-circle-arrow-left:before{content:"\e132"}#bootstrap-theme .glyphicon-circle-arrow-up:before{content:"\e133"}#bootstrap-theme .glyphicon-circle-arrow-down:before{content:"\e134"}#bootstrap-theme .glyphicon-globe:before{content:"\e135"}#bootstrap-theme .glyphicon-wrench:before{content:"\e136"}#bootstrap-theme .glyphicon-tasks:before{content:"\e137"}#bootstrap-theme .glyphicon-filter:before{content:"\e138"}#bootstrap-theme .glyphicon-briefcase:before{content:"\e139"}#bootstrap-theme .glyphicon-fullscreen:before{content:"\e140"}#bootstrap-theme .glyphicon-dashboard:before{content:"\e141"}#bootstrap-theme .glyphicon-paperclip:before{content:"\e142"}#bootstrap-theme .glyphicon-heart-empty:before{content:"\e143"}#bootstrap-theme .glyphicon-link:before{content:"\e144"}#bootstrap-theme .glyphicon-phone:before{content:"\e145"}#bootstrap-theme .glyphicon-pushpin:before{content:"\e146"}#bootstrap-theme .glyphicon-usd:before{content:"\e148"}#bootstrap-theme .glyphicon-gbp:before{content:"\e149"}#bootstrap-theme .glyphicon-sort:before{content:"\e150"}#bootstrap-theme .glyphicon-sort-by-alphabet:before{content:"\e151"}#bootstrap-theme .glyphicon-sort-by-alphabet-alt:before{content:"\e152"}#bootstrap-theme .glyphicon-sort-by-order:before{content:"\e153"}#bootstrap-theme .glyphicon-sort-by-order-alt:before{content:"\e154"}#bootstrap-theme .glyphicon-sort-by-attributes:before{content:"\e155"}#bootstrap-theme .glyphicon-sort-by-attributes-alt:before{content:"\e156"}#bootstrap-theme .glyphicon-unchecked:before{content:"\e157"}#bootstrap-theme .glyphicon-expand:before{content:"\e158"}#bootstrap-theme .glyphicon-collapse-down:before{content:"\e159"}#bootstrap-theme .glyphicon-collapse-up:before{content:"\e160"}#bootstrap-theme .glyphicon-log-in:before{content:"\e161"}#bootstrap-theme .glyphicon-flash:before{content:"\e162"}#bootstrap-theme .glyphicon-log-out:before{content:"\e163"}#bootstrap-theme .glyphicon-new-window:before{content:"\e164"}#bootstrap-theme .glyphicon-record:before{content:"\e165"}#bootstrap-theme .glyphicon-save:before{content:"\e166"}#bootstrap-theme .glyphicon-open:before{content:"\e167"}#bootstrap-theme .glyphicon-saved:before{content:"\e168"}#bootstrap-theme .glyphicon-import:before{content:"\e169"}#bootstrap-theme .glyphicon-export:before{content:"\e170"}#bootstrap-theme .glyphicon-send:before{content:"\e171"}#bootstrap-theme .glyphicon-floppy-disk:before{content:"\e172"}#bootstrap-theme .glyphicon-floppy-saved:before{content:"\e173"}#bootstrap-theme .glyphicon-floppy-remove:before{content:"\e174"}#bootstrap-theme .glyphicon-floppy-save:before{content:"\e175"}#bootstrap-theme .glyphicon-floppy-open:before{content:"\e176"}#bootstrap-theme .glyphicon-credit-card:before{content:"\e177"}#bootstrap-theme .glyphicon-transfer:before{content:"\e178"}#bootstrap-theme .glyphicon-cutlery:before{content:"\e179"}#bootstrap-theme .glyphicon-header:before{content:"\e180"}#bootstrap-theme .glyphicon-compressed:before{content:"\e181"}#bootstrap-theme .glyphicon-earphone:before{content:"\e182"}#bootstrap-theme .glyphicon-phone-alt:before{content:"\e183"}#bootstrap-theme .glyphicon-tower:before{content:"\e184"}#bootstrap-theme .glyphicon-stats:before{content:"\e185"}#bootstrap-theme .glyphicon-sd-video:before{content:"\e186"}#bootstrap-theme .glyphicon-hd-video:before{content:"\e187"}#bootstrap-theme .glyphicon-subtitles:before{content:"\e188"}#bootstrap-theme .glyphicon-sound-stereo:before{content:"\e189"}#bootstrap-theme .glyphicon-sound-dolby:before{content:"\e190"}#bootstrap-theme .glyphicon-sound-5-1:before{content:"\e191"}#bootstrap-theme .glyphicon-sound-6-1:before{content:"\e192"}#bootstrap-theme .glyphicon-sound-7-1:before{content:"\e193"}#bootstrap-theme .glyphicon-copyright-mark:before{content:"\e194"}#bootstrap-theme .glyphicon-registration-mark:before{content:"\e195"}#bootstrap-theme .glyphicon-cloud-download:before{content:"\e197"}#bootstrap-theme .glyphicon-cloud-upload:before{content:"\e198"}#bootstrap-theme .glyphicon-tree-conifer:before{content:"\e199"}#bootstrap-theme .glyphicon-tree-deciduous:before{content:"\e200"}#bootstrap-theme .glyphicon-cd:before{content:"\e201"}#bootstrap-theme .glyphicon-save-file:before{content:"\e202"}#bootstrap-theme .glyphicon-open-file:before{content:"\e203"}#bootstrap-theme .glyphicon-level-up:before{content:"\e204"}#bootstrap-theme .glyphicon-copy:before{content:"\e205"}#bootstrap-theme .glyphicon-paste:before{content:"\e206"}#bootstrap-theme .glyphicon-alert:before{content:"\e209"}#bootstrap-theme .glyphicon-equalizer:before{content:"\e210"}#bootstrap-theme .glyphicon-king:before{content:"\e211"}#bootstrap-theme .glyphicon-queen:before{content:"\e212"}#bootstrap-theme .glyphicon-pawn:before{content:"\e213"}#bootstrap-theme .glyphicon-bishop:before{content:"\e214"}#bootstrap-theme .glyphicon-knight:before{content:"\e215"}#bootstrap-theme .glyphicon-baby-formula:before{content:"\e216"}#bootstrap-theme .glyphicon-tent:before{content:"\26fa"}#bootstrap-theme .glyphicon-blackboard:before{content:"\e218"}#bootstrap-theme .glyphicon-bed:before{content:"\e219"}#bootstrap-theme .glyphicon-apple:before{content:"\f8ff"}#bootstrap-theme .glyphicon-erase:before{content:"\e221"}#bootstrap-theme .glyphicon-hourglass:before{content:"\231b"}#bootstrap-theme .glyphicon-lamp:before{content:"\e223"}#bootstrap-theme .glyphicon-duplicate:before{content:"\e224"}#bootstrap-theme .glyphicon-piggy-bank:before{content:"\e225"}#bootstrap-theme .glyphicon-scissors:before{content:"\e226"}#bootstrap-theme .glyphicon-bitcoin:before{content:"\e227"}#bootstrap-theme .glyphicon-btc:before{content:"\e227"}#bootstrap-theme .glyphicon-xbt:before{content:"\e227"}#bootstrap-theme .glyphicon-yen:before{content:"\00a5"}#bootstrap-theme .glyphicon-jpy:before{content:"\00a5"}#bootstrap-theme .glyphicon-ruble:before{content:"\20bd"}#bootstrap-theme .glyphicon-rub:before{content:"\20bd"}#bootstrap-theme .glyphicon-scale:before{content:"\e230"}#bootstrap-theme .glyphicon-ice-lolly:before{content:"\e231"}#bootstrap-theme .glyphicon-ice-lolly-tasted:before{content:"\e232"}#bootstrap-theme .glyphicon-education:before{content:"\e233"}#bootstrap-theme .glyphicon-option-horizontal:before{content:"\e234"}#bootstrap-theme .glyphicon-option-vertical:before{content:"\e235"}#bootstrap-theme .glyphicon-menu-hamburger:before{content:"\e236"}#bootstrap-theme .glyphicon-modal-window:before{content:"\e237"}#bootstrap-theme .glyphicon-oil:before{content:"\e238"}#bootstrap-theme .glyphicon-grain:before{content:"\e239"}#bootstrap-theme .glyphicon-sunglasses:before{content:"\e240"}#bootstrap-theme .glyphicon-text-size:before{content:"\e241"}#bootstrap-theme .glyphicon-text-color:before{content:"\e242"}#bootstrap-theme .glyphicon-text-background:before{content:"\e243"}#bootstrap-theme .glyphicon-object-align-top:before{content:"\e244"}#bootstrap-theme .glyphicon-object-align-bottom:before{content:"\e245"}#bootstrap-theme .glyphicon-object-align-horizontal:before{content:"\e246"}#bootstrap-theme .glyphicon-object-align-left:before{content:"\e247"}#bootstrap-theme .glyphicon-object-align-vertical:before{content:"\e248"}#bootstrap-theme .glyphicon-object-align-right:before{content:"\e249"}#bootstrap-theme .glyphicon-triangle-right:before{content:"\e250"}#bootstrap-theme .glyphicon-triangle-left:before{content:"\e251"}#bootstrap-theme .glyphicon-triangle-bottom:before{content:"\e252"}#bootstrap-theme .glyphicon-triangle-top:before{content:"\e253"}#bootstrap-theme .glyphicon-console:before{content:"\e254"}#bootstrap-theme .glyphicon-superscript:before{content:"\e255"}#bootstrap-theme .glyphicon-subscript:before{content:"\e256"}#bootstrap-theme .glyphicon-menu-left:before{content:"\e257"}#bootstrap-theme .glyphicon-menu-right:before{content:"\e258"}#bootstrap-theme .glyphicon-menu-down:before{content:"\e259"}#bootstrap-theme .glyphicon-menu-up:before{content:"\e260"}#bootstrap-theme *{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}#bootstrap-theme *:before, #bootstrap-theme *:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html#bootstrap-theme, #bootstrap-theme{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body#bootstrap-theme, #bootstrap-theme{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:1.5384615385;color:#4d4d69;background-color:#e8eef0}#bootstrap-theme input, #bootstrap-theme button, #bootstrap-theme select, #bootstrap-theme textarea{font-family:inherit;font-size:inherit;line-height:inherit}#bootstrap-theme a{color:#0071bd;text-decoration:none}#bootstrap-theme a:hover, #bootstrap-theme a:focus{color:#004371;text-decoration:underline}#bootstrap-theme a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}#bootstrap-theme figure{margin:0}#bootstrap-theme img{vertical-align:middle}#bootstrap-theme .img-responsive{display:block;max-width:100%;height:auto}#bootstrap-theme .img-rounded{border-radius:6px}#bootstrap-theme .img-thumbnail{padding:4px;line-height:1.5384615385;background-color:#e8eef0;border:1px solid #ddd;border-radius:2px;-webkit-transition:all 0.2s ease-in-out;-o-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out;display:inline-block;max-width:100%;height:auto}#bootstrap-theme .img-circle{border-radius:50%}#bootstrap-theme hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #d3dee2}#bootstrap-theme .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}#bootstrap-theme .sr-only-focusable:active, #bootstrap-theme .sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}#bootstrap-theme [role="button"]{cursor:pointer}#bootstrap-theme h1, #bootstrap-theme h2, #bootstrap-theme h3, #bootstrap-theme h4, #bootstrap-theme h5, #bootstrap-theme h6, #bootstrap-theme .h1, #bootstrap-theme .h2, #bootstrap-theme .h3, #bootstrap-theme .h4, #bootstrap-theme .h5, #bootstrap-theme .h6{font-family:inherit;font-weight:500;line-height:1.1;color:#464354}#bootstrap-theme h1 small, #bootstrap-theme h1 .small, #bootstrap-theme h2 small, #bootstrap-theme h2 .small, #bootstrap-theme h3 small, #bootstrap-theme h3 .small, #bootstrap-theme h4 small, #bootstrap-theme h4 .small, #bootstrap-theme h5 small, #bootstrap-theme h5 .small, #bootstrap-theme h6 small, #bootstrap-theme h6 .small, #bootstrap-theme .h1 small, #bootstrap-theme .h1 .small, #bootstrap-theme .h2 small, #bootstrap-theme .h2 .small, #bootstrap-theme .h3 small, #bootstrap-theme .h3 .small, #bootstrap-theme .h4 small, #bootstrap-theme .h4 .small, #bootstrap-theme .h5 small, #bootstrap-theme .h5 .small, #bootstrap-theme .h6 small, #bootstrap-theme .h6 .small{font-weight:400;line-height:1;color:#e8eef0}#bootstrap-theme h1, #bootstrap-theme .h1, #bootstrap-theme h2, #bootstrap-theme .h2, #bootstrap-theme h3, #bootstrap-theme .h3{margin-top:20px;margin-bottom:10px}#bootstrap-theme h1 small, #bootstrap-theme h1 .small, #bootstrap-theme .h1 small, #bootstrap-theme .h1 .small, #bootstrap-theme h2 small, #bootstrap-theme h2 .small, #bootstrap-theme .h2 small, #bootstrap-theme .h2 .small, #bootstrap-theme h3 small, #bootstrap-theme h3 .small, #bootstrap-theme .h3 small, #bootstrap-theme .h3 .small{font-size:65%}#bootstrap-theme h4, #bootstrap-theme .h4, #bootstrap-theme h5, #bootstrap-theme .h5, #bootstrap-theme h6, #bootstrap-theme .h6{margin-top:10px;margin-bottom:10px}#bootstrap-theme h4 small, #bootstrap-theme h4 .small, #bootstrap-theme .h4 small, #bootstrap-theme .h4 .small, #bootstrap-theme h5 small, #bootstrap-theme h5 .small, #bootstrap-theme .h5 small, #bootstrap-theme .h5 .small, #bootstrap-theme h6 small, #bootstrap-theme h6 .small, #bootstrap-theme .h6 small, #bootstrap-theme .h6 .small{font-size:75%}#bootstrap-theme h1, #bootstrap-theme .h1{font-size:24px}#bootstrap-theme h2, #bootstrap-theme .h2{font-size:18px}#bootstrap-theme h3, #bootstrap-theme .h3{font-size:16px}#bootstrap-theme h4, #bootstrap-theme .h4{font-size:14px}#bootstrap-theme h5, #bootstrap-theme .h5{font-size:13px}#bootstrap-theme h6, #bootstrap-theme .h6{font-size:12px}#bootstrap-theme p{margin:0 0 10px}#bootstrap-theme .lead{margin-bottom:20px;font-size:14px;font-weight:300;line-height:1.4}@media (min-width: 768px){#bootstrap-theme .lead{font-size:19.5px}}#bootstrap-theme small, #bootstrap-theme .small{font-size:92%}#bootstrap-theme mark, #bootstrap-theme .mark{padding:.2em;background-color:#fcf8e3}#bootstrap-theme .text-left{text-align:left}#bootstrap-theme .text-right{text-align:right}#bootstrap-theme .text-center{text-align:center}#bootstrap-theme .text-justify{text-align:justify}#bootstrap-theme .text-nowrap{white-space:nowrap}#bootstrap-theme .text-lowercase{text-transform:lowercase}#bootstrap-theme .text-uppercase, #bootstrap-theme .initialism{text-transform:uppercase}#bootstrap-theme .text-capitalize{text-transform:capitalize}#bootstrap-theme .text-muted{color:#aab2b9}#bootstrap-theme .text-primary{color:#0071bd}#bootstrap-theme a.text-primary:hover, #bootstrap-theme a.text-primary:focus{color:#00538a}#bootstrap-theme .text-success{color:#4d994d}#bootstrap-theme a.text-success:hover, #bootstrap-theme a.text-success:focus{color:#3c773c}#bootstrap-theme .text-info{color:#0071bd}#bootstrap-theme a.text-info:hover, #bootstrap-theme a.text-info:focus{color:#00538a}#bootstrap-theme .text-warning{color:#bf5900}#bootstrap-theme a.text-warning:hover, #bootstrap-theme a.text-warning:focus{color:#8c4100}#bootstrap-theme .text-danger{color:#cf3458}#bootstrap-theme a.text-danger:hover, #bootstrap-theme a.text-danger:focus{color:#a82846}#bootstrap-theme .bg-primary{color:#fff}#bootstrap-theme .bg-primary{background-color:#0071bd}#bootstrap-theme a.bg-primary:hover, #bootstrap-theme a.bg-primary:focus{background-color:#00538a}#bootstrap-theme .bg-success{background-color:#dff0d8}#bootstrap-theme a.bg-success:hover, #bootstrap-theme a.bg-success:focus{background-color:#c1e2b3}#bootstrap-theme .bg-info{background-color:#d9edf7}#bootstrap-theme a.bg-info:hover, #bootstrap-theme a.bg-info:focus{background-color:#afd9ee}#bootstrap-theme .bg-warning{background-color:#fcf8e3}#bootstrap-theme a.bg-warning:hover, #bootstrap-theme a.bg-warning:focus{background-color:#f7ecb5}#bootstrap-theme .bg-danger{background-color:#f2dede}#bootstrap-theme a.bg-danger:hover, #bootstrap-theme a.bg-danger:focus{background-color:#e4b9b9}#bootstrap-theme .page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #f3f6f7}#bootstrap-theme ul, #bootstrap-theme ol{margin-top:0;margin-bottom:10px}#bootstrap-theme ul ul, #bootstrap-theme ul ol, #bootstrap-theme ol ul, #bootstrap-theme ol ol{margin-bottom:0}#bootstrap-theme .list-unstyled{padding-left:0;list-style:none}#bootstrap-theme .list-inline{padding-left:0;list-style:none;margin-left:-5px}#bootstrap-theme .list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}#bootstrap-theme dl{margin-top:0;margin-bottom:20px}#bootstrap-theme dt, #bootstrap-theme dd{line-height:1.5384615385}#bootstrap-theme dt{font-weight:700}#bootstrap-theme dd{margin-left:0}#bootstrap-theme .dl-horizontal dd:before, #bootstrap-theme .dl-horizontal dd:after{display:table;content:" "}#bootstrap-theme .dl-horizontal dd:after{clear:both}@media (min-width: 768px){#bootstrap-theme .dl-horizontal dt{float:left;width:230px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}#bootstrap-theme .dl-horizontal dd{margin-left:250px}}#bootstrap-theme abbr[title], #bootstrap-theme abbr[data-original-title]{cursor:help}#bootstrap-theme .initialism{font-size:90%}#bootstrap-theme blockquote{padding:10px 20px;margin:0 0 20px;font-size:16.25px;border-left:5px solid #0071bd}#bootstrap-theme blockquote p:last-child, #bootstrap-theme blockquote ul:last-child, #bootstrap-theme blockquote ol:last-child{margin-bottom:0}#bootstrap-theme blockquote footer, #bootstrap-theme blockquote small, #bootstrap-theme blockquote .small{display:block;font-size:80%;line-height:1.5384615385;color:#4d4d69}#bootstrap-theme blockquote footer:before, #bootstrap-theme blockquote small:before, #bootstrap-theme blockquote .small:before{content:"\2014 \00A0"}#bootstrap-theme .blockquote-reverse, #bootstrap-theme blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #0071bd;border-left:0}#bootstrap-theme .blockquote-reverse footer:before, #bootstrap-theme .blockquote-reverse small:before, #bootstrap-theme .blockquote-reverse .small:before, #bootstrap-theme blockquote.pull-right footer:before, #bootstrap-theme blockquote.pull-right small:before, #bootstrap-theme blockquote.pull-right .small:before{content:""}#bootstrap-theme .blockquote-reverse footer:after, #bootstrap-theme .blockquote-reverse small:after, #bootstrap-theme .blockquote-reverse .small:after, #bootstrap-theme blockquote.pull-right footer:after, #bootstrap-theme blockquote.pull-right small:after, #bootstrap-theme blockquote.pull-right .small:after{content:"\00A0 \2014"}#bootstrap-theme address{margin-bottom:20px;font-style:normal;line-height:1.5384615385}#bootstrap-theme code, #bootstrap-theme kbd, #bootstrap-theme pre, #bootstrap-theme samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}#bootstrap-theme code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:2px}#bootstrap-theme kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:2px;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}#bootstrap-theme kbd kbd{padding:0;font-size:100%;font-weight:700;box-shadow:none}#bootstrap-theme pre{display:block;padding:9.5px;margin:0 0 10px;font-size:12px;line-height:1.5384615385;color:#4d4d69;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:2px}#bootstrap-theme pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}#bootstrap-theme .pre-scrollable{max-height:340px;overflow-y:scroll}#bootstrap-theme .container{padding-right:8px;padding-left:8px;margin-right:auto;margin-left:auto}#bootstrap-theme .container:before, #bootstrap-theme .container:after{display:table;content:" "}#bootstrap-theme .container:after{clear:both}@media (min-width: 768px){#bootstrap-theme .container{width:736px}}@media (min-width: 992px){#bootstrap-theme .container{width:956px}}@media (min-width: 1200px){#bootstrap-theme .container{width:1156px}}#bootstrap-theme .container-fluid{padding-right:8px;padding-left:8px;margin-right:auto;margin-left:auto}#bootstrap-theme .container-fluid:before, #bootstrap-theme .container-fluid:after{display:table;content:" "}#bootstrap-theme .container-fluid:after{clear:both}#bootstrap-theme .row{margin-right:-8px;margin-left:-8px}#bootstrap-theme .row:before, #bootstrap-theme .row:after{display:table;content:" "}#bootstrap-theme .row:after{clear:both}#bootstrap-theme .row-no-gutters{margin-right:0;margin-left:0}#bootstrap-theme .row-no-gutters [class*="col-"]{padding-right:0;padding-left:0}#bootstrap-theme .col-xs-1, #bootstrap-theme .col-sm-1, #bootstrap-theme .col-md-1, #bootstrap-theme .col-lg-1, #bootstrap-theme .col-xs-2, #bootstrap-theme .col-sm-2, #bootstrap-theme .col-md-2, #bootstrap-theme .col-lg-2, #bootstrap-theme .col-xs-3, #bootstrap-theme .col-sm-3, #bootstrap-theme .col-md-3, #bootstrap-theme .col-lg-3, #bootstrap-theme .col-xs-4, #bootstrap-theme .col-sm-4, #bootstrap-theme .col-md-4, #bootstrap-theme .col-lg-4, #bootstrap-theme .col-xs-5, #bootstrap-theme .col-sm-5, #bootstrap-theme .col-md-5, #bootstrap-theme .col-lg-5, #bootstrap-theme .col-xs-6, #bootstrap-theme .col-sm-6, #bootstrap-theme .col-md-6, #bootstrap-theme .col-lg-6, #bootstrap-theme .col-xs-7, #bootstrap-theme .col-sm-7, #bootstrap-theme .col-md-7, #bootstrap-theme .col-lg-7, #bootstrap-theme .col-xs-8, #bootstrap-theme .col-sm-8, #bootstrap-theme .col-md-8, #bootstrap-theme .col-lg-8, #bootstrap-theme .col-xs-9, #bootstrap-theme .col-sm-9, #bootstrap-theme .col-md-9, #bootstrap-theme .col-lg-9, #bootstrap-theme .col-xs-10, #bootstrap-theme .col-sm-10, #bootstrap-theme .col-md-10, #bootstrap-theme .col-lg-10, #bootstrap-theme .col-xs-11, #bootstrap-theme .col-sm-11, #bootstrap-theme .col-md-11, #bootstrap-theme .col-lg-11, #bootstrap-theme .col-xs-12, #bootstrap-theme .col-sm-12, #bootstrap-theme .col-md-12, #bootstrap-theme .col-lg-12{position:relative;min-height:1px;padding-right:8px;padding-left:8px}#bootstrap-theme .col-xs-1, #bootstrap-theme .col-xs-2, #bootstrap-theme .col-xs-3, #bootstrap-theme .col-xs-4, #bootstrap-theme .col-xs-5, #bootstrap-theme .col-xs-6, #bootstrap-theme .col-xs-7, #bootstrap-theme .col-xs-8, #bootstrap-theme .col-xs-9, #bootstrap-theme .col-xs-10, #bootstrap-theme .col-xs-11, #bootstrap-theme .col-xs-12{float:left}#bootstrap-theme .col-xs-1{width:8.3333333333%}#bootstrap-theme .col-xs-2{width:16.6666666667%}#bootstrap-theme .col-xs-3{width:25%}#bootstrap-theme .col-xs-4{width:33.3333333333%}#bootstrap-theme .col-xs-5{width:41.6666666667%}#bootstrap-theme .col-xs-6{width:50%}#bootstrap-theme .col-xs-7{width:58.3333333333%}#bootstrap-theme .col-xs-8{width:66.6666666667%}#bootstrap-theme .col-xs-9{width:75%}#bootstrap-theme .col-xs-10{width:83.3333333333%}#bootstrap-theme .col-xs-11{width:91.6666666667%}#bootstrap-theme .col-xs-12{width:100%}#bootstrap-theme .col-xs-pull-0{right:auto}#bootstrap-theme .col-xs-pull-1{right:8.3333333333%}#bootstrap-theme .col-xs-pull-2{right:16.6666666667%}#bootstrap-theme .col-xs-pull-3{right:25%}#bootstrap-theme .col-xs-pull-4{right:33.3333333333%}#bootstrap-theme .col-xs-pull-5{right:41.6666666667%}#bootstrap-theme .col-xs-pull-6{right:50%}#bootstrap-theme .col-xs-pull-7{right:58.3333333333%}#bootstrap-theme .col-xs-pull-8{right:66.6666666667%}#bootstrap-theme .col-xs-pull-9{right:75%}#bootstrap-theme .col-xs-pull-10{right:83.3333333333%}#bootstrap-theme .col-xs-pull-11{right:91.6666666667%}#bootstrap-theme .col-xs-pull-12{right:100%}#bootstrap-theme .col-xs-push-0{left:auto}#bootstrap-theme .col-xs-push-1{left:8.3333333333%}#bootstrap-theme .col-xs-push-2{left:16.6666666667%}#bootstrap-theme .col-xs-push-3{left:25%}#bootstrap-theme .col-xs-push-4{left:33.3333333333%}#bootstrap-theme .col-xs-push-5{left:41.6666666667%}#bootstrap-theme .col-xs-push-6{left:50%}#bootstrap-theme .col-xs-push-7{left:58.3333333333%}#bootstrap-theme .col-xs-push-8{left:66.6666666667%}#bootstrap-theme .col-xs-push-9{left:75%}#bootstrap-theme .col-xs-push-10{left:83.3333333333%}#bootstrap-theme .col-xs-push-11{left:91.6666666667%}#bootstrap-theme .col-xs-push-12{left:100%}#bootstrap-theme .col-xs-offset-0{margin-left:0%}#bootstrap-theme .col-xs-offset-1{margin-left:8.3333333333%}#bootstrap-theme .col-xs-offset-2{margin-left:16.6666666667%}#bootstrap-theme .col-xs-offset-3{margin-left:25%}#bootstrap-theme .col-xs-offset-4{margin-left:33.3333333333%}#bootstrap-theme .col-xs-offset-5{margin-left:41.6666666667%}#bootstrap-theme .col-xs-offset-6{margin-left:50%}#bootstrap-theme .col-xs-offset-7{margin-left:58.3333333333%}#bootstrap-theme .col-xs-offset-8{margin-left:66.6666666667%}#bootstrap-theme .col-xs-offset-9{margin-left:75%}#bootstrap-theme .col-xs-offset-10{margin-left:83.3333333333%}#bootstrap-theme .col-xs-offset-11{margin-left:91.6666666667%}#bootstrap-theme .col-xs-offset-12{margin-left:100%}@media (min-width: 768px){#bootstrap-theme .col-sm-1, #bootstrap-theme .col-sm-2, #bootstrap-theme .col-sm-3, #bootstrap-theme .col-sm-4, #bootstrap-theme .col-sm-5, #bootstrap-theme .col-sm-6, #bootstrap-theme .col-sm-7, #bootstrap-theme .col-sm-8, #bootstrap-theme .col-sm-9, #bootstrap-theme .col-sm-10, #bootstrap-theme .col-sm-11, #bootstrap-theme .col-sm-12{float:left}#bootstrap-theme .col-sm-1{width:8.3333333333%}#bootstrap-theme .col-sm-2{width:16.6666666667%}#bootstrap-theme .col-sm-3{width:25%}#bootstrap-theme .col-sm-4{width:33.3333333333%}#bootstrap-theme .col-sm-5{width:41.6666666667%}#bootstrap-theme .col-sm-6{width:50%}#bootstrap-theme .col-sm-7{width:58.3333333333%}#bootstrap-theme .col-sm-8{width:66.6666666667%}#bootstrap-theme .col-sm-9{width:75%}#bootstrap-theme .col-sm-10{width:83.3333333333%}#bootstrap-theme .col-sm-11{width:91.6666666667%}#bootstrap-theme .col-sm-12{width:100%}#bootstrap-theme .col-sm-pull-0{right:auto}#bootstrap-theme .col-sm-pull-1{right:8.3333333333%}#bootstrap-theme .col-sm-pull-2{right:16.6666666667%}#bootstrap-theme .col-sm-pull-3{right:25%}#bootstrap-theme .col-sm-pull-4{right:33.3333333333%}#bootstrap-theme .col-sm-pull-5{right:41.6666666667%}#bootstrap-theme .col-sm-pull-6{right:50%}#bootstrap-theme .col-sm-pull-7{right:58.3333333333%}#bootstrap-theme .col-sm-pull-8{right:66.6666666667%}#bootstrap-theme .col-sm-pull-9{right:75%}#bootstrap-theme .col-sm-pull-10{right:83.3333333333%}#bootstrap-theme .col-sm-pull-11{right:91.6666666667%}#bootstrap-theme .col-sm-pull-12{right:100%}#bootstrap-theme .col-sm-push-0{left:auto}#bootstrap-theme .col-sm-push-1{left:8.3333333333%}#bootstrap-theme .col-sm-push-2{left:16.6666666667%}#bootstrap-theme .col-sm-push-3{left:25%}#bootstrap-theme .col-sm-push-4{left:33.3333333333%}#bootstrap-theme .col-sm-push-5{left:41.6666666667%}#bootstrap-theme .col-sm-push-6{left:50%}#bootstrap-theme .col-sm-push-7{left:58.3333333333%}#bootstrap-theme .col-sm-push-8{left:66.6666666667%}#bootstrap-theme .col-sm-push-9{left:75%}#bootstrap-theme .col-sm-push-10{left:83.3333333333%}#bootstrap-theme .col-sm-push-11{left:91.6666666667%}#bootstrap-theme .col-sm-push-12{left:100%}#bootstrap-theme .col-sm-offset-0{margin-left:0%}#bootstrap-theme .col-sm-offset-1{margin-left:8.3333333333%}#bootstrap-theme .col-sm-offset-2{margin-left:16.6666666667%}#bootstrap-theme .col-sm-offset-3{margin-left:25%}#bootstrap-theme .col-sm-offset-4{margin-left:33.3333333333%}#bootstrap-theme .col-sm-offset-5{margin-left:41.6666666667%}#bootstrap-theme .col-sm-offset-6{margin-left:50%}#bootstrap-theme .col-sm-offset-7{margin-left:58.3333333333%}#bootstrap-theme .col-sm-offset-8{margin-left:66.6666666667%}#bootstrap-theme .col-sm-offset-9{margin-left:75%}#bootstrap-theme .col-sm-offset-10{margin-left:83.3333333333%}#bootstrap-theme .col-sm-offset-11{margin-left:91.6666666667%}#bootstrap-theme .col-sm-offset-12{margin-left:100%}}@media (min-width: 992px){#bootstrap-theme .col-md-1, #bootstrap-theme .col-md-2, #bootstrap-theme .col-md-3, #bootstrap-theme .col-md-4, #bootstrap-theme .col-md-5, #bootstrap-theme .col-md-6, #bootstrap-theme .col-md-7, #bootstrap-theme .col-md-8, #bootstrap-theme .col-md-9, #bootstrap-theme .col-md-10, #bootstrap-theme .col-md-11, #bootstrap-theme .col-md-12{float:left}#bootstrap-theme .col-md-1{width:8.3333333333%}#bootstrap-theme .col-md-2{width:16.6666666667%}#bootstrap-theme .col-md-3{width:25%}#bootstrap-theme .col-md-4{width:33.3333333333%}#bootstrap-theme .col-md-5{width:41.6666666667%}#bootstrap-theme .col-md-6{width:50%}#bootstrap-theme .col-md-7{width:58.3333333333%}#bootstrap-theme .col-md-8{width:66.6666666667%}#bootstrap-theme .col-md-9{width:75%}#bootstrap-theme .col-md-10{width:83.3333333333%}#bootstrap-theme .col-md-11{width:91.6666666667%}#bootstrap-theme .col-md-12{width:100%}#bootstrap-theme .col-md-pull-0{right:auto}#bootstrap-theme .col-md-pull-1{right:8.3333333333%}#bootstrap-theme .col-md-pull-2{right:16.6666666667%}#bootstrap-theme .col-md-pull-3{right:25%}#bootstrap-theme .col-md-pull-4{right:33.3333333333%}#bootstrap-theme .col-md-pull-5{right:41.6666666667%}#bootstrap-theme .col-md-pull-6{right:50%}#bootstrap-theme .col-md-pull-7{right:58.3333333333%}#bootstrap-theme .col-md-pull-8{right:66.6666666667%}#bootstrap-theme .col-md-pull-9{right:75%}#bootstrap-theme .col-md-pull-10{right:83.3333333333%}#bootstrap-theme .col-md-pull-11{right:91.6666666667%}#bootstrap-theme .col-md-pull-12{right:100%}#bootstrap-theme .col-md-push-0{left:auto}#bootstrap-theme .col-md-push-1{left:8.3333333333%}#bootstrap-theme .col-md-push-2{left:16.6666666667%}#bootstrap-theme .col-md-push-3{left:25%}#bootstrap-theme .col-md-push-4{left:33.3333333333%}#bootstrap-theme .col-md-push-5{left:41.6666666667%}#bootstrap-theme .col-md-push-6{left:50%}#bootstrap-theme .col-md-push-7{left:58.3333333333%}#bootstrap-theme .col-md-push-8{left:66.6666666667%}#bootstrap-theme .col-md-push-9{left:75%}#bootstrap-theme .col-md-push-10{left:83.3333333333%}#bootstrap-theme .col-md-push-11{left:91.6666666667%}#bootstrap-theme .col-md-push-12{left:100%}#bootstrap-theme .col-md-offset-0{margin-left:0%}#bootstrap-theme .col-md-offset-1{margin-left:8.3333333333%}#bootstrap-theme .col-md-offset-2{margin-left:16.6666666667%}#bootstrap-theme .col-md-offset-3{margin-left:25%}#bootstrap-theme .col-md-offset-4{margin-left:33.3333333333%}#bootstrap-theme .col-md-offset-5{margin-left:41.6666666667%}#bootstrap-theme .col-md-offset-6{margin-left:50%}#bootstrap-theme .col-md-offset-7{margin-left:58.3333333333%}#bootstrap-theme .col-md-offset-8{margin-left:66.6666666667%}#bootstrap-theme .col-md-offset-9{margin-left:75%}#bootstrap-theme .col-md-offset-10{margin-left:83.3333333333%}#bootstrap-theme .col-md-offset-11{margin-left:91.6666666667%}#bootstrap-theme .col-md-offset-12{margin-left:100%}}@media (min-width: 1200px){#bootstrap-theme .col-lg-1, #bootstrap-theme .col-lg-2, #bootstrap-theme .col-lg-3, #bootstrap-theme .col-lg-4, #bootstrap-theme .col-lg-5, #bootstrap-theme .col-lg-6, #bootstrap-theme .col-lg-7, #bootstrap-theme .col-lg-8, #bootstrap-theme .col-lg-9, #bootstrap-theme .col-lg-10, #bootstrap-theme .col-lg-11, #bootstrap-theme .col-lg-12{float:left}#bootstrap-theme .col-lg-1{width:8.3333333333%}#bootstrap-theme .col-lg-2{width:16.6666666667%}#bootstrap-theme .col-lg-3{width:25%}#bootstrap-theme .col-lg-4{width:33.3333333333%}#bootstrap-theme .col-lg-5{width:41.6666666667%}#bootstrap-theme .col-lg-6{width:50%}#bootstrap-theme .col-lg-7{width:58.3333333333%}#bootstrap-theme .col-lg-8{width:66.6666666667%}#bootstrap-theme .col-lg-9{width:75%}#bootstrap-theme .col-lg-10{width:83.3333333333%}#bootstrap-theme .col-lg-11{width:91.6666666667%}#bootstrap-theme .col-lg-12{width:100%}#bootstrap-theme .col-lg-pull-0{right:auto}#bootstrap-theme .col-lg-pull-1{right:8.3333333333%}#bootstrap-theme .col-lg-pull-2{right:16.6666666667%}#bootstrap-theme .col-lg-pull-3{right:25%}#bootstrap-theme .col-lg-pull-4{right:33.3333333333%}#bootstrap-theme .col-lg-pull-5{right:41.6666666667%}#bootstrap-theme .col-lg-pull-6{right:50%}#bootstrap-theme .col-lg-pull-7{right:58.3333333333%}#bootstrap-theme .col-lg-pull-8{right:66.6666666667%}#bootstrap-theme .col-lg-pull-9{right:75%}#bootstrap-theme .col-lg-pull-10{right:83.3333333333%}#bootstrap-theme .col-lg-pull-11{right:91.6666666667%}#bootstrap-theme .col-lg-pull-12{right:100%}#bootstrap-theme .col-lg-push-0{left:auto}#bootstrap-theme .col-lg-push-1{left:8.3333333333%}#bootstrap-theme .col-lg-push-2{left:16.6666666667%}#bootstrap-theme .col-lg-push-3{left:25%}#bootstrap-theme .col-lg-push-4{left:33.3333333333%}#bootstrap-theme .col-lg-push-5{left:41.6666666667%}#bootstrap-theme .col-lg-push-6{left:50%}#bootstrap-theme .col-lg-push-7{left:58.3333333333%}#bootstrap-theme .col-lg-push-8{left:66.6666666667%}#bootstrap-theme .col-lg-push-9{left:75%}#bootstrap-theme .col-lg-push-10{left:83.3333333333%}#bootstrap-theme .col-lg-push-11{left:91.6666666667%}#bootstrap-theme .col-lg-push-12{left:100%}#bootstrap-theme .col-lg-offset-0{margin-left:0%}#bootstrap-theme .col-lg-offset-1{margin-left:8.3333333333%}#bootstrap-theme .col-lg-offset-2{margin-left:16.6666666667%}#bootstrap-theme .col-lg-offset-3{margin-left:25%}#bootstrap-theme .col-lg-offset-4{margin-left:33.3333333333%}#bootstrap-theme .col-lg-offset-5{margin-left:41.6666666667%}#bootstrap-theme .col-lg-offset-6{margin-left:50%}#bootstrap-theme .col-lg-offset-7{margin-left:58.3333333333%}#bootstrap-theme .col-lg-offset-8{margin-left:66.6666666667%}#bootstrap-theme .col-lg-offset-9{margin-left:75%}#bootstrap-theme .col-lg-offset-10{margin-left:83.3333333333%}#bootstrap-theme .col-lg-offset-11{margin-left:91.6666666667%}#bootstrap-theme .col-lg-offset-12{margin-left:100%}}#bootstrap-theme table{background-color:#fff}#bootstrap-theme table col[class*="col-"]{position:static;display:table-column;float:none}#bootstrap-theme table td[class*="col-"], #bootstrap-theme table th[class*="col-"]{position:static;display:table-cell;float:none}#bootstrap-theme caption{padding-top:10px 20px;padding-bottom:10px 20px;color:#aab2b9;text-align:left}#bootstrap-theme th{text-align:left}#bootstrap-theme .table{width:100%;max-width:100%;margin-bottom:20px}#bootstrap-theme .table>thead>tr>th, #bootstrap-theme .table>thead>tr>td, #bootstrap-theme .table>tbody>tr>th, #bootstrap-theme .table>tbody>tr>td, #bootstrap-theme .table>tfoot>tr>th, #bootstrap-theme .table>tfoot>tr>td{padding:10px 20px;line-height:1.5384615385;vertical-align:top;border-top:1px solid #e8eef0}#bootstrap-theme .table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #e8eef0}#bootstrap-theme .table>caption+thead>tr:first-child>th, #bootstrap-theme .table>caption+thead>tr:first-child>td, #bootstrap-theme .table>colgroup+thead>tr:first-child>th, #bootstrap-theme .table>colgroup+thead>tr:first-child>td, #bootstrap-theme .table>thead:first-child>tr:first-child>th, #bootstrap-theme .table>thead:first-child>tr:first-child>td{border-top:0}#bootstrap-theme .table>tbody+tbody{border-top:2px solid #e8eef0}#bootstrap-theme .table .table{background-color:#e8eef0}#bootstrap-theme .table-condensed>thead>tr>th, #bootstrap-theme .table-condensed>thead>tr>td, #bootstrap-theme .table-condensed>tbody>tr>th, #bootstrap-theme .table-condensed>tbody>tr>td, #bootstrap-theme .table-condensed>tfoot>tr>th, #bootstrap-theme .table-condensed>tfoot>tr>td{padding:5px}#bootstrap-theme .table-bordered{border:1px solid #e8eef0}#bootstrap-theme .table-bordered>thead>tr>th, #bootstrap-theme .table-bordered>thead>tr>td, #bootstrap-theme .table-bordered>tbody>tr>th, #bootstrap-theme .table-bordered>tbody>tr>td, #bootstrap-theme .table-bordered>tfoot>tr>th, #bootstrap-theme .table-bordered>tfoot>tr>td{border:1px solid #e8eef0}#bootstrap-theme .table-bordered>thead>tr>th, #bootstrap-theme .table-bordered>thead>tr>td{border-bottom-width:2px}#bootstrap-theme .table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}#bootstrap-theme .table-hover>tbody>tr:hover{background-color:#f5f5f5}#bootstrap-theme .table>thead>tr>td.active, #bootstrap-theme .table>thead>tr>th.active, #bootstrap-theme .table>thead>tr.active>td, #bootstrap-theme .table>thead>tr.active>th, #bootstrap-theme .table>tbody>tr>td.active, #bootstrap-theme .table>tbody>tr>th.active, #bootstrap-theme .table>tbody>tr.active>td, #bootstrap-theme .table>tbody>tr.active>th, #bootstrap-theme .table>tfoot>tr>td.active, #bootstrap-theme .table>tfoot>tr>th.active, #bootstrap-theme .table>tfoot>tr.active>td, #bootstrap-theme .table>tfoot>tr.active>th{background-color:#f5f5f5}#bootstrap-theme .table-hover>tbody>tr>td.active:hover, #bootstrap-theme .table-hover>tbody>tr>th.active:hover, #bootstrap-theme .table-hover>tbody>tr.active:hover>td, #bootstrap-theme .table-hover>tbody>tr:hover>.active, #bootstrap-theme .table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}#bootstrap-theme .table>thead>tr>td.success, #bootstrap-theme .table>thead>tr>th.success, #bootstrap-theme .table>thead>tr.success>td, #bootstrap-theme .table>thead>tr.success>th, #bootstrap-theme .table>tbody>tr>td.success, #bootstrap-theme .table>tbody>tr>th.success, #bootstrap-theme .table>tbody>tr.success>td, #bootstrap-theme .table>tbody>tr.success>th, #bootstrap-theme .table>tfoot>tr>td.success, #bootstrap-theme .table>tfoot>tr>th.success, #bootstrap-theme .table>tfoot>tr.success>td, #bootstrap-theme .table>tfoot>tr.success>th{background-color:#dff0d8}#bootstrap-theme .table-hover>tbody>tr>td.success:hover, #bootstrap-theme .table-hover>tbody>tr>th.success:hover, #bootstrap-theme .table-hover>tbody>tr.success:hover>td, #bootstrap-theme .table-hover>tbody>tr:hover>.success, #bootstrap-theme .table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}#bootstrap-theme .table>thead>tr>td.info, #bootstrap-theme .table>thead>tr>th.info, #bootstrap-theme .table>thead>tr.info>td, #bootstrap-theme .table>thead>tr.info>th, #bootstrap-theme .table>tbody>tr>td.info, #bootstrap-theme .table>tbody>tr>th.info, #bootstrap-theme .table>tbody>tr.info>td, #bootstrap-theme .table>tbody>tr.info>th, #bootstrap-theme .table>tfoot>tr>td.info, #bootstrap-theme .table>tfoot>tr>th.info, #bootstrap-theme .table>tfoot>tr.info>td, #bootstrap-theme .table>tfoot>tr.info>th{background-color:#d9edf7}#bootstrap-theme .table-hover>tbody>tr>td.info:hover, #bootstrap-theme .table-hover>tbody>tr>th.info:hover, #bootstrap-theme .table-hover>tbody>tr.info:hover>td, #bootstrap-theme .table-hover>tbody>tr:hover>.info, #bootstrap-theme .table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}#bootstrap-theme .table>thead>tr>td.warning, #bootstrap-theme .table>thead>tr>th.warning, #bootstrap-theme .table>thead>tr.warning>td, #bootstrap-theme .table>thead>tr.warning>th, #bootstrap-theme .table>tbody>tr>td.warning, #bootstrap-theme .table>tbody>tr>th.warning, #bootstrap-theme .table>tbody>tr.warning>td, #bootstrap-theme .table>tbody>tr.warning>th, #bootstrap-theme .table>tfoot>tr>td.warning, #bootstrap-theme .table>tfoot>tr>th.warning, #bootstrap-theme .table>tfoot>tr.warning>td, #bootstrap-theme .table>tfoot>tr.warning>th{background-color:#fcf8e3}#bootstrap-theme .table-hover>tbody>tr>td.warning:hover, #bootstrap-theme .table-hover>tbody>tr>th.warning:hover, #bootstrap-theme .table-hover>tbody>tr.warning:hover>td, #bootstrap-theme .table-hover>tbody>tr:hover>.warning, #bootstrap-theme .table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}#bootstrap-theme .table>thead>tr>td.danger, #bootstrap-theme .table>thead>tr>th.danger, #bootstrap-theme .table>thead>tr.danger>td, #bootstrap-theme .table>thead>tr.danger>th, #bootstrap-theme .table>tbody>tr>td.danger, #bootstrap-theme .table>tbody>tr>th.danger, #bootstrap-theme .table>tbody>tr.danger>td, #bootstrap-theme .table>tbody>tr.danger>th, #bootstrap-theme .table>tfoot>tr>td.danger, #bootstrap-theme .table>tfoot>tr>th.danger, #bootstrap-theme .table>tfoot>tr.danger>td, #bootstrap-theme .table>tfoot>tr.danger>th{background-color:#f2dede}#bootstrap-theme .table-hover>tbody>tr>td.danger:hover, #bootstrap-theme .table-hover>tbody>tr>th.danger:hover, #bootstrap-theme .table-hover>tbody>tr.danger:hover>td, #bootstrap-theme .table-hover>tbody>tr:hover>.danger, #bootstrap-theme .table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}#bootstrap-theme .table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width: 767px){#bootstrap-theme .table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #e8eef0}#bootstrap-theme .table-responsive>.table{margin-bottom:0}#bootstrap-theme .table-responsive>.table>thead>tr>th, #bootstrap-theme .table-responsive>.table>thead>tr>td, #bootstrap-theme .table-responsive>.table>tbody>tr>th, #bootstrap-theme .table-responsive>.table>tbody>tr>td, #bootstrap-theme .table-responsive>.table>tfoot>tr>th, #bootstrap-theme .table-responsive>.table>tfoot>tr>td{white-space:nowrap}#bootstrap-theme .table-responsive>.table-bordered{border:0}#bootstrap-theme .table-responsive>.table-bordered>thead>tr>th:first-child, #bootstrap-theme .table-responsive>.table-bordered>thead>tr>td:first-child, #bootstrap-theme .table-responsive>.table-bordered>tbody>tr>th:first-child, #bootstrap-theme .table-responsive>.table-bordered>tbody>tr>td:first-child, #bootstrap-theme .table-responsive>.table-bordered>tfoot>tr>th:first-child, #bootstrap-theme .table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}#bootstrap-theme .table-responsive>.table-bordered>thead>tr>th:last-child, #bootstrap-theme .table-responsive>.table-bordered>thead>tr>td:last-child, #bootstrap-theme .table-responsive>.table-bordered>tbody>tr>th:last-child, #bootstrap-theme .table-responsive>.table-bordered>tbody>tr>td:last-child, #bootstrap-theme .table-responsive>.table-bordered>tfoot>tr>th:last-child, #bootstrap-theme .table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}#bootstrap-theme .table-responsive>.table-bordered>tbody>tr:last-child>th, #bootstrap-theme .table-responsive>.table-bordered>tbody>tr:last-child>td, #bootstrap-theme .table-responsive>.table-bordered>tfoot>tr:last-child>th, #bootstrap-theme .table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}#bootstrap-theme fieldset{min-width:0;padding:0;margin:0;border:0}#bootstrap-theme legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:19.5px;line-height:inherit;color:#4d4d69;border:0;border-bottom:1px solid #e5e5e5}#bootstrap-theme label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}#bootstrap-theme input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-appearance:none;appearance:none}#bootstrap-theme input[type="radio"], #bootstrap-theme input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}#bootstrap-theme input[type="radio"][disabled], #bootstrap-theme input[type="radio"].disabled, #bootstrap-theme fieldset[disabled] input[type="radio"], #bootstrap-theme input[type="checkbox"][disabled], #bootstrap-theme input[type="checkbox"].disabled, #bootstrap-theme fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}#bootstrap-theme input[type="file"]{display:block}#bootstrap-theme input[type="range"]{display:block;width:100%}#bootstrap-theme select[multiple], #bootstrap-theme select[size]{height:auto}#bootstrap-theme input[type="file"]:focus, #bootstrap-theme input[type="radio"]:focus, #bootstrap-theme input[type="checkbox"]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}#bootstrap-theme output{display:block;padding-top:5px;font-size:13px;line-height:1.5384615385;color:#464354}#bootstrap-theme .form-control{display:block;width:100%;height:30px;padding:4px 10px;font-size:13px;line-height:1.5384615385;color:#464354;background-color:#fff;background-image:none;border:1px solid #c2cfd8;border-radius:2px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s;-o-transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s;transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s}#bootstrap-theme .form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6)}#bootstrap-theme .form-control::-moz-placeholder{color:#9494a5;opacity:1}#bootstrap-theme .form-control:-ms-input-placeholder{color:#9494a5}#bootstrap-theme .form-control::-webkit-input-placeholder{color:#9494a5}#bootstrap-theme .form-control::-ms-expand{background-color:transparent;border:0}#bootstrap-theme .form-control[disabled], #bootstrap-theme .form-control[readonly], #bootstrap-theme fieldset[disabled] .form-control{background-color:#f3f6f7;opacity:1}#bootstrap-theme .form-control[disabled], #bootstrap-theme fieldset[disabled] .form-control{cursor:not-allowed}#bootstrap-theme textarea.form-control{height:auto}@media screen and (-webkit-min-device-pixel-ratio: 0){#bootstrap-theme input[type="date"].form-control, #bootstrap-theme input[type="time"].form-control, #bootstrap-theme input[type="datetime-local"].form-control, #bootstrap-theme input[type="month"].form-control{line-height:30px}#bootstrap-theme input[type="date"].input-sm, #bootstrap-theme .input-group-sm>input.form-control[type="date"], #bootstrap-theme .input-group-sm>input.input-group-addon[type="date"], #bootstrap-theme .input-group-sm>.input-group-btn>input.btn[type="date"], #bootstrap-theme .input-group-sm input[type="date"], #bootstrap-theme input[type="time"].input-sm, #bootstrap-theme .input-group-sm>input.form-control[type="time"], #bootstrap-theme .input-group-sm>input.input-group-addon[type="time"], #bootstrap-theme .input-group-sm>.input-group-btn>input.btn[type="time"], #bootstrap-theme .input-group-sm input[type="time"], #bootstrap-theme input[type="datetime-local"].input-sm, #bootstrap-theme .input-group-sm>input.form-control[type="datetime-local"], #bootstrap-theme .input-group-sm>input.input-group-addon[type="datetime-local"], #bootstrap-theme .input-group-sm>.input-group-btn>input.btn[type="datetime-local"], #bootstrap-theme .input-group-sm input[type="datetime-local"], #bootstrap-theme input[type="month"].input-sm, #bootstrap-theme .input-group-sm>input.form-control[type="month"], #bootstrap-theme .input-group-sm>input.input-group-addon[type="month"], #bootstrap-theme .input-group-sm>.input-group-btn>input.btn[type="month"], #bootstrap-theme .input-group-sm input[type="month"]{line-height:24px}#bootstrap-theme input[type="date"].input-lg, #bootstrap-theme .input-group-lg>input.form-control[type="date"], #bootstrap-theme .input-group-lg>input.input-group-addon[type="date"], #bootstrap-theme .input-group-lg>.input-group-btn>input.btn[type="date"], #bootstrap-theme .input-group-lg input[type="date"], #bootstrap-theme input[type="time"].input-lg, #bootstrap-theme .input-group-lg>input.form-control[type="time"], #bootstrap-theme .input-group-lg>input.input-group-addon[type="time"], #bootstrap-theme .input-group-lg>.input-group-btn>input.btn[type="time"], #bootstrap-theme .input-group-lg input[type="time"], #bootstrap-theme input[type="datetime-local"].input-lg, #bootstrap-theme .input-group-lg>input.form-control[type="datetime-local"], #bootstrap-theme .input-group-lg>input.input-group-addon[type="datetime-local"], #bootstrap-theme .input-group-lg>.input-group-btn>input.btn[type="datetime-local"], #bootstrap-theme .input-group-lg input[type="datetime-local"], #bootstrap-theme input[type="month"].input-lg, #bootstrap-theme .input-group-lg>input.form-control[type="month"], #bootstrap-theme .input-group-lg>input.input-group-addon[type="month"], #bootstrap-theme .input-group-lg>.input-group-btn>input.btn[type="month"], #bootstrap-theme .input-group-lg input[type="month"]{line-height:45px}}#bootstrap-theme .form-group{margin-bottom:15px}#bootstrap-theme .radio, #bootstrap-theme .checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}#bootstrap-theme .radio.disabled label, #bootstrap-theme fieldset[disabled] .radio label, #bootstrap-theme .checkbox.disabled label, #bootstrap-theme fieldset[disabled] .checkbox label{cursor:not-allowed}#bootstrap-theme .radio label, #bootstrap-theme .checkbox label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}#bootstrap-theme .radio input[type="radio"], #bootstrap-theme .radio-inline input[type="radio"], #bootstrap-theme .checkbox input[type="checkbox"], #bootstrap-theme .checkbox-inline input[type="checkbox"]{position:absolute;margin-top:4px \9;margin-left:-20px}#bootstrap-theme .radio+.radio, #bootstrap-theme .checkbox+.checkbox{margin-top:-5px}#bootstrap-theme .radio-inline, #bootstrap-theme .checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}#bootstrap-theme .radio-inline.disabled, #bootstrap-theme fieldset[disabled] .radio-inline, #bootstrap-theme .checkbox-inline.disabled, #bootstrap-theme fieldset[disabled] .checkbox-inline{cursor:not-allowed}#bootstrap-theme .radio-inline+.radio-inline, #bootstrap-theme .checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}#bootstrap-theme .form-control-static{min-height:33px;padding-top:5px;padding-bottom:5px;margin-bottom:0}#bootstrap-theme .form-control-static.input-lg, #bootstrap-theme .input-group-lg>.form-control-static.form-control, #bootstrap-theme .input-group-lg>.form-control-static.input-group-addon, #bootstrap-theme .input-group-lg>.input-group-btn>.form-control-static.btn, #bootstrap-theme .form-control-static.input-sm, #bootstrap-theme .input-group-sm>.form-control-static.form-control, #bootstrap-theme .input-group-sm>.form-control-static.input-group-addon, #bootstrap-theme .input-group-sm>.input-group-btn>.form-control-static.btn{padding-right:0;padding-left:0}#bootstrap-theme .input-sm, #bootstrap-theme .input-group-sm>.form-control, #bootstrap-theme .input-group-sm>.input-group-addon, #bootstrap-theme .input-group-sm>.input-group-btn>.btn{height:24px;padding:2px 10px;font-size:12px;line-height:1.5;border-radius:2px}#bootstrap-theme select.input-sm, #bootstrap-theme .input-group-sm>select.form-control, #bootstrap-theme .input-group-sm>select.input-group-addon, #bootstrap-theme .input-group-sm>.input-group-btn>select.btn{height:24px;line-height:24px}#bootstrap-theme textarea.input-sm, #bootstrap-theme .input-group-sm>textarea.form-control, #bootstrap-theme .input-group-sm>textarea.input-group-addon, #bootstrap-theme .input-group-sm>.input-group-btn>textarea.btn, #bootstrap-theme select[multiple].input-sm, #bootstrap-theme .input-group-sm>select.form-control[multiple], #bootstrap-theme .input-group-sm>select.input-group-addon[multiple], #bootstrap-theme .input-group-sm>.input-group-btn>select.btn[multiple]{height:auto}#bootstrap-theme .form-group-sm .form-control{height:24px;padding:2px 10px;font-size:12px;line-height:1.5;border-radius:2px}#bootstrap-theme .form-group-sm select.form-control{height:24px;line-height:24px}#bootstrap-theme .form-group-sm textarea.form-control, #bootstrap-theme .form-group-sm select[multiple].form-control{height:auto}#bootstrap-theme .form-group-sm .form-control-static{height:24px;min-height:32px;padding:3px 10px;font-size:12px;line-height:1.5}#bootstrap-theme .input-lg, #bootstrap-theme .input-group-lg>.form-control, #bootstrap-theme .input-group-lg>.input-group-addon, #bootstrap-theme .input-group-lg>.input-group-btn>.btn{height:45px;padding:10px 16px;font-size:17px;line-height:1.3333333;border-radius:2px}#bootstrap-theme select.input-lg, #bootstrap-theme .input-group-lg>select.form-control, #bootstrap-theme .input-group-lg>select.input-group-addon, #bootstrap-theme .input-group-lg>.input-group-btn>select.btn{height:45px;line-height:45px}#bootstrap-theme textarea.input-lg, #bootstrap-theme .input-group-lg>textarea.form-control, #bootstrap-theme .input-group-lg>textarea.input-group-addon, #bootstrap-theme .input-group-lg>.input-group-btn>textarea.btn, #bootstrap-theme select[multiple].input-lg, #bootstrap-theme .input-group-lg>select.form-control[multiple], #bootstrap-theme .input-group-lg>select.input-group-addon[multiple], #bootstrap-theme .input-group-lg>.input-group-btn>select.btn[multiple]{height:auto}#bootstrap-theme .form-group-lg .form-control{height:45px;padding:10px 16px;font-size:17px;line-height:1.3333333;border-radius:2px}#bootstrap-theme .form-group-lg select.form-control{height:45px;line-height:45px}#bootstrap-theme .form-group-lg textarea.form-control, #bootstrap-theme .form-group-lg select[multiple].form-control{height:auto}#bootstrap-theme .form-group-lg .form-control-static{height:45px;min-height:37px;padding:11px 16px;font-size:17px;line-height:1.3333333}#bootstrap-theme .has-feedback{position:relative}#bootstrap-theme .has-feedback .form-control{padding-right:37.5px}#bootstrap-theme .form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:30px;height:30px;line-height:30px;text-align:center;pointer-events:none}#bootstrap-theme .input-lg+.form-control-feedback, #bootstrap-theme .input-group-lg>.form-control+.form-control-feedback, #bootstrap-theme .input-group-lg>.input-group-addon+.form-control-feedback, #bootstrap-theme .input-group-lg>.input-group-btn>.btn+.form-control-feedback, #bootstrap-theme .input-group-lg+.form-control-feedback, #bootstrap-theme .form-group-lg .form-control+.form-control-feedback{width:45px;height:45px;line-height:45px}#bootstrap-theme .input-sm+.form-control-feedback, #bootstrap-theme .input-group-sm>.form-control+.form-control-feedback, #bootstrap-theme .input-group-sm>.input-group-addon+.form-control-feedback, #bootstrap-theme .input-group-sm>.input-group-btn>.btn+.form-control-feedback, #bootstrap-theme .input-group-sm+.form-control-feedback, #bootstrap-theme .form-group-sm .form-control+.form-control-feedback{width:24px;height:24px;line-height:24px}#bootstrap-theme .has-success .help-block, #bootstrap-theme .has-success .control-label, #bootstrap-theme .has-success .radio, #bootstrap-theme .has-success .checkbox, #bootstrap-theme .has-success .radio-inline, #bootstrap-theme .has-success .checkbox-inline, #bootstrap-theme .has-success.radio label, #bootstrap-theme .has-success.checkbox label, #bootstrap-theme .has-success.radio-inline label, #bootstrap-theme .has-success.checkbox-inline label{color:#4d994d}#bootstrap-theme .has-success .form-control{border-color:#4d994d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}#bootstrap-theme .has-success .form-control:focus{border-color:#3c773c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #89c389;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #89c389}#bootstrap-theme .has-success .input-group-addon{color:#4d994d;background-color:#dff0d8;border-color:#4d994d}#bootstrap-theme .has-success .form-control-feedback{color:#4d994d}#bootstrap-theme .has-warning .help-block, #bootstrap-theme .has-warning .control-label, #bootstrap-theme .has-warning .radio, #bootstrap-theme .has-warning .checkbox, #bootstrap-theme .has-warning .radio-inline, #bootstrap-theme .has-warning .checkbox-inline, #bootstrap-theme .has-warning.radio label, #bootstrap-theme .has-warning.checkbox label, #bootstrap-theme .has-warning.radio-inline label, #bootstrap-theme .has-warning.checkbox-inline label{color:#bf5900}#bootstrap-theme .has-warning .form-control{border-color:#bf5900;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}#bootstrap-theme .has-warning .form-control:focus{border-color:#8c4100;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ff8b26;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ff8b26}#bootstrap-theme .has-warning .input-group-addon{color:#bf5900;background-color:#fcf8e3;border-color:#bf5900}#bootstrap-theme .has-warning .form-control-feedback{color:#bf5900}#bootstrap-theme .has-error .help-block, #bootstrap-theme .has-error .control-label, #bootstrap-theme .has-error .radio, #bootstrap-theme .has-error .checkbox, #bootstrap-theme .has-error .radio-inline, #bootstrap-theme .has-error .checkbox-inline, #bootstrap-theme .has-error.radio label, #bootstrap-theme .has-error.checkbox label, #bootstrap-theme .has-error.radio-inline label, #bootstrap-theme .has-error.checkbox-inline label{color:#cf3458}#bootstrap-theme .has-error .form-control{border-color:#cf3458;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}#bootstrap-theme .has-error .form-control:focus{border-color:#a82846;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #e3869c;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #e3869c}#bootstrap-theme .has-error .input-group-addon{color:#cf3458;background-color:#f2dede;border-color:#cf3458}#bootstrap-theme .has-error .form-control-feedback{color:#cf3458}#bootstrap-theme .has-feedback label ~ .form-control-feedback{top:25px}#bootstrap-theme .has-feedback label.sr-only ~ .form-control-feedback{top:0}#bootstrap-theme .help-block{display:block;margin-top:5px;margin-bottom:10px;color:#8b8baa}@media (min-width: 768px){#bootstrap-theme .form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}#bootstrap-theme .form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}#bootstrap-theme .form-inline .form-control-static{display:inline-block}#bootstrap-theme .form-inline .input-group{display:inline-table;vertical-align:middle}#bootstrap-theme .form-inline .input-group .input-group-addon, #bootstrap-theme .form-inline .input-group .input-group-btn, #bootstrap-theme .form-inline .input-group .form-control{width:auto}#bootstrap-theme .form-inline .input-group>.form-control{width:100%}#bootstrap-theme .form-inline .control-label{margin-bottom:0;vertical-align:middle}#bootstrap-theme .form-inline .radio, #bootstrap-theme .form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}#bootstrap-theme .form-inline .radio label, #bootstrap-theme .form-inline .checkbox label{padding-left:0}#bootstrap-theme .form-inline .radio input[type="radio"], #bootstrap-theme .form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}#bootstrap-theme .form-inline .has-feedback .form-control-feedback{top:0}}#bootstrap-theme .form-horizontal .radio, #bootstrap-theme .form-horizontal .checkbox, #bootstrap-theme .form-horizontal .radio-inline, #bootstrap-theme .form-horizontal .checkbox-inline{padding-top:5px;margin-top:0;margin-bottom:0}#bootstrap-theme .form-horizontal .radio, #bootstrap-theme .form-horizontal .checkbox{min-height:25px}#bootstrap-theme .form-horizontal .form-group{margin-right:-8px;margin-left:-8px}#bootstrap-theme .form-horizontal .form-group:before, #bootstrap-theme .form-horizontal .form-group:after{display:table;content:" "}#bootstrap-theme .form-horizontal .form-group:after{clear:both}@media (min-width: 768px){#bootstrap-theme .form-horizontal .control-label{padding-top:5px;margin-bottom:0;text-align:right}}#bootstrap-theme .form-horizontal .has-feedback .form-control-feedback{right:8px}@media (min-width: 768px){#bootstrap-theme .form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:17px}}@media (min-width: 768px){#bootstrap-theme .form-horizontal .form-group-sm .control-label{padding-top:3px;font-size:12px}}#bootstrap-theme .btn{display:inline-block;margin-bottom:0;font-weight:500;text-align:center;white-space:nowrap;vertical-align:middle;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;padding:4px 10px;font-size:13px;line-height:1.5384615385;border-radius:2px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}#bootstrap-theme .btn:focus, #bootstrap-theme .btn.focus, #bootstrap-theme .btn:active:focus, #bootstrap-theme .btn:active.focus, #bootstrap-theme .btn.active:focus, #bootstrap-theme .btn.active.focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}#bootstrap-theme .btn:hover, #bootstrap-theme .btn:focus, #bootstrap-theme .btn.focus{color:#464354;text-decoration:none}#bootstrap-theme .btn:active, #bootstrap-theme .btn.active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}#bootstrap-theme .btn.disabled, #bootstrap-theme .btn[disabled], #bootstrap-theme fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);opacity:.65;-webkit-box-shadow:none;box-shadow:none}#bootstrap-theme a.btn.disabled, #bootstrap-theme fieldset[disabled] a.btn{pointer-events:none}#bootstrap-theme .btn-default{color:#464354;background-color:#fff;border-color:#fff}#bootstrap-theme .btn-default:focus, #bootstrap-theme .btn-default.focus{color:#464354;background-color:#e6e5e5;border-color:#bfbfbf}#bootstrap-theme .btn-default:hover{color:#464354;background-color:#e6e5e5;border-color:#e0e0e0}#bootstrap-theme .btn-default:active, #bootstrap-theme .btn-default.active, #bootstrap-theme .open>.btn-default.dropdown-toggle{color:#464354;background-color:#e6e5e5;background-image:none;border-color:#e0e0e0}#bootstrap-theme .btn-default:active:hover, #bootstrap-theme .btn-default:active:focus, #bootstrap-theme .btn-default:active.focus, #bootstrap-theme .btn-default.active:hover, #bootstrap-theme .btn-default.active:focus, #bootstrap-theme .btn-default.active.focus, #bootstrap-theme .open>.btn-default.dropdown-toggle:hover, #bootstrap-theme .open>.btn-default.dropdown-toggle:focus, #bootstrap-theme .open>.btn-default.dropdown-toggle.focus{color:#464354;background-color:#d4d4d4;border-color:#bfbfbf}#bootstrap-theme .btn-default.disabled:hover, #bootstrap-theme .btn-default.disabled:focus, #bootstrap-theme .btn-default.disabled.focus, #bootstrap-theme .btn-default[disabled]:hover, #bootstrap-theme .btn-default[disabled]:focus, #bootstrap-theme .btn-default[disabled].focus, #bootstrap-theme fieldset[disabled] .btn-default:hover, #bootstrap-theme fieldset[disabled] .btn-default:focus, #bootstrap-theme fieldset[disabled] .btn-default.focus{background-color:#fff;border-color:#fff}#bootstrap-theme .btn-default .badge{color:#fff;background-color:#464354}#bootstrap-theme .btn-primary{color:#fff;background-color:#0071bd;border-color:#0062a4}#bootstrap-theme .btn-primary:focus, #bootstrap-theme .btn-primary.focus{color:#fff;background-color:#00538a;border-color:#001624}#bootstrap-theme .btn-primary:hover{color:#fff;background-color:#00538a;border-color:#003d66}#bootstrap-theme .btn-primary:active, #bootstrap-theme .btn-primary.active, #bootstrap-theme .open>.btn-primary.dropdown-toggle{color:#fff;background-color:#00538a;background-image:none;border-color:#003d66}#bootstrap-theme .btn-primary:active:hover, #bootstrap-theme .btn-primary:active:focus, #bootstrap-theme .btn-primary:active.focus, #bootstrap-theme .btn-primary.active:hover, #bootstrap-theme .btn-primary.active:focus, #bootstrap-theme .btn-primary.active.focus, #bootstrap-theme .open>.btn-primary.dropdown-toggle:hover, #bootstrap-theme .open>.btn-primary.dropdown-toggle:focus, #bootstrap-theme .open>.btn-primary.dropdown-toggle.focus{color:#fff;background-color:#003d66;border-color:#001624}#bootstrap-theme .btn-primary.disabled:hover, #bootstrap-theme .btn-primary.disabled:focus, #bootstrap-theme .btn-primary.disabled.focus, #bootstrap-theme .btn-primary[disabled]:hover, #bootstrap-theme .btn-primary[disabled]:focus, #bootstrap-theme .btn-primary[disabled].focus, #bootstrap-theme fieldset[disabled] .btn-primary:hover, #bootstrap-theme fieldset[disabled] .btn-primary:focus, #bootstrap-theme fieldset[disabled] .btn-primary.focus{background-color:#0071bd;border-color:#0062a4}#bootstrap-theme .btn-primary .badge{color:#0071bd;background-color:#fff}#bootstrap-theme .btn-success{color:#464354;background-color:#44cb7e;border-color:#35c071}#bootstrap-theme .btn-success:focus, #bootstrap-theme .btn-success.focus{color:#464354;background-color:#30ac65;border-color:#1a5c36}#bootstrap-theme .btn-success:hover{color:#464354;background-color:#30ac65;border-color:#289055}#bootstrap-theme .btn-success:active, #bootstrap-theme .btn-success.active, #bootstrap-theme .open>.btn-success.dropdown-toggle{color:#464354;background-color:#30ac65;background-image:none;border-color:#289055}#bootstrap-theme .btn-success:active:hover, #bootstrap-theme .btn-success:active:focus, #bootstrap-theme .btn-success:active.focus, #bootstrap-theme .btn-success.active:hover, #bootstrap-theme .btn-success.active:focus, #bootstrap-theme .btn-success.active.focus, #bootstrap-theme .open>.btn-success.dropdown-toggle:hover, #bootstrap-theme .open>.btn-success.dropdown-toggle:focus, #bootstrap-theme .open>.btn-success.dropdown-toggle.focus{color:#464354;background-color:#289055;border-color:#1a5c36}#bootstrap-theme .btn-success.disabled:hover, #bootstrap-theme .btn-success.disabled:focus, #bootstrap-theme .btn-success.disabled.focus, #bootstrap-theme .btn-success[disabled]:hover, #bootstrap-theme .btn-success[disabled]:focus, #bootstrap-theme .btn-success[disabled].focus, #bootstrap-theme fieldset[disabled] .btn-success:hover, #bootstrap-theme fieldset[disabled] .btn-success:focus, #bootstrap-theme fieldset[disabled] .btn-success.focus{background-color:#44cb7e;border-color:#35c071}#bootstrap-theme .btn-success .badge{color:#44cb7e;background-color:#464354}#bootstrap-theme .btn-info{color:#fff;background-color:#0071bd;border-color:#0062a4}#bootstrap-theme .btn-info:focus, #bootstrap-theme .btn-info.focus{color:#fff;background-color:#00538a;border-color:#001624}#bootstrap-theme .btn-info:hover{color:#fff;background-color:#00538a;border-color:#003d66}#bootstrap-theme .btn-info:active, #bootstrap-theme .btn-info.active, #bootstrap-theme .open>.btn-info.dropdown-toggle{color:#fff;background-color:#00538a;background-image:none;border-color:#003d66}#bootstrap-theme .btn-info:active:hover, #bootstrap-theme .btn-info:active:focus, #bootstrap-theme .btn-info:active.focus, #bootstrap-theme .btn-info.active:hover, #bootstrap-theme .btn-info.active:focus, #bootstrap-theme .btn-info.active.focus, #bootstrap-theme .open>.btn-info.dropdown-toggle:hover, #bootstrap-theme .open>.btn-info.dropdown-toggle:focus, #bootstrap-theme .open>.btn-info.dropdown-toggle.focus{color:#fff;background-color:#003d66;border-color:#001624}#bootstrap-theme .btn-info.disabled:hover, #bootstrap-theme .btn-info.disabled:focus, #bootstrap-theme .btn-info.disabled.focus, #bootstrap-theme .btn-info[disabled]:hover, #bootstrap-theme .btn-info[disabled]:focus, #bootstrap-theme .btn-info[disabled].focus, #bootstrap-theme fieldset[disabled] .btn-info:hover, #bootstrap-theme fieldset[disabled] .btn-info:focus, #bootstrap-theme fieldset[disabled] .btn-info.focus{background-color:#0071bd;border-color:#0062a4}#bootstrap-theme .btn-info .badge{color:#0071bd;background-color:#fff}#bootstrap-theme .btn-warning{color:#464354;background-color:#e6ab5e;border-color:#e39f48}#bootstrap-theme .btn-warning:focus, #bootstrap-theme .btn-warning.focus{color:#464354;background-color:#df9432;border-color:#945e17}#bootstrap-theme .btn-warning:hover{color:#464354;background-color:#df9432;border-color:#cd8220}#bootstrap-theme .btn-warning:active, #bootstrap-theme .btn-warning.active, #bootstrap-theme .open>.btn-warning.dropdown-toggle{color:#464354;background-color:#df9432;background-image:none;border-color:#cd8220}#bootstrap-theme .btn-warning:active:hover, #bootstrap-theme .btn-warning:active:focus, #bootstrap-theme .btn-warning:active.focus, #bootstrap-theme .btn-warning.active:hover, #bootstrap-theme .btn-warning.active:focus, #bootstrap-theme .btn-warning.active.focus, #bootstrap-theme .open>.btn-warning.dropdown-toggle:hover, #bootstrap-theme .open>.btn-warning.dropdown-toggle:focus, #bootstrap-theme .open>.btn-warning.dropdown-toggle.focus{color:#464354;background-color:#cd8220;border-color:#945e17}#bootstrap-theme .btn-warning.disabled:hover, #bootstrap-theme .btn-warning.disabled:focus, #bootstrap-theme .btn-warning.disabled.focus, #bootstrap-theme .btn-warning[disabled]:hover, #bootstrap-theme .btn-warning[disabled]:focus, #bootstrap-theme .btn-warning[disabled].focus, #bootstrap-theme fieldset[disabled] .btn-warning:hover, #bootstrap-theme fieldset[disabled] .btn-warning:focus, #bootstrap-theme fieldset[disabled] .btn-warning.focus{background-color:#e6ab5e;border-color:#e39f48}#bootstrap-theme .btn-warning .badge{color:#e6ab5e;background-color:#464354}#bootstrap-theme .btn-danger{color:#fff;background-color:#cf3458;border-color:#bd2d4e}#bootstrap-theme .btn-danger:focus, #bootstrap-theme .btn-danger.focus{color:#fff;background-color:#a82846;border-color:#561423}#bootstrap-theme .btn-danger:hover{color:#fff;background-color:#a82846;border-color:#8b213a}#bootstrap-theme .btn-danger:active, #bootstrap-theme .btn-danger.active, #bootstrap-theme .open>.btn-danger.dropdown-toggle{color:#fff;background-color:#a82846;background-image:none;border-color:#8b213a}#bootstrap-theme .btn-danger:active:hover, #bootstrap-theme .btn-danger:active:focus, #bootstrap-theme .btn-danger:active.focus, #bootstrap-theme .btn-danger.active:hover, #bootstrap-theme .btn-danger.active:focus, #bootstrap-theme .btn-danger.active.focus, #bootstrap-theme .open>.btn-danger.dropdown-toggle:hover, #bootstrap-theme .open>.btn-danger.dropdown-toggle:focus, #bootstrap-theme .open>.btn-danger.dropdown-toggle.focus{color:#fff;background-color:#8b213a;border-color:#561423}#bootstrap-theme .btn-danger.disabled:hover, #bootstrap-theme .btn-danger.disabled:focus, #bootstrap-theme .btn-danger.disabled.focus, #bootstrap-theme .btn-danger[disabled]:hover, #bootstrap-theme .btn-danger[disabled]:focus, #bootstrap-theme .btn-danger[disabled].focus, #bootstrap-theme fieldset[disabled] .btn-danger:hover, #bootstrap-theme fieldset[disabled] .btn-danger:focus, #bootstrap-theme fieldset[disabled] .btn-danger.focus{background-color:#cf3458;border-color:#bd2d4e}#bootstrap-theme .btn-danger .badge{color:#cf3458;background-color:#fff}#bootstrap-theme .btn-link{font-weight:400;color:#0071bd;border-radius:0}#bootstrap-theme .btn-link, #bootstrap-theme .btn-link:active, #bootstrap-theme .btn-link.active, #bootstrap-theme .btn-link[disabled], #bootstrap-theme fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}#bootstrap-theme .btn-link, #bootstrap-theme .btn-link:hover, #bootstrap-theme .btn-link:focus, #bootstrap-theme .btn-link:active{border-color:transparent}#bootstrap-theme .btn-link:hover, #bootstrap-theme .btn-link:focus{color:#004371;text-decoration:underline;background-color:transparent}#bootstrap-theme .btn-link[disabled]:hover, #bootstrap-theme .btn-link[disabled]:focus, #bootstrap-theme fieldset[disabled] .btn-link:hover, #bootstrap-theme fieldset[disabled] .btn-link:focus{color:#e8eef0;text-decoration:none}#bootstrap-theme .btn-lg, #bootstrap-theme .btn-group-lg>.btn{padding:10px 16px;font-size:17px;line-height:1.3333333;border-radius:6px}#bootstrap-theme .btn-sm, #bootstrap-theme .btn-group-sm>.btn{padding:2px 10px;font-size:12px;line-height:1.5;border-radius:2px}#bootstrap-theme .btn-xs, #bootstrap-theme .btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:2px}#bootstrap-theme .btn-block{display:block;width:100%}#bootstrap-theme .btn-block+.btn-block{margin-top:5px}#bootstrap-theme input[type="submit"].btn-block, #bootstrap-theme input[type="reset"].btn-block, #bootstrap-theme input[type="button"].btn-block{width:100%}#bootstrap-theme .fade{opacity:0;-webkit-transition:opacity 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.15s linear}#bootstrap-theme .fade.in{opacity:1}#bootstrap-theme .collapse{display:none}#bootstrap-theme .collapse.in{display:block}#bootstrap-theme tr.collapse.in{display:table-row}#bootstrap-theme tbody.collapse.in{display:table-row-group}#bootstrap-theme .collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-property:height,visibility;transition-property:height,visibility;-webkit-transition-duration:0.35s;transition-duration:0.35s;-webkit-transition-timing-function:ease;transition-timing-function:ease}#bootstrap-theme .caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid \9;border-right:4px solid transparent;border-left:4px solid transparent}#bootstrap-theme .dropup, #bootstrap-theme .dropdown{position:relative}#bootstrap-theme .dropdown-toggle:focus{outline:0}#bootstrap-theme .dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:13px;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);border-radius:2px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.175);box-shadow:0 6px 12px rgba(0,0,0,0.175)}#bootstrap-theme .dropdown-menu.pull-right{right:0;left:auto}#bootstrap-theme .dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}#bootstrap-theme .dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.5384615385;color:#4d4d69;white-space:nowrap}#bootstrap-theme .dropdown-menu>li>a:hover, #bootstrap-theme .dropdown-menu>li>a:focus{color:#42425a;text-decoration:none;background-color:#f3f6f7}#bootstrap-theme .dropdown-menu>.active>a, #bootstrap-theme .dropdown-menu>.active>a:hover, #bootstrap-theme .dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;background-color:#0071bd;outline:0}#bootstrap-theme .dropdown-menu>.disabled>a, #bootstrap-theme .dropdown-menu>.disabled>a:hover, #bootstrap-theme .dropdown-menu>.disabled>a:focus{color:#e8eef0}#bootstrap-theme .dropdown-menu>.disabled>a:hover, #bootstrap-theme .dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false)}#bootstrap-theme .open>.dropdown-menu{display:block}#bootstrap-theme .open>a{outline:0}#bootstrap-theme .dropdown-menu-right{right:0;left:auto}#bootstrap-theme .dropdown-menu-left{right:auto;left:0}#bootstrap-theme .dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.5384615385;color:#464354;white-space:nowrap}#bootstrap-theme .dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}#bootstrap-theme .pull-right>.dropdown-menu{right:0;left:auto}#bootstrap-theme .dropup .caret, #bootstrap-theme .navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid \9}#bootstrap-theme .dropup .dropdown-menu, #bootstrap-theme .navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width: 768px){#bootstrap-theme .navbar-right .dropdown-menu{right:0;left:auto}#bootstrap-theme .navbar-right .dropdown-menu-left{left:0;right:auto}}#bootstrap-theme .btn-group, #bootstrap-theme .btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}#bootstrap-theme .btn-group>.btn, #bootstrap-theme .btn-group-vertical>.btn{position:relative;float:left}#bootstrap-theme .btn-group>.btn:hover, #bootstrap-theme .btn-group>.btn:focus, #bootstrap-theme .btn-group>.btn:active, #bootstrap-theme .btn-group>.btn.active, #bootstrap-theme .btn-group-vertical>.btn:hover, #bootstrap-theme .btn-group-vertical>.btn:focus, #bootstrap-theme .btn-group-vertical>.btn:active, #bootstrap-theme .btn-group-vertical>.btn.active{z-index:2}#bootstrap-theme .btn-group .btn+.btn, #bootstrap-theme .btn-group .btn+.btn-group, #bootstrap-theme .btn-group .btn-group+.btn, #bootstrap-theme .btn-group .btn-group+.btn-group{margin-left:-1px}#bootstrap-theme .btn-toolbar{margin-left:-5px}#bootstrap-theme .btn-toolbar:before, #bootstrap-theme .btn-toolbar:after{display:table;content:" "}#bootstrap-theme .btn-toolbar:after{clear:both}#bootstrap-theme .btn-toolbar .btn, #bootstrap-theme .btn-toolbar .btn-group, #bootstrap-theme .btn-toolbar .input-group{float:left}#bootstrap-theme .btn-toolbar>.btn, #bootstrap-theme .btn-toolbar>.btn-group, #bootstrap-theme .btn-toolbar>.input-group{margin-left:5px}#bootstrap-theme .btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}#bootstrap-theme .btn-group>.btn:first-child{margin-left:0}#bootstrap-theme .btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}#bootstrap-theme .btn-group>.btn:last-child:not(:first-child), #bootstrap-theme .btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}#bootstrap-theme .btn-group>.btn-group{float:left}#bootstrap-theme .btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}#bootstrap-theme .btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child, #bootstrap-theme .btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}#bootstrap-theme .btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}#bootstrap-theme .btn-group .dropdown-toggle:active, #bootstrap-theme .btn-group.open .dropdown-toggle{outline:0}#bootstrap-theme .btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}#bootstrap-theme .btn-group>.btn-lg+.dropdown-toggle, #bootstrap-theme .btn-group-lg.btn-group>.btn+.dropdown-toggle{padding-right:12px;padding-left:12px}#bootstrap-theme .btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}#bootstrap-theme .btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}#bootstrap-theme .btn .caret{margin-left:0}#bootstrap-theme .btn-lg .caret, #bootstrap-theme .btn-group-lg>.btn .caret{border-width:5px 5px 0;border-bottom-width:0}#bootstrap-theme .dropup .btn-lg .caret, #bootstrap-theme .dropup .btn-group-lg>.btn .caret{border-width:0 5px 5px}#bootstrap-theme .btn-group-vertical>.btn, #bootstrap-theme .btn-group-vertical>.btn-group, #bootstrap-theme .btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}#bootstrap-theme .btn-group-vertical>.btn-group:before, #bootstrap-theme .btn-group-vertical>.btn-group:after{display:table;content:" "}#bootstrap-theme .btn-group-vertical>.btn-group:after{clear:both}#bootstrap-theme .btn-group-vertical>.btn-group>.btn{float:none}#bootstrap-theme .btn-group-vertical>.btn+.btn, #bootstrap-theme .btn-group-vertical>.btn+.btn-group, #bootstrap-theme .btn-group-vertical>.btn-group+.btn, #bootstrap-theme .btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}#bootstrap-theme .btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}#bootstrap-theme .btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:2px;border-top-right-radius:2px;border-bottom-right-radius:0;border-bottom-left-radius:0}#bootstrap-theme .btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:2px;border-bottom-left-radius:2px}#bootstrap-theme .btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}#bootstrap-theme .btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child, #bootstrap-theme .btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}#bootstrap-theme .btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}#bootstrap-theme .btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}#bootstrap-theme .btn-group-justified>.btn, #bootstrap-theme .btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}#bootstrap-theme .btn-group-justified>.btn-group .btn{width:100%}#bootstrap-theme .btn-group-justified>.btn-group .dropdown-menu{left:auto}#bootstrap-theme [data-toggle="buttons"]>.btn input[type="radio"], #bootstrap-theme [data-toggle="buttons"]>.btn input[type="checkbox"], #bootstrap-theme [data-toggle="buttons"]>.btn-group>.btn input[type="radio"], #bootstrap-theme [data-toggle="buttons"]>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}#bootstrap-theme .input-group{position:relative;display:table;border-collapse:separate}#bootstrap-theme .input-group[class*="col-"]{float:none;padding-right:0;padding-left:0}#bootstrap-theme .input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}#bootstrap-theme .input-group .form-control:focus{z-index:3}#bootstrap-theme .input-group-addon, #bootstrap-theme .input-group-btn, #bootstrap-theme .input-group .form-control{display:table-cell}#bootstrap-theme .input-group-addon:not(:first-child):not(:last-child), #bootstrap-theme .input-group-btn:not(:first-child):not(:last-child), #bootstrap-theme .input-group .form-control:not(:first-child):not(:last-child){border-radius:0}#bootstrap-theme .input-group-addon, #bootstrap-theme .input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}#bootstrap-theme .input-group-addon{padding:4px 10px;font-size:13px;font-weight:400;line-height:1;color:#464354;text-align:center;background-color:#f3f6f7;border:1px solid #c2cfd8;border-radius:2px}#bootstrap-theme .input-group-addon.input-sm, #bootstrap-theme .input-group-sm>.input-group-addon, #bootstrap-theme .input-group-sm>.input-group-btn>.input-group-addon.btn{padding:2px 10px;font-size:12px;border-radius:2px}#bootstrap-theme .input-group-addon.input-lg, #bootstrap-theme .input-group-lg>.input-group-addon, #bootstrap-theme .input-group-lg>.input-group-btn>.input-group-addon.btn{padding:10px 16px;font-size:17px;border-radius:2px}#bootstrap-theme .input-group-addon input[type="radio"], #bootstrap-theme .input-group-addon input[type="checkbox"]{margin-top:0}#bootstrap-theme .input-group .form-control:first-child, #bootstrap-theme .input-group-addon:first-child, #bootstrap-theme .input-group-btn:first-child>.btn, #bootstrap-theme .input-group-btn:first-child>.btn-group>.btn, #bootstrap-theme .input-group-btn:first-child>.dropdown-toggle, #bootstrap-theme .input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle), #bootstrap-theme .input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}#bootstrap-theme .input-group-addon:first-child{border-right:0}#bootstrap-theme .input-group .form-control:last-child, #bootstrap-theme .input-group-addon:last-child, #bootstrap-theme .input-group-btn:last-child>.btn, #bootstrap-theme .input-group-btn:last-child>.btn-group>.btn, #bootstrap-theme .input-group-btn:last-child>.dropdown-toggle, #bootstrap-theme .input-group-btn:first-child>.btn:not(:first-child), #bootstrap-theme .input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}#bootstrap-theme .input-group-addon:last-child{border-left:0}#bootstrap-theme .input-group-btn{position:relative;font-size:0;white-space:nowrap}#bootstrap-theme .input-group-btn>.btn{position:relative}#bootstrap-theme .input-group-btn>.btn+.btn{margin-left:-1px}#bootstrap-theme .input-group-btn>.btn:hover, #bootstrap-theme .input-group-btn>.btn:focus, #bootstrap-theme .input-group-btn>.btn:active{z-index:2}#bootstrap-theme .input-group-btn:first-child>.btn, #bootstrap-theme .input-group-btn:first-child>.btn-group{margin-right:-1px}#bootstrap-theme .input-group-btn:last-child>.btn, #bootstrap-theme .input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}#bootstrap-theme .nav{padding-left:0;margin-bottom:0;list-style:none}#bootstrap-theme .nav:before, #bootstrap-theme .nav:after{display:table;content:" "}#bootstrap-theme .nav:after{clear:both}#bootstrap-theme .nav>li{position:relative;display:block}#bootstrap-theme .nav>li>a{position:relative;display:block;padding:10px 20px}#bootstrap-theme .nav>li>a:hover, #bootstrap-theme .nav>li>a:focus{text-decoration:none;background-color:#f3f6f7}#bootstrap-theme .nav>li.disabled>a{color:#bcbcc8}#bootstrap-theme .nav>li.disabled>a:hover, #bootstrap-theme .nav>li.disabled>a:focus{color:#bcbcc8;text-decoration:none;cursor:not-allowed;background-color:transparent}#bootstrap-theme .nav .open>a, #bootstrap-theme .nav .open>a:hover, #bootstrap-theme .nav .open>a:focus{background-color:#f3f6f7;border-color:#0071bd}#bootstrap-theme .nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}#bootstrap-theme .nav>li>a>img{max-width:none}#bootstrap-theme .nav-tabs{border-bottom:1px solid #d3dee2}#bootstrap-theme .nav-tabs>li{float:left;margin-bottom:-1px}#bootstrap-theme .nav-tabs>li>a{margin-right:2px;line-height:1.5384615385;border:1px solid transparent;border-radius:2px 2px 0 0}#bootstrap-theme .nav-tabs>li>a:hover{border-color:#f3f6f7 #f3f6f7 #d3dee2}#bootstrap-theme .nav-tabs>li.active>a, #bootstrap-theme .nav-tabs>li.active>a:hover, #bootstrap-theme .nav-tabs>li.active>a:focus{color:#0071bd;cursor:default;background-color:#fff;border:1px solid #d3dee2;border-bottom-color:transparent}#bootstrap-theme .nav-pills>li{float:left}#bootstrap-theme .nav-pills>li>a{border-radius:2px}#bootstrap-theme .nav-pills>li+li{margin-left:2px}#bootstrap-theme .nav-pills>li.active>a, #bootstrap-theme .nav-pills>li.active>a:hover, #bootstrap-theme .nav-pills>li.active>a:focus{color:#fff;background-color:#0071bd}#bootstrap-theme .nav-stacked>li{float:none}#bootstrap-theme .nav-stacked>li+li{margin-top:2px;margin-left:0}#bootstrap-theme .nav-justified, #bootstrap-theme .nav-tabs.nav-justified{width:100%}#bootstrap-theme .nav-justified>li, #bootstrap-theme .nav-tabs.nav-justified>li{float:none}#bootstrap-theme .nav-justified>li>a, #bootstrap-theme .nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}#bootstrap-theme .nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width: 768px){#bootstrap-theme .nav-justified>li, #bootstrap-theme .nav-tabs.nav-justified>li{display:table-cell;width:1%}#bootstrap-theme .nav-justified>li>a, #bootstrap-theme .nav-tabs.nav-justified>li>a{margin-bottom:0}}#bootstrap-theme .nav-tabs-justified, #bootstrap-theme .nav-tabs.nav-justified{border-bottom:0}#bootstrap-theme .nav-tabs-justified>li>a, #bootstrap-theme .nav-tabs.nav-justified>li>a{margin-right:0;border-radius:2px}#bootstrap-theme .nav-tabs-justified>.active>a, #bootstrap-theme .nav-tabs.nav-justified>.active>a, #bootstrap-theme .nav-tabs-justified>.active>a:hover, #bootstrap-theme .nav-tabs.nav-justified>.active>a:hover, #bootstrap-theme .nav-tabs-justified>.active>a:focus, #bootstrap-theme .nav-tabs.nav-justified>.active>a:focus{border:1px solid #ddd}@media (min-width: 768px){#bootstrap-theme .nav-tabs-justified>li>a, #bootstrap-theme .nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:2px 2px 0 0}#bootstrap-theme .nav-tabs-justified>.active>a, #bootstrap-theme .nav-tabs.nav-justified>.active>a, #bootstrap-theme .nav-tabs-justified>.active>a:hover, #bootstrap-theme .nav-tabs.nav-justified>.active>a:hover, #bootstrap-theme .nav-tabs-justified>.active>a:focus, #bootstrap-theme .nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#e8eef0}}#bootstrap-theme .tab-content>.tab-pane{display:none}#bootstrap-theme .tab-content>.active{display:block}#bootstrap-theme .nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}#bootstrap-theme .navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}#bootstrap-theme .navbar:before, #bootstrap-theme .navbar:after{display:table;content:" "}#bootstrap-theme .navbar:after{clear:both}@media (min-width: 768px){#bootstrap-theme .navbar{border-radius:2px}}#bootstrap-theme .navbar-header:before, #bootstrap-theme .navbar-header:after{display:table;content:" "}#bootstrap-theme .navbar-header:after{clear:both}@media (min-width: 768px){#bootstrap-theme .navbar-header{float:left}}#bootstrap-theme .navbar-collapse{padding-right:8px;padding-left:8px;overflow-x:visible;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}#bootstrap-theme .navbar-collapse:before, #bootstrap-theme .navbar-collapse:after{display:table;content:" "}#bootstrap-theme .navbar-collapse:after{clear:both}#bootstrap-theme .navbar-collapse.in{overflow-y:auto}@media (min-width: 768px){#bootstrap-theme .navbar-collapse{width:auto;border-top:0;box-shadow:none}#bootstrap-theme .navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}#bootstrap-theme .navbar-collapse.in{overflow-y:visible}#bootstrap-theme .navbar-fixed-top .navbar-collapse, #bootstrap-theme .navbar-static-top .navbar-collapse, #bootstrap-theme .navbar-fixed-bottom .navbar-collapse{padding-right:0;padding-left:0}}#bootstrap-theme .navbar-fixed-top, #bootstrap-theme .navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}#bootstrap-theme .navbar-fixed-top .navbar-collapse, #bootstrap-theme .navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width: 480px) and (orientation: landscape){#bootstrap-theme .navbar-fixed-top .navbar-collapse, #bootstrap-theme .navbar-fixed-bottom .navbar-collapse{max-height:200px}}@media (min-width: 768px){#bootstrap-theme .navbar-fixed-top, #bootstrap-theme .navbar-fixed-bottom{border-radius:0}}#bootstrap-theme .navbar-fixed-top{top:0;border-width:0 0 1px}#bootstrap-theme .navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}#bootstrap-theme .container>.navbar-header, #bootstrap-theme .container>.navbar-collapse, #bootstrap-theme .container-fluid>.navbar-header, #bootstrap-theme .container-fluid>.navbar-collapse{margin-right:-8px;margin-left:-8px}@media (min-width: 768px){#bootstrap-theme .container>.navbar-header, #bootstrap-theme .container>.navbar-collapse, #bootstrap-theme .container-fluid>.navbar-header, #bootstrap-theme .container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}#bootstrap-theme .navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width: 768px){#bootstrap-theme .navbar-static-top{border-radius:0}}#bootstrap-theme .navbar-brand{float:left;height:50px;padding:15px 8px;font-size:17px;line-height:20px}#bootstrap-theme .navbar-brand:hover, #bootstrap-theme .navbar-brand:focus{text-decoration:none}#bootstrap-theme .navbar-brand>img{display:block}@media (min-width: 768px){#bootstrap-theme .navbar>.container .navbar-brand, #bootstrap-theme .navbar>.container-fluid .navbar-brand{margin-left:-8px}}#bootstrap-theme .navbar-toggle{position:relative;float:right;padding:9px 10px;margin-right:8px;margin-top:8px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:2px}#bootstrap-theme .navbar-toggle:focus{outline:0}#bootstrap-theme .navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}#bootstrap-theme .navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width: 768px){#bootstrap-theme .navbar-toggle{display:none}}#bootstrap-theme .navbar-nav{margin:7.5px -8px}#bootstrap-theme .navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width: 767px){#bootstrap-theme .navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}#bootstrap-theme .navbar-nav .open .dropdown-menu>li>a, #bootstrap-theme .navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}#bootstrap-theme .navbar-nav .open .dropdown-menu>li>a{line-height:20px}#bootstrap-theme .navbar-nav .open .dropdown-menu>li>a:hover, #bootstrap-theme .navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width: 768px){#bootstrap-theme .navbar-nav{float:left;margin:0}#bootstrap-theme .navbar-nav>li{float:left}#bootstrap-theme .navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}#bootstrap-theme .navbar-form{padding:10px 8px;margin-right:-8px;margin-left:-8px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:10px;margin-bottom:10px}@media (min-width: 768px){#bootstrap-theme .navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}#bootstrap-theme .navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}#bootstrap-theme .navbar-form .form-control-static{display:inline-block}#bootstrap-theme .navbar-form .input-group{display:inline-table;vertical-align:middle}#bootstrap-theme .navbar-form .input-group .input-group-addon, #bootstrap-theme .navbar-form .input-group .input-group-btn, #bootstrap-theme .navbar-form .input-group .form-control{width:auto}#bootstrap-theme .navbar-form .input-group>.form-control{width:100%}#bootstrap-theme .navbar-form .control-label{margin-bottom:0;vertical-align:middle}#bootstrap-theme .navbar-form .radio, #bootstrap-theme .navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}#bootstrap-theme .navbar-form .radio label, #bootstrap-theme .navbar-form .checkbox label{padding-left:0}#bootstrap-theme .navbar-form .radio input[type="radio"], #bootstrap-theme .navbar-form .checkbox input[type="checkbox"]{position:relative;margin-left:0}#bootstrap-theme .navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width: 767px){#bootstrap-theme .navbar-form .form-group{margin-bottom:5px}#bootstrap-theme .navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width: 768px){#bootstrap-theme .navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}#bootstrap-theme .navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}#bootstrap-theme .navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:2px;border-top-right-radius:2px;border-bottom-right-radius:0;border-bottom-left-radius:0}#bootstrap-theme .navbar-btn{margin-top:10px;margin-bottom:10px}#bootstrap-theme .navbar-btn.btn-sm, #bootstrap-theme .btn-group-sm>.navbar-btn.btn{margin-top:13px;margin-bottom:13px}#bootstrap-theme .navbar-btn.btn-xs, #bootstrap-theme .btn-group-xs>.navbar-btn.btn{margin-top:14px;margin-bottom:14px}#bootstrap-theme .navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width: 768px){#bootstrap-theme .navbar-text{float:left;margin-right:8px;margin-left:8px}}@media (min-width: 768px){#bootstrap-theme .navbar-left{float:left !important}#bootstrap-theme .navbar-right{float:right !important;margin-right:-8px}#bootstrap-theme .navbar-right ~ .navbar-right{margin-right:0}}#bootstrap-theme .navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}#bootstrap-theme .navbar-default .navbar-brand{color:#777}#bootstrap-theme .navbar-default .navbar-brand:hover, #bootstrap-theme .navbar-default .navbar-brand:focus{color:#5e5d5d;background-color:rgba(0,0,0,0)}#bootstrap-theme .navbar-default .navbar-text{color:#777}#bootstrap-theme .navbar-default .navbar-nav>li>a{color:#777}#bootstrap-theme .navbar-default .navbar-nav>li>a:hover, #bootstrap-theme .navbar-default .navbar-nav>li>a:focus{color:#333;background-color:rgba(0,0,0,0)}#bootstrap-theme .navbar-default .navbar-nav>.active>a, #bootstrap-theme .navbar-default .navbar-nav>.active>a:hover, #bootstrap-theme .navbar-default .navbar-nav>.active>a:focus{color:#555;background-color:#e7e7e7}#bootstrap-theme .navbar-default .navbar-nav>.disabled>a, #bootstrap-theme .navbar-default .navbar-nav>.disabled>a:hover, #bootstrap-theme .navbar-default .navbar-nav>.disabled>a:focus{color:#ccc;background-color:rgba(0,0,0,0)}#bootstrap-theme .navbar-default .navbar-nav>.open>a, #bootstrap-theme .navbar-default .navbar-nav>.open>a:hover, #bootstrap-theme .navbar-default .navbar-nav>.open>a:focus{color:#555;background-color:#e7e7e7}@media (max-width: 767px){#bootstrap-theme .navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}#bootstrap-theme .navbar-default .navbar-nav .open .dropdown-menu>li>a:hover, #bootstrap-theme .navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#333;background-color:rgba(0,0,0,0)}#bootstrap-theme .navbar-default .navbar-nav .open .dropdown-menu>.active>a, #bootstrap-theme .navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover, #bootstrap-theme .navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#555;background-color:#e7e7e7}#bootstrap-theme .navbar-default .navbar-nav .open .dropdown-menu>.disabled>a, #bootstrap-theme .navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover, #bootstrap-theme .navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#ccc;background-color:rgba(0,0,0,0)}}#bootstrap-theme .navbar-default .navbar-toggle{border-color:#ddd}#bootstrap-theme .navbar-default .navbar-toggle:hover, #bootstrap-theme .navbar-default .navbar-toggle:focus{background-color:#ddd}#bootstrap-theme .navbar-default .navbar-toggle .icon-bar{background-color:#888}#bootstrap-theme .navbar-default .navbar-collapse, #bootstrap-theme .navbar-default .navbar-form{border-color:#e7e7e7}#bootstrap-theme .navbar-default .navbar-link{color:#777}#bootstrap-theme .navbar-default .navbar-link:hover{color:#333}#bootstrap-theme .navbar-default .btn-link{color:#777}#bootstrap-theme .navbar-default .btn-link:hover, #bootstrap-theme .navbar-default .btn-link:focus{color:#333}#bootstrap-theme .navbar-default .btn-link[disabled]:hover, #bootstrap-theme .navbar-default .btn-link[disabled]:focus, #bootstrap-theme fieldset[disabled] .navbar-default .btn-link:hover, #bootstrap-theme fieldset[disabled] .navbar-default .btn-link:focus{color:#ccc}#bootstrap-theme .navbar-inverse{background-color:#222;border-color:#090808}#bootstrap-theme .navbar-inverse .navbar-brand{color:#fff}#bootstrap-theme .navbar-inverse .navbar-brand:hover, #bootstrap-theme .navbar-inverse .navbar-brand:focus{color:#fff;background-color:rgba(0,0,0,0)}#bootstrap-theme .navbar-inverse .navbar-text{color:#fff}#bootstrap-theme .navbar-inverse .navbar-nav>li>a{color:#fff}#bootstrap-theme .navbar-inverse .navbar-nav>li>a:hover, #bootstrap-theme .navbar-inverse .navbar-nav>li>a:focus{color:#fff;background-color:rgba(0,0,0,0)}#bootstrap-theme .navbar-inverse .navbar-nav>.active>a, #bootstrap-theme .navbar-inverse .navbar-nav>.active>a:hover, #bootstrap-theme .navbar-inverse .navbar-nav>.active>a:focus{color:#fff;background-color:#090808}#bootstrap-theme .navbar-inverse .navbar-nav>.disabled>a, #bootstrap-theme .navbar-inverse .navbar-nav>.disabled>a:hover, #bootstrap-theme .navbar-inverse .navbar-nav>.disabled>a:focus{color:#444;background-color:rgba(0,0,0,0)}#bootstrap-theme .navbar-inverse .navbar-nav>.open>a, #bootstrap-theme .navbar-inverse .navbar-nav>.open>a:hover, #bootstrap-theme .navbar-inverse .navbar-nav>.open>a:focus{color:#fff;background-color:#090808}@media (max-width: 767px){#bootstrap-theme .navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#090808}#bootstrap-theme .navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#090808}#bootstrap-theme .navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#fff}#bootstrap-theme .navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover, #bootstrap-theme .navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#fff;background-color:rgba(0,0,0,0)}#bootstrap-theme .navbar-inverse .navbar-nav .open .dropdown-menu>.active>a, #bootstrap-theme .navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover, #bootstrap-theme .navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-color:#090808}#bootstrap-theme .navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a, #bootstrap-theme .navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover, #bootstrap-theme .navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444;background-color:rgba(0,0,0,0)}}#bootstrap-theme .navbar-inverse .navbar-toggle{border-color:#333}#bootstrap-theme .navbar-inverse .navbar-toggle:hover, #bootstrap-theme .navbar-inverse .navbar-toggle:focus{background-color:#333}#bootstrap-theme .navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}#bootstrap-theme .navbar-inverse .navbar-collapse, #bootstrap-theme .navbar-inverse .navbar-form{border-color:#101010}#bootstrap-theme .navbar-inverse .navbar-link{color:#fff}#bootstrap-theme .navbar-inverse .navbar-link:hover{color:#fff}#bootstrap-theme .navbar-inverse .btn-link{color:#fff}#bootstrap-theme .navbar-inverse .btn-link:hover, #bootstrap-theme .navbar-inverse .btn-link:focus{color:#fff}#bootstrap-theme .navbar-inverse .btn-link[disabled]:hover, #bootstrap-theme .navbar-inverse .btn-link[disabled]:focus, #bootstrap-theme fieldset[disabled] .navbar-inverse .btn-link:hover, #bootstrap-theme fieldset[disabled] .navbar-inverse .btn-link:focus{color:#444}#bootstrap-theme .breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:2px}#bootstrap-theme .breadcrumb>li{display:inline-block}#bootstrap-theme .breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"> "}#bootstrap-theme .breadcrumb>.active{color:#e8eef0}#bootstrap-theme .pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:2px}#bootstrap-theme .pagination>li{display:inline}#bootstrap-theme .pagination>li>a, #bootstrap-theme .pagination>li>span{position:relative;float:left;padding:4px 10px;margin-left:-1px;line-height:1.5384615385;color:#4d4d69;text-decoration:none;background-color:rgba(0,0,0,0);border:1px solid #ddd}#bootstrap-theme .pagination>li>a:hover, #bootstrap-theme .pagination>li>a:focus, #bootstrap-theme .pagination>li>span:hover, #bootstrap-theme .pagination>li>span:focus{z-index:2;color:#464354;background-color:rgba(0,0,0,0);border-color:#ddd}#bootstrap-theme .pagination>li:first-child>a, #bootstrap-theme .pagination>li:first-child>span{margin-left:0;border-top-left-radius:2px;border-bottom-left-radius:2px}#bootstrap-theme .pagination>li:last-child>a, #bootstrap-theme .pagination>li:last-child>span{border-top-right-radius:2px;border-bottom-right-radius:2px}#bootstrap-theme .pagination>.active>a, #bootstrap-theme .pagination>.active>a:hover, #bootstrap-theme .pagination>.active>a:focus, #bootstrap-theme .pagination>.active>span, #bootstrap-theme .pagination>.active>span:hover, #bootstrap-theme .pagination>.active>span:focus{z-index:3;color:#464354;cursor:default;background-color:rgba(0,0,0,0);border-color:#0071bd}#bootstrap-theme .pagination>.disabled>span, #bootstrap-theme .pagination>.disabled>span:hover, #bootstrap-theme .pagination>.disabled>span:focus, #bootstrap-theme .pagination>.disabled>a, #bootstrap-theme .pagination>.disabled>a:hover, #bootstrap-theme .pagination>.disabled>a:focus{color:#e8eef0;cursor:not-allowed;background-color:rgba(0,0,0,0);border-color:#ddd}#bootstrap-theme .pagination-lg>li>a, #bootstrap-theme .pagination-lg>li>span{padding:10px 16px;font-size:17px;line-height:1.3333333}#bootstrap-theme .pagination-lg>li:first-child>a, #bootstrap-theme .pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}#bootstrap-theme .pagination-lg>li:last-child>a, #bootstrap-theme .pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}#bootstrap-theme .pagination-sm>li>a, #bootstrap-theme .pagination-sm>li>span{padding:2px 10px;font-size:12px;line-height:1.5}#bootstrap-theme .pagination-sm>li:first-child>a, #bootstrap-theme .pagination-sm>li:first-child>span{border-top-left-radius:2px;border-bottom-left-radius:2px}#bootstrap-theme .pagination-sm>li:last-child>a, #bootstrap-theme .pagination-sm>li:last-child>span{border-top-right-radius:2px;border-bottom-right-radius:2px}#bootstrap-theme .pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}#bootstrap-theme .pager:before, #bootstrap-theme .pager:after{display:table;content:" "}#bootstrap-theme .pager:after{clear:both}#bootstrap-theme .pager li{display:inline}#bootstrap-theme .pager li>a, #bootstrap-theme .pager li>span{display:inline-block;padding:5px 14px;background-color:rgba(0,0,0,0);border:1px solid #ddd;border-radius:15px}#bootstrap-theme .pager li>a:hover, #bootstrap-theme .pager li>a:focus{text-decoration:none;background-color:rgba(0,0,0,0)}#bootstrap-theme .pager .next>a, #bootstrap-theme .pager .next>span{float:right}#bootstrap-theme .pager .previous>a, #bootstrap-theme .pager .previous>span{float:left}#bootstrap-theme .pager .disabled>a, #bootstrap-theme .pager .disabled>a:hover, #bootstrap-theme .pager .disabled>a:focus, #bootstrap-theme .pager .disabled>span{color:#e8eef0;cursor:not-allowed;background-color:rgba(0,0,0,0)}#bootstrap-theme .label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}#bootstrap-theme .label:empty{display:none}#bootstrap-theme .btn .label{position:relative;top:-1px}#bootstrap-theme a.label:hover, #bootstrap-theme a.label:focus{color:#fff;text-decoration:none;cursor:pointer}#bootstrap-theme .label-default{background-color:#c2cfd8}#bootstrap-theme .label-default[href]:hover, #bootstrap-theme .label-default[href]:focus{background-color:#a3b7c4}#bootstrap-theme .label-primary{background-color:#0071bd}#bootstrap-theme .label-primary[href]:hover, #bootstrap-theme .label-primary[href]:focus{background-color:#00538a}#bootstrap-theme .label-success{background-color:#44cb7e}#bootstrap-theme .label-success[href]:hover, #bootstrap-theme .label-success[href]:focus{background-color:#30ac65}#bootstrap-theme .label-info{background-color:#0071bd}#bootstrap-theme .label-info[href]:hover, #bootstrap-theme .label-info[href]:focus{background-color:#00538a}#bootstrap-theme .label-warning{background-color:#e6ab5e}#bootstrap-theme .label-warning[href]:hover, #bootstrap-theme .label-warning[href]:focus{background-color:#df9432}#bootstrap-theme .label-danger{background-color:#cf3458}#bootstrap-theme .label-danger[href]:hover, #bootstrap-theme .label-danger[href]:focus{background-color:#a82846}#bootstrap-theme .badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:400;line-height:1.1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#0071bd;border-radius:10px}#bootstrap-theme .badge:empty{display:none}#bootstrap-theme .btn .badge{position:relative;top:-1px}#bootstrap-theme .btn-xs .badge, #bootstrap-theme .btn-group-xs>.btn .badge, #bootstrap-theme .btn-group-xs>.btn .badge{top:0;padding:1px 5px}#bootstrap-theme .list-group-item.active>.badge, #bootstrap-theme .nav-pills>.active>a>.badge{color:#0071bd;background-color:#fff}#bootstrap-theme .list-group-item>.badge{float:right}#bootstrap-theme .list-group-item>.badge+.badge{margin-right:5px}#bootstrap-theme .nav-pills>li>a>.badge{margin-left:3px}#bootstrap-theme a.badge:hover, #bootstrap-theme a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}#bootstrap-theme .jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#f3f6f7}#bootstrap-theme .jumbotron h1, #bootstrap-theme .jumbotron .h1{color:inherit}#bootstrap-theme .jumbotron p{margin-bottom:15px;font-size:20px;font-weight:200}#bootstrap-theme .jumbotron>hr{border-top-color:#d4dfe3}#bootstrap-theme .container .jumbotron, #bootstrap-theme .container-fluid .jumbotron{padding-right:8px;padding-left:8px;border-radius:6px}#bootstrap-theme .jumbotron .container{max-width:100%}@media screen and (min-width: 768px){#bootstrap-theme .jumbotron{padding-top:48px;padding-bottom:48px}#bootstrap-theme .container .jumbotron, #bootstrap-theme .container-fluid .jumbotron{padding-right:60px;padding-left:60px}#bootstrap-theme .jumbotron h1, #bootstrap-theme .jumbotron .h1{font-size:59px}}#bootstrap-theme .thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.5384615385;background-color:#e8eef0;border:1px solid #ddd;border-radius:2px;-webkit-transition:border 0.2s ease-in-out;-o-transition:border 0.2s ease-in-out;transition:border 0.2s ease-in-out}#bootstrap-theme .thumbnail>img, #bootstrap-theme .thumbnail a>img{display:block;max-width:100%;height:auto;margin-right:auto;margin-left:auto}#bootstrap-theme .thumbnail .caption{padding:9px;color:#4d4d69}#bootstrap-theme a.thumbnail:hover, #bootstrap-theme a.thumbnail:focus, #bootstrap-theme a.thumbnail.active{border-color:#0071bd}#bootstrap-theme .alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:2px}#bootstrap-theme .alert h4{margin-top:0;color:inherit}#bootstrap-theme .alert .alert-link{font-weight:bold}#bootstrap-theme .alert>p, #bootstrap-theme .alert>ul{margin-bottom:0}#bootstrap-theme .alert>p+p{margin-top:5px}#bootstrap-theme .alert-dismissable, #bootstrap-theme .alert-dismissible{padding-right:35px}#bootstrap-theme .alert-dismissable .close, #bootstrap-theme .alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}#bootstrap-theme .alert-success{color:#464354;background-color:#bcecd1;border-color:#44cb7e}#bootstrap-theme .alert-success hr{border-top-color:#35c071}#bootstrap-theme .alert-success .alert-link{color:#2e2c38}#bootstrap-theme .alert-info{color:#464354;background-color:#57bbff;border-color:#0071bd}#bootstrap-theme .alert-info hr{border-top-color:#0062a4}#bootstrap-theme .alert-info .alert-link{color:#2e2c38}#bootstrap-theme .alert-warning{color:#464354;background-color:#fbf0e2;border-color:#e6ab5e}#bootstrap-theme .alert-warning hr{border-top-color:#e39f48}#bootstrap-theme .alert-warning .alert-link{color:#2e2c38}#bootstrap-theme .alert-danger{color:#464354;background-color:#ecb0be;border-color:#cf3458}#bootstrap-theme .alert-danger hr{border-top-color:#bd2d4e}#bootstrap-theme .alert-danger .alert-link{color:#2e2c38}@-webkit-keyframes progress-bar-stripes{#bootstrap-theme from{background-position:40px 0}#bootstrap-theme to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}#bootstrap-theme .progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:2px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}#bootstrap-theme .progress-bar{float:left;width:0%;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#0071bd;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-transition:width 0.6s ease;-o-transition:width 0.6s ease;transition:width 0.6s ease}#bootstrap-theme .progress-striped .progress-bar, #bootstrap-theme .progress-bar-striped{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-size:40px 40px}#bootstrap-theme .progress.active .progress-bar, #bootstrap-theme .progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}#bootstrap-theme .progress-bar-success{background-color:#44cb7e}#bootstrap-theme .progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}#bootstrap-theme .progress-bar-info{background-color:#0071bd}#bootstrap-theme .progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}#bootstrap-theme .progress-bar-warning{background-color:#e6ab5e}#bootstrap-theme .progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}#bootstrap-theme .progress-bar-danger{background-color:#cf3458}#bootstrap-theme .progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}#bootstrap-theme .media{margin-top:15px}#bootstrap-theme .media:first-child{margin-top:0}#bootstrap-theme .media, #bootstrap-theme .media-body{overflow:hidden;zoom:1}#bootstrap-theme .media-body{width:10000px}#bootstrap-theme .media-object{display:block}#bootstrap-theme .media-object.img-thumbnail{max-width:none}#bootstrap-theme .media-right, #bootstrap-theme .media>.pull-right{padding-left:10px}#bootstrap-theme .media-left, #bootstrap-theme .media>.pull-left{padding-right:10px}#bootstrap-theme .media-left, #bootstrap-theme .media-right, #bootstrap-theme .media-body{display:table-cell;vertical-align:top}#bootstrap-theme .media-middle{vertical-align:middle}#bootstrap-theme .media-bottom{vertical-align:bottom}#bootstrap-theme .media-heading{margin-top:0;margin-bottom:5px}#bootstrap-theme .media-list{padding-left:0;list-style:none}#bootstrap-theme .list-group{padding-left:0;margin-bottom:20px}#bootstrap-theme .list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}#bootstrap-theme .list-group-item:first-child{border-top-left-radius:2px;border-top-right-radius:2px}#bootstrap-theme .list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:2px;border-bottom-left-radius:2px}#bootstrap-theme .list-group-item.disabled, #bootstrap-theme .list-group-item.disabled:hover, #bootstrap-theme .list-group-item.disabled:focus{color:#e8eef0;cursor:not-allowed;background-color:#f3f6f7}#bootstrap-theme .list-group-item.disabled .list-group-item-heading, #bootstrap-theme .list-group-item.disabled:hover .list-group-item-heading, #bootstrap-theme .list-group-item.disabled:focus .list-group-item-heading{color:inherit}#bootstrap-theme .list-group-item.disabled .list-group-item-text, #bootstrap-theme .list-group-item.disabled:hover .list-group-item-text, #bootstrap-theme .list-group-item.disabled:focus .list-group-item-text{color:#e8eef0}#bootstrap-theme .list-group-item.active, #bootstrap-theme .list-group-item.active:hover, #bootstrap-theme .list-group-item.active:focus{z-index:2;color:#fff;background-color:#0071bd;border-color:#0071bd}#bootstrap-theme .list-group-item.active .list-group-item-heading, #bootstrap-theme .list-group-item.active .list-group-item-heading>small, #bootstrap-theme .list-group-item.active .list-group-item-heading>.small, #bootstrap-theme .list-group-item.active:hover .list-group-item-heading, #bootstrap-theme .list-group-item.active:hover .list-group-item-heading>small, #bootstrap-theme .list-group-item.active:hover .list-group-item-heading>.small, #bootstrap-theme .list-group-item.active:focus .list-group-item-heading, #bootstrap-theme .list-group-item.active:focus .list-group-item-heading>small, #bootstrap-theme .list-group-item.active:focus .list-group-item-heading>.small{color:inherit}#bootstrap-theme .list-group-item.active .list-group-item-text, #bootstrap-theme .list-group-item.active:hover .list-group-item-text, #bootstrap-theme .list-group-item.active:focus .list-group-item-text{color:#8ad0ff}#bootstrap-theme a.list-group-item, #bootstrap-theme button.list-group-item{color:#555}#bootstrap-theme a.list-group-item .list-group-item-heading, #bootstrap-theme button.list-group-item .list-group-item-heading{color:#333}#bootstrap-theme a.list-group-item:hover, #bootstrap-theme a.list-group-item:focus, #bootstrap-theme button.list-group-item:hover, #bootstrap-theme button.list-group-item:focus{color:#555;text-decoration:none;background-color:#f5f5f5}#bootstrap-theme button.list-group-item{width:100%;text-align:left}#bootstrap-theme .list-group-item-success{color:#4d994d;background-color:#dff0d8}#bootstrap-theme a.list-group-item-success, #bootstrap-theme button.list-group-item-success{color:#4d994d}#bootstrap-theme a.list-group-item-success .list-group-item-heading, #bootstrap-theme button.list-group-item-success .list-group-item-heading{color:inherit}#bootstrap-theme a.list-group-item-success:hover, #bootstrap-theme a.list-group-item-success:focus, #bootstrap-theme button.list-group-item-success:hover, #bootstrap-theme button.list-group-item-success:focus{color:#4d994d;background-color:#d0e9c6}#bootstrap-theme a.list-group-item-success.active, #bootstrap-theme a.list-group-item-success.active:hover, #bootstrap-theme a.list-group-item-success.active:focus, #bootstrap-theme button.list-group-item-success.active, #bootstrap-theme button.list-group-item-success.active:hover, #bootstrap-theme button.list-group-item-success.active:focus{color:#fff;background-color:#4d994d;border-color:#4d994d}#bootstrap-theme .list-group-item-info{color:#0071bd;background-color:#d9edf7}#bootstrap-theme a.list-group-item-info, #bootstrap-theme button.list-group-item-info{color:#0071bd}#bootstrap-theme a.list-group-item-info .list-group-item-heading, #bootstrap-theme button.list-group-item-info .list-group-item-heading{color:inherit}#bootstrap-theme a.list-group-item-info:hover, #bootstrap-theme a.list-group-item-info:focus, #bootstrap-theme button.list-group-item-info:hover, #bootstrap-theme button.list-group-item-info:focus{color:#0071bd;background-color:#c4e3f3}#bootstrap-theme a.list-group-item-info.active, #bootstrap-theme a.list-group-item-info.active:hover, #bootstrap-theme a.list-group-item-info.active:focus, #bootstrap-theme button.list-group-item-info.active, #bootstrap-theme button.list-group-item-info.active:hover, #bootstrap-theme button.list-group-item-info.active:focus{color:#fff;background-color:#0071bd;border-color:#0071bd}#bootstrap-theme .list-group-item-warning{color:#bf5900;background-color:#fcf8e3}#bootstrap-theme a.list-group-item-warning, #bootstrap-theme button.list-group-item-warning{color:#bf5900}#bootstrap-theme a.list-group-item-warning .list-group-item-heading, #bootstrap-theme button.list-group-item-warning .list-group-item-heading{color:inherit}#bootstrap-theme a.list-group-item-warning:hover, #bootstrap-theme a.list-group-item-warning:focus, #bootstrap-theme button.list-group-item-warning:hover, #bootstrap-theme button.list-group-item-warning:focus{color:#bf5900;background-color:#faf2cc}#bootstrap-theme a.list-group-item-warning.active, #bootstrap-theme a.list-group-item-warning.active:hover, #bootstrap-theme a.list-group-item-warning.active:focus, #bootstrap-theme button.list-group-item-warning.active, #bootstrap-theme button.list-group-item-warning.active:hover, #bootstrap-theme button.list-group-item-warning.active:focus{color:#fff;background-color:#bf5900;border-color:#bf5900}#bootstrap-theme .list-group-item-danger{color:#cf3458;background-color:#f2dede}#bootstrap-theme a.list-group-item-danger, #bootstrap-theme button.list-group-item-danger{color:#cf3458}#bootstrap-theme a.list-group-item-danger .list-group-item-heading, #bootstrap-theme button.list-group-item-danger .list-group-item-heading{color:inherit}#bootstrap-theme a.list-group-item-danger:hover, #bootstrap-theme a.list-group-item-danger:focus, #bootstrap-theme button.list-group-item-danger:hover, #bootstrap-theme button.list-group-item-danger:focus{color:#cf3458;background-color:#ebcccc}#bootstrap-theme a.list-group-item-danger.active, #bootstrap-theme a.list-group-item-danger.active:hover, #bootstrap-theme a.list-group-item-danger.active:focus, #bootstrap-theme button.list-group-item-danger.active, #bootstrap-theme button.list-group-item-danger.active:hover, #bootstrap-theme button.list-group-item-danger.active:focus{color:#fff;background-color:#cf3458;border-color:#cf3458}#bootstrap-theme .list-group-item-heading{margin-top:0;margin-bottom:5px}#bootstrap-theme .list-group-item-text{margin-bottom:0;line-height:1.3}#bootstrap-theme .panel{margin-bottom:20px;background-color:rgba(0,0,0,0);border:1px solid transparent;border-radius:3px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05)}#bootstrap-theme .panel-body{padding:15px 20px}#bootstrap-theme .panel-body:before, #bootstrap-theme .panel-body:after{display:table;content:" "}#bootstrap-theme .panel-body:after{clear:both}#bootstrap-theme .panel-heading{padding:15px 20px;border-bottom:1px solid transparent;border-top-left-radius:2px;border-top-right-radius:2px}#bootstrap-theme .panel-heading>.dropdown .dropdown-toggle{color:inherit}#bootstrap-theme .panel-title{margin-top:0;margin-bottom:0;font-size:15px;color:inherit}#bootstrap-theme .panel-title>a, #bootstrap-theme .panel-title>small, #bootstrap-theme .panel-title>.small, #bootstrap-theme .panel-title>small>a, #bootstrap-theme .panel-title>.small>a{color:inherit}#bootstrap-theme .panel-footer{padding:20px;background-color:#fff;border-top:1px solid #e8eef0;border-bottom-right-radius:2px;border-bottom-left-radius:2px}#bootstrap-theme .panel>.list-group, #bootstrap-theme .panel>.panel-collapse>.list-group{margin-bottom:0}#bootstrap-theme .panel>.list-group .list-group-item, #bootstrap-theme .panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}#bootstrap-theme .panel>.list-group:first-child .list-group-item:first-child, #bootstrap-theme .panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:2px;border-top-right-radius:2px}#bootstrap-theme .panel>.list-group:last-child .list-group-item:last-child, #bootstrap-theme .panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:2px;border-bottom-left-radius:2px}#bootstrap-theme .panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}#bootstrap-theme .panel-heading+.list-group .list-group-item:first-child{border-top-width:0}#bootstrap-theme .list-group+.panel-footer{border-top-width:0}#bootstrap-theme .panel>.table, #bootstrap-theme .panel>.table-responsive>.table, #bootstrap-theme .panel>.panel-collapse>.table{margin-bottom:0}#bootstrap-theme .panel>.table caption, #bootstrap-theme .panel>.table-responsive>.table caption, #bootstrap-theme .panel>.panel-collapse>.table caption{padding-right:15px 20px;padding-left:15px 20px}#bootstrap-theme .panel>.table:first-child, #bootstrap-theme .panel>.table-responsive:first-child>.table:first-child{border-top-left-radius:2px;border-top-right-radius:2px}#bootstrap-theme .panel>.table:first-child>thead:first-child>tr:first-child, #bootstrap-theme .panel>.table:first-child>tbody:first-child>tr:first-child, #bootstrap-theme .panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child, #bootstrap-theme .panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:2px;border-top-right-radius:2px}#bootstrap-theme .panel>.table:first-child>thead:first-child>tr:first-child td:first-child, #bootstrap-theme .panel>.table:first-child>thead:first-child>tr:first-child th:first-child, #bootstrap-theme .panel>.table:first-child>tbody:first-child>tr:first-child td:first-child, #bootstrap-theme .panel>.table:first-child>tbody:first-child>tr:first-child th:first-child, #bootstrap-theme .panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child, #bootstrap-theme .panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child, #bootstrap-theme .panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child, #bootstrap-theme .panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:2px}#bootstrap-theme .panel>.table:first-child>thead:first-child>tr:first-child td:last-child, #bootstrap-theme .panel>.table:first-child>thead:first-child>tr:first-child th:last-child, #bootstrap-theme .panel>.table:first-child>tbody:first-child>tr:first-child td:last-child, #bootstrap-theme .panel>.table:first-child>tbody:first-child>tr:first-child th:last-child, #bootstrap-theme .panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child, #bootstrap-theme .panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child, #bootstrap-theme .panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child, #bootstrap-theme .panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:2px}#bootstrap-theme .panel>.table:last-child, #bootstrap-theme .panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:2px;border-bottom-left-radius:2px}#bootstrap-theme .panel>.table:last-child>tbody:last-child>tr:last-child, #bootstrap-theme .panel>.table:last-child>tfoot:last-child>tr:last-child, #bootstrap-theme .panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child, #bootstrap-theme .panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:2px;border-bottom-left-radius:2px}#bootstrap-theme .panel>.table:last-child>tbody:last-child>tr:last-child td:first-child, #bootstrap-theme .panel>.table:last-child>tbody:last-child>tr:last-child th:first-child, #bootstrap-theme .panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child, #bootstrap-theme .panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child, #bootstrap-theme .panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child, #bootstrap-theme .panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child, #bootstrap-theme .panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child, #bootstrap-theme .panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:2px}#bootstrap-theme .panel>.table:last-child>tbody:last-child>tr:last-child td:last-child, #bootstrap-theme .panel>.table:last-child>tbody:last-child>tr:last-child th:last-child, #bootstrap-theme .panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child, #bootstrap-theme .panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child, #bootstrap-theme .panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child, #bootstrap-theme .panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child, #bootstrap-theme .panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child, #bootstrap-theme .panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:2px}#bootstrap-theme .panel>.panel-body+.table, #bootstrap-theme .panel>.panel-body+.table-responsive, #bootstrap-theme .panel>.table+.panel-body, #bootstrap-theme .panel>.table-responsive+.panel-body{border-top:1px solid #e8eef0}#bootstrap-theme .panel>.table>tbody:first-child>tr:first-child th, #bootstrap-theme .panel>.table>tbody:first-child>tr:first-child td{border-top:0}#bootstrap-theme .panel>.table-bordered, #bootstrap-theme .panel>.table-responsive>.table-bordered{border:0}#bootstrap-theme .panel>.table-bordered>thead>tr>th:first-child, #bootstrap-theme .panel>.table-bordered>thead>tr>td:first-child, #bootstrap-theme .panel>.table-bordered>tbody>tr>th:first-child, #bootstrap-theme .panel>.table-bordered>tbody>tr>td:first-child, #bootstrap-theme .panel>.table-bordered>tfoot>tr>th:first-child, #bootstrap-theme .panel>.table-bordered>tfoot>tr>td:first-child, #bootstrap-theme .panel>.table-responsive>.table-bordered>thead>tr>th:first-child, #bootstrap-theme .panel>.table-responsive>.table-bordered>thead>tr>td:first-child, #bootstrap-theme .panel>.table-responsive>.table-bordered>tbody>tr>th:first-child, #bootstrap-theme .panel>.table-responsive>.table-bordered>tbody>tr>td:first-child, #bootstrap-theme .panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child, #bootstrap-theme .panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}#bootstrap-theme .panel>.table-bordered>thead>tr>th:last-child, #bootstrap-theme .panel>.table-bordered>thead>tr>td:last-child, #bootstrap-theme .panel>.table-bordered>tbody>tr>th:last-child, #bootstrap-theme .panel>.table-bordered>tbody>tr>td:last-child, #bootstrap-theme .panel>.table-bordered>tfoot>tr>th:last-child, #bootstrap-theme .panel>.table-bordered>tfoot>tr>td:last-child, #bootstrap-theme .panel>.table-responsive>.table-bordered>thead>tr>th:last-child, #bootstrap-theme .panel>.table-responsive>.table-bordered>thead>tr>td:last-child, #bootstrap-theme .panel>.table-responsive>.table-bordered>tbody>tr>th:last-child, #bootstrap-theme .panel>.table-responsive>.table-bordered>tbody>tr>td:last-child, #bootstrap-theme .panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child, #bootstrap-theme .panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}#bootstrap-theme .panel>.table-bordered>thead>tr:first-child>td, #bootstrap-theme .panel>.table-bordered>thead>tr:first-child>th, #bootstrap-theme .panel>.table-bordered>tbody>tr:first-child>td, #bootstrap-theme .panel>.table-bordered>tbody>tr:first-child>th, #bootstrap-theme .panel>.table-responsive>.table-bordered>thead>tr:first-child>td, #bootstrap-theme .panel>.table-responsive>.table-bordered>thead>tr:first-child>th, #bootstrap-theme .panel>.table-responsive>.table-bordered>tbody>tr:first-child>td, #bootstrap-theme .panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}#bootstrap-theme .panel>.table-bordered>tbody>tr:last-child>td, #bootstrap-theme .panel>.table-bordered>tbody>tr:last-child>th, #bootstrap-theme .panel>.table-bordered>tfoot>tr:last-child>td, #bootstrap-theme .panel>.table-bordered>tfoot>tr:last-child>th, #bootstrap-theme .panel>.table-responsive>.table-bordered>tbody>tr:last-child>td, #bootstrap-theme .panel>.table-responsive>.table-bordered>tbody>tr:last-child>th, #bootstrap-theme .panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td, #bootstrap-theme .panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}#bootstrap-theme .panel>.table-responsive{margin-bottom:0;border:0}#bootstrap-theme .panel-group{margin-bottom:20px}#bootstrap-theme .panel-group .panel{margin-bottom:0;border-radius:3px}#bootstrap-theme .panel-group .panel+.panel{margin-top:5px}#bootstrap-theme .panel-group .panel-heading{border-bottom:0}#bootstrap-theme .panel-group .panel-heading+.panel-collapse>.panel-body, #bootstrap-theme .panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid #e8eef0}#bootstrap-theme .panel-group .panel-footer{border-top:0}#bootstrap-theme .panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #e8eef0}#bootstrap-theme .panel-default{border-color:#ddd}#bootstrap-theme .panel-default>.panel-heading{color:#464354;background-color:#f3f6f7;border-color:#ddd}#bootstrap-theme .panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}#bootstrap-theme .panel-default>.panel-heading .badge{color:#f3f6f7;background-color:#464354}#bootstrap-theme .panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}#bootstrap-theme .panel-primary{border-color:#0071bd}#bootstrap-theme .panel-primary>.panel-heading{color:#fff;background-color:#0071bd;border-color:#0071bd}#bootstrap-theme .panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#0071bd}#bootstrap-theme .panel-primary>.panel-heading .badge{color:#0071bd;background-color:#fff}#bootstrap-theme .panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#0071bd}#bootstrap-theme .panel-success{border-color:#44cb7e}#bootstrap-theme .panel-success>.panel-heading{color:#464354;background-color:#44cb7e;border-color:#44cb7e}#bootstrap-theme .panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#44cb7e}#bootstrap-theme .panel-success>.panel-heading .badge{color:#44cb7e;background-color:#464354}#bootstrap-theme .panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#44cb7e}#bootstrap-theme .panel-info{border-color:#0071bd}#bootstrap-theme .panel-info>.panel-heading{color:#fff;background-color:#0071bd;border-color:#0071bd}#bootstrap-theme .panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#0071bd}#bootstrap-theme .panel-info>.panel-heading .badge{color:#0071bd;background-color:#fff}#bootstrap-theme .panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#0071bd}#bootstrap-theme .panel-warning{border-color:#e6ab5e}#bootstrap-theme .panel-warning>.panel-heading{color:#464354;background-color:#e6ab5e;border-color:#e6ab5e}#bootstrap-theme .panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#e6ab5e}#bootstrap-theme .panel-warning>.panel-heading .badge{color:#e6ab5e;background-color:#464354}#bootstrap-theme .panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#e6ab5e}#bootstrap-theme .panel-danger{border-color:#cf3458}#bootstrap-theme .panel-danger>.panel-heading{color:#fff;background-color:#cf3458;border-color:#cf3458}#bootstrap-theme .panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#cf3458}#bootstrap-theme .panel-danger>.panel-heading .badge{color:#cf3458;background-color:#fff}#bootstrap-theme .panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#cf3458}#bootstrap-theme .embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}#bootstrap-theme .embed-responsive .embed-responsive-item, #bootstrap-theme .embed-responsive iframe, #bootstrap-theme .embed-responsive embed, #bootstrap-theme .embed-responsive object, #bootstrap-theme .embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}#bootstrap-theme .embed-responsive-16by9{padding-bottom:56.25%}#bootstrap-theme .embed-responsive-4by3{padding-bottom:75%}#bootstrap-theme .well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:2px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}#bootstrap-theme .well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}#bootstrap-theme .well-lg{padding:24px;border-radius:6px}#bootstrap-theme .well-sm{padding:9px;border-radius:2px}#bootstrap-theme .close{float:right;font-size:19.5px;font-weight:bold;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}#bootstrap-theme .close:hover, #bootstrap-theme .close:focus{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}#bootstrap-theme button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none;appearance:none}#bootstrap-theme .modal-open{overflow:hidden}#bootstrap-theme .modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}#bootstrap-theme .modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);-ms-transform:translate(0, -25%);-o-transform:translate(0, -25%);transform:translate(0, -25%);-webkit-transition:-webkit-transform 0.3s ease-out;-moz-transition:-moz-transform 0.3s ease-out;-o-transition:-o-transform 0.3s ease-out;transition:transform 0.3s ease-out}#bootstrap-theme .modal.in .modal-dialog{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0)}#bootstrap-theme .modal-open .modal{overflow-x:hidden;overflow-y:auto}#bootstrap-theme .modal-dialog{position:relative;width:auto;margin:10px}#bootstrap-theme .modal-content{position:relative;background-color:#fff;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 3px 9px rgba(0,0,0,0.5);box-shadow:0 3px 9px rgba(0,0,0,0.5);outline:0}#bootstrap-theme .modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}#bootstrap-theme .modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}#bootstrap-theme .modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}#bootstrap-theme .modal-header{padding:10px 29px;border-bottom:1px solid #e5e5e5}#bootstrap-theme .modal-header:before, #bootstrap-theme .modal-header:after{display:table;content:" "}#bootstrap-theme .modal-header:after{clear:both}#bootstrap-theme .modal-header .close{margin-top:-2px}#bootstrap-theme .modal-title{margin:0;line-height:1.5384615385}#bootstrap-theme .modal-body{position:relative;padding:20px 30px}#bootstrap-theme .modal-footer{padding:20px 30px;text-align:right;border-top:1px solid #e8eef0}#bootstrap-theme .modal-footer:before, #bootstrap-theme .modal-footer:after{display:table;content:" "}#bootstrap-theme .modal-footer:after{clear:both}#bootstrap-theme .modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}#bootstrap-theme .modal-footer .btn-group .btn+.btn{margin-left:-1px}#bootstrap-theme .modal-footer .btn-block+.btn-block{margin-left:0}#bootstrap-theme .modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width: 768px){#bootstrap-theme .modal-dialog{width:555px;margin:30px auto}#bootstrap-theme .modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,0.5);box-shadow:0 5px 15px rgba(0,0,0,0.5)}#bootstrap-theme .modal-sm{width:360px}}@media (min-width: 992px){#bootstrap-theme .modal-lg{width:945px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.5384615385;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:12px;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:2px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.5384615385;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:13px;background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover>.arrow{border-width:11px}.popover>.arrow, .popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,0.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.popover-title{padding:8px 14px;margin:0;font-size:13px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}#bootstrap-theme .carousel{position:relative}#bootstrap-theme .carousel-inner{position:relative;width:100%;overflow:hidden}#bootstrap-theme .carousel-inner>.item{position:relative;display:none;-webkit-transition:0.6s ease-in-out left;-o-transition:0.6s ease-in-out left;transition:0.6s ease-in-out left}#bootstrap-theme .carousel-inner>.item>img, #bootstrap-theme .carousel-inner>.item>a>img{display:block;max-width:100%;height:auto;line-height:1}@media all and (transform-3d), (-webkit-transform-3d){#bootstrap-theme .carousel-inner>.item{-webkit-transition:-webkit-transform 0.6s ease-in-out;-moz-transition:-moz-transform 0.6s ease-in-out;-o-transition:-o-transform 0.6s ease-in-out;transition:transform 0.6s ease-in-out;-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;-moz-perspective:1000px;perspective:1000px}#bootstrap-theme .carousel-inner>.item.next, #bootstrap-theme .carousel-inner>.item.active.right{-webkit-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);left:0}#bootstrap-theme .carousel-inner>.item.prev, #bootstrap-theme .carousel-inner>.item.active.left{-webkit-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}#bootstrap-theme .carousel-inner>.item.next.left, #bootstrap-theme .carousel-inner>.item.prev.right, #bootstrap-theme .carousel-inner>.item.active{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);left:0}}#bootstrap-theme .carousel-inner>.active, #bootstrap-theme .carousel-inner>.next, #bootstrap-theme .carousel-inner>.prev{display:block}#bootstrap-theme .carousel-inner>.active{left:0}#bootstrap-theme .carousel-inner>.next, #bootstrap-theme .carousel-inner>.prev{position:absolute;top:0;width:100%}#bootstrap-theme .carousel-inner>.next{left:100%}#bootstrap-theme .carousel-inner>.prev{left:-100%}#bootstrap-theme .carousel-inner>.next.left, #bootstrap-theme .carousel-inner>.prev.right{left:0}#bootstrap-theme .carousel-inner>.active.left{left:-100%}#bootstrap-theme .carousel-inner>.active.right{left:100%}#bootstrap-theme .carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:.5}#bootstrap-theme .carousel-control.left{background-image:-webkit-linear-gradient(left, rgba(0,0,0,0.5) 0%, rgba(0,0,0,0.0001) 100%);background-image:-o-linear-gradient(left, rgba(0,0,0,0.5) 0%, rgba(0,0,0,0.0001) 100%);background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0%, rgba(0,0,0,0.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}#bootstrap-theme .carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left, rgba(0,0,0,0.0001) 0%, rgba(0,0,0,0.5) 100%);background-image:-o-linear-gradient(left, rgba(0,0,0,0.0001) 0%, rgba(0,0,0,0.5) 100%);background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0%, rgba(0,0,0,0.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}#bootstrap-theme .carousel-control:hover, #bootstrap-theme .carousel-control:focus{color:#fff;text-decoration:none;outline:0;filter:alpha(opacity=90);opacity:.9}#bootstrap-theme .carousel-control .icon-prev, #bootstrap-theme .carousel-control .icon-next, #bootstrap-theme .carousel-control .glyphicon-chevron-left, #bootstrap-theme .carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}#bootstrap-theme .carousel-control .icon-prev, #bootstrap-theme .carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}#bootstrap-theme .carousel-control .icon-next, #bootstrap-theme .carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}#bootstrap-theme .carousel-control .icon-prev, #bootstrap-theme .carousel-control .icon-next{width:20px;height:20px;font-family:serif;line-height:1}#bootstrap-theme .carousel-control .icon-prev:before{content:"\2039"}#bootstrap-theme .carousel-control .icon-next:before{content:"\203a"}#bootstrap-theme .carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}#bootstrap-theme .carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}#bootstrap-theme .carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}#bootstrap-theme .carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}#bootstrap-theme .carousel-caption .btn{text-shadow:none}@media screen and (min-width: 768px){#bootstrap-theme .carousel-control .glyphicon-chevron-left, #bootstrap-theme .carousel-control .glyphicon-chevron-right, #bootstrap-theme .carousel-control .icon-prev, #bootstrap-theme .carousel-control .icon-next{width:30px;height:30px;margin-top:-10px;font-size:30px}#bootstrap-theme .carousel-control .glyphicon-chevron-left, #bootstrap-theme .carousel-control .icon-prev{margin-left:-10px}#bootstrap-theme .carousel-control .glyphicon-chevron-right, #bootstrap-theme .carousel-control .icon-next{margin-right:-10px}#bootstrap-theme .carousel-caption{right:20%;left:20%;padding-bottom:30px}#bootstrap-theme .carousel-indicators{bottom:20px}}#bootstrap-theme .clearfix:before, #bootstrap-theme .clearfix:after{display:table;content:" "}#bootstrap-theme .clearfix:after{clear:both}#bootstrap-theme .center-block{display:block;margin-right:auto;margin-left:auto}#bootstrap-theme .pull-right{float:right !important}#bootstrap-theme .pull-left{float:left !important}#bootstrap-theme .hide{display:none !important}#bootstrap-theme .show{display:block !important}#bootstrap-theme .invisible{visibility:hidden}#bootstrap-theme .text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}#bootstrap-theme .hidden{display:none !important}#bootstrap-theme .affix{position:fixed}@-ms-viewport{width:device-width}#bootstrap-theme .visible-xs{display:none !important}#bootstrap-theme .visible-sm{display:none !important}#bootstrap-theme .visible-md{display:none !important}#bootstrap-theme .visible-lg{display:none !important}#bootstrap-theme .visible-xs-block, #bootstrap-theme .visible-xs-inline, #bootstrap-theme .visible-xs-inline-block, #bootstrap-theme .visible-sm-block, #bootstrap-theme .visible-sm-inline, #bootstrap-theme .visible-sm-inline-block, #bootstrap-theme .visible-md-block, #bootstrap-theme .visible-md-inline, #bootstrap-theme .visible-md-inline-block, #bootstrap-theme .visible-lg-block, #bootstrap-theme .visible-lg-inline, #bootstrap-theme .visible-lg-inline-block{display:none !important}@media (max-width: 767px){#bootstrap-theme .visible-xs{display:block !important}#bootstrap-theme table.visible-xs{display:table !important}#bootstrap-theme tr.visible-xs{display:table-row !important}#bootstrap-theme th.visible-xs, #bootstrap-theme td.visible-xs{display:table-cell !important}}@media (max-width: 767px){#bootstrap-theme .visible-xs-block{display:block !important}}@media (max-width: 767px){#bootstrap-theme .visible-xs-inline{display:inline !important}}@media (max-width: 767px){#bootstrap-theme .visible-xs-inline-block{display:inline-block !important}}@media (min-width: 768px) and (max-width: 991px){#bootstrap-theme .visible-sm{display:block !important}#bootstrap-theme table.visible-sm{display:table !important}#bootstrap-theme tr.visible-sm{display:table-row !important}#bootstrap-theme th.visible-sm, #bootstrap-theme td.visible-sm{display:table-cell !important}}@media (min-width: 768px) and (max-width: 991px){#bootstrap-theme .visible-sm-block{display:block !important}}@media (min-width: 768px) and (max-width: 991px){#bootstrap-theme .visible-sm-inline{display:inline !important}}@media (min-width: 768px) and (max-width: 991px){#bootstrap-theme .visible-sm-inline-block{display:inline-block !important}}@media (min-width: 992px) and (max-width: 1199px){#bootstrap-theme .visible-md{display:block !important}#bootstrap-theme table.visible-md{display:table !important}#bootstrap-theme tr.visible-md{display:table-row !important}#bootstrap-theme th.visible-md, #bootstrap-theme td.visible-md{display:table-cell !important}}@media (min-width: 992px) and (max-width: 1199px){#bootstrap-theme .visible-md-block{display:block !important}}@media (min-width: 992px) and (max-width: 1199px){#bootstrap-theme .visible-md-inline{display:inline !important}}@media (min-width: 992px) and (max-width: 1199px){#bootstrap-theme .visible-md-inline-block{display:inline-block !important}}@media (min-width: 1200px){#bootstrap-theme .visible-lg{display:block !important}#bootstrap-theme table.visible-lg{display:table !important}#bootstrap-theme tr.visible-lg{display:table-row !important}#bootstrap-theme th.visible-lg, #bootstrap-theme td.visible-lg{display:table-cell !important}}@media (min-width: 1200px){#bootstrap-theme .visible-lg-block{display:block !important}}@media (min-width: 1200px){#bootstrap-theme .visible-lg-inline{display:inline !important}}@media (min-width: 1200px){#bootstrap-theme .visible-lg-inline-block{display:inline-block !important}}@media (max-width: 767px){#bootstrap-theme .hidden-xs{display:none !important}}@media (min-width: 768px) and (max-width: 991px){#bootstrap-theme .hidden-sm{display:none !important}}@media (min-width: 992px) and (max-width: 1199px){#bootstrap-theme .hidden-md{display:none !important}}@media (min-width: 1200px){#bootstrap-theme .hidden-lg{display:none !important}}#bootstrap-theme .visible-print{display:none !important}@media print{#bootstrap-theme .visible-print{display:block !important}#bootstrap-theme table.visible-print{display:table !important}#bootstrap-theme tr.visible-print{display:table-row !important}#bootstrap-theme th.visible-print, #bootstrap-theme td.visible-print{display:table-cell !important}}#bootstrap-theme .visible-print-block{display:none !important}@media print{#bootstrap-theme .visible-print-block{display:block !important}}#bootstrap-theme .visible-print-inline{display:none !important}@media print{#bootstrap-theme .visible-print-inline{display:inline !important}}#bootstrap-theme .visible-print-inline-block{display:none !important}@media print{#bootstrap-theme .visible-print-inline-block{display:inline-block !important}}@media print{#bootstrap-theme .hidden-print{display:none !important}}#bootstrap-theme .form-horizontal .form-group .editable-controls.form-group{margin-left:0;margin-right:0}#bootstrap-theme [text-angular]{border:1px solid #e8eef0;padding:0}#bootstrap-theme [text-angular] .ta-editor{border:0 !important}#bootstrap-theme [text-angular] .form-control{border:0;box-shadow:none !important;min-height:120px}#bootstrap-theme [text-angular] .form-control.ta-scroll-window{height:auto}#bootstrap-theme [text-angular] .form-control.ta-scroll-window>.ta-bind{min-height:120px;outline:0}#bootstrap-theme [text-angular-toolbar]{background-color:#f4f7f8;border:0;margin-left:0;padding:10px}#bootstrap-theme [text-angular-toolbar] .btn{background:transparent !important;border:0;box-shadow:none;color:#4d4d69;padding-left:10px;padding-right:10px}#bootstrap-theme [text-angular-toolbar] .btn.active{color:#464354 !important}#bootstrap-theme [text-angular-toolbar] .btn:hover:not(:disabled){color:#4d4d69}#bootstrap-theme [text-angular-toolbar] .btn .fa{font-size:12px;font-weight:700}#bootstrap-theme .civihr-ui-select+.input-group-btn .btn{border-top-left-radius:0;border-bottom-left-radius:0;margin-left:-1px}#bootstrap-theme .civihr-ui-select.ui-select-multiple{min-height:30px}#bootstrap-theme .civihr-ui-select.ui-select-multiple .ui-select-search{height:1.9em;text-indent:10px}#bootstrap-theme .civihr-ui-select.ui-select-multiple .ui-select-match .close{line-height:0.8}#bootstrap-theme .civihr-ui-select.ui-select-multiple .ui-select-match-item{border-color:#0071bd;margin:0 2px 2px 1px;padding:2px 8px;text-transform:none}#bootstrap-theme .civihr-ui-select .ui-select-match-text, #bootstrap-theme .civihr-ui-select .ui-select-placeholder{text-transform:none}#bootstrap-theme .civihr-ui-select .ui-select-match-close{color:#0071bd;cursor:pointer}#bootstrap-theme .civihr-ui-select .ui-select-match-close:hover{color:#4d4d69}#bootstrap-theme .civihr-ui-select .ui-select-toggle{border-color:#c2cfd8;padding:4px 30px 4px 12px}#bootstrap-theme .civihr-ui-select .ui-select-toggle:hover{border-color:#c2cfd8}#bootstrap-theme .civihr-ui-select .ui-select-toggle[disabled]{background-color:#f3f6f7;border-color:#c2cfd8;opacity:1}#bootstrap-theme .civihr-ui-select .ui-select-toggle[disabled]:hover, #bootstrap-theme .civihr-ui-select .ui-select-toggle[disabled]:focus, #bootstrap-theme .civihr-ui-select .ui-select-toggle[disabled]:active{background-color:#f3f6f7;border-color:#c2cfd8}#bootstrap-theme .ui-select-container:not(.civihr-ui-select):not(.ui-select-multiple) .btn.ui-select-toggle{border-color:#c2cfd8;height:30px;padding:4px 10px;text-transform:none}#bootstrap-theme .ui-select-container:not(.civihr-ui-select):not(.ui-select-multiple) .btn:hover{background:#fff}#bootstrap-theme .ui-select-container:not(.civihr-ui-select):not(.ui-select-multiple) .dropdown-menu{border:1px solid #c2cfd8;border-top:1px solid transparent}#bootstrap-theme .ui-select-container:not(.civihr-ui-select):not(.ui-select-multiple) .ui-select-match-text{color:#555;padding-right:3em}#bootstrap-theme .ui-select-container:not(.civihr-ui-select):not(.ui-select-multiple) .ui-select-match-text span{text-overflow:ellipsis}#bootstrap-theme .ui-select-container:not(.civihr-ui-select):not(.ui-select-multiple) .ui-select-match a{margin-right:14px !important}#bootstrap-theme .ui-select-container:not(.civihr-ui-select):not(.ui-select-multiple) .glyphicon-remove{font-size:10px}#bootstrap-theme .ui-select-container:not(.civihr-ui-select):not(.ui-select-multiple) .caret{display:none}#bootstrap-theme .ui-select-container:not(.civihr-ui-select):not(.ui-select-multiple) input{border:1px solid #c2cfd8 !important;box-shadow:none !important;height:30px}#bootstrap-theme .ui-select-container:not(.civihr-ui-select):not(.ui-select-multiple)::after{content:'\f002';bottom:0;font-family:"FontAwesome";display:inline-block;height:100%;line-height:30px;pointer-events:none;position:absolute;right:0;text-align:center;width:32px;z-index:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}#bootstrap-theme .ui-select-container:not(.civihr-ui-select) .text-muted{color:#4d4d69}#bootstrap-theme .ui-select-multiple:not(.civihr-ui-select){border:1px solid #c2cfd8;min-height:30px;padding:3px !important}#bootstrap-theme .ui-select-multiple:not(.civihr-ui-select) .ui-select-match-item{border:1px solid #ccc;padding:1px 5px;text-transform:none}#bootstrap-theme .ui-select-multiple:not(.civihr-ui-select) .ui-select-search{padding-left:6px !important}#bootstrap-theme .ui-select-multiple:not(.civihr-ui-select)::after{content:'\f002';bottom:0;font-family:"FontAwesome";display:inline-block;height:100%;line-height:30px;pointer-events:none;position:absolute;right:0;text-align:center;width:32px;z-index:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}#bootstrap-theme .ui-select-bootstrap .ui-select-choices-row:not(.civihr-ui-select)>span{color:#555;font-size:12px;padding:3px 10px}#bootstrap-theme .ui-select-bootstrap .ui-select-choices-row:not(.civihr-ui-select).active>span{background:#eeeded;color:#555}#bootstrap-theme [uib-datepicker-popup]{border-color:#c2cfd8}#bootstrap-theme [uib-datepicker-popup] .form-control{border-color:#c2cfd8}#bootstrap-theme [uib-datepicker-popup-wrap]+.input-group-btn>button{border-color:#c2cfd8;color:#c2cfd8}#bootstrap-theme .uib-left, #bootstrap-theme .uib-right, #bootstrap-theme .uib-title{border:0;color:#464354}#bootstrap-theme .uib-left:hover, #bootstrap-theme .uib-right:hover, #bootstrap-theme .uib-title:hover{background:transparent}#bootstrap-theme .uib-left i, #bootstrap-theme .uib-right i{font-weight:300}#bootstrap-theme .uib-title strong{font-size:12px;font-weight:700}#bootstrap-theme .uib-daypicker{outline:0}#bootstrap-theme .uib-daypicker th{background:#fff;color:#555;padding:7px;vertical-align:middle}#bootstrap-theme .uib-daypicker tbody{background:rgba(0,0,0,0);border-top:0;box-shadow:0}#bootstrap-theme .uib-day, #bootstrap-theme .uib-month, #bootstrap-theme .uib-year{padding:7px}#bootstrap-theme .uib-day .text-muted, #bootstrap-theme .uib-month .text-muted, #bootstrap-theme .uib-year .text-muted{color:#dcdddd}#bootstrap-theme .uib-day .text-info, #bootstrap-theme .uib-month .text-info, #bootstrap-theme .uib-year .text-info{background-color:rgba(0,0,0,0);border-radius:50%;color:#0071bd}#bootstrap-theme .uib-day .btn-default, #bootstrap-theme .uib-month .btn-default, #bootstrap-theme .uib-year .btn-default{background:transparent;border:0;color:#464354;line-height:1;min-width:0;padding:7px}#bootstrap-theme .uib-day .btn-default span, #bootstrap-theme .uib-month .btn-default span, #bootstrap-theme .uib-year .btn-default span{padding:0 !important}#bootstrap-theme .uib-day .btn-default.active, #bootstrap-theme .uib-month .btn-default.active, #bootstrap-theme .uib-year .btn-default.active{background-color:#0071bd;border-radius:50%;box-shadow:none;color:#fff}#bootstrap-theme .uib-day .btn-default.active .text-info, #bootstrap-theme .uib-month .btn-default.active .text-info, #bootstrap-theme .uib-year .btn-default.active .text-info{color:inherit}#bootstrap-theme .uib-datepicker-popup{border:0;padding:10px !important}#bootstrap-theme .mobile [type='date'][uib-datepicker-popup]{line-height:normal}#bootstrap-theme .mobile [type='date'][uib-datepicker-popup]::-webkit-inner-spin-button, #bootstrap-theme .mobile [type='date'][uib-datepicker-popup]::-webkit-clear-button{-webkit-appearance:none;appearance:none;display:none}#bootstrap-theme .mobile [type='date'][uib-datepicker-popup]::-webkit-calendar-picker-indicator{background:transparent;bottom:0;color:transparent;height:auto;left:0;position:absolute;right:-50px;top:0;width:auto}#bootstrap-theme .mobile [type='date'][uib-datepicker-popup]+.input-group-addon{border-left:1px solid #c2cfd8 !important;height:30px !important;line-height:30px !important;padding:0 !important;pointer-events:none;position:absolute !important;right:0 !important;width:38px !important;z-index:3}#bootstrap-theme .badge{min-width:25px}#bootstrap-theme .badge-danger{background:#cf3458}#bootstrap-theme .badge-secondary{background:#464354}#bootstrap-theme .badge-success{background:#44cb7e;color:#464354}#bootstrap-theme .badge-warning{background:#e6ab5e;color:#464354}#bootstrap-theme .btn-group .btn.active{background-color:#555}#bootstrap-theme .btn{border-color:transparent;padding:7px 19px;text-transform:uppercase}#bootstrap-theme .btn:focus, #bootstrap-theme .btn:hover{border-color:transparent}#bootstrap-theme .btn:active, #bootstrap-theme .btn.active, #bootstrap-theme .open>.btn.dropdown-toggle{border-color:transparent}#bootstrap-theme .btn:active:hover, #bootstrap-theme .btn:active:focus, #bootstrap-theme .btn:active.focus, #bootstrap-theme .btn.active:hover, #bootstrap-theme .btn.active:focus, #bootstrap-theme .btn.active.focus, #bootstrap-theme .open>.btn.dropdown-toggle:hover, #bootstrap-theme .open>.btn.dropdown-toggle:focus, #bootstrap-theme .open>.btn.dropdown-toggle.focus{border-color:transparent}#bootstrap-theme .btn:focus, #bootstrap-theme .btn:active, #bootstrap-theme .btn.active{-webkit-box-shadow:none;box-shadow:none;outline:none !important}#bootstrap-theme .btn-default:hover{background-color:#fff;color:#4d4d69}#bootstrap-theme .btn-lg, #bootstrap-theme .btn-group-lg>.btn{padding:10px 16px;font-size:13px;line-height:1.5384615385;border-radius:2px}#bootstrap-theme .btn-sm, #bootstrap-theme .btn-group-sm>.btn{padding:5px 10px}#bootstrap-theme .btn-xs, #bootstrap-theme .btn-group-xs>.btn{padding:0 5px}#bootstrap-theme .btn-block+.btn-block{margin-top:10px}#bootstrap-theme .btn-link{text-transform:none}#bootstrap-theme .btn-shadow{-webkit-box-shadow:0 3px 18px 0 rgba(48,40,40,0.25);box-shadow:0 3px 18px 0 rgba(48,40,40,0.25)}#bootstrap-theme .btn-icon{margin-right:5px}#bootstrap-theme .btn-primary:hover{background-color:#005c99}#bootstrap-theme .btn-success{color:#464354}#bootstrap-theme .btn-success:hover{background-color:#33b86c;color:#000}#bootstrap-theme .btn-info:hover{background-color:#005c99}#bootstrap-theme .btn-warning{color:#464354}#bootstrap-theme .btn-warning:hover{background-color:#e19b3f;color:#000}#bootstrap-theme .btn-danger:hover{background-color:#b52b4b}#bootstrap-theme .btn .caret{margin:0 5px}#bootstrap-theme .btn-secondary{color:#fff;background-color:#4d4d69;border-color:#4d4d69;border-color:transparent}#bootstrap-theme .btn-secondary:focus, #bootstrap-theme .btn-secondary.focus{color:#fff;background-color:#37374c;border-color:#17171f}#bootstrap-theme .btn-secondary:hover{color:#fff;background-color:#37374c;border-color:#333346}#bootstrap-theme .btn-secondary:active, #bootstrap-theme .btn-secondary.active, #bootstrap-theme .open>.btn-secondary.dropdown-toggle{color:#fff;background-color:#37374c;background-image:none;border-color:#333346}#bootstrap-theme .btn-secondary:active:hover, #bootstrap-theme .btn-secondary:active:focus, #bootstrap-theme .btn-secondary:active.focus, #bootstrap-theme .btn-secondary.active:hover, #bootstrap-theme .btn-secondary.active:focus, #bootstrap-theme .btn-secondary.active.focus, #bootstrap-theme .open>.btn-secondary.dropdown-toggle:hover, #bootstrap-theme .open>.btn-secondary.dropdown-toggle:focus, #bootstrap-theme .open>.btn-secondary.dropdown-toggle.focus{color:#fff;background-color:#282837;border-color:#17171f}#bootstrap-theme .btn-secondary.disabled:hover, #bootstrap-theme .btn-secondary.disabled:focus, #bootstrap-theme .btn-secondary.disabled.focus, #bootstrap-theme .btn-secondary[disabled]:hover, #bootstrap-theme .btn-secondary[disabled]:focus, #bootstrap-theme .btn-secondary[disabled].focus, #bootstrap-theme fieldset[disabled] .btn-secondary:hover, #bootstrap-theme fieldset[disabled] .btn-secondary:focus, #bootstrap-theme fieldset[disabled] .btn-secondary.focus{background-color:#4d4d69;border-color:#4d4d69}#bootstrap-theme .btn-secondary .badge{color:#4d4d69;background-color:#fff}#bootstrap-theme .btn-secondary:focus, #bootstrap-theme .btn-secondary:hover{border-color:transparent}#bootstrap-theme .btn-secondary:active, #bootstrap-theme .btn-secondary.active, #bootstrap-theme .open>.btn-secondary.dropdown-toggle{border-color:transparent}#bootstrap-theme .btn-secondary:active:hover, #bootstrap-theme .btn-secondary:active:focus, #bootstrap-theme .btn-secondary:active.focus, #bootstrap-theme .btn-secondary.active:hover, #bootstrap-theme .btn-secondary.active:focus, #bootstrap-theme .btn-secondary.active.focus, #bootstrap-theme .open>.btn-secondary.dropdown-toggle:hover, #bootstrap-theme .open>.btn-secondary.dropdown-toggle:focus, #bootstrap-theme .open>.btn-secondary.dropdown-toggle.focus{border-color:transparent}#bootstrap-theme .btn-secondary:hover{background-color:#3e3e54}#bootstrap-theme .chart svg:not(:root){overflow:visible}#bootstrap-theme .chart-axis path, #bootstrap-theme .chart-axis line{fill:none;stroke:#f3f6f7}#bootstrap-theme .chart-axis-x>.tick, #bootstrap-theme .chart-axis-y>.tick{fill:#4d4d69}#bootstrap-theme .dropdown-header{padding:7px 19px 7px 24px}#bootstrap-theme .dropdown-menu{-webkit-box-shadow:0 3px 18px 0 rgba(48,40,40,0.25);box-shadow:0 3px 18px 0 rgba(48,40,40,0.25);border:0;margin:0;padding:4px 0 9px 0}#bootstrap-theme .dropdown-menu>li{border:0;margin:0;padding:0}#bootstrap-theme .dropdown-menu>li>a{padding:7px 19px 7px 24px}#bootstrap-theme .dropdown-menu .divider{margin:7px 0}#bootstrap-theme label{color:#464354;font-weight:600}#bootstrap-theme .checkbox>label, #bootstrap-theme .radio>label{color:#4d4d69}#bootstrap-theme .form-control{-webkit-box-shadow:inset 0 0 3px 0 rgba(0,0,0,0.2);box-shadow:inset 0 0 3px 0 rgba(0,0,0,0.2)}#bootstrap-theme .form-control[disabled], #bootstrap-theme [disabled] .form-control{color:#c2cfd8}#bootstrap-theme .form-control:focus{-webkit-box-shadow:inset 0 0 3px 0 rgba(0,0,0,0.2);box-shadow:inset 0 0 3px 0 rgba(0,0,0,0.2);border-color:#0071bd}#bootstrap-theme .form-control[readonly]{background-color:#fff;color:#9494a5}#bootstrap-theme .form-error-message{margin-top:5px}#bootstrap-theme .form-horizontal .has-feedback [uib-datepicker-popup]+.form-control-feedback{right:41px}@media (min-width: 768px){#bootstrap-theme .form-horizontal .control-label{text-align:left}#bootstrap-theme .form-horizontal .form-group{overflow:initial}#bootstrap-theme .form-horizontal .form-group [type='checkbox']{margin-top:8px}#bootstrap-theme .form-horizontal .form-group-lg .control-label, #bootstrap-theme .form-horizontal .form-group-sm .control-label{font-size:13px}}#bootstrap-theme .form-horizontal-inline .form-group{margin-bottom:0}@media (min-width: 768px){#bootstrap-theme .form-inline .checkbox [type='checkbox'], #bootstrap-theme .form-inline .radio [type='radio']{margin-right:10px}}#bootstrap-theme .form-inline-tabs .form-group{border:1px solid #c2cfd8;box-shadow:inset 0 0 3px 0 rgba(0,0,0,0.2);line-height:1em;min-width:180px;padding:8px 15px}#bootstrap-theme .form-inline-tabs .form-group.active{background-color:#f3f6f7}#bootstrap-theme .form-inline-tabs .form-group:first-child{border-radius:2px 0 0 2px}#bootstrap-theme .form-inline-tabs .form-group:last-child{border-radius:0 2px 2px 0}#bootstrap-theme .has-success .control-label, #bootstrap-theme .has-warning .control-label, #bootstrap-theme .has-error .control-label{color:#464354}#bootstrap-theme .has-success .help-block, #bootstrap-theme .has-success .control-label, #bootstrap-theme .has-success .radio, #bootstrap-theme .has-success .checkbox, #bootstrap-theme .has-success .radio-inline, #bootstrap-theme .has-success .checkbox-inline, #bootstrap-theme .has-success.radio label, #bootstrap-theme .has-success.checkbox label, #bootstrap-theme .has-success.radio-inline label, #bootstrap-theme .has-success.checkbox-inline label{color:#4d994d}#bootstrap-theme .has-success .form-control{border-color:#44cb7e;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}#bootstrap-theme .has-success .form-control:focus{border-color:#30ac65;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #94e1b5;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #94e1b5}#bootstrap-theme .has-success .input-group-addon{color:#4d994d;background-color:#dff0d8;border-color:#44cb7e}#bootstrap-theme .has-success .form-control-feedback{color:#4d994d}#bootstrap-theme .has-success .radio label, #bootstrap-theme .has-success .checkbox label, #bootstrap-theme .has-success .radio-inline label, #bootstrap-theme .has-success .checkbox-inline label{color:#4d994d}#bootstrap-theme .has-warning .help-block, #bootstrap-theme .has-warning .control-label, #bootstrap-theme .has-warning .radio, #bootstrap-theme .has-warning .checkbox, #bootstrap-theme .has-warning .radio-inline, #bootstrap-theme .has-warning .checkbox-inline, #bootstrap-theme .has-warning.radio label, #bootstrap-theme .has-warning.checkbox label, #bootstrap-theme .has-warning.radio-inline label, #bootstrap-theme .has-warning.checkbox-inline label{color:#bf5900}#bootstrap-theme .has-warning .form-control{border-color:#e6ab5e;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}#bootstrap-theme .has-warning .form-control:focus{border-color:#df9432;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #f4d9b6;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #f4d9b6}#bootstrap-theme .has-warning .input-group-addon{color:#bf5900;background-color:#fcf8e3;border-color:#e6ab5e}#bootstrap-theme .has-warning .form-control-feedback{color:#bf5900}#bootstrap-theme .has-warning .radio label, #bootstrap-theme .has-warning .checkbox label, #bootstrap-theme .has-warning .radio-inline label, #bootstrap-theme .has-warning .checkbox-inline label{color:#bf5900}#bootstrap-theme .has-error .radio label, #bootstrap-theme .has-error .checkbox label, #bootstrap-theme .has-error .radio-inline label, #bootstrap-theme .has-error .checkbox-inline label{color:#cf3458}#bootstrap-theme .input-group-btn .btn-default{border-color:#c2cfd8;text-transform:none}#bootstrap-theme .form-submitted .ng-invalid, #bootstrap-theme .ng-submitted .ng-invalid{border:1px solid #cf3458}#bootstrap-theme .required-mark::after{color:#cf3458;content:'*';margin-left:5px}#bootstrap-theme .required-mark-input{position:relative}#bootstrap-theme .required-mark-input::after{bottom:0;position:absolute;right:-10px;top:0}#bootstrap-theme a.fa:hover{text-decoration:none}#bootstrap-theme .input-group-addon{color:#464354}#bootstrap-theme .label{font-size:92.308%;font-weight:400}#bootstrap-theme .label-success, #bootstrap-theme .label-warning{color:#464354}#bootstrap-theme .modal-content{-webkit-box-shadow:0 3px 18px 0 rgba(48,40,40,0.25);box-shadow:0 3px 18px 0 rgba(48,40,40,0.25);border:0;border-radius:2px}#bootstrap-theme .modal-footer{padding:20px}#bootstrap-theme .modal-sm .modal-footer{padding:10px}#bootstrap-theme .modal-header{background:#f3f6f7;border-bottom:0;border-radius:2px}#bootstrap-theme .modal-sm .modal-header{padding:10px 20px}@media (min-width: 768px){#bootstrap-theme .modal-dialog{margin-top:50px}}#bootstrap-theme .modal-civihr-bootstrap{left:initial !important;position:initial !important;top:initial !important}#bootstrap-theme .modal-civihr-bootstrap .loading-spinner{left:initial !important;margin:0 auto -20px !important;position:initial !important;top:initial !important}#bootstrap-theme .nav>li>a{color:#464354;font-weight:600}#bootstrap-theme .nav>.disabled>a:hover{border-left-color:transparent;border-right-color:transparent;border-top-color:transparent}#bootstrap-theme .nav-stacked{background:#fff}#bootstrap-theme .nav-stacked>li>a{padding:15px 20px}#bootstrap-theme .nav-stacked>li>a:active{background:#e8eef0}#bootstrap-theme .nav-stacked>li+li{margin-top:0}#bootstrap-theme .tab-content{background:#fff}#bootstrap-theme .tab-pane{padding:15px 20px}#bootstrap-theme .nav-pills>li>a:focus{background-color:transparent}#bootstrap-theme .nav-pills-horizontal{-webkit-box-shadow:0 3px 18px 0 rgba(48,40,40,0.25);box-shadow:0 3px 18px 0 rgba(48,40,40,0.25);padding-left:20px}#bootstrap-theme .nav-pills-horizontal>li{opacity:0.75}#bootstrap-theme .nav-pills-horizontal>li>a{padding:15px 20px}#bootstrap-theme .nav-pills-horizontal>li+li{margin-left:0}#bootstrap-theme .nav-pills-horizontal>.disabled{opacity:0.4}#bootstrap-theme .nav-pills-horizontal>.active{opacity:1}#bootstrap-theme .nav-pills-horizontal>.active>a{background:transparent !important;position:relative}#bootstrap-theme .nav-pills-horizontal>.active>a::after{border-bottom:3px solid transparent;bottom:0;content:'';left:0;position:absolute;width:100%}#bootstrap-theme .nav-pills-horizontal>.active>a:hover{background:transparent}#bootstrap-theme .nav-pills-horizontal-default{background:#fff}#bootstrap-theme .nav-pills-horizontal-default>li>a{color:#464354 !important}#bootstrap-theme .nav-pills-horizontal-default>li.active>a:after{border-color:#464354}#bootstrap-theme .nav-pills-horizontal-primary{background:#0071bd}#bootstrap-theme .nav-pills-horizontal-primary>li>a{color:#fff !important}#bootstrap-theme .nav-pills-horizontal-primary>li>a:hover{background:#0080d5}#bootstrap-theme .nav-pills-horizontal-primary>li.active>a:after{border-color:#fff}#bootstrap-theme .nav-pills-horizontal-primary>li.active>a:hover{background:#0071bd}#bootstrap-theme .nav-pills-horizontal-success{background:#44cb7e}#bootstrap-theme .nav-pills-horizontal-success>li>a{color:#464354 !important}#bootstrap-theme .nav-pills-horizontal-success>li>a:hover{background:#57d08b}#bootstrap-theme .nav-pills-horizontal-success>li.active>a:after{border-color:#464354}#bootstrap-theme .nav-pills-horizontal-success>li.active>a:hover{background:#44cb7e}#bootstrap-theme .nav-pills-horizontal-info{background:#0071bd}#bootstrap-theme .nav-pills-horizontal-info>li>a{color:#fff !important}#bootstrap-theme .nav-pills-horizontal-info>li>a:hover{background:#0080d5}#bootstrap-theme .nav-pills-horizontal-info>li.active>a:after{border-color:#fff}#bootstrap-theme .nav-pills-horizontal-info>li.active>a:hover{background:#0071bd}#bootstrap-theme .nav-pills-horizontal-warning{background:#e6ab5e}#bootstrap-theme .nav-pills-horizontal-warning>li>a{color:#464354 !important}#bootstrap-theme .nav-pills-horizontal-warning>li>a:hover{background:#e9b673}#bootstrap-theme .nav-pills-horizontal-warning>li.active>a:after{border-color:#464354}#bootstrap-theme .nav-pills-horizontal-warning>li.active>a:hover{background:#e6ab5e}#bootstrap-theme .nav-pills-horizontal-danger{background:#cf3458}#bootstrap-theme .nav-pills-horizontal-danger>li>a{color:#fff !important}#bootstrap-theme .nav-pills-horizontal-danger>li>a:hover{background:#d34868}#bootstrap-theme .nav-pills-horizontal-danger>li.active>a:after{border-color:#fff}#bootstrap-theme .nav-pills-horizontal-danger>li.active>a:hover{background:#cf3458}#bootstrap-theme .nav-pills-stacked-sidebar{background:transparent}#bootstrap-theme .nav-pills-stacked-sidebar>li{position:relative;z-index:2}#bootstrap-theme .nav-pills-stacked-sidebar>li>a{background:#fff}#bootstrap-theme .nav-pills-stacked-sidebar>.active{z-index:0}#bootstrap-theme .nav-pills-stacked-sidebar>.active::after, #bootstrap-theme .nav-pills-stacked-sidebar>.active::before{-webkit-box-shadow:0 3px 18px 0 rgba(48,40,40,0.25);box-shadow:0 3px 18px 0 rgba(48,40,40,0.25);content:'';display:block;height:999999px;left:0;position:absolute;width:100%;z-index:1}#bootstrap-theme .nav-pills-stacked-sidebar>.active::after{top:100%}#bootstrap-theme .nav-pills-stacked-sidebar>.active::before{bottom:100%}#bootstrap-theme .nav-pills-stacked-sidebar>.active>a{background:transparent !important;color:#464354 !important}#bootstrap-theme .panel-primary>.panel-heading>.nav-tabs{border:0;margin:-10px 0}#bootstrap-theme .panel-primary li>a{background:rgba(0,0,0,0) !important;border-color:#fff !important;border-width:0;color:#fff !important;font-weight:600;padding:15px 15px 12px}#bootstrap-theme .panel-primary .active>a, #bootstrap-theme .panel-primary li:hover>a{border-width:0 0 3px 0}#bootstrap-theme .panel-default .nav-tabs:not(.nav-tabs-stacked){background:#f3f6f7;padding:5px 0 0 20px}#bootstrap-theme .panel-default .nav-tabs-stacked{border-bottom:0}#bootstrap-theme .panel-default .nav-tabs-stacked>li{float:none}#bootstrap-theme .panel-default .nav-tabs-stacked a{margin-right:0;padding:15px 20px}#bootstrap-theme .panel-default .nav-tabs-stacked a:hover{border-color:transparent #d3dee2 transparent transparent}#bootstrap-theme .panel-default .nav-tabs-stacked>.active>a, #bootstrap-theme .panel-default .nav-tabs-stacked>.active>a:hover, #bootstrap-theme .panel-default .nav-tabs-stacked>.active>a:focus{border-bottom-color:#d3dee2;border-color:#d3dee2 transparent #d3dee2 transparent}#bootstrap-theme .panel-default .nav-tabs-stacked-wrapper{border-top:1px solid #d3dee2;display:flex}#bootstrap-theme .panel-default .nav-tabs-stacked-wrapper .active:first-child>a{border-top-color:transparent !important}#bootstrap-theme .panel-default .nav-tabs-stacked-wrapper .nav-tabs-stacked{margin-right:-16px}#bootstrap-theme .panel-default .nav-tabs-stacked-wrapper .nav-tabs-stacked-content{display:flex}#bootstrap-theme .panel-default .nav-tabs-stacked-wrapper .tab-content{flex:1}#bootstrap-theme .panel-default .panel-heading+.nav-tabs{border-top:1px solid #e8eef0}#bootstrap-theme strong{font-weight:600}#bootstrap-theme .pagination{margin:0;vertical-align:top}#bootstrap-theme .pagination>li{text-transform:capitalize}#bootstrap-theme .pagination>li.active>a, #bootstrap-theme .pagination>li.first:not(.disabled)>a, #bootstrap-theme .pagination>li.last:not(.disabled)>a, #bootstrap-theme .pagination>li.next:not(.disabled)>a, #bootstrap-theme .pagination>li.prev:not(.disabled)>a{color:#464354;font-weight:bold}#bootstrap-theme .pagination>li>a, #bootstrap-theme .pagination>li>span{border:none;padding:0 5px}#bootstrap-theme .pagination>li>a:hover, #bootstrap-theme .pagination>li>span:hover{text-decoration:underline}#bootstrap-theme .pagination>li.first a:before{content:"«";font-weight:400;margin-right:3px}#bootstrap-theme .pagination>li.last a:after{content:"»";font-weight:400;margin-left:3px}#bootstrap-theme .pagination>li.next a:after{content:"›";font-weight:400;margin-left:3px}#bootstrap-theme .pagination>li.prev a:before{content:"‹";font-weight:400;margin-right:3px}#bootstrap-theme .panel{-webkit-box-shadow:none;box-shadow:none;border:none;margin-bottom:16px}#bootstrap-theme .panel-body{background:#fff}#bootstrap-theme .panel-body>*:last-child{margin-bottom:0}#bootstrap-theme .panel-body>*:last-child.form-horizontal>.row:last-child .form-group{margin-bottom:0}#bootstrap-theme .panel-body-small{padding:15px 20px}#bootstrap-theme .panel-heading{border:none}#bootstrap-theme .panel-title{font-size:18px;font-weight:600;line-height:1.1}#bootstrap-theme .panel-title-sm{font-size:16px;font-weight:600}#bootstrap-theme .panel-body-abstract{padding:8px 20px}#bootstrap-theme .panel-body-extra{padding:5px 20px 10px 35px}#bootstrap-theme .panel-subheading{padding:15px 20px}#bootstrap-theme .panel-heading+.panel-subheading{border-top:1px solid transparent}#bootstrap-theme .panel-subtitle{color:inherit;font-size:13px;font-weight:700;line-height:1.1;margin:0}#bootstrap-theme .panel-nest-group{margin-bottom:20px}#bootstrap-theme .panel-nest-group>.panel{margin-bottom:10px}#bootstrap-theme .panel-nest-group .panel-nested{margin:0 20px}#bootstrap-theme .panel-default{-webkit-box-shadow:0 3px 18px 0 rgba(48,40,40,0.25);box-shadow:0 3px 18px 0 rgba(48,40,40,0.25)}#bootstrap-theme .panel-default .panel-body:not(.panel-body-extra){border-top:1px solid #d3dee2}#bootstrap-theme .panel-default>.panel-heading .btn-group-sm{margin:-13px 0 -10px 0}#bootstrap-theme .panel-default>.panel-heading .btn-group-sm>.btn{padding-left:20px;padding-right:20px}#bootstrap-theme .panel-default>.panel-subheading{background:#f3f6f7;border-top-color:#e8eef0;color:#464354}#bootstrap-theme .panel-default[class*="panel-strip-"]{border-left:6px solid transparent}#bootstrap-theme .panel-default.panel-strip-primary{border-left-color:#0071bd}#bootstrap-theme .panel-default.panel-strip-success{border-left-color:#44cb7e}#bootstrap-theme .panel-default.panel-strip-info{border-left-color:#0071bd}#bootstrap-theme .panel-default.panel-strip-warning{border-left-color:#e6ab5e}#bootstrap-theme .panel-default.panel-strip-danger{border-left-color:#cf3458}#bootstrap-theme .panel-primary>.panel-subheading{background:#0071bd;color:#fff;border-top-color:#0071bd;position:relative}#bootstrap-theme .panel-primary>.panel-subheading:before{content:"";background:#0071bd;top:-11px;height:10px;left:0;position:absolute;width:100%;z-index:2}#bootstrap-theme .panel-success>.panel-heading{color:#464354}#bootstrap-theme .panel-success>.panel-subheading{background:#44cb7e;color:#464354;border-top-color:#44cb7e;position:relative}#bootstrap-theme .panel-success>.panel-subheading:before{content:"";background:#44cb7e;top:-11px;height:10px;left:0;position:absolute;width:100%;z-index:2}#bootstrap-theme .panel-info>.panel-subheading{background:#0071bd;color:#fff;border-top-color:#0071bd;position:relative}#bootstrap-theme .panel-info>.panel-subheading:before{content:"";background:#0071bd;top:-11px;height:10px;left:0;position:absolute;width:100%;z-index:2}#bootstrap-theme .panel-warning>.panel-subheading{background:#e6ab5e;color:#464354;border-top-color:#e6ab5e;position:relative}#bootstrap-theme .panel-warning>.panel-subheading:before{content:"";background:#e6ab5e;top:-11px;height:10px;left:0;position:absolute;width:100%;z-index:2}#bootstrap-theme .panel-danger>.panel-subheading{background:#cf3458;color:#fff;border-top-color:#cf3458;position:relative}#bootstrap-theme .panel-danger>.panel-subheading:before{content:"";background:#cf3458;top:-11px;height:10px;left:0;position:absolute;width:100%;z-index:2}#bootstrap-theme .panel-primary>.panel-body, #bootstrap-theme .panel-primary>.panel-footer, #bootstrap-theme .panel-primary>.panel-heading, #bootstrap-theme .panel-primary>.panel-subheading, #bootstrap-theme .panel-success>.panel-body, #bootstrap-theme .panel-success>.panel-footer, #bootstrap-theme .panel-success>.panel-heading, #bootstrap-theme .panel-success>.panel-subheading, #bootstrap-theme .panel-info>.panel-body, #bootstrap-theme .panel-info>.panel-footer, #bootstrap-theme .panel-info>.panel-heading, #bootstrap-theme .panel-info>.panel-subheading, #bootstrap-theme .panel-warning>.panel-body, #bootstrap-theme .panel-warning>.panel-footer, #bootstrap-theme .panel-warning>.panel-heading, #bootstrap-theme .panel-warning>.panel-subheading, #bootstrap-theme .panel-danger>.panel-body, #bootstrap-theme .panel-danger>.panel-footer, #bootstrap-theme .panel-danger>.panel-heading, #bootstrap-theme .panel-danger>.panel-subheading{-webkit-box-shadow:0 3px 18px 0 rgba(48,40,40,0.25);box-shadow:0 3px 18px 0 rgba(48,40,40,0.25)}#bootstrap-theme .panel-primary>.panel-body, #bootstrap-theme .panel-success>.panel-body, #bootstrap-theme .panel-info>.panel-body, #bootstrap-theme .panel-warning>.panel-body, #bootstrap-theme .panel-danger>.panel-body{position:relative}#bootstrap-theme .panel-primary>.panel-body:before, #bootstrap-theme .panel-success>.panel-body:before, #bootstrap-theme .panel-info>.panel-body:before, #bootstrap-theme .panel-warning>.panel-body:before, #bootstrap-theme .panel-danger>.panel-body:before{content:"";background:#fff;bottom:0;height:10px;left:0;position:absolute;width:100%;z-index:2}#bootstrap-theme .panel-primary>.panel-footer, #bootstrap-theme .panel-success>.panel-footer, #bootstrap-theme .panel-info>.panel-footer, #bootstrap-theme .panel-warning>.panel-footer, #bootstrap-theme .panel-danger>.panel-footer{position:relative}#bootstrap-theme .panel-primary:not(.panel-w-subheading)>.panel-heading, #bootstrap-theme .panel-primary.panel-w-subheading>.panel-subheading, #bootstrap-theme .panel-success:not(.panel-w-subheading)>.panel-heading, #bootstrap-theme .panel-success.panel-w-subheading>.panel-subheading, #bootstrap-theme .panel-info:not(.panel-w-subheading)>.panel-heading, #bootstrap-theme .panel-info.panel-w-subheading>.panel-subheading, #bootstrap-theme .panel-warning:not(.panel-w-subheading)>.panel-heading, #bootstrap-theme .panel-warning.panel-w-subheading>.panel-subheading, #bootstrap-theme .panel-danger:not(.panel-w-subheading)>.panel-heading, #bootstrap-theme .panel-danger.panel-w-subheading>.panel-subheading{border-bottom-right-radius:2px;border-bottom-left-radius:2px;margin-bottom:10px}#bootstrap-theme .panel-heading-w-buttons .btn{margin:-8px 0 -8px 10px}#bootstrap-theme [data-toggle="collapse"]:before{font-family:'FontAwesome';font-style:normal;text-rendering:auto;font-size:.8em;color:#4d4d69;color:inherit;content:"";font-size:inherit;margin-right:10px}#bootstrap-theme [data-toggle="collapse"].collapsed:before{content:""}#bootstrap-theme .progress-bar-success, #bootstrap-theme .progress-bar-warning{color:#464354}#bootstrap-theme table, #bootstrap-theme .table{margin-bottom:0}#bootstrap-theme table th, #bootstrap-theme .table th{background:#f3f6f7}#bootstrap-theme table>thead>tr>th, #bootstrap-theme .table>thead>tr>th{border-bottom:0;color:#464354;text-transform:capitalize}#bootstrap-theme table:not(.table-bordered)>tbody>tr:first-child>td, #bootstrap-theme .table:not(.table-bordered)>tbody>tr:first-child>td{border-color:#d3dee2}#bootstrap-theme .table-tab{margin:-15px -20px}#bootstrap-theme .table-tab>table>thead>tr>th{background:#fff}#bootstrap-theme .panel-default>.table, #bootstrap-theme .panel-default>.table-responsive>.table, #bootstrap-theme .panel-default>.panel-collapse>.table{border-top:1px solid #d3dee2}#bootstrap-theme .panel .table, #bootstrap-theme .panel .table-responsive{margin-bottom:0}#bootstrap-theme h1, #bootstrap-theme .h1{font-weight:600}#bootstrap-theme h2, #bootstrap-theme .h2{font-weight:600}#bootstrap-theme h3, #bootstrap-theme .h3{font-weight:600}#bootstrap-theme h4, #bootstrap-theme .h4{font-weight:500}#bootstrap-theme h5, #bootstrap-theme .h5{font-weight:700}#bootstrap-theme h6, #bootstrap-theme .h6{font-weight:500}#bootstrap-theme .text-default{color:#4d4d69}#bootstrap-theme a.text-default:hover, #bootstrap-theme a.text-default:focus{color:#37374c}#bootstrap-theme .text-default-darker{color:#464354}#bootstrap-theme a.text-default-darker:hover, #bootstrap-theme a.text-default-darker:focus{color:#2e2c38}#bootstrap-theme .page-header{-webkit-box-shadow:0 3px 18px 0 rgba(48,40,40,0.25);box-shadow:0 3px 18px 0 rgba(48,40,40,0.25);background:#fff;border:0;padding:13px 20px;margin:0 0 15px 0}#bootstrap-theme .page-header>h1, #bootstrap-theme .page-header>h2, #bootstrap-theme .page-header>h3, #bootstrap-theme .page-header>h4, #bootstrap-theme .page-header>h5, #bootstrap-theme .page-header>h6{font-size:1.3846153846em;font-weight:600;line-height:1.34;margin:0}#bootstrap-theme .page-header-primary{background:#0071bd}#bootstrap-theme .page-header-primary>h1, #bootstrap-theme .page-header-primary>h2, #bootstrap-theme .page-header-primary>h3, #bootstrap-theme .page-header-primary>h4, #bootstrap-theme .page-header-primary>h5, #bootstrap-theme .page-header-primary>h6{color:#fff}#bootstrap-theme .page-header-primary>h1 small, #bootstrap-theme .page-header-primary>h2 small, #bootstrap-theme .page-header-primary>h3 small, #bootstrap-theme .page-header-primary>h4 small, #bootstrap-theme .page-header-primary>h5 small, #bootstrap-theme .page-header-primary>h6 small{color:#e8eef0}#bootstrap-theme .page-header-success{background:#44cb7e}#bootstrap-theme .page-header-success>h1, #bootstrap-theme .page-header-success>h2, #bootstrap-theme .page-header-success>h3, #bootstrap-theme .page-header-success>h4, #bootstrap-theme .page-header-success>h5, #bootstrap-theme .page-header-success>h6{color:#464354}#bootstrap-theme .page-header-success>h1 small, #bootstrap-theme .page-header-success>h2 small, #bootstrap-theme .page-header-success>h3 small, #bootstrap-theme .page-header-success>h4 small, #bootstrap-theme .page-header-success>h5 small, #bootstrap-theme .page-header-success>h6 small{color:#464354}#bootstrap-theme .page-header-info{background:#0071bd}#bootstrap-theme .page-header-info>h1, #bootstrap-theme .page-header-info>h2, #bootstrap-theme .page-header-info>h3, #bootstrap-theme .page-header-info>h4, #bootstrap-theme .page-header-info>h5, #bootstrap-theme .page-header-info>h6{color:#fff}#bootstrap-theme .page-header-info>h1 small, #bootstrap-theme .page-header-info>h2 small, #bootstrap-theme .page-header-info>h3 small, #bootstrap-theme .page-header-info>h4 small, #bootstrap-theme .page-header-info>h5 small, #bootstrap-theme .page-header-info>h6 small{color:#e8eef0}#bootstrap-theme .page-header-warning{background:#e6ab5e}#bootstrap-theme .page-header-warning>h1, #bootstrap-theme .page-header-warning>h2, #bootstrap-theme .page-header-warning>h3, #bootstrap-theme .page-header-warning>h4, #bootstrap-theme .page-header-warning>h5, #bootstrap-theme .page-header-warning>h6{color:#464354}#bootstrap-theme .page-header-warning>h1 small, #bootstrap-theme .page-header-warning>h2 small, #bootstrap-theme .page-header-warning>h3 small, #bootstrap-theme .page-header-warning>h4 small, #bootstrap-theme .page-header-warning>h5 small, #bootstrap-theme .page-header-warning>h6 small{color:#464354}#bootstrap-theme .page-header-danger{background:#cf3458}#bootstrap-theme .page-header-danger>h1, #bootstrap-theme .page-header-danger>h2, #bootstrap-theme .page-header-danger>h3, #bootstrap-theme .page-header-danger>h4, #bootstrap-theme .page-header-danger>h5, #bootstrap-theme .page-header-danger>h6{color:#fff}#bootstrap-theme .page-header-danger>h1 small, #bootstrap-theme .page-header-danger>h2 small, #bootstrap-theme .page-header-danger>h3 small, #bootstrap-theme .page-header-danger>h4 small, #bootstrap-theme .page-header-danger>h5 small, #bootstrap-theme .page-header-danger>h6 small{color:#e8eef0}#bootstrap-theme dl:last-child{margin-bottom:0}#bootstrap-theme dt{color:#464354;font-weight:600}#bootstrap-theme dt:not(:first-child){margin-top:1.7692307692em}@media (min-width: 768px){#bootstrap-theme .dl-horizontal dt{text-align:left}#bootstrap-theme .dl-horizontal dt:not(:first-child)+dd{margin-top:1.7692307692em}}@media (min-width: 768px){#bootstrap-theme .dl-horizontal-half dd{margin-left:125px}#bootstrap-theme .dl-horizontal-half dt{width:125px}}@media (min-width: 768px){#bootstrap-theme .dl-horizontal-full:before, #bootstrap-theme .dl-horizontal-full:after{display:table;content:" "}#bootstrap-theme .dl-horizontal-full:after{clear:both}#bootstrap-theme .dl-horizontal-full dt{width:auto}#bootstrap-theme .dl-horizontal-full dd{float:right;margin-left:0}}@media (min-width: 768px){#bootstrap-theme .dl-horizontal-inline{display:inline-block;vertical-align:top}#bootstrap-theme .dl-horizontal-inline:before, #bootstrap-theme .dl-horizontal-inline:after{display:table;content:" "}#bootstrap-theme .dl-horizontal-inline:after{clear:both}#bootstrap-theme .dl-horizontal-inline dt{width:auto}#bootstrap-theme .dl-horizontal-inline dd{float:left;margin-left:10px}}#bootstrap-theme .dl-inline>dd, #bootstrap-theme .dl-inline>dt{display:inline-block}#bootstrap-theme .dl-inline>dt{margin:0 5px 0 0}#bootstrap-theme .dl-inline>dt:not(:first-child){margin-left:15px}#bootstrap-theme .select2-container .select2-choices{margin-bottom:0;padding-left:0}#bootstrap-theme .select2-container .select2-choice>.select2-chosen{font-size:13px}#bootstrap-theme .select2-container.form-control.select2-dropdown-open{border-color:#0071bd}#bootstrap-theme .ui-dialog-content#bootstrap-theme{background:#fff;padding:10px 20px}#bootstrap-theme .btn-slide>ul.panel{display:none !important}#bootstrap-theme .crm-button-type-next, #bootstrap-theme .crm-button-type-upload{color:#fff;background-image:none;background-color:transparent;border-color:#0071bd}#bootstrap-theme .crm-button-type-next:focus, #bootstrap-theme .crm-button-type-next.focus, #bootstrap-theme .crm-button-type-next:active, #bootstrap-theme .crm-button-type-next.active, #bootstrap-theme .open>.crm-button-type-next.dropdown-toggle, #bootstrap-theme .crm-button-type-upload:focus, #bootstrap-theme .crm-button-type-upload.focus, #bootstrap-theme .crm-button-type-upload:active, #bootstrap-theme .crm-button-type-upload.active, #bootstrap-theme .open>.crm-button-type-upload.dropdown-toggle{color:#fff;background-color:#0071bd;border-color:#0071bd}#bootstrap-theme .crm-button-type-next:hover, #bootstrap-theme .crm-button-type-upload:hover{color:#fff;background-color:#005c99;border-color:#005c99}#bootstrap-theme .crm-button-type-next.disabled:focus, #bootstrap-theme .crm-button-type-next.disabled.focus, #bootstrap-theme .crm-button-type-next:disabled:focus, #bootstrap-theme .crm-button-type-next:disabled.focus, #bootstrap-theme fieldset[disabled] .crm-button-type-next:focus, #bootstrap-theme fieldset[disabled] .crm-button-type-next.focus, #bootstrap-theme .crm-button-type-upload.disabled:focus, #bootstrap-theme .crm-button-type-upload.disabled.focus, #bootstrap-theme .crm-button-type-upload:disabled:focus, #bootstrap-theme .crm-button-type-upload:disabled.focus, #bootstrap-theme fieldset[disabled] .crm-button-type-upload:focus, #bootstrap-theme fieldset[disabled] .crm-button-type-upload.focus{border-color:#fff}#bootstrap-theme .crm-button-type-next.disabled:hover, #bootstrap-theme .crm-button-type-next:disabled:hover, #bootstrap-theme fieldset[disabled] .crm-button-type-next:hover, #bootstrap-theme .crm-button-type-upload.disabled:hover, #bootstrap-theme .crm-button-type-upload:disabled:hover, #bootstrap-theme fieldset[disabled] .crm-button-type-upload:hover{border-color:#fff}#bootstrap-theme .crm-button-type-next .crm-i, #bootstrap-theme .crm-button-type-upload .crm-i{display:none}#bootstrap-theme .crm-button-type-back, #bootstrap-theme .crm-button-type-cancel, #bootstrap-theme .crm-button-type-delete{border-color:#e8eef0}#bootstrap-theme .crm-button-type-back input, #bootstrap-theme .crm-button-type-cancel input, #bootstrap-theme .crm-button-type-delete input{color:#464354 !important}#bootstrap-theme .crm-button-type-back input:hover, #bootstrap-theme .crm-button-type-cancel input:hover, #bootstrap-theme .crm-button-type-delete input:hover{color:#4d4d69 !important}#bootstrap-theme .crm-error{border-radius:2px;display:inline-block;font-size:12px;margin-top:5px}#bootstrap-theme input[type=file].error{padding-left:5px}#bootstrap-theme .crm-form-radio+label::before{min-width:16px;width:16px;height:16px}#bootstrap-theme .crm-form-radio+label::after{top:8px}#bootstrap-theme .crm-form-text{height:30px}#bootstrap-theme a.crm-i:hover, #bootstrap-theme a.helpicon:hover{text-decoration:none}#bootstrap-theme label .crm-marker-description{font-weight:normal}#bootstrap-theme .panel .panel-footer .button:last-child, #bootstrap-theme .panel .panel-footer .crm-button:last-child{margin-right:0}#bootstrap-theme table thead.sticky th>.sticky-header{background:#f3f6f7;border-bottom:1px solid #d3dee2;height:auto;margin-top:8px}#bootstrap-theme .crm-weight-arrow img.order-icon{height:18px;width:13px}#bootstrap-theme .crm-container.ui-dialog{z-index:2001}#bootstrap-theme .ui-widget-overlay.ui-front{z-index:2000}@media (max-width: 480px){#bootstrap-theme .modal-dialog:not(.modal-sm){border:none;border-radius:0;box-shadow:none;height:100%;margin:0;width:100%}#bootstrap-theme .modal-dialog:not(.modal-sm) .close{font-size:30px;pointer-events:auto}#bootstrap-theme .modal-dialog:not(.modal-sm) .modal-content{border:none;border-radius:0;height:100%;overflow-x:hidden;overflow-y:auto}#bootstrap-theme .modal-dialog:not(.modal-sm) .modal-header{pointer-events:none}#bootstrap-theme .modal-dialog:not(.modal-sm) .modal-body, #bootstrap-theme .modal-dialog:not(.modal-sm) .modal-header, #bootstrap-theme .modal-dialog:not(.modal-sm) .modal-footer{border-radius:0;padding:10px}}#bootstrap-theme .btn-default-outline{color:#fff;background-image:none;background-color:transparent;border-color:#fff}#bootstrap-theme .btn-default-outline:focus, #bootstrap-theme .btn-default-outline.focus, #bootstrap-theme .btn-default-outline:active, #bootstrap-theme .btn-default-outline.active, #bootstrap-theme .open>.btn-default-outline.dropdown-toggle{color:#fff;background-color:#fff;border-color:#fff}#bootstrap-theme .btn-default-outline:hover{color:#fff;background-color:#ededed;border-color:#ededed}#bootstrap-theme .btn-default-outline.disabled:focus, #bootstrap-theme .btn-default-outline.disabled.focus, #bootstrap-theme .btn-default-outline:disabled:focus, #bootstrap-theme .btn-default-outline:disabled.focus, #bootstrap-theme fieldset[disabled] .btn-default-outline:focus, #bootstrap-theme fieldset[disabled] .btn-default-outline.focus{border-color:#fff}#bootstrap-theme .btn-default-outline.disabled:hover, #bootstrap-theme .btn-default-outline:disabled:hover, #bootstrap-theme fieldset[disabled] .btn-default-outline:hover{border-color:#fff}#bootstrap-theme .open .btn-default-outline, #bootstrap-theme .btn-default-outline:hover, #bootstrap-theme .btn-default-outline:active{background-color:#fff !important;color:#4d4d69 !important}#bootstrap-theme .btn-primary-outline{color:#464354;background-image:none;background-color:transparent;border-color:#0071bd}#bootstrap-theme .btn-primary-outline:focus, #bootstrap-theme .btn-primary-outline.focus, #bootstrap-theme .btn-primary-outline:active, #bootstrap-theme .btn-primary-outline.active, #bootstrap-theme .open>.btn-primary-outline.dropdown-toggle{color:#fff;background-color:#0071bd;border-color:#0071bd}#bootstrap-theme .btn-primary-outline:hover{color:#fff;background-color:#005c99;border-color:#005c99}#bootstrap-theme .btn-primary-outline.disabled:focus, #bootstrap-theme .btn-primary-outline.disabled.focus, #bootstrap-theme .btn-primary-outline:disabled:focus, #bootstrap-theme .btn-primary-outline:disabled.focus, #bootstrap-theme fieldset[disabled] .btn-primary-outline:focus, #bootstrap-theme fieldset[disabled] .btn-primary-outline.focus{border-color:#75708d}#bootstrap-theme .btn-primary-outline.disabled:hover, #bootstrap-theme .btn-primary-outline:disabled:hover, #bootstrap-theme fieldset[disabled] .btn-primary-outline:hover{border-color:#75708d}#bootstrap-theme .btn-secondary-outline{color:#464354;background-image:none;background-color:transparent;border-color:#4d4d69;border-color:#464354 !important}#bootstrap-theme .btn-secondary-outline:focus, #bootstrap-theme .btn-secondary-outline.focus, #bootstrap-theme .btn-secondary-outline:active, #bootstrap-theme .btn-secondary-outline.active, #bootstrap-theme .open>.btn-secondary-outline.dropdown-toggle{color:#fff;background-color:#4d4d69;border-color:#4d4d69}#bootstrap-theme .btn-secondary-outline:hover{color:#fff;background-color:#3e3e54;border-color:#3e3e54}#bootstrap-theme .btn-secondary-outline.disabled:focus, #bootstrap-theme .btn-secondary-outline.disabled.focus, #bootstrap-theme .btn-secondary-outline:disabled:focus, #bootstrap-theme .btn-secondary-outline:disabled.focus, #bootstrap-theme fieldset[disabled] .btn-secondary-outline:focus, #bootstrap-theme fieldset[disabled] .btn-secondary-outline.focus{border-color:#75708d}#bootstrap-theme .btn-secondary-outline.disabled:hover, #bootstrap-theme .btn-secondary-outline:disabled:hover, #bootstrap-theme fieldset[disabled] .btn-secondary-outline:hover{border-color:#75708d}#bootstrap-theme .btn-secondary-outline:hover{color:#fff !important}#bootstrap-theme .btn-info-outline{color:#464354;background-image:none;background-color:transparent;border-color:#0071bd}#bootstrap-theme .btn-info-outline:focus, #bootstrap-theme .btn-info-outline.focus, #bootstrap-theme .btn-info-outline:active, #bootstrap-theme .btn-info-outline.active, #bootstrap-theme .open>.btn-info-outline.dropdown-toggle{color:#fff;background-color:#0071bd;border-color:#0071bd}#bootstrap-theme .btn-info-outline:hover{color:#fff;background-color:#005c99;border-color:#005c99}#bootstrap-theme .btn-info-outline.disabled:focus, #bootstrap-theme .btn-info-outline.disabled.focus, #bootstrap-theme .btn-info-outline:disabled:focus, #bootstrap-theme .btn-info-outline:disabled.focus, #bootstrap-theme fieldset[disabled] .btn-info-outline:focus, #bootstrap-theme fieldset[disabled] .btn-info-outline.focus{border-color:#75708d}#bootstrap-theme .btn-info-outline.disabled:hover, #bootstrap-theme .btn-info-outline:disabled:hover, #bootstrap-theme fieldset[disabled] .btn-info-outline:hover{border-color:#75708d}#bootstrap-theme .btn-success-outline{color:#464354;background-image:none;background-color:transparent;border-color:#44cb7e}#bootstrap-theme .btn-success-outline:focus, #bootstrap-theme .btn-success-outline.focus, #bootstrap-theme .btn-success-outline:active, #bootstrap-theme .btn-success-outline.active, #bootstrap-theme .open>.btn-success-outline.dropdown-toggle{color:#fff;background-color:#44cb7e;border-color:#44cb7e}#bootstrap-theme .btn-success-outline:hover{color:#000;background-color:#33b86c;border-color:#33b86c}#bootstrap-theme .btn-success-outline.disabled:focus, #bootstrap-theme .btn-success-outline.disabled.focus, #bootstrap-theme .btn-success-outline:disabled:focus, #bootstrap-theme .btn-success-outline:disabled.focus, #bootstrap-theme fieldset[disabled] .btn-success-outline:focus, #bootstrap-theme fieldset[disabled] .btn-success-outline.focus{border-color:#75708d}#bootstrap-theme .btn-success-outline.disabled:hover, #bootstrap-theme .btn-success-outline:disabled:hover, #bootstrap-theme fieldset[disabled] .btn-success-outline:hover{border-color:#75708d}#bootstrap-theme .btn-warning-outline{color:#464354;background-image:none;background-color:transparent;border-color:#e6ab5e}#bootstrap-theme .btn-warning-outline:focus, #bootstrap-theme .btn-warning-outline.focus, #bootstrap-theme .btn-warning-outline:active, #bootstrap-theme .btn-warning-outline.active, #bootstrap-theme .open>.btn-warning-outline.dropdown-toggle{color:#fff;background-color:#e6ab5e;border-color:#e6ab5e}#bootstrap-theme .btn-warning-outline:hover{color:#000;background-color:#e19b3f;border-color:#e19b3f}#bootstrap-theme .btn-warning-outline.disabled:focus, #bootstrap-theme .btn-warning-outline.disabled.focus, #bootstrap-theme .btn-warning-outline:disabled:focus, #bootstrap-theme .btn-warning-outline:disabled.focus, #bootstrap-theme fieldset[disabled] .btn-warning-outline:focus, #bootstrap-theme fieldset[disabled] .btn-warning-outline.focus{border-color:#75708d}#bootstrap-theme .btn-warning-outline.disabled:hover, #bootstrap-theme .btn-warning-outline:disabled:hover, #bootstrap-theme fieldset[disabled] .btn-warning-outline:hover{border-color:#75708d}#bootstrap-theme .btn-danger-outline{color:#464354;background-image:none;background-color:transparent;border-color:#cf3458}#bootstrap-theme .btn-danger-outline:focus, #bootstrap-theme .btn-danger-outline.focus, #bootstrap-theme .btn-danger-outline:active, #bootstrap-theme .btn-danger-outline.active, #bootstrap-theme .open>.btn-danger-outline.dropdown-toggle{color:#fff;background-color:#cf3458;border-color:#cf3458}#bootstrap-theme .btn-danger-outline:hover{color:#fff;background-color:#b52b4b;border-color:#b52b4b}#bootstrap-theme .btn-danger-outline.disabled:focus, #bootstrap-theme .btn-danger-outline.disabled.focus, #bootstrap-theme .btn-danger-outline:disabled:focus, #bootstrap-theme .btn-danger-outline:disabled.focus, #bootstrap-theme fieldset[disabled] .btn-danger-outline:focus, #bootstrap-theme fieldset[disabled] .btn-danger-outline.focus{border-color:#75708d}#bootstrap-theme .btn-danger-outline.disabled:hover, #bootstrap-theme .btn-danger-outline:disabled:hover, #bootstrap-theme fieldset[disabled] .btn-danger-outline:hover{border-color:#75708d}#bootstrap-theme .chart-color-0{background:#5A6779 !important;border-color:#5A6779 !important;fill:#5A6779 !important}#bootstrap-theme .chart-color-bg-0{background:#5A6779 !important;fill:#5A6779 !important}#bootstrap-theme .chart-color-border-0{border-color:#5A6779 !important}#bootstrap-theme .chart-color-stroke-0{stroke:#5A6779 !important}#bootstrap-theme .chart-color-28{background:#5A6779 !important;border-color:#5A6779 !important;fill:#5A6779 !important}#bootstrap-theme .chart-color-bg-28{background:#5A6779 !important;fill:#5A6779 !important}#bootstrap-theme .chart-color-border-28{border-color:#5A6779 !important}#bootstrap-theme .chart-color-stroke-28{stroke:#5A6779 !important}#bootstrap-theme .chart-color-56{background:#5A6779 !important;border-color:#5A6779 !important;fill:#5A6779 !important}#bootstrap-theme .chart-color-bg-56{background:#5A6779 !important;fill:#5A6779 !important}#bootstrap-theme .chart-color-border-56{border-color:#5A6779 !important}#bootstrap-theme .chart-color-stroke-56{stroke:#5A6779 !important}#bootstrap-theme .chart-color-84{background:#5A6779 !important;border-color:#5A6779 !important;fill:#5A6779 !important}#bootstrap-theme .chart-color-bg-84{background:#5A6779 !important;fill:#5A6779 !important}#bootstrap-theme .chart-color-border-84{border-color:#5A6779 !important}#bootstrap-theme .chart-color-stroke-84{stroke:#5A6779 !important}#bootstrap-theme .chart-color-112{background:#5A6779 !important;border-color:#5A6779 !important;fill:#5A6779 !important}#bootstrap-theme .chart-color-bg-112{background:#5A6779 !important;fill:#5A6779 !important}#bootstrap-theme .chart-color-border-112{border-color:#5A6779 !important}#bootstrap-theme .chart-color-stroke-112{stroke:#5A6779 !important}#bootstrap-theme .chart-color-1{background:#E5807F !important;border-color:#E5807F !important;fill:#E5807F !important}#bootstrap-theme .chart-color-bg-1{background:#E5807F !important;fill:#E5807F !important}#bootstrap-theme .chart-color-border-1{border-color:#E5807F !important}#bootstrap-theme .chart-color-stroke-1{stroke:#E5807F !important}#bootstrap-theme .chart-color-29{background:#E5807F !important;border-color:#E5807F !important;fill:#E5807F !important}#bootstrap-theme .chart-color-bg-29{background:#E5807F !important;fill:#E5807F !important}#bootstrap-theme .chart-color-border-29{border-color:#E5807F !important}#bootstrap-theme .chart-color-stroke-29{stroke:#E5807F !important}#bootstrap-theme .chart-color-57{background:#E5807F !important;border-color:#E5807F !important;fill:#E5807F !important}#bootstrap-theme .chart-color-bg-57{background:#E5807F !important;fill:#E5807F !important}#bootstrap-theme .chart-color-border-57{border-color:#E5807F !important}#bootstrap-theme .chart-color-stroke-57{stroke:#E5807F !important}#bootstrap-theme .chart-color-85{background:#E5807F !important;border-color:#E5807F !important;fill:#E5807F !important}#bootstrap-theme .chart-color-bg-85{background:#E5807F !important;fill:#E5807F !important}#bootstrap-theme .chart-color-border-85{border-color:#E5807F !important}#bootstrap-theme .chart-color-stroke-85{stroke:#E5807F !important}#bootstrap-theme .chart-color-113{background:#E5807F !important;border-color:#E5807F !important;fill:#E5807F !important}#bootstrap-theme .chart-color-bg-113{background:#E5807F !important;fill:#E5807F !important}#bootstrap-theme .chart-color-border-113{border-color:#E5807F !important}#bootstrap-theme .chart-color-stroke-113{stroke:#E5807F !important}#bootstrap-theme .chart-color-2{background:#ECA67F !important;border-color:#ECA67F !important;fill:#ECA67F !important}#bootstrap-theme .chart-color-bg-2{background:#ECA67F !important;fill:#ECA67F !important}#bootstrap-theme .chart-color-border-2{border-color:#ECA67F !important}#bootstrap-theme .chart-color-stroke-2{stroke:#ECA67F !important}#bootstrap-theme .chart-color-30{background:#ECA67F !important;border-color:#ECA67F !important;fill:#ECA67F !important}#bootstrap-theme .chart-color-bg-30{background:#ECA67F !important;fill:#ECA67F !important}#bootstrap-theme .chart-color-border-30{border-color:#ECA67F !important}#bootstrap-theme .chart-color-stroke-30{stroke:#ECA67F !important}#bootstrap-theme .chart-color-58{background:#ECA67F !important;border-color:#ECA67F !important;fill:#ECA67F !important}#bootstrap-theme .chart-color-bg-58{background:#ECA67F !important;fill:#ECA67F !important}#bootstrap-theme .chart-color-border-58{border-color:#ECA67F !important}#bootstrap-theme .chart-color-stroke-58{stroke:#ECA67F !important}#bootstrap-theme .chart-color-86{background:#ECA67F !important;border-color:#ECA67F !important;fill:#ECA67F !important}#bootstrap-theme .chart-color-bg-86{background:#ECA67F !important;fill:#ECA67F !important}#bootstrap-theme .chart-color-border-86{border-color:#ECA67F !important}#bootstrap-theme .chart-color-stroke-86{stroke:#ECA67F !important}#bootstrap-theme .chart-color-114{background:#ECA67F !important;border-color:#ECA67F !important;fill:#ECA67F !important}#bootstrap-theme .chart-color-bg-114{background:#ECA67F !important;fill:#ECA67F !important}#bootstrap-theme .chart-color-border-114{border-color:#ECA67F !important}#bootstrap-theme .chart-color-stroke-114{stroke:#ECA67F !important}#bootstrap-theme .chart-color-3{background:#8EC68A !important;border-color:#8EC68A !important;fill:#8EC68A !important}#bootstrap-theme .chart-color-bg-3{background:#8EC68A !important;fill:#8EC68A !important}#bootstrap-theme .chart-color-border-3{border-color:#8EC68A !important}#bootstrap-theme .chart-color-stroke-3{stroke:#8EC68A !important}#bootstrap-theme .chart-color-31{background:#8EC68A !important;border-color:#8EC68A !important;fill:#8EC68A !important}#bootstrap-theme .chart-color-bg-31{background:#8EC68A !important;fill:#8EC68A !important}#bootstrap-theme .chart-color-border-31{border-color:#8EC68A !important}#bootstrap-theme .chart-color-stroke-31{stroke:#8EC68A !important}#bootstrap-theme .chart-color-59{background:#8EC68A !important;border-color:#8EC68A !important;fill:#8EC68A !important}#bootstrap-theme .chart-color-bg-59{background:#8EC68A !important;fill:#8EC68A !important}#bootstrap-theme .chart-color-border-59{border-color:#8EC68A !important}#bootstrap-theme .chart-color-stroke-59{stroke:#8EC68A !important}#bootstrap-theme .chart-color-87{background:#8EC68A !important;border-color:#8EC68A !important;fill:#8EC68A !important}#bootstrap-theme .chart-color-bg-87{background:#8EC68A !important;fill:#8EC68A !important}#bootstrap-theme .chart-color-border-87{border-color:#8EC68A !important}#bootstrap-theme .chart-color-stroke-87{stroke:#8EC68A !important}#bootstrap-theme .chart-color-115{background:#8EC68A !important;border-color:#8EC68A !important;fill:#8EC68A !important}#bootstrap-theme .chart-color-bg-115{background:#8EC68A !important;fill:#8EC68A !important}#bootstrap-theme .chart-color-border-115{border-color:#8EC68A !important}#bootstrap-theme .chart-color-stroke-115{stroke:#8EC68A !important}#bootstrap-theme .chart-color-4{background:#C096AA !important;border-color:#C096AA !important;fill:#C096AA !important}#bootstrap-theme .chart-color-bg-4{background:#C096AA !important;fill:#C096AA !important}#bootstrap-theme .chart-color-border-4{border-color:#C096AA !important}#bootstrap-theme .chart-color-stroke-4{stroke:#C096AA !important}#bootstrap-theme .chart-color-32{background:#C096AA !important;border-color:#C096AA !important;fill:#C096AA !important}#bootstrap-theme .chart-color-bg-32{background:#C096AA !important;fill:#C096AA !important}#bootstrap-theme .chart-color-border-32{border-color:#C096AA !important}#bootstrap-theme .chart-color-stroke-32{stroke:#C096AA !important}#bootstrap-theme .chart-color-60{background:#C096AA !important;border-color:#C096AA !important;fill:#C096AA !important}#bootstrap-theme .chart-color-bg-60{background:#C096AA !important;fill:#C096AA !important}#bootstrap-theme .chart-color-border-60{border-color:#C096AA !important}#bootstrap-theme .chart-color-stroke-60{stroke:#C096AA !important}#bootstrap-theme .chart-color-88{background:#C096AA !important;border-color:#C096AA !important;fill:#C096AA !important}#bootstrap-theme .chart-color-bg-88{background:#C096AA !important;fill:#C096AA !important}#bootstrap-theme .chart-color-border-88{border-color:#C096AA !important}#bootstrap-theme .chart-color-stroke-88{stroke:#C096AA !important}#bootstrap-theme .chart-color-116{background:#C096AA !important;border-color:#C096AA !important;fill:#C096AA !important}#bootstrap-theme .chart-color-bg-116{background:#C096AA !important;fill:#C096AA !important}#bootstrap-theme .chart-color-border-116{border-color:#C096AA !important}#bootstrap-theme .chart-color-stroke-116{stroke:#C096AA !important}#bootstrap-theme .chart-color-5{background:#9579A8 !important;border-color:#9579A8 !important;fill:#9579A8 !important}#bootstrap-theme .chart-color-bg-5{background:#9579A8 !important;fill:#9579A8 !important}#bootstrap-theme .chart-color-border-5{border-color:#9579A8 !important}#bootstrap-theme .chart-color-stroke-5{stroke:#9579A8 !important}#bootstrap-theme .chart-color-33{background:#9579A8 !important;border-color:#9579A8 !important;fill:#9579A8 !important}#bootstrap-theme .chart-color-bg-33{background:#9579A8 !important;fill:#9579A8 !important}#bootstrap-theme .chart-color-border-33{border-color:#9579A8 !important}#bootstrap-theme .chart-color-stroke-33{stroke:#9579A8 !important}#bootstrap-theme .chart-color-61{background:#9579A8 !important;border-color:#9579A8 !important;fill:#9579A8 !important}#bootstrap-theme .chart-color-bg-61{background:#9579A8 !important;fill:#9579A8 !important}#bootstrap-theme .chart-color-border-61{border-color:#9579A8 !important}#bootstrap-theme .chart-color-stroke-61{stroke:#9579A8 !important}#bootstrap-theme .chart-color-89{background:#9579A8 !important;border-color:#9579A8 !important;fill:#9579A8 !important}#bootstrap-theme .chart-color-bg-89{background:#9579A8 !important;fill:#9579A8 !important}#bootstrap-theme .chart-color-border-89{border-color:#9579A8 !important}#bootstrap-theme .chart-color-stroke-89{stroke:#9579A8 !important}#bootstrap-theme .chart-color-117{background:#9579A8 !important;border-color:#9579A8 !important;fill:#9579A8 !important}#bootstrap-theme .chart-color-bg-117{background:#9579A8 !important;fill:#9579A8 !important}#bootstrap-theme .chart-color-border-117{border-color:#9579A8 !important}#bootstrap-theme .chart-color-stroke-117{stroke:#9579A8 !important}#bootstrap-theme .chart-color-6{background:#42B0CB !important;border-color:#42B0CB !important;fill:#42B0CB !important}#bootstrap-theme .chart-color-bg-6{background:#42B0CB !important;fill:#42B0CB !important}#bootstrap-theme .chart-color-border-6{border-color:#42B0CB !important}#bootstrap-theme .chart-color-stroke-6{stroke:#42B0CB !important}#bootstrap-theme .chart-color-34{background:#42B0CB !important;border-color:#42B0CB !important;fill:#42B0CB !important}#bootstrap-theme .chart-color-bg-34{background:#42B0CB !important;fill:#42B0CB !important}#bootstrap-theme .chart-color-border-34{border-color:#42B0CB !important}#bootstrap-theme .chart-color-stroke-34{stroke:#42B0CB !important}#bootstrap-theme .chart-color-62{background:#42B0CB !important;border-color:#42B0CB !important;fill:#42B0CB !important}#bootstrap-theme .chart-color-bg-62{background:#42B0CB !important;fill:#42B0CB !important}#bootstrap-theme .chart-color-border-62{border-color:#42B0CB !important}#bootstrap-theme .chart-color-stroke-62{stroke:#42B0CB !important}#bootstrap-theme .chart-color-90{background:#42B0CB !important;border-color:#42B0CB !important;fill:#42B0CB !important}#bootstrap-theme .chart-color-bg-90{background:#42B0CB !important;fill:#42B0CB !important}#bootstrap-theme .chart-color-border-90{border-color:#42B0CB !important}#bootstrap-theme .chart-color-stroke-90{stroke:#42B0CB !important}#bootstrap-theme .chart-color-118{background:#42B0CB !important;border-color:#42B0CB !important;fill:#42B0CB !important}#bootstrap-theme .chart-color-bg-118{background:#42B0CB !important;fill:#42B0CB !important}#bootstrap-theme .chart-color-border-118{border-color:#42B0CB !important}#bootstrap-theme .chart-color-stroke-118{stroke:#42B0CB !important}#bootstrap-theme .chart-color-7{background:#3D4A5E !important;border-color:#3D4A5E !important;fill:#3D4A5E !important}#bootstrap-theme .chart-color-bg-7{background:#3D4A5E !important;fill:#3D4A5E !important}#bootstrap-theme .chart-color-border-7{border-color:#3D4A5E !important}#bootstrap-theme .chart-color-stroke-7{stroke:#3D4A5E !important}#bootstrap-theme .chart-color-35{background:#3D4A5E !important;border-color:#3D4A5E !important;fill:#3D4A5E !important}#bootstrap-theme .chart-color-bg-35{background:#3D4A5E !important;fill:#3D4A5E !important}#bootstrap-theme .chart-color-border-35{border-color:#3D4A5E !important}#bootstrap-theme .chart-color-stroke-35{stroke:#3D4A5E !important}#bootstrap-theme .chart-color-63{background:#3D4A5E !important;border-color:#3D4A5E !important;fill:#3D4A5E !important}#bootstrap-theme .chart-color-bg-63{background:#3D4A5E !important;fill:#3D4A5E !important}#bootstrap-theme .chart-color-border-63{border-color:#3D4A5E !important}#bootstrap-theme .chart-color-stroke-63{stroke:#3D4A5E !important}#bootstrap-theme .chart-color-91{background:#3D4A5E !important;border-color:#3D4A5E !important;fill:#3D4A5E !important}#bootstrap-theme .chart-color-bg-91{background:#3D4A5E !important;fill:#3D4A5E !important}#bootstrap-theme .chart-color-border-91{border-color:#3D4A5E !important}#bootstrap-theme .chart-color-stroke-91{stroke:#3D4A5E !important}#bootstrap-theme .chart-color-119{background:#3D4A5E !important;border-color:#3D4A5E !important;fill:#3D4A5E !important}#bootstrap-theme .chart-color-bg-119{background:#3D4A5E !important;fill:#3D4A5E !important}#bootstrap-theme .chart-color-border-119{border-color:#3D4A5E !important}#bootstrap-theme .chart-color-stroke-119{stroke:#3D4A5E !important}#bootstrap-theme .chart-color-8{background:#E56A6A !important;border-color:#E56A6A !important;fill:#E56A6A !important}#bootstrap-theme .chart-color-bg-8{background:#E56A6A !important;fill:#E56A6A !important}#bootstrap-theme .chart-color-border-8{border-color:#E56A6A !important}#bootstrap-theme .chart-color-stroke-8{stroke:#E56A6A !important}#bootstrap-theme .chart-color-36{background:#E56A6A !important;border-color:#E56A6A !important;fill:#E56A6A !important}#bootstrap-theme .chart-color-bg-36{background:#E56A6A !important;fill:#E56A6A !important}#bootstrap-theme .chart-color-border-36{border-color:#E56A6A !important}#bootstrap-theme .chart-color-stroke-36{stroke:#E56A6A !important}#bootstrap-theme .chart-color-64{background:#E56A6A !important;border-color:#E56A6A !important;fill:#E56A6A !important}#bootstrap-theme .chart-color-bg-64{background:#E56A6A !important;fill:#E56A6A !important}#bootstrap-theme .chart-color-border-64{border-color:#E56A6A !important}#bootstrap-theme .chart-color-stroke-64{stroke:#E56A6A !important}#bootstrap-theme .chart-color-92{background:#E56A6A !important;border-color:#E56A6A !important;fill:#E56A6A !important}#bootstrap-theme .chart-color-bg-92{background:#E56A6A !important;fill:#E56A6A !important}#bootstrap-theme .chart-color-border-92{border-color:#E56A6A !important}#bootstrap-theme .chart-color-stroke-92{stroke:#E56A6A !important}#bootstrap-theme .chart-color-120{background:#E56A6A !important;border-color:#E56A6A !important;fill:#E56A6A !important}#bootstrap-theme .chart-color-bg-120{background:#E56A6A !important;fill:#E56A6A !important}#bootstrap-theme .chart-color-border-120{border-color:#E56A6A !important}#bootstrap-theme .chart-color-stroke-120{stroke:#E56A6A !important}#bootstrap-theme .chart-color-9{background:#FA8F55 !important;border-color:#FA8F55 !important;fill:#FA8F55 !important}#bootstrap-theme .chart-color-bg-9{background:#FA8F55 !important;fill:#FA8F55 !important}#bootstrap-theme .chart-color-border-9{border-color:#FA8F55 !important}#bootstrap-theme .chart-color-stroke-9{stroke:#FA8F55 !important}#bootstrap-theme .chart-color-37{background:#FA8F55 !important;border-color:#FA8F55 !important;fill:#FA8F55 !important}#bootstrap-theme .chart-color-bg-37{background:#FA8F55 !important;fill:#FA8F55 !important}#bootstrap-theme .chart-color-border-37{border-color:#FA8F55 !important}#bootstrap-theme .chart-color-stroke-37{stroke:#FA8F55 !important}#bootstrap-theme .chart-color-65{background:#FA8F55 !important;border-color:#FA8F55 !important;fill:#FA8F55 !important}#bootstrap-theme .chart-color-bg-65{background:#FA8F55 !important;fill:#FA8F55 !important}#bootstrap-theme .chart-color-border-65{border-color:#FA8F55 !important}#bootstrap-theme .chart-color-stroke-65{stroke:#FA8F55 !important}#bootstrap-theme .chart-color-93{background:#FA8F55 !important;border-color:#FA8F55 !important;fill:#FA8F55 !important}#bootstrap-theme .chart-color-bg-93{background:#FA8F55 !important;fill:#FA8F55 !important}#bootstrap-theme .chart-color-border-93{border-color:#FA8F55 !important}#bootstrap-theme .chart-color-stroke-93{stroke:#FA8F55 !important}#bootstrap-theme .chart-color-121{background:#FA8F55 !important;border-color:#FA8F55 !important;fill:#FA8F55 !important}#bootstrap-theme .chart-color-bg-121{background:#FA8F55 !important;fill:#FA8F55 !important}#bootstrap-theme .chart-color-border-121{border-color:#FA8F55 !important}#bootstrap-theme .chart-color-stroke-121{stroke:#FA8F55 !important}#bootstrap-theme .chart-color-10{background:#6DAD68 !important;border-color:#6DAD68 !important;fill:#6DAD68 !important}#bootstrap-theme .chart-color-bg-10{background:#6DAD68 !important;fill:#6DAD68 !important}#bootstrap-theme .chart-color-border-10{border-color:#6DAD68 !important}#bootstrap-theme .chart-color-stroke-10{stroke:#6DAD68 !important}#bootstrap-theme .chart-color-38{background:#6DAD68 !important;border-color:#6DAD68 !important;fill:#6DAD68 !important}#bootstrap-theme .chart-color-bg-38{background:#6DAD68 !important;fill:#6DAD68 !important}#bootstrap-theme .chart-color-border-38{border-color:#6DAD68 !important}#bootstrap-theme .chart-color-stroke-38{stroke:#6DAD68 !important}#bootstrap-theme .chart-color-66{background:#6DAD68 !important;border-color:#6DAD68 !important;fill:#6DAD68 !important}#bootstrap-theme .chart-color-bg-66{background:#6DAD68 !important;fill:#6DAD68 !important}#bootstrap-theme .chart-color-border-66{border-color:#6DAD68 !important}#bootstrap-theme .chart-color-stroke-66{stroke:#6DAD68 !important}#bootstrap-theme .chart-color-94{background:#6DAD68 !important;border-color:#6DAD68 !important;fill:#6DAD68 !important}#bootstrap-theme .chart-color-bg-94{background:#6DAD68 !important;fill:#6DAD68 !important}#bootstrap-theme .chart-color-border-94{border-color:#6DAD68 !important}#bootstrap-theme .chart-color-stroke-94{stroke:#6DAD68 !important}#bootstrap-theme .chart-color-122{background:#6DAD68 !important;border-color:#6DAD68 !important;fill:#6DAD68 !important}#bootstrap-theme .chart-color-bg-122{background:#6DAD68 !important;fill:#6DAD68 !important}#bootstrap-theme .chart-color-border-122{border-color:#6DAD68 !important}#bootstrap-theme .chart-color-stroke-122{stroke:#6DAD68 !important}#bootstrap-theme .chart-color-11{background:#B37995 !important;border-color:#B37995 !important;fill:#B37995 !important}#bootstrap-theme .chart-color-bg-11{background:#B37995 !important;fill:#B37995 !important}#bootstrap-theme .chart-color-border-11{border-color:#B37995 !important}#bootstrap-theme .chart-color-stroke-11{stroke:#B37995 !important}#bootstrap-theme .chart-color-39{background:#B37995 !important;border-color:#B37995 !important;fill:#B37995 !important}#bootstrap-theme .chart-color-bg-39{background:#B37995 !important;fill:#B37995 !important}#bootstrap-theme .chart-color-border-39{border-color:#B37995 !important}#bootstrap-theme .chart-color-stroke-39{stroke:#B37995 !important}#bootstrap-theme .chart-color-67{background:#B37995 !important;border-color:#B37995 !important;fill:#B37995 !important}#bootstrap-theme .chart-color-bg-67{background:#B37995 !important;fill:#B37995 !important}#bootstrap-theme .chart-color-border-67{border-color:#B37995 !important}#bootstrap-theme .chart-color-stroke-67{stroke:#B37995 !important}#bootstrap-theme .chart-color-95{background:#B37995 !important;border-color:#B37995 !important;fill:#B37995 !important}#bootstrap-theme .chart-color-bg-95{background:#B37995 !important;fill:#B37995 !important}#bootstrap-theme .chart-color-border-95{border-color:#B37995 !important}#bootstrap-theme .chart-color-stroke-95{stroke:#B37995 !important}#bootstrap-theme .chart-color-123{background:#B37995 !important;border-color:#B37995 !important;fill:#B37995 !important}#bootstrap-theme .chart-color-bg-123{background:#B37995 !important;fill:#B37995 !important}#bootstrap-theme .chart-color-border-123{border-color:#B37995 !important}#bootstrap-theme .chart-color-stroke-123{stroke:#B37995 !important}#bootstrap-theme .chart-color-12{background:#84619C !important;border-color:#84619C !important;fill:#84619C !important}#bootstrap-theme .chart-color-bg-12{background:#84619C !important;fill:#84619C !important}#bootstrap-theme .chart-color-border-12{border-color:#84619C !important}#bootstrap-theme .chart-color-stroke-12{stroke:#84619C !important}#bootstrap-theme .chart-color-40{background:#84619C !important;border-color:#84619C !important;fill:#84619C !important}#bootstrap-theme .chart-color-bg-40{background:#84619C !important;fill:#84619C !important}#bootstrap-theme .chart-color-border-40{border-color:#84619C !important}#bootstrap-theme .chart-color-stroke-40{stroke:#84619C !important}#bootstrap-theme .chart-color-68{background:#84619C !important;border-color:#84619C !important;fill:#84619C !important}#bootstrap-theme .chart-color-bg-68{background:#84619C !important;fill:#84619C !important}#bootstrap-theme .chart-color-border-68{border-color:#84619C !important}#bootstrap-theme .chart-color-stroke-68{stroke:#84619C !important}#bootstrap-theme .chart-color-96{background:#84619C !important;border-color:#84619C !important;fill:#84619C !important}#bootstrap-theme .chart-color-bg-96{background:#84619C !important;fill:#84619C !important}#bootstrap-theme .chart-color-border-96{border-color:#84619C !important}#bootstrap-theme .chart-color-stroke-96{stroke:#84619C !important}#bootstrap-theme .chart-color-124{background:#84619C !important;border-color:#84619C !important;fill:#84619C !important}#bootstrap-theme .chart-color-bg-124{background:#84619C !important;fill:#84619C !important}#bootstrap-theme .chart-color-border-124{border-color:#84619C !important}#bootstrap-theme .chart-color-stroke-124{stroke:#84619C !important}#bootstrap-theme .chart-color-13{background:#2997B3 !important;border-color:#2997B3 !important;fill:#2997B3 !important}#bootstrap-theme .chart-color-bg-13{background:#2997B3 !important;fill:#2997B3 !important}#bootstrap-theme .chart-color-border-13{border-color:#2997B3 !important}#bootstrap-theme .chart-color-stroke-13{stroke:#2997B3 !important}#bootstrap-theme .chart-color-41{background:#2997B3 !important;border-color:#2997B3 !important;fill:#2997B3 !important}#bootstrap-theme .chart-color-bg-41{background:#2997B3 !important;fill:#2997B3 !important}#bootstrap-theme .chart-color-border-41{border-color:#2997B3 !important}#bootstrap-theme .chart-color-stroke-41{stroke:#2997B3 !important}#bootstrap-theme .chart-color-69{background:#2997B3 !important;border-color:#2997B3 !important;fill:#2997B3 !important}#bootstrap-theme .chart-color-bg-69{background:#2997B3 !important;fill:#2997B3 !important}#bootstrap-theme .chart-color-border-69{border-color:#2997B3 !important}#bootstrap-theme .chart-color-stroke-69{stroke:#2997B3 !important}#bootstrap-theme .chart-color-97{background:#2997B3 !important;border-color:#2997B3 !important;fill:#2997B3 !important}#bootstrap-theme .chart-color-bg-97{background:#2997B3 !important;fill:#2997B3 !important}#bootstrap-theme .chart-color-border-97{border-color:#2997B3 !important}#bootstrap-theme .chart-color-stroke-97{stroke:#2997B3 !important}#bootstrap-theme .chart-color-125{background:#2997B3 !important;border-color:#2997B3 !important;fill:#2997B3 !important}#bootstrap-theme .chart-color-bg-125{background:#2997B3 !important;fill:#2997B3 !important}#bootstrap-theme .chart-color-border-125{border-color:#2997B3 !important}#bootstrap-theme .chart-color-stroke-125{stroke:#2997B3 !important}#bootstrap-theme .chart-color-14{background:#263345 !important;border-color:#263345 !important;fill:#263345 !important}#bootstrap-theme .chart-color-bg-14{background:#263345 !important;fill:#263345 !important}#bootstrap-theme .chart-color-border-14{border-color:#263345 !important}#bootstrap-theme .chart-color-stroke-14{stroke:#263345 !important}#bootstrap-theme .chart-color-42{background:#263345 !important;border-color:#263345 !important;fill:#263345 !important}#bootstrap-theme .chart-color-bg-42{background:#263345 !important;fill:#263345 !important}#bootstrap-theme .chart-color-border-42{border-color:#263345 !important}#bootstrap-theme .chart-color-stroke-42{stroke:#263345 !important}#bootstrap-theme .chart-color-70{background:#263345 !important;border-color:#263345 !important;fill:#263345 !important}#bootstrap-theme .chart-color-bg-70{background:#263345 !important;fill:#263345 !important}#bootstrap-theme .chart-color-border-70{border-color:#263345 !important}#bootstrap-theme .chart-color-stroke-70{stroke:#263345 !important}#bootstrap-theme .chart-color-98{background:#263345 !important;border-color:#263345 !important;fill:#263345 !important}#bootstrap-theme .chart-color-bg-98{background:#263345 !important;fill:#263345 !important}#bootstrap-theme .chart-color-border-98{border-color:#263345 !important}#bootstrap-theme .chart-color-stroke-98{stroke:#263345 !important}#bootstrap-theme .chart-color-126{background:#263345 !important;border-color:#263345 !important;fill:#263345 !important}#bootstrap-theme .chart-color-bg-126{background:#263345 !important;fill:#263345 !important}#bootstrap-theme .chart-color-border-126{border-color:#263345 !important}#bootstrap-theme .chart-color-stroke-126{stroke:#263345 !important}#bootstrap-theme .chart-color-15{background:#CC4A49 !important;border-color:#CC4A49 !important;fill:#CC4A49 !important}#bootstrap-theme .chart-color-bg-15{background:#CC4A49 !important;fill:#CC4A49 !important}#bootstrap-theme .chart-color-border-15{border-color:#CC4A49 !important}#bootstrap-theme .chart-color-stroke-15{stroke:#CC4A49 !important}#bootstrap-theme .chart-color-43{background:#CC4A49 !important;border-color:#CC4A49 !important;fill:#CC4A49 !important}#bootstrap-theme .chart-color-bg-43{background:#CC4A49 !important;fill:#CC4A49 !important}#bootstrap-theme .chart-color-border-43{border-color:#CC4A49 !important}#bootstrap-theme .chart-color-stroke-43{stroke:#CC4A49 !important}#bootstrap-theme .chart-color-71{background:#CC4A49 !important;border-color:#CC4A49 !important;fill:#CC4A49 !important}#bootstrap-theme .chart-color-bg-71{background:#CC4A49 !important;fill:#CC4A49 !important}#bootstrap-theme .chart-color-border-71{border-color:#CC4A49 !important}#bootstrap-theme .chart-color-stroke-71{stroke:#CC4A49 !important}#bootstrap-theme .chart-color-99{background:#CC4A49 !important;border-color:#CC4A49 !important;fill:#CC4A49 !important}#bootstrap-theme .chart-color-bg-99{background:#CC4A49 !important;fill:#CC4A49 !important}#bootstrap-theme .chart-color-border-99{border-color:#CC4A49 !important}#bootstrap-theme .chart-color-stroke-99{stroke:#CC4A49 !important}#bootstrap-theme .chart-color-127{background:#CC4A49 !important;border-color:#CC4A49 !important;fill:#CC4A49 !important}#bootstrap-theme .chart-color-bg-127{background:#CC4A49 !important;fill:#CC4A49 !important}#bootstrap-theme .chart-color-border-127{border-color:#CC4A49 !important}#bootstrap-theme .chart-color-stroke-127{stroke:#CC4A49 !important}#bootstrap-theme .chart-color-16{background:#D97038 !important;border-color:#D97038 !important;fill:#D97038 !important}#bootstrap-theme .chart-color-bg-16{background:#D97038 !important;fill:#D97038 !important}#bootstrap-theme .chart-color-border-16{border-color:#D97038 !important}#bootstrap-theme .chart-color-stroke-16{stroke:#D97038 !important}#bootstrap-theme .chart-color-44{background:#D97038 !important;border-color:#D97038 !important;fill:#D97038 !important}#bootstrap-theme .chart-color-bg-44{background:#D97038 !important;fill:#D97038 !important}#bootstrap-theme .chart-color-border-44{border-color:#D97038 !important}#bootstrap-theme .chart-color-stroke-44{stroke:#D97038 !important}#bootstrap-theme .chart-color-72{background:#D97038 !important;border-color:#D97038 !important;fill:#D97038 !important}#bootstrap-theme .chart-color-bg-72{background:#D97038 !important;fill:#D97038 !important}#bootstrap-theme .chart-color-border-72{border-color:#D97038 !important}#bootstrap-theme .chart-color-stroke-72{stroke:#D97038 !important}#bootstrap-theme .chart-color-100{background:#D97038 !important;border-color:#D97038 !important;fill:#D97038 !important}#bootstrap-theme .chart-color-bg-100{background:#D97038 !important;fill:#D97038 !important}#bootstrap-theme .chart-color-border-100{border-color:#D97038 !important}#bootstrap-theme .chart-color-stroke-100{stroke:#D97038 !important}#bootstrap-theme .chart-color-128{background:#D97038 !important;border-color:#D97038 !important;fill:#D97038 !important}#bootstrap-theme .chart-color-bg-128{background:#D97038 !important;fill:#D97038 !important}#bootstrap-theme .chart-color-border-128{border-color:#D97038 !important}#bootstrap-theme .chart-color-stroke-128{stroke:#D97038 !important}#bootstrap-theme .chart-color-17{background:#4F944A !important;border-color:#4F944A !important;fill:#4F944A !important}#bootstrap-theme .chart-color-bg-17{background:#4F944A !important;fill:#4F944A !important}#bootstrap-theme .chart-color-border-17{border-color:#4F944A !important}#bootstrap-theme .chart-color-stroke-17{stroke:#4F944A !important}#bootstrap-theme .chart-color-45{background:#4F944A !important;border-color:#4F944A !important;fill:#4F944A !important}#bootstrap-theme .chart-color-bg-45{background:#4F944A !important;fill:#4F944A !important}#bootstrap-theme .chart-color-border-45{border-color:#4F944A !important}#bootstrap-theme .chart-color-stroke-45{stroke:#4F944A !important}#bootstrap-theme .chart-color-73{background:#4F944A !important;border-color:#4F944A !important;fill:#4F944A !important}#bootstrap-theme .chart-color-bg-73{background:#4F944A !important;fill:#4F944A !important}#bootstrap-theme .chart-color-border-73{border-color:#4F944A !important}#bootstrap-theme .chart-color-stroke-73{stroke:#4F944A !important}#bootstrap-theme .chart-color-101{background:#4F944A !important;border-color:#4F944A !important;fill:#4F944A !important}#bootstrap-theme .chart-color-bg-101{background:#4F944A !important;fill:#4F944A !important}#bootstrap-theme .chart-color-border-101{border-color:#4F944A !important}#bootstrap-theme .chart-color-stroke-101{stroke:#4F944A !important}#bootstrap-theme .chart-color-129{background:#4F944A !important;border-color:#4F944A !important;fill:#4F944A !important}#bootstrap-theme .chart-color-bg-129{background:#4F944A !important;fill:#4F944A !important}#bootstrap-theme .chart-color-border-129{border-color:#4F944A !important}#bootstrap-theme .chart-color-stroke-129{stroke:#4F944A !important}#bootstrap-theme .chart-color-18{background:#995978 !important;border-color:#995978 !important;fill:#995978 !important}#bootstrap-theme .chart-color-bg-18{background:#995978 !important;fill:#995978 !important}#bootstrap-theme .chart-color-border-18{border-color:#995978 !important}#bootstrap-theme .chart-color-stroke-18{stroke:#995978 !important}#bootstrap-theme .chart-color-46{background:#995978 !important;border-color:#995978 !important;fill:#995978 !important}#bootstrap-theme .chart-color-bg-46{background:#995978 !important;fill:#995978 !important}#bootstrap-theme .chart-color-border-46{border-color:#995978 !important}#bootstrap-theme .chart-color-stroke-46{stroke:#995978 !important}#bootstrap-theme .chart-color-74{background:#995978 !important;border-color:#995978 !important;fill:#995978 !important}#bootstrap-theme .chart-color-bg-74{background:#995978 !important;fill:#995978 !important}#bootstrap-theme .chart-color-border-74{border-color:#995978 !important}#bootstrap-theme .chart-color-stroke-74{stroke:#995978 !important}#bootstrap-theme .chart-color-102{background:#995978 !important;border-color:#995978 !important;fill:#995978 !important}#bootstrap-theme .chart-color-bg-102{background:#995978 !important;fill:#995978 !important}#bootstrap-theme .chart-color-border-102{border-color:#995978 !important}#bootstrap-theme .chart-color-stroke-102{stroke:#995978 !important}#bootstrap-theme .chart-color-130{background:#995978 !important;border-color:#995978 !important;fill:#995978 !important}#bootstrap-theme .chart-color-bg-130{background:#995978 !important;fill:#995978 !important}#bootstrap-theme .chart-color-border-130{border-color:#995978 !important}#bootstrap-theme .chart-color-stroke-130{stroke:#995978 !important}#bootstrap-theme .chart-color-19{background:#5F3D76 !important;border-color:#5F3D76 !important;fill:#5F3D76 !important}#bootstrap-theme .chart-color-bg-19{background:#5F3D76 !important;fill:#5F3D76 !important}#bootstrap-theme .chart-color-border-19{border-color:#5F3D76 !important}#bootstrap-theme .chart-color-stroke-19{stroke:#5F3D76 !important}#bootstrap-theme .chart-color-47{background:#5F3D76 !important;border-color:#5F3D76 !important;fill:#5F3D76 !important}#bootstrap-theme .chart-color-bg-47{background:#5F3D76 !important;fill:#5F3D76 !important}#bootstrap-theme .chart-color-border-47{border-color:#5F3D76 !important}#bootstrap-theme .chart-color-stroke-47{stroke:#5F3D76 !important}#bootstrap-theme .chart-color-75{background:#5F3D76 !important;border-color:#5F3D76 !important;fill:#5F3D76 !important}#bootstrap-theme .chart-color-bg-75{background:#5F3D76 !important;fill:#5F3D76 !important}#bootstrap-theme .chart-color-border-75{border-color:#5F3D76 !important}#bootstrap-theme .chart-color-stroke-75{stroke:#5F3D76 !important}#bootstrap-theme .chart-color-103{background:#5F3D76 !important;border-color:#5F3D76 !important;fill:#5F3D76 !important}#bootstrap-theme .chart-color-bg-103{background:#5F3D76 !important;fill:#5F3D76 !important}#bootstrap-theme .chart-color-border-103{border-color:#5F3D76 !important}#bootstrap-theme .chart-color-stroke-103{stroke:#5F3D76 !important}#bootstrap-theme .chart-color-131{background:#5F3D76 !important;border-color:#5F3D76 !important;fill:#5F3D76 !important}#bootstrap-theme .chart-color-bg-131{background:#5F3D76 !important;fill:#5F3D76 !important}#bootstrap-theme .chart-color-border-131{border-color:#5F3D76 !important}#bootstrap-theme .chart-color-stroke-131{stroke:#5F3D76 !important}#bootstrap-theme .chart-color-20{background:#147E99 !important;border-color:#147E99 !important;fill:#147E99 !important}#bootstrap-theme .chart-color-bg-20{background:#147E99 !important;fill:#147E99 !important}#bootstrap-theme .chart-color-border-20{border-color:#147E99 !important}#bootstrap-theme .chart-color-stroke-20{stroke:#147E99 !important}#bootstrap-theme .chart-color-48{background:#147E99 !important;border-color:#147E99 !important;fill:#147E99 !important}#bootstrap-theme .chart-color-bg-48{background:#147E99 !important;fill:#147E99 !important}#bootstrap-theme .chart-color-border-48{border-color:#147E99 !important}#bootstrap-theme .chart-color-stroke-48{stroke:#147E99 !important}#bootstrap-theme .chart-color-76{background:#147E99 !important;border-color:#147E99 !important;fill:#147E99 !important}#bootstrap-theme .chart-color-bg-76{background:#147E99 !important;fill:#147E99 !important}#bootstrap-theme .chart-color-border-76{border-color:#147E99 !important}#bootstrap-theme .chart-color-stroke-76{stroke:#147E99 !important}#bootstrap-theme .chart-color-104{background:#147E99 !important;border-color:#147E99 !important;fill:#147E99 !important}#bootstrap-theme .chart-color-bg-104{background:#147E99 !important;fill:#147E99 !important}#bootstrap-theme .chart-color-border-104{border-color:#147E99 !important}#bootstrap-theme .chart-color-stroke-104{stroke:#147E99 !important}#bootstrap-theme .chart-color-132{background:#147E99 !important;border-color:#147E99 !important;fill:#147E99 !important}#bootstrap-theme .chart-color-bg-132{background:#147E99 !important;fill:#147E99 !important}#bootstrap-theme .chart-color-border-132{border-color:#147E99 !important}#bootstrap-theme .chart-color-stroke-132{stroke:#147E99 !important}#bootstrap-theme .chart-color-21{background:#151D2C !important;border-color:#151D2C !important;fill:#151D2C !important}#bootstrap-theme .chart-color-bg-21{background:#151D2C !important;fill:#151D2C !important}#bootstrap-theme .chart-color-border-21{border-color:#151D2C !important}#bootstrap-theme .chart-color-stroke-21{stroke:#151D2C !important}#bootstrap-theme .chart-color-49{background:#151D2C !important;border-color:#151D2C !important;fill:#151D2C !important}#bootstrap-theme .chart-color-bg-49{background:#151D2C !important;fill:#151D2C !important}#bootstrap-theme .chart-color-border-49{border-color:#151D2C !important}#bootstrap-theme .chart-color-stroke-49{stroke:#151D2C !important}#bootstrap-theme .chart-color-77{background:#151D2C !important;border-color:#151D2C !important;fill:#151D2C !important}#bootstrap-theme .chart-color-bg-77{background:#151D2C !important;fill:#151D2C !important}#bootstrap-theme .chart-color-border-77{border-color:#151D2C !important}#bootstrap-theme .chart-color-stroke-77{stroke:#151D2C !important}#bootstrap-theme .chart-color-105{background:#151D2C !important;border-color:#151D2C !important;fill:#151D2C !important}#bootstrap-theme .chart-color-bg-105{background:#151D2C !important;fill:#151D2C !important}#bootstrap-theme .chart-color-border-105{border-color:#151D2C !important}#bootstrap-theme .chart-color-stroke-105{stroke:#151D2C !important}#bootstrap-theme .chart-color-133{background:#151D2C !important;border-color:#151D2C !important;fill:#151D2C !important}#bootstrap-theme .chart-color-bg-133{background:#151D2C !important;fill:#151D2C !important}#bootstrap-theme .chart-color-border-133{border-color:#151D2C !important}#bootstrap-theme .chart-color-stroke-133{stroke:#151D2C !important}#bootstrap-theme .chart-color-22{background:#B32E2E !important;border-color:#B32E2E !important;fill:#B32E2E !important}#bootstrap-theme .chart-color-bg-22{background:#B32E2E !important;fill:#B32E2E !important}#bootstrap-theme .chart-color-border-22{border-color:#B32E2E !important}#bootstrap-theme .chart-color-stroke-22{stroke:#B32E2E !important}#bootstrap-theme .chart-color-50{background:#B32E2E !important;border-color:#B32E2E !important;fill:#B32E2E !important}#bootstrap-theme .chart-color-bg-50{background:#B32E2E !important;fill:#B32E2E !important}#bootstrap-theme .chart-color-border-50{border-color:#B32E2E !important}#bootstrap-theme .chart-color-stroke-50{stroke:#B32E2E !important}#bootstrap-theme .chart-color-78{background:#B32E2E !important;border-color:#B32E2E !important;fill:#B32E2E !important}#bootstrap-theme .chart-color-bg-78{background:#B32E2E !important;fill:#B32E2E !important}#bootstrap-theme .chart-color-border-78{border-color:#B32E2E !important}#bootstrap-theme .chart-color-stroke-78{stroke:#B32E2E !important}#bootstrap-theme .chart-color-106{background:#B32E2E !important;border-color:#B32E2E !important;fill:#B32E2E !important}#bootstrap-theme .chart-color-bg-106{background:#B32E2E !important;fill:#B32E2E !important}#bootstrap-theme .chart-color-border-106{border-color:#B32E2E !important}#bootstrap-theme .chart-color-stroke-106{stroke:#B32E2E !important}#bootstrap-theme .chart-color-134{background:#B32E2E !important;border-color:#B32E2E !important;fill:#B32E2E !important}#bootstrap-theme .chart-color-bg-134{background:#B32E2E !important;fill:#B32E2E !important}#bootstrap-theme .chart-color-border-134{border-color:#B32E2E !important}#bootstrap-theme .chart-color-stroke-134{stroke:#B32E2E !important}#bootstrap-theme .chart-color-23{background:#BF561D !important;border-color:#BF561D !important;fill:#BF561D !important}#bootstrap-theme .chart-color-bg-23{background:#BF561D !important;fill:#BF561D !important}#bootstrap-theme .chart-color-border-23{border-color:#BF561D !important}#bootstrap-theme .chart-color-stroke-23{stroke:#BF561D !important}#bootstrap-theme .chart-color-51{background:#BF561D !important;border-color:#BF561D !important;fill:#BF561D !important}#bootstrap-theme .chart-color-bg-51{background:#BF561D !important;fill:#BF561D !important}#bootstrap-theme .chart-color-border-51{border-color:#BF561D !important}#bootstrap-theme .chart-color-stroke-51{stroke:#BF561D !important}#bootstrap-theme .chart-color-79{background:#BF561D !important;border-color:#BF561D !important;fill:#BF561D !important}#bootstrap-theme .chart-color-bg-79{background:#BF561D !important;fill:#BF561D !important}#bootstrap-theme .chart-color-border-79{border-color:#BF561D !important}#bootstrap-theme .chart-color-stroke-79{stroke:#BF561D !important}#bootstrap-theme .chart-color-107{background:#BF561D !important;border-color:#BF561D !important;fill:#BF561D !important}#bootstrap-theme .chart-color-bg-107{background:#BF561D !important;fill:#BF561D !important}#bootstrap-theme .chart-color-border-107{border-color:#BF561D !important}#bootstrap-theme .chart-color-stroke-107{stroke:#BF561D !important}#bootstrap-theme .chart-color-135{background:#BF561D !important;border-color:#BF561D !important;fill:#BF561D !important}#bootstrap-theme .chart-color-bg-135{background:#BF561D !important;fill:#BF561D !important}#bootstrap-theme .chart-color-border-135{border-color:#BF561D !important}#bootstrap-theme .chart-color-stroke-135{stroke:#BF561D !important}#bootstrap-theme .chart-color-24{background:#377A31 !important;border-color:#377A31 !important;fill:#377A31 !important}#bootstrap-theme .chart-color-bg-24{background:#377A31 !important;fill:#377A31 !important}#bootstrap-theme .chart-color-border-24{border-color:#377A31 !important}#bootstrap-theme .chart-color-stroke-24{stroke:#377A31 !important}#bootstrap-theme .chart-color-52{background:#377A31 !important;border-color:#377A31 !important;fill:#377A31 !important}#bootstrap-theme .chart-color-bg-52{background:#377A31 !important;fill:#377A31 !important}#bootstrap-theme .chart-color-border-52{border-color:#377A31 !important}#bootstrap-theme .chart-color-stroke-52{stroke:#377A31 !important}#bootstrap-theme .chart-color-80{background:#377A31 !important;border-color:#377A31 !important;fill:#377A31 !important}#bootstrap-theme .chart-color-bg-80{background:#377A31 !important;fill:#377A31 !important}#bootstrap-theme .chart-color-border-80{border-color:#377A31 !important}#bootstrap-theme .chart-color-stroke-80{stroke:#377A31 !important}#bootstrap-theme .chart-color-108{background:#377A31 !important;border-color:#377A31 !important;fill:#377A31 !important}#bootstrap-theme .chart-color-bg-108{background:#377A31 !important;fill:#377A31 !important}#bootstrap-theme .chart-color-border-108{border-color:#377A31 !important}#bootstrap-theme .chart-color-stroke-108{stroke:#377A31 !important}#bootstrap-theme .chart-color-136{background:#377A31 !important;border-color:#377A31 !important;fill:#377A31 !important}#bootstrap-theme .chart-color-bg-136{background:#377A31 !important;fill:#377A31 !important}#bootstrap-theme .chart-color-border-136{border-color:#377A31 !important}#bootstrap-theme .chart-color-stroke-136{stroke:#377A31 !important}#bootstrap-theme .chart-color-25{background:#803D5E !important;border-color:#803D5E !important;fill:#803D5E !important}#bootstrap-theme .chart-color-bg-25{background:#803D5E !important;fill:#803D5E !important}#bootstrap-theme .chart-color-border-25{border-color:#803D5E !important}#bootstrap-theme .chart-color-stroke-25{stroke:#803D5E !important}#bootstrap-theme .chart-color-53{background:#803D5E !important;border-color:#803D5E !important;fill:#803D5E !important}#bootstrap-theme .chart-color-bg-53{background:#803D5E !important;fill:#803D5E !important}#bootstrap-theme .chart-color-border-53{border-color:#803D5E !important}#bootstrap-theme .chart-color-stroke-53{stroke:#803D5E !important}#bootstrap-theme .chart-color-81{background:#803D5E !important;border-color:#803D5E !important;fill:#803D5E !important}#bootstrap-theme .chart-color-bg-81{background:#803D5E !important;fill:#803D5E !important}#bootstrap-theme .chart-color-border-81{border-color:#803D5E !important}#bootstrap-theme .chart-color-stroke-81{stroke:#803D5E !important}#bootstrap-theme .chart-color-109{background:#803D5E !important;border-color:#803D5E !important;fill:#803D5E !important}#bootstrap-theme .chart-color-bg-109{background:#803D5E !important;fill:#803D5E !important}#bootstrap-theme .chart-color-border-109{border-color:#803D5E !important}#bootstrap-theme .chart-color-stroke-109{stroke:#803D5E !important}#bootstrap-theme .chart-color-137{background:#803D5E !important;border-color:#803D5E !important;fill:#803D5E !important}#bootstrap-theme .chart-color-bg-137{background:#803D5E !important;fill:#803D5E !important}#bootstrap-theme .chart-color-border-137{border-color:#803D5E !important}#bootstrap-theme .chart-color-stroke-137{stroke:#803D5E !important}#bootstrap-theme .chart-color-26{background:#47275C !important;border-color:#47275C !important;fill:#47275C !important}#bootstrap-theme .chart-color-bg-26{background:#47275C !important;fill:#47275C !important}#bootstrap-theme .chart-color-border-26{border-color:#47275C !important}#bootstrap-theme .chart-color-stroke-26{stroke:#47275C !important}#bootstrap-theme .chart-color-54{background:#47275C !important;border-color:#47275C !important;fill:#47275C !important}#bootstrap-theme .chart-color-bg-54{background:#47275C !important;fill:#47275C !important}#bootstrap-theme .chart-color-border-54{border-color:#47275C !important}#bootstrap-theme .chart-color-stroke-54{stroke:#47275C !important}#bootstrap-theme .chart-color-82{background:#47275C !important;border-color:#47275C !important;fill:#47275C !important}#bootstrap-theme .chart-color-bg-82{background:#47275C !important;fill:#47275C !important}#bootstrap-theme .chart-color-border-82{border-color:#47275C !important}#bootstrap-theme .chart-color-stroke-82{stroke:#47275C !important}#bootstrap-theme .chart-color-110{background:#47275C !important;border-color:#47275C !important;fill:#47275C !important}#bootstrap-theme .chart-color-bg-110{background:#47275C !important;fill:#47275C !important}#bootstrap-theme .chart-color-border-110{border-color:#47275C !important}#bootstrap-theme .chart-color-stroke-110{stroke:#47275C !important}#bootstrap-theme .chart-color-138{background:#47275C !important;border-color:#47275C !important;fill:#47275C !important}#bootstrap-theme .chart-color-bg-138{background:#47275C !important;fill:#47275C !important}#bootstrap-theme .chart-color-border-138{border-color:#47275C !important}#bootstrap-theme .chart-color-stroke-138{stroke:#47275C !important}#bootstrap-theme .chart-color-27{background:#056780 !important;border-color:#056780 !important;fill:#056780 !important}#bootstrap-theme .chart-color-bg-27{background:#056780 !important;fill:#056780 !important}#bootstrap-theme .chart-color-border-27{border-color:#056780 !important}#bootstrap-theme .chart-color-stroke-27{stroke:#056780 !important}#bootstrap-theme .chart-color-55{background:#056780 !important;border-color:#056780 !important;fill:#056780 !important}#bootstrap-theme .chart-color-bg-55{background:#056780 !important;fill:#056780 !important}#bootstrap-theme .chart-color-border-55{border-color:#056780 !important}#bootstrap-theme .chart-color-stroke-55{stroke:#056780 !important}#bootstrap-theme .chart-color-83{background:#056780 !important;border-color:#056780 !important;fill:#056780 !important}#bootstrap-theme .chart-color-bg-83{background:#056780 !important;fill:#056780 !important}#bootstrap-theme .chart-color-border-83{border-color:#056780 !important}#bootstrap-theme .chart-color-stroke-83{stroke:#056780 !important}#bootstrap-theme .chart-color-111{background:#056780 !important;border-color:#056780 !important;fill:#056780 !important}#bootstrap-theme .chart-color-bg-111{background:#056780 !important;fill:#056780 !important}#bootstrap-theme .chart-color-border-111{border-color:#056780 !important}#bootstrap-theme .chart-color-stroke-111{stroke:#056780 !important}#bootstrap-theme .chart-color-139{background:#056780 !important;border-color:#056780 !important;fill:#056780 !important}#bootstrap-theme .chart-color-bg-139{background:#056780 !important;fill:#056780 !important}#bootstrap-theme .chart-color-border-139{border-color:#056780 !important}#bootstrap-theme .chart-color-stroke-139{stroke:#056780 !important}#bootstrap-theme .crm_custom-select{background:#fff;display:inline-block;position:relative}#bootstrap-theme .crm_custom-select>select{background:transparent;border:1px solid #c2cfd8;height:30px;padding:4px 44px 4px 12px;position:relative;width:100%;z-index:2;-moz-appearance:none;-webkit-appearance:none;-webkit-border-radius:0px}#bootstrap-theme .crm_custom-select>select::-ms-expand{display:none}#bootstrap-theme .crm_custom-select>select:disabled+.crm_custom-select__arrow{z-index:2}#bootstrap-theme .crm_custom-select>select:focus+.crm_custom-select__arrow{border-color:#66afe9}#bootstrap-theme .ie9 .crm_custom-select>select{padding-right:10px}#bootstrap-theme .ie9 .crm_custom-select .crm_custom-select__arrow{display:none}#bootstrap-theme .crm_custom-select--full{display:block;width:auto}#bootstrap-theme .crm_custom-select--transparent{background:transparent}#bootstrap-theme .crm_custom-select--transparent option{background:#fff}#bootstrap-theme .crm_custom-select__arrow{border-left:1px solid #c2cfd8;bottom:0;display:inline-block;line-height:31px;position:absolute;right:0;text-align:center;top:0;width:32px;z-index:1}#bootstrap-theme .crm_custom-select__arrow:before{content:'\f0d7';color:#4d4d69;font-family:"FontAwesome";font-style:normal;text-rendering:auto;-webkit-font-smoothing:antialiased}#bootstrap-theme .has-error .crm_custom-select__arrow{border-color:#cf3458 !important}#bootstrap-theme .has-feedback>.crm_custom-select>select{padding-right:54px}#bootstrap-theme .has-feedback>.crm_custom-select+.form-control-feedback{right:27px !important}#bootstrap-theme .crm-editable-enabled{display:block;position:relative}#bootstrap-theme .crm-editable-enabled .crm-i{opacity:1}#bootstrap-theme .crm-editable-enabled .crm-editable-form button{background-color:#f3f6f7;border:1px solid #c2cfd8;border-radius:0 0 0 2px;bottom:-29px;color:#0071bd;height:30px;left:auto;outline:none;padding:1px 6px;right:32px}#bootstrap-theme .crm-editable-enabled .crm-editable-form button .crm-i{opacity:1}#bootstrap-theme .crm-editable-enabled .crm-editable-form button[type='cancel']{border-left:0;border-left:0;border-radius:0 0 2px;color:#cf3458;right:0}#bootstrap-theme .crm-editable-enabled .crm-editable-form select{background:#fff;border:1px solid #c2cfd8;border-radius:2px 2px 0;outline:none;width:100%}#bootstrap-theme .crm-editable-enabled .crm-editable-form input{border:1px solid #c2cfd8;border-radius:2px 2px 0 2px;display:block;outline:none;width:100% !important}#bootstrap-theme .crm-editable-enabled .crm-editable-form textarea{border:1px solid #c2cfd8;border-radius:2px 2px 0 2px;display:block;outline:none;width:100% !important}#bootstrap-theme .crm-editable-enabled:not(.crm-editable-editing):hover{border:1px solid #c2cfd8;border-radius:2px;padding:1px 30px 1px 3px}#bootstrap-theme .crm-editable-enabled:not(.crm-editable-editing):hover::before{background-color:#e8eef0;border-left:1px solid #c2cfd8;border-radius:0 2px 2px 0;bottom:0;content:'';height:100%;position:absolute;right:0;text-align:center;top:0;vertical-align:middle;width:30px}#bootstrap-theme .crm-editable-enabled:not(.crm-editable-editing):hover::after{font-family:'FontAwesome';font-style:normal;text-rendering:auto;font-size:13px;content:"";color:#4d4d69;position:absolute;right:0;text-align:center;top:calc(50% - 9px);width:30px}#bootstrap-theme .crm-editable-enabled[crm-editable-tab-title].crm-editable-editing{border:1px solid #c2cfd8}#bootstrap-theme .crm-editable-enabled[crm-editable-tab-title]:not(.crm-editable-editing):hover{padding:1px 3px}#bootstrap-theme .crm-editable-enabled[crm-editable-tab-title]:not(.crm-editable-editing):hover::before{display:none}#bootstrap-theme .crm-editable-enabled[crm-editable-tab-title]:not(.crm-editable-editing):hover::after{display:none}#bootstrap-theme .crm_notification{-webkit-box-shadow:0 3px 18px 0 rgba(48,40,40,0.25);box-shadow:0 3px 18px 0 rgba(48,40,40,0.25);background:#fff;border-radius:2px;padding:20px;width:360px}#bootstrap-theme .crm_notification+.crm_notification{margin-top:20px}#bootstrap-theme .crm_notification__close{float:right}#bootstrap-theme .crm_notification__close:before{font-family:'FontAwesome';font-style:normal;text-rendering:auto;font-size:1em;content:"";color:#4d4d69;color:#464354}#bootstrap-theme .crm_notification__header{margin-bottom:10px}#bootstrap-theme .crm_notification__heading>.crm_notification__title{display:inline-block;margin:0 0 0 5px}#bootstrap-theme .crm_notification__icon:before{font-family:'FontAwesome';font-style:normal;text-rendering:auto;font-size:1em;color:#4d4d69}#bootstrap-theme .crm_notification__title{font-size:13px}#bootstrap-theme .crm_notification--danger .crm_notification__icon:before{content:"";color:#cf3458}#bootstrap-theme .crm_notification--info .crm_notification__icon:before{content:"";color:#0071bd}#bootstrap-theme .crm_notification--success .crm_notification__icon:before{content:"";color:#44cb7e}@media (min-width: 992px){#bootstrap-theme .crm_page__content{overflow:hidden}#bootstrap-theme .crm_page__content>.row>[class*="col-"]:first-child{max-width:186px}#bootstrap-theme .crm_page__content .crm_page__main{margin:0 auto;max-width:1004px}#bootstrap-theme .crm_page__content .crm_page__sidebar{margin-bottom:-99999px;padding-bottom:99999px}}@media (min-width: 1240px){#bootstrap-theme .crm_page__content>.row>[class*="col-"]:last-child{float:none;margin-left:170px;overflow:hidden;width:auto}}#bootstrap-theme .crm_page__footer{text-align:center}#bootstrap-theme .crm_page__footer>.crm_page__footer__logo{display:inline-block;margin-bottom:15px}#bootstrap-theme .crm_page__footer__logo{color:#464354;font-size:1.6923076923em}#bootstrap-theme .crm_page__main{padding:16px 16px 50px 16px}#bootstrap-theme .crm_page__main>.crm_page__footer{margin-top:50px}@media (min-width: 992px){#bootstrap-theme .crm_page__main{padding-left:0}}@media (min-width: 1240px){#bootstrap-theme .crm_page__main{padding-right:0}}#bootstrap-theme .crm_page__sidebar{background:#fff}#bootstrap-theme .crm_page__topbar{background:#222831;color:#fff}#bootstrap-theme .crm_page__topbar .breadcrumb{background:transparent;margin-bottom:0;padding:15px}#bootstrap-theme .crm_page__topbar .breadcrumb>li{color:#fff}#bootstrap-theme .crm_page__topbar .breadcrumb>li:before{color:#fff}#bootstrap-theme .crm_page__topbar .breadcrumb>li>a{color:#fff}#bootstrap-theme .crm_page__topbar__link{display:inline-block;padding:15px}#bootstrap-theme .crm_page__topbar__link>a{color:#fff !important}#bootstrap-theme .crm_show-more__button{margin-top:30px}#bootstrap-theme .crm_spinner{background:rgba(255,255,255,0.5);animation:fadeIn 0.3s;min-height:100px;position:relative;text-align:center;width:100%;margin-bottom:15px}#bootstrap-theme .crm_spinner__img{background-image:url("data:image/gif;base64,R0lGODlhIAAgAOfzAAABAAACAAEEAAIFAQQHAgUIBAcJBQgLBwoMCAsNCgwPCw4QDA8RDRASDxETEBIUERMUEhQVExUWFBYYFRcYFhgZFxkbGBocGRscGhwdGx0fHB4fHR8gHiAhHyEjICIkISMkIiQlIyUnJCYoJScoJigpJykrKCosKSstKiwtKy0uLC4vLS8xLjAyLzEzMDIzMTM0MjQ2MzU3NDY4NTc5Njg5Nzk6ODo7OTs9Ojw+Oz0/PD5APT9APkBBP0FCQEFDQUNFQkRGQ0VHREZIRUdJRkhJR0lKSEpLSUpMSkxOS01PTE5QTU9RTlBST1FTUFJUUVNUUlRVU1VWVFZXVVZYVVdZVllbWFpcWVtdWlxeW11fXF5gXV9hXmBiX2FjYGJkYWNlYmRlY2VmZGZnZWdoZmhpZ2hqZ2lraGpsaWttamxua21vbG5wbW9xbnFzcHJ0cXN1cnR2c3V3dHZ4dXd5dnh6d3l7eHp8eXt9enx+e31/fH6AfX+BfoCCf4GDgIKEgYOFgoSGg4WHhIaIhYeJhoiKh4mLiIqMiYuNioyOi42PjI6QjY+RjpCSj5GTkJKUkZOVkpSWk5WXlJaYlZeZlpial5qbmJudmZyem52fnJ6gnZ+hnqCin6GjoKKkoaOloqSmo6WnpKaopaeppqiqp6mrqKqsqautqqyuq62vrK6wrbCyrrGzr7K0sbO1srS2s7W3tLa4tbe5tri6t7m7uLq8ubu9ury+u72/vL7BvcDCvsHDv8LEwcPFwsTGw8XHxMbIxcfJxsjKx8nLyMrMycvOys3Py87QzM/RztDSz9HT0NLU0dPV0tTW09XX1NbY1dja1tnb19rc2dvd2tze293f3N7g3d/h3uDi3+Hk4OPl4eTm4+Xn5Obo5efp5ujq5+nr6Ors6evu6u3v6+7w7e/x7vDy7/Hz8PL08fP18vT38/b49Pf59vj69/n7+Pr8+fv9+vz/+/7//P///////////////////////////////////////////////////yH/C05FVFNDQVBFMi4wAwEAAAAh+QQBBQD/ACwAAAAAIAAgAAAI/gDlCRxIUCA8gcJ+HSzIsCFDbcbAqUIQQJTDiw5z4XrVI0AAB+UwihQoLhevPQFGKAggZqTIaLmOhQgQp1AACL5cXhSmC04AFKSCjQig5J1OhuiAqSJgwNI1Xp4CIBh1tGC3XzcC+DgnDxovLAGAfKs68NofqbQGUiNFIYAmsvLe9QIRwEpBa20CXOhGlt2ZAAbEMTzmIoAbssA6BEDkkBLFXkfdyQlgAqORACv4uix2IYAsjLsYBLjjbeQ7LwGkpBP5JUCHVtpE5pLaamQ5BwGqBMuGUUWAJ4JHptJrqdc1h4sCUAjFzuU7GwGo2MJ1vCA4AgGuZDPq8hjgTrtwsPEmqCUABlvVXaoDE6CJxl5cBdZaEKAQsXCmzWmrFgpCAD+8MNOOQOo40R4xyohEjjXMHBOML8CIEUAD5Qwo0CjYtZHGHI404uGHjRhChxtsqJHGiW4EIscDAbRBkCUexSjjjDTSGAVB4ATixRFHVPHEEUYEeYQSTljhxRdIJqlkGKUxRMwwywQTDDHMRNNNfHDJg04utvRyjDXenONOlgWZ4006FpKp5ppstummTgEBACH5BAEFAP8ALAAAAAAgACAAAAj+AOUJHEiw4DlzAtW5K8iwYcNwvGiBs+WCDkKHGAu6W2Yr2CAHAQwEy0hSoDdczfIgCBngU8mM74T9ItQgQA07Abyoe+nQmi5BIGPsekRABDmeDN3pMrTSyDldn0QEgIa0YLNDBgIcSSfPmiwiAQhVHXgO0Eoo4gSyO1YmwI6xAgUxCPDkG8FviUAurGqp5hB0GnV5CNCqaisJAWw4POcjQBakuiAE0JExEAELPHPVrJEtozEFAUaSTEXBZq+SHwLEIRkKsYxW50qOCRDDrsNQEQLAgEXtJaoAFWhxZRgrN4pcwWKXhBcAAaDOBX1VmMxrlzWkja0IuyhwXY8AExS7dcpVlZFNWs0IuqtDIEAADUYAsYJOkloAD5xygSN4zhegIKUFwMAJRggCTEbwcGCAIr/8UlA4ucRyzCxplOBecxhMoUk04XjTjTYgZjNFAG78gks0BHVzSy4s7tKLLIAMscCFAXzAhSW12IILLoEEkAMruTCj3jXQFGkkNddcA4seO3AAWnM+6NGKKAQ48Eo07cDljSpzFEHChSuAVAtcGilTyh5H5BbALGQ6BI4zsLQyXJt01mnnnW0GBAAh+QQBBQD/ACwAAAAAIAAgAAAI/gDlCRxIsCBBeAYTKlxIjlg1hAsjJgTXy1Ywb6QsgZPI0ZsvXcESHSEQABDHiB594dKCIIDLLicVggO2C84Cl0LqBPgS0+C4XYs+uPQgKFihnT0JjtOUxGWHOuSu+YKDNKk8Y2IcBHiQxpjAbMHSVI2p7hAJl09sESRXTCzPmLhekAShKl1Bd8vcnixHxWUAOe8URlMzViE6Ri4J2KgVURybwgXPsWrhkoUfaBLdtYE8sBYUlyDUwIrWjuMczlcZBNBw55MvZqU5EgpwJSEvlxIEAVsW+CTiJwnfSboZYIa1no0CLFmXkN0rIFoD5Pl2MvkRbwbhHcNlLBQOAwFMrnQaJzG5EWoJq+mqFWyXnhQuj4iKaJ6Z3YLvznlzFgwXKTUKBLDAEsEoZF4xGykEjzvoZGPMKU0F0EAY1xhkXjAVngRPObOY4BICfwRjDToCJcdEMMy4Y1UmQgVwgiWxAGPNHwFUccwv9yUFzhwguBQFJsKItcQxtpBnlUDFEBZABWqQEcARwNRi5JHysNPLES5BEIAW7oQDEZUCtXPKBC6FAeZCbUSB3ZlstklQQAAh+QQBBQD/ACwAAAAAIAAgAAAI/gDlCRxIsKDBgwgTKlzI8CC8b93cNZw4jpmuW9kmLnQXrReuXsGYCQv2TqPBbr527fKFyMcCAgFmmRw4zherSF5MBNgJM4Aqk+yiqYrTo8HOABhUDGGDgMGxhuB8RTqz4ugCHWAW+YIWzFIAId4UMpP05YaGox+6WNKVraS8breuBLjjlqC7WmhSUDBwNIWaX93OGdRm60OAUAXhaeq5M0IRQbCuwUuoTVUAD8EKqpMRgECNOJl8BWvGbqG3OQGGoCv4jhIBCZOC9ToGDmqOAGoOhpMTwMCecXUXajOgoJTBc79qxQmAwJLGWgEuVDNYjlYuYXsCLIA0EU6AGwi7wnn85UZ7p4YxAsBJ2M5Zr19rAig4tbCaUVQLyR37lUY+/YSbGMAANAzB440xZ8jHSUJcBNBDRg25k80b8gkyWUHr2BAAHtSY5E4hzM2RzYUC+ZJBAKhEM5M8jjB3BzDhDOSaCLwQuGKLBvhRizHouCNGAF/sYuOKjRiQ4y668KKhKbkMORM8jRDg2zGLGCDBME2uSFAj8hUSSQA+2JKllgO1iEAHAaBRy5hkCtRib75Qw0w7bRL0iANMaFPnQcvEqFBAACH5BAEFAP8ALAAAAAAgACAAAAj+AOUJHEiwoMGDCBMqXMiwocOHECOaOxcxoTtvzXzp0jWu4sB357Id04VLFy+S4gyiG5arpcuXuGSJkqRI0SJGjCyBetQolq5yBFEFGEq0qNGjRvEQ5DSUDRsvWKJaqXJFDBw7d7JqtUMGi5YHAb4Q9HYjwJJgtWjNojWM2jh069i1m0uX3S9fbggESCnQWzA7BgJMGgYt3DuG7nKNQhFg0UB4xW4BKwsDYrdaYwKEYEdQmy5itBAQiPSQGScHAVgZ9HwsS4AV2hz+IhLACdDVu2JJCMCnYbpGASbUSui5T4AOxxh2sxAgy0JtsmgEUKNuoZwAF6AplzR0mEJvYN+pOKTWJAARdwnDBMCA3uEt5qEQ8poQwFLEN8GrqXRtw1vEcBsEkARFBNEylCftVGRJAAhswpc84wgRwBbBeCRPCwHM8Es4An0SQAWcWGPhMQzyEUw44VAQwBm3EOgRGwG8cIoyYASQAivBtOeRNh0EcEgkBBiACC/NwGOhQJkEcIIHAbQwjC2xHSlPN0AQZQkvu5gjpUCeBHbGOb0wk+CW7CBSBkXo6IhQQAAh+QQBBQD/ACwAAAAAIAAgAAAI/gDlCRxIsKDBgwgTKlzIsKHDhw2t1YLYMBoDAguOWAJHESEtAgEMBBjpIQ6wceneUXyHThwCA3agmKAwMgCCI490NdN2rmc5cuV+lnNHEFwuYDECwDkWa9KaIikQ1NTAI0wgTrl64aJFq1Y1gt1w9SIUQAg4a9m0VRPmSlKaFTURgNiBplOzY7+iEXR3DJevACvCHVTnDVqmKFIDEHCgwYebawW98eq1QcJEhfDkBZNzoqZiIuoIKts1JcAhiMrm+ABJ4RfBc70aBchCtGE6Yb0WSfVSeyA1VgRe6G0IrRYbkGlCF3yXqwYBXA3L5Yojks86hN7KBIDUkJickXqUwR+ENyqAlXMLtc0ZuSbdQmgdIljDvGdkmHYMz2kJUEqhopFfPPSfF96MB4lIZfTG0CwOnPCLSgVJIpUa7j10DQwBzAJNQZuMtMU4HUURACDAoDOQKSNFMRxFiAQwxC/KCJTJSEXscl1HwDzAwC+/eCMJSEfQ0k1HA4UQACu4zCEVFbQcoyBFVwSQByEgqUFLLwUSKRAoAUgg1Ri65HKMlgOJA1IATcySiy7okSlQEhV8kU0vt6zo5jvDEJVNNBAyFBAAIfkEAQUA/wAsAAAAACAAIAAACP4A5QkcSLCgwYMIEypcWBAdqk3gGEocOCxAgBydJi5cVy6XxQAKchDTeDBdr12OApC5YtHAmHMkCZLTpatRgCu8RI0gEEBDqXQxBfbqVSpAF16+fM3xYPHILXQxlfEaFYBOOG3RnLHaUsGil1jl2k3slitSgDvuBLJD940WGIsOrMiilq3cQne4GJ0V23DZEbhoegULxqwbX4PEFu1NqCqDxQ2PfuXKpUsZPIPWFC1O6E5OA4tQXlH+Zc6guUSbFTqL4iCAhTajqh189yj1wlQ+LJawBNPgJ9sKwcHyMyMAgUAHXwFH2G2XsE0ULKY56PHNOobecuG2SCDOO+oBwKlAVehNFZwFARAEyZbQY5VxCqn9WWExR6qF7qkldAXE4gpH4jDkkRXCHKSNF60ZMIg1E3mExSzfEeRHawFIEU1aDQaAhSzhDLQLCBZlMEpQHm1BCzTyMLOFRRF4wUxQ8ni0xi+iHIJeA0J0og2MMQZgBhofWAREIrlYcxmMHj3A0wmawFLkkTDq8lEauexyi5E8ClQOHl94w8tk0UCZ5UDL/ILimAf1FlNAACH5BAEFAP8ALAAAAAAgACAAAAj+AOUJHEhQ3jt17woqXMhwYCMalhpKVPgOnLd0AQIYgARvosdzuXxBCtBAYyKPE8nVCrYkABU7GRWhbFiOlrYQARrtshJz5sJyt1pJIMBsly4mBAJM8lmwHC86BJLI61aMV5GMizoyleeUSABNAttlA/ZEo8yt5k6hCOCNoDpoXjQCcsf0XKIGJ7QSFPcmIxpwPtOtCSCHobu+AcwcM4fy25EAtiSOSTpGV7R1E6GBQKBtYpqMYGgB04aZYbAASch51BOAgBlbtop1S1gQnqYAgehOhCeo9Zxjt3RdU7gOS4BRM9kBylgm2K1oCstRuIDL57kwoLMtXBZARzOU7rrEBevlJKmhhYECaOm2e1yzWqMoGVoQYMEwhTEM/Onc0N0wSWo0gQICGQXABHsEnRPABqQMt9A1jjAxwgQFSvCEJbzwR5ApAdQAjDV6yaMLHSY4YECBJ+SxyzjohEiQcXP0Qo074mgSBYEFMpBEJ+fMBAECmigShgkFGhABCmK0shUxGVFQoAYvPFHHKg5udUxGCLRwBiGYtBJMNIxtJRA8vowSTCm9/JILMeLQJiZB5tSCiy7auPimQO5UA41ud/bp550BAQA7");background-repeat:no-repeat;bottom:0;display:inline-block;height:32px;left:0;margin:auto;position:absolute;right:0;top:0;width:32px}#bootstrap-theme .table.table-transparent{background-color:transparent;border:1px solid #d3dee2}#bootstrap-theme .table.table-transparent th{background-color:transparent}#bootstrap-theme .crm_wizard__body, #bootstrap-theme .crm_wizard__title{margin-bottom:30px}#bootstrap-theme .crm_wizard__title .crm_wizard__title__number{border:2px solid;border-radius:50px;display:inline-block;height:32px;line-height:28px;margin-right:5px;text-align:center;width:32px}#bootstrap-theme .crm_wizard__title .nav-pills{padding:0}#bootstrap-theme .crm_wizard__title .nav-pills a, #bootstrap-theme .crm_wizard__title .nav-pills a:active, #bootstrap-theme .crm_wizard__title .nav-pills a:hover{color:#0071bd;padding:15px 20px}#bootstrap-theme .crm_wizard__title .nav-pills li.active a{background:#f3f6f7;color:#0071bd;position:relative}#bootstrap-theme .crm_wizard__title .nav-pills li.active a:after, #bootstrap-theme .crm_wizard__title .nav-pills li.active a:before{border-color:transparent;border-style:solid;border-width:30px 0 30px 8px;bottom:0;content:'';height:100%;position:absolute;top:0;width:0}#bootstrap-theme .crm_wizard__title .nav-pills li.active a:after{border-left-color:#f3f6f7;right:-7px}#bootstrap-theme .crm_wizard__title .nav-pills li.active a:before{border-left-color:#fff;left:0}#bootstrap-theme .crm_wizard__title .nav-pills li.active:first-child a:before{display:none}#bootstrap-theme .crm_wizard__title .nav-pills li:not(.active) a{color:#464354}#bootstrap-theme .crm_wizard__title .nav-pills li:not(.active) a:after, #bootstrap-theme .crm_wizard__title .nav-pills li:not(.active) a:before{background:#f3f6f7;content:'';height:31px;position:absolute;right:-9px;width:1px}#bootstrap-theme .crm_wizard__title .nav-pills li:not(.active) a:after{-webkit-transform:skewX(-15deg);-ms-transform:skewX(-15deg);-moz-transform:skewX(-15deg);transform:skewX(-15deg);bottom:0}#bootstrap-theme .crm_wizard__title .nav-pills li:not(.active) a:before{-webkit-transform:skewX(15deg);-ms-transform:skewX(15deg);-moz-transform:skewX(15deg);transform:skewX(15deg);top:0}#bootstrap-theme .crm_wizard__title .nav-pills li:not(.active) a:hover{background:none;color:#0071bd}#bootstrap-theme .crm_wizard__title .nav-pills li.completed a{color:#00B0B9}#bootstrap-theme .crm_wizard__title .panel-body{padding:0}#bootstrap-theme .crm_wizard__body .form-group, #bootstrap-theme .crm_wizard__body .form-group-lg{max-width:550px}#bootstrap-theme .crm_wizard__body .form-group-lg .form-control{min-width:100%;margin-bottom:15px}#bootstrap-theme .crm_wizard__body .form-control, #bootstrap-theme .crm_wizard__body .form-group .select2-container{max-width:370px}#bootstrap-theme .crm_wizard__body .select2-container{height:auto}#bootstrap-theme .crm_wizard .panel-body{border-radius:2px}#bootstrap-theme .chr_wysiwyg{border-bottom:none !important}#bootstrap-theme .chr_wysiwyg [text-angular-toolbar] .btn[disabled]{opacity:1}#bootstrap-theme .chr_wysiwyg .placeholder-text{opacity:0.6}#bootstrap-theme .chr_wysiwyg__action{background-color:#fff;border:1px solid #e8eef0;border-top:none;padding:5px 0;text-align:right}#bootstrap-theme .chr_wysiwyg__action hr{margin:0 10px}#bootstrap-theme .chr_wysiwyg__action .btn-link{margin-top:5px}#bootstrap-theme .chr_wysiwyg__action .btn-link[disabled]{opacity:0.6}#bootstrap-theme .chr_wysiwyg__action .fa{margin-right:5px}#bootstrap-theme .crm_collapsible-content{padding:10px}#bootstrap-theme .crm_collapsible-content .crm_collapsible-content__title{margin-bottom:10px;text-transform:uppercase}#bootstrap-theme .crm_collapsible-content .crm_collapsible-content__title .fa{color:#464354;margin-right:10px}#bootstrap-theme .crm_notification-badge{background:#0071bd;border-radius:2px;color:#fff;display:inline-block;font-size:13px;line-height:14px;padding:5px 8px;text-align:center;vertical-align:middle;white-space:nowrap}#bootstrap-theme .crm_notification-badge:hover{color:#fff;opacity:0.8;text-decoration:none}#bootstrap-theme .crm_notification-badge__info{background:#0071bd;color:#fff}#bootstrap-theme .crm_notification-badge__success{background:#44cb7e;color:#464354}#bootstrap-theme .crm_notification-badge__warning{background:#e6ab5e;color:#464354}#bootstrap-theme .crm_notification-badge__danger{background:#cf3458;color:#fff}#bootstrap-theme .pointer{cursor:pointer !important} .crm-container.ui-dialog{z-index:2001} .ui-widget-overlay.ui-front{z-index:2000} .mobile [type='date'][uib-datepicker-popup]{line-height:normal} .mobile [type='date'][uib-datepicker-popup]::-webkit-inner-spin-button, .mobile [type='date'][uib-datepicker-popup]::-webkit-clear-button{-webkit-appearance:none;appearance:none;display:none} .mobile [type='date'][uib-datepicker-popup]::-webkit-calendar-picker-indicator{background:transparent;bottom:0;color:transparent;height:auto;left:0;position:absolute;right:-50px;top:0;width:auto} .mobile [type='date'][uib-datepicker-popup]+.input-group-addon{border-left:1px solid #c2cfd8 !important;height:30px !important;line-height:30px !important;padding:0 !important;pointer-events:none;position:absolute !important;right:0 !important;width:38px !important;z-index:3}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/lib/shoreditch/dropdown.js b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/lib/shoreditch/dropdown.js
new file mode 100644
index 00000000..4ded8501
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/lib/shoreditch/dropdown.js
@@ -0,0 +1,165 @@
+/* ========================================================================
+ * Bootstrap: dropdown.js v3.4.1
+ * https://getbootstrap.com/docs/3.4/javascript/#dropdowns
+ * ========================================================================
+ * Copyright 2011-2019 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // DROPDOWN CLASS DEFINITION
+ // =========================
+
+ var backdrop = '.dropdown-backdrop'
+ var toggle = '[data-toggle="dropdown"]'
+ var Dropdown = function (element) {
+ $(element).on('click.bs.dropdown', this.toggle)
+ }
+
+ Dropdown.VERSION = '3.4.1'
+
+ function getParent($this) {
+ var selector = $this.attr('data-target')
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
+ }
+
+ var $parent = selector !== '#' ? $(document).find(selector) : null
+
+ return $parent && $parent.length ? $parent : $this.parent()
+ }
+
+ function clearMenus(e) {
+ if (e && e.which === 3) return
+ $(backdrop).remove()
+ $(toggle).each(function () {
+ var $this = $(this)
+ var $parent = getParent($this)
+ var relatedTarget = { relatedTarget: this }
+
+ if (!$parent.hasClass('open')) return
+
+ if (e && e.type == 'click' && /input|textarea/i.test(e.target.tagName) && $.contains($parent[0], e.target)) return
+
+ $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
+
+ if (e.isDefaultPrevented()) return
+
+ $this.attr('aria-expanded', 'false')
+ $parent.removeClass('open').trigger($.Event('hidden.bs.dropdown', relatedTarget))
+ })
+ }
+
+ Dropdown.prototype.toggle = function (e) {
+ var $this = $(this)
+
+ if ($this.is('.disabled, :disabled')) return
+
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
+
+ clearMenus()
+
+ if (!isActive) {
+ if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
+ // if mobile we use a backdrop because click events don't delegate
+ $(document.createElement('div'))
+ .addClass('dropdown-backdrop')
+ .insertAfter($(this))
+ .on('click', clearMenus)
+ }
+
+ var relatedTarget = { relatedTarget: this }
+ $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget))
+
+ if (e.isDefaultPrevented()) return
+
+ $this
+ .trigger('focus')
+ .attr('aria-expanded', 'true')
+
+ $parent
+ .toggleClass('open')
+ .trigger($.Event('shown.bs.dropdown', relatedTarget))
+ }
+
+ return false
+ }
+
+ Dropdown.prototype.keydown = function (e) {
+ if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return
+
+ var $this = $(this)
+
+ e.preventDefault()
+ e.stopPropagation()
+
+ if ($this.is('.disabled, :disabled')) return
+
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
+
+ if (!isActive && e.which != 27 || isActive && e.which == 27) {
+ if (e.which == 27) $parent.find(toggle).trigger('focus')
+ return $this.trigger('click')
+ }
+
+ var desc = ' li:not(.disabled):visible a'
+ var $items = $parent.find('.dropdown-menu' + desc)
+
+ if (!$items.length) return
+
+ var index = $items.index(e.target)
+
+ if (e.which == 38 && index > 0) index-- // up
+ if (e.which == 40 && index < $items.length - 1) index++ // down
+ if (!~index) index = 0
+
+ $items.eq(index).trigger('focus')
+ }
+
+
+ // DROPDOWN PLUGIN DEFINITION
+ // ==========================
+
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.dropdown')
+
+ if (!data) $this.data('bs.dropdown', (data = new Dropdown(this)))
+ if (typeof option == 'string') data[option].call($this)
+ })
+ }
+
+ var old = $.fn.dropdown
+
+ $.fn.dropdown = Plugin
+ $.fn.dropdown.Constructor = Dropdown
+
+
+ // DROPDOWN NO CONFLICT
+ // ====================
+
+ $.fn.dropdown.noConflict = function () {
+ $.fn.dropdown = old
+ return this
+ }
+
+
+ // APPLY TO STANDARD DROPDOWN ELEMENTS
+ // ===================================
+
+ $(document)
+ .on('click.bs.dropdown.data-api', clearMenus)
+ .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
+ .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)
+ .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown)
+ .on('keydown.bs.dropdown.data-api', '.dropdown-menu', Dropdown.prototype.keydown)
+
+}(jQuery);
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/phpunit.xml.dist b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/phpunit.xml.dist
new file mode 100644
index 00000000..bf600a87
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/phpunit.xml.dist
@@ -0,0 +1,29 @@
+<phpunit backupGlobals="false"
+ backupStaticAttributes="false"
+ colors="true"
+ convertErrorsToExceptions="true"
+ convertNoticesToExceptions="true"
+ convertWarningsToExceptions="true"
+ processIsolation="false"
+ stopOnFailure="false"
+ syntaxCheck="false"
+ bootstrap="tests/phpunit/bootstrap.php"
+>
+ <testsuites>
+ <testsuite name="My Test Suite">
+ <directory>./tests/phpunit</directory>
+ </testsuite>
+ </testsuites>
+
+ <filter>
+ <whitelist>
+ <directory suffix=".php">./</directory>
+ </whitelist>
+ </filter>
+
+ <listeners>
+ <listener class="Civi\Test\CiviTestListener">
+ <arguments></arguments>
+ </listener>
+ </listeners>
+</phpunit>
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/readme.md b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/readme.md
new file mode 100644
index 00000000..831b7f73
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/readme.md
@@ -0,0 +1,120 @@
+CiviCRM API Version 4
+=====================
+
+Welcome
+-------
+
+This is the latest version of the API (Application Programming Interface) for CiviCRM. If you are here because you're trying to install an extension that requires this, just install this and you're done!
+
+If you are a developer, read on...
+
+Using Api4
+----------
+
+Once installed you can navigate to **Support -> Developer -> Api4 Explorer** in the menu. This gives a live, interactive code generator in which you can build and test api calls:
+
+![Screenshot](/images/ApiExplorer.png)
+
+Output
+------
+
+The php binding returns an [arrayObject](http://php.net/manual/en/class.arrayobject.php). This gives immediate access to the results, plus allows returning additional metadata properties.
+
+
+```php
+$result = \Civi\Api4\Contact::get()->execute();
+
+// you can loop through the results directly
+foreach ($result as $contact) {}
+
+// you can just grab the first one
+$contact1 = $result->first();
+
+// reindex results on-the-fly (replacement for sequential=1 in v3)
+$result->indexBy('id');
+
+// or fetch some metadata about the call
+$entity = $result->entity; // "Contact"
+```
+
+We can do the something very similar in javascript thanks to js arrays also being objects:
+
+```javascript
+CRM.api4('Contact', 'get', params).then(function(result) {
+ // you can loop through the results
+ result.forEach(function(contact, n) {});
+
+ // you can just grab the first one
+ var contact1 = result[0];
+
+ // or fetch some metadata about the call
+ var entity = result.entity; // "Contact"
+});
+```
+
+Notable changes from Version 3:
+-------------------------------
+
+* **Api wrapper**
+ - In addition to the familiar style of `civicrm_api4('Entity', 'action', $params)` there is now an OO style in php `\Civi\Api4\Entity::action()`.
+ - When chaining api calls together, backreferences to values from the main api call must be explicitly given (discoverable in the api explorer).
+ - `$checkPermissions` always defaults to `TRUE`. In api3 it defaulted to `TRUE` in REST/Javascript but `FALSE` in php.
+ - A 4th param `index` controls how results are returned:
+ Passing a string will index all results by that key e.g. `civicrm_api4('Contact', 'get', $params, 'id')` will index by id.
+ Passing a number will return the result at that index e.g. `civicrm_api4('Contact', 'get', $params, 0)` will return the first result and is the same as `\Civi\Api4\Contact::get()->execute()->first()`. `-1` is the equivalent of `$result->last()`.
+* **Actions**
+ - `Get` no longer sets a default limit of 25 outside the api explorer.
+ - Use the `Update` action to update an entity rather than `Create` with an id.
+ - `Update` and `Delete` can be performed on multiple items at once by specifying a `where` clause, vs a single item by id in api3.
+ - `getsingle` is gone, use `$result->first()` or `index` `0`.
+ - `getoptions` is no longer a standalone action, but part of `getFields`.
+* **Input**
+ - Instead of a single `$params` array containing a mishmash of fields, options and parameters, each api4 parameters is distinct.
+ e.g. for the `Get` action: `select`, `where`, `orderBy` and `limit` are different params.
+ - Custom fields are refered to by name rather than id. E.g. use `constituent_information.Most_Important_Issue` instead of `custom_4`.
+* **Output**
+ - Output is an array with object properties rather than a nested array.
+ - In PHP, you can `foreach` the results arrayObject directly, or you can call methods on it like `$result->first()` or `$result->indexBy('foo')`.
+ - By default, results are indexed sequentially like api3 `sequential => 1`, but the `index` param or `indexBy()` method let you change that.
+ e.g. `civicrm_api4('Contact', 'get', $params, 'id')` or `\Civi\Api4\Contact::get()->execute()->indexBy('id')` will index results by id.
+
+Security
+--------
+
+Each `action` object has a `$checkPermissions` property. This always defaults to `TRUE`, and for calls from REST it cannot be disabled.
+
+Architecture
+------------
+
+* An [**Entity**](Civi/Api4/Generic/AbstractEntity.php) is a class implementing one or more static methods (`get()`, `create()`, `delete()`, etc).
+* Each static method constructs and returns an [**Action object**](Civi/Api4/Generic/AbstractAction.php).
+* All actions extend the [AbstractAction class](Civi/Api4/Generic/AbstractAction.php). A number of other abstract action classes build on this, e.g. [AbstractBatchAction](Civi/Api4/Generic/AbstractBatchAction.php) is the base class for batch-process actions (`delete`, `update`, `replace`).
+* Most entity classes correspond to a `CRM_Core_DAO` subclass. E.g. `Civi\Api4\Contact` corresponds to `CRM_Contact_DAO_Contact`.
+* A set of **`DAO` action classes** (e.g. [DAOGetAction](Civi/Api4/Generic/DAOGetAction.php), [DAODeleteAction](Civi/Api4/Generic/DAODeleteAction.php)) exists to support DAO-based entities. [DAOGetAction](Civi/Api4/Generic/DAOGetAction.php) uses [`Api4SelectQuery`](Civi/API/Api4SelectQuery.php) to query the database.
+* A set of **`Basic` action classes** (e.g. [BasicGetAction](Civi/Api4/Generic/BasicGetAction.php), [BasicBatchAction](Civi/Api4/Generic/BasicBatchAction.php)) exists to support many other use-cases, e.g. file-based entities.
+* The base action `execute()` method calls the core [`civi_api_kernel`](https://github.com/civicrm/civicrm-core/blob/master/Civi/API/Kernel.php)
+service `runRequest()` method which invokes hooks and then calls the `_run` method for that action.
+* Each action object has a `_run()` method that accepts and updates a [`Result`](Civi/Api4/Generic/Result.php) object (which is an extended [ArrayObject](http://php.net/manual/en/class.arrayobject.php)).
+
+Extending Api4
+--------------
+
+#### Modifying an existing entity/action:
+
+To alter the behavior of an existing entiy action, use [hook_civicrm_apiWrappers](https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_apiWrappers).
+
+#### Adding an action to an existing entity:
+
+Create a class which extends a generic action class (see below). Give it the same namespace and file location as other actions for that entity. It will be picked up automatically by api4's getActions file scanner.
+
+#### Adding a new api entity:
+
+If your entity has a database table and DAO, simply add a class to the `Civi/Api4` directory of your extension. Give the file and class the same name as your entity, and extend the [DAOEntity class](Civi/Api4/Generic/DAOEntity.php).
+
+For specialty apis, try the `BasicGet`, `BasicCreate`, `BasicUpdate`, `BasicBatch` and `BasicReplace` actions as in [this example](tests/phpunit/Mock/Api4/MockBasicEntity.php).
+
+Tests
+-----
+
+Tests are located in the `tests` directory (surprise!)
+To run the entire Api4 test suite go to the api4 extension directory and type `phpunit4` from the command line.
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/services.xml b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/services.xml
new file mode 100644
index 00000000..24d0b061
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/services.xml
@@ -0,0 +1,122 @@
+<container xmlns="http://symfony.com/schema/dic/services"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
+
+ <services>
+
+ <service id="spec_gatherer" class="Civi\Api4\Service\Spec\SpecGatherer"/>
+
+ <service id="schema_map_builder" class="Civi\Api4\Service\Schema\SchemaMapBuilder" public="false">
+ <argument type="service" id="dispatcher" />
+ </service>
+
+ <service id="schema_map" class="Civi\Api4\Service\Schema\SchemaMap">
+ <factory service="schema_map_builder" method="build"/>
+ </service>
+
+ <service id="joiner" class="Civi\Api4\Service\Schema\Joiner">
+ <argument type="service" id="schema_map"/>
+ </service>
+
+ <service id="action_object_provider" class="Civi\Api4\Provider\ActionObjectProvider">
+ <tag name="event_subscriber"/>
+ </service>
+
+ <service id="contact_creation.spec_provider" class="Civi\Api4\Service\Spec\Provider\ContactCreationSpecProvider">
+ <tag name="spec_provider"/>
+ </service>
+
+ <service class="Civi\Api4\Service\Spec\Provider\ContactTypeCreationSpecProvider">
+ <tag name="spec_provider"/>
+ </service>
+
+ <service class="Civi\Api4\Service\Spec\Provider\AddressCreationSpecProvider">
+ <tag name="spec_provider"/>
+ </service>
+
+ <service id="option_value_creation.spec_provider" class="Civi\Api4\Service\Spec\Provider\OptionValueCreationSpecProvider">
+ <tag name="spec_provider"/>
+ </service>
+
+ <service class="Civi\Api4\Service\Spec\Provider\ActivityCreationSpecProvider">
+ <tag name="spec_provider"/>
+ </service>
+
+ <service class="Civi\Api4\Service\Spec\Provider\ActionScheduleCreationSpecProvider">
+ <tag name="spec_provider"/>
+ </service>
+
+ <service class="Civi\Api4\Service\Spec\Provider\EmailCreationSpecProvider">
+ <tag name="spec_provider"/>
+ </service>
+
+ <service class="Civi\Api4\Service\Spec\Provider\PhoneCreationSpecProvider">
+ <tag name="spec_provider"/>
+ </service>
+
+ <service class="Civi\Api4\Service\Spec\Provider\EventCreationSpecProvider">
+ <tag name="spec_provider"/>
+ </service>
+
+ <service class="Civi\Api4\Service\Spec\Provider\NoteCreationSpecProvider">
+ <tag name="spec_provider"/>
+ </service>
+
+ <service class="Civi\Api4\Service\Spec\Provider\ContributionCreationSpecProvider">
+ <tag name="spec_provider"/>
+ </service>
+
+ <service class="Civi\Api4\Service\Spec\Provider\CustomGroupCreationSpecProvider">
+ <tag name="spec_provider"/>
+ </service>
+
+ <service class="Civi\Api4\Service\Spec\Provider\GroupCreationSpecProvider">
+ <tag name="spec_provider"/>
+ </service>
+
+ <service class="Civi\Api4\Service\Spec\Provider\NavigationCreationSpecProvider">
+ <tag name="spec_provider"/>
+ </service>
+
+ <service id="custom_group.pre_creation.subscriber" class="Civi\Api4\Event\Subscriber\CustomGroupPreCreationSubscriber">
+ <tag name="event_subscriber"/>
+ </service>
+
+ <service id="custom_field.pre_creation.subsciber" class="Civi\Api4\Event\Subscriber\CustomFieldPreCreationSubscriber">
+ <tag name="event_subscriber"/>
+ </service>
+
+ <service id="option_value.pre_creation.subscriber" class="Civi\Api4\Event\Subscriber\OptionValuePreCreationSubscriber">
+ <tag name="event_subscriber"/>
+ </service>
+
+ <service id="activity.pre_creation.subscriber" class="Civi\Api4\Event\Subscriber\ActivityPreCreationSubscriber">
+ <tag name="event_subscriber"/>
+ </service>
+
+ <service id="contact.schema_map.subscriber" class="Civi\Api4\Event\Subscriber\ContactSchemaMapSubscriber">
+ <tag name="event_subscriber"/>
+ </service>
+
+ <service id="activity.schema_map.subscriber" class="Civi\Api4\Event\Subscriber\ActivitySchemaMapSubscriber">
+ <tag name="event_subscriber"/>
+ </service>
+
+ <service id="api4.permission_check.subscriber" class="Civi\Api4\Event\Subscriber\PermissionCheckSubscriber">
+ <tag name="event_subscriber"/>
+ </service>
+
+ <service id="api4.required_fields.subscriber" class="Civi\Api4\Event\Subscriber\ValidateFieldsSubscriber">
+ <tag name="event_subscriber"/>
+ </service>
+
+ <service id="api4.post_select_query.subscriber" class="Civi\Api4\Event\Subscriber\PostSelectQuerySubscriber">
+ <tag name="event_subscriber"/>
+ </service>
+
+ <service class="Civi\Api4\Service\Spec\Provider\CustomValueSpecProvider">
+ <tag name="spec_provider"/>
+ </service>
+
+ </services>
+</container>
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/templates/CRM/Api4/Page/Api4Explorer.tpl b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/templates/CRM/Api4/Page/Api4Explorer.tpl
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/templates/CRM/Api4/Page/Api4Explorer.tpl
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/BaseCustomValueTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/BaseCustomValueTest.php
new file mode 100644
index 00000000..41fe7281
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/BaseCustomValueTest.php
@@ -0,0 +1,31 @@
+<?php
+
+namespace Civi\Test\Api4\Action;
+
+use Civi\Test\Api4\UnitTestCase;
+use Civi\Test\Api4\Traits\TableDropperTrait;
+
+abstract class BaseCustomValueTest extends UnitTestCase {
+
+ use \Civi\Test\Api4\Traits\OptionCleanupTrait {
+ setUp as setUpOptionCleanup;
+ }
+ use TableDropperTrait;
+
+ /**
+ * Set up baseline for testing
+ */
+ public function setUp() {
+ $this->setUpOptionCleanup();
+ $cleanup_params = [
+ 'tablesToTruncate' => [
+ 'civicrm_custom_group',
+ 'civicrm_custom_field',
+ ],
+ ];
+
+ $this->dropByPrefix('civicrm_value_mycontact');
+ $this->cleanup($cleanup_params);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/BasicActionsTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/BasicActionsTest.php
new file mode 100644
index 00000000..29523389
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/BasicActionsTest.php
@@ -0,0 +1,154 @@
+<?php
+
+namespace Civi\Test\Api4\Action;
+
+use Civi\Test\Api4\UnitTestCase;
+use Civi\Api4\MockBasicEntity;
+
+/**
+ * @group headless
+ */
+class BasicActionsTest extends UnitTestCase {
+
+ public function testCrud() {
+ MockBasicEntity::delete()->addWhere('id', '>', 0)->execute();
+
+ $id1 = MockBasicEntity::create()->addValue('foo', 'one')->execute()->first()['id'];
+
+ $result = MockBasicEntity::get()->execute();
+ $this->assertCount(1, $result);
+
+ $id2 = MockBasicEntity::create()->addValue('foo', 'two')->execute()->first()['id'];
+
+ $result = MockBasicEntity::get()->execute();
+ $this->assertCount(2, $result);
+
+ MockBasicEntity::update()->addWhere('id', '=', $id2)->addValue('foo', 'new')->execute();
+
+ $result = MockBasicEntity::get()->addOrderBy('id', 'DESC')->setLimit(1)->execute();
+ $this->assertCount(1, $result);
+ $this->assertEquals('new', $result->first()['foo']);
+
+ MockBasicEntity::delete()->addWhere('id', '=', $id2);
+ $result = MockBasicEntity::get()->execute();
+ $this->assertEquals('one', $result->first()['foo']);
+ }
+
+ public function testReplace() {
+ MockBasicEntity::delete()->addWhere('id', '>', 0)->execute();
+
+ $objects = [
+ ['group' => 'one', 'color' => 'red'],
+ ['group' => 'one', 'color' => 'blue'],
+ ['group' => 'one', 'color' => 'green'],
+ ['group' => 'two', 'color' => 'orange'],
+ ];
+
+ foreach ($objects as &$object) {
+ $object['id'] = MockBasicEntity::create()->setValues($object)->execute()->first()['id'];
+ }
+
+ // Keep red, change blue, delete green, and add yellow
+ $replacements = [
+ ['color' => 'red', 'id' => $objects[0]['id']],
+ ['color' => 'not blue', 'id' => $objects[1]['id']],
+ ['color' => 'yellow']
+ ];
+
+ MockBasicEntity::replace()->addWhere('group', '=', 'one')->setRecords($replacements)->execute();
+
+ $newObjects = MockBasicEntity::get()->addOrderBy('id', 'DESC')->execute()->indexBy('id');
+
+ $this->assertCount(4, $newObjects);
+
+ $this->assertEquals('yellow', $newObjects->first()['color']);
+
+ $this->assertEquals('not blue', $newObjects[$objects[1]['id']]['color']);
+
+ // Ensure group two hasn't been altered
+ $this->assertEquals('orange', $newObjects[$objects[3]['id']]['color']);
+ $this->assertEquals('two', $newObjects[$objects[3]['id']]['group']);
+ }
+
+ public function testBatchFrobnicate() {
+ MockBasicEntity::delete()->addWhere('id', '>', 0)->execute();
+
+ $objects = [
+ ['group' => 'one', 'color' => 'red', 'number' => 10],
+ ['group' => 'one', 'color' => 'blue', 'number' => 20],
+ ['group' => 'one', 'color' => 'green', 'number' => 30],
+ ['group' => 'two', 'color' => 'blue', 'number' => 40],
+ ];
+ foreach ($objects as &$object) {
+ $object['id'] = MockBasicEntity::create()->setValues($object)->execute()->first()['id'];
+ }
+
+ $result = MockBasicEntity::batchFrobnicate()->addWhere('color', '=', 'blue')->execute();
+ $this->assertEquals(2, count($result));
+ $this->assertEquals([400, 1600], \CRM_Utils_Array::collect('frobnication', (array) $result));
+ }
+
+ public function testGetFields() {
+ $getFields = MockBasicEntity::getFields()->execute()->indexBy('name');
+
+ $this->assertCount(6, $getFields);
+ $this->assertEquals('Id', $getFields['id']['title']);
+ // Ensure default data type is "String" when not specified
+ $this->assertEquals('String', $getFields['color']['data_type']);
+
+ // Getfields should default to loadOptions = false and reduce them to bool
+ $this->assertTrue($getFields['group']['options']);
+ $this->assertFalse($getFields['id']['options']);
+
+ // Now load options
+ $getFields = MockBasicEntity::getFields()
+ ->addWhere('name', '=', 'group')
+ ->setLoadOptions(TRUE)
+ ->execute()->indexBy('name');
+
+ $this->assertCount(1, $getFields);
+ $this->assertArrayHasKey('one', $getFields['group']['options']);
+ }
+
+ public function testItemsToGet() {
+ $get = MockBasicEntity::get()
+ ->addWhere('color', 'NOT IN', ['yellow'])
+ ->addWhere('color', 'IN', ['red', 'blue'])
+ ->addWhere('color', '!=', 'green')
+ ->addWhere('group', '=', 'one');
+
+ $this->assertEquals(['red', 'blue'], $get->_itemsToGet('color'));
+ $this->assertEquals(['one'], $get->_itemsToGet('group'));
+ }
+
+ public function testFieldsToGet() {
+ $get = MockBasicEntity::get()
+ ->addWhere('color', '!=', 'green');
+
+ // If no "select" is set, should always return true
+ $this->assertTrue($get->_isFieldSelected('color'));
+ $this->assertTrue($get->_isFieldSelected('shape'));
+ $this->assertTrue($get->_isFieldSelected('size'));
+
+ // With a non-empty "select" fieldsToSelect() will return fields needed to evaluate each clause.
+ $get->addSelect('id');
+ $this->assertTrue($get->_isFieldSelected('color'));
+ $this->assertTrue($get->_isFieldSelected('id'));
+ $this->assertFalse($get->_isFieldSelected('shape'));
+ $this->assertFalse($get->_isFieldSelected('size'));
+ $this->assertFalse($get->_isFieldSelected('weight'));
+ $this->assertFalse($get->_isFieldSelected('group'));
+
+ $get->addClause('OR', ['shape', '=', 'round'], ['AND', [['size', '=', 'big'], ['weight', '!=', 'small']]]);
+ $this->assertTrue($get->_isFieldSelected('color'));
+ $this->assertTrue($get->_isFieldSelected('id'));
+ $this->assertTrue($get->_isFieldSelected('shape'));
+ $this->assertTrue($get->_isFieldSelected('size'));
+ $this->assertTrue($get->_isFieldSelected('weight'));
+ $this->assertFalse($get->_isFieldSelected('group'));
+
+ $get->addOrderBy('group');
+ $this->assertTrue($get->_isFieldSelected('group'));
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/BasicCustomFieldTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/BasicCustomFieldTest.php
new file mode 100644
index 00000000..85687a08
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/BasicCustomFieldTest.php
@@ -0,0 +1,187 @@
+<?php
+
+namespace Civi\Test\Api4\Action;
+
+use Civi\Api4\Contact;
+use Civi\Api4\CustomField;
+use Civi\Api4\CustomGroup;
+
+/**
+ * @group headless
+ */
+class BasicCustomFieldTest extends BaseCustomValueTest {
+
+ public function testWithSingleField() {
+
+ $customGroup = CustomGroup::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('name', 'MyContactFields')
+ ->addValue('extends', 'Contact')
+ ->execute()
+ ->first();
+
+ CustomField::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('label', 'FavColor')
+ ->addValue('custom_group_id', $customGroup['id'])
+ ->addValue('html_type', 'Text')
+ ->addValue('data_type', 'String')
+ ->execute();
+
+ $contactId = Contact::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('first_name', 'Johann')
+ ->addValue('last_name', 'Tester')
+ ->addValue('contact_type', 'Individual')
+ ->addValue('MyContactFields.FavColor', 'Red')
+ ->execute()
+ ->first()['id'];
+
+ $contact = Contact::get()
+ ->setCheckPermissions(FALSE)
+ ->addSelect('first_name')
+ ->addSelect('MyContactFields.FavColor')
+ ->addWhere('id', '=', $contactId)
+ ->addWhere('MyContactFields.FavColor', '=', 'Red')
+ ->execute()
+ ->first();
+
+ $this->assertArrayHasKey('MyContactFields', $contact);
+ $contactFields = $contact['MyContactFields'];
+ $this->assertArrayHasKey('FavColor', $contactFields);
+ $this->assertEquals('Red', $contactFields['FavColor']);
+
+ Contact::update()
+ ->addWhere('id', '=', $contactId)
+ ->addValue('MyContactFields.FavColor', 'Blue')
+ ->execute();
+
+ $contact = Contact::get()
+ ->setCheckPermissions(FALSE)
+ ->addSelect('MyContactFields.FavColor')
+ ->addWhere('id', '=', $contactId)
+ ->execute()
+ ->first();
+
+ $contactFields = $contact['MyContactFields'];
+ $this->assertEquals('Blue', $contactFields['FavColor']);
+ }
+
+ public function testWithTwoFields() {
+
+ $customGroup = CustomGroup::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('name', 'MyContactFields')
+ ->addValue('extends', 'Contact')
+ ->execute()
+ ->first();
+
+ CustomField::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('label', 'FavColor')
+ ->addValue('custom_group_id', $customGroup['id'])
+ ->addValue('html_type', 'Text')
+ ->addValue('data_type', 'String')
+ ->execute();
+
+ CustomField::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('label', 'FavFood')
+ ->addValue('custom_group_id', $customGroup['id'])
+ ->addValue('html_type', 'Text')
+ ->addValue('data_type', 'String')
+ ->execute();
+
+ $contactId1 = Contact::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('first_name', 'Johann')
+ ->addValue('last_name', 'Tester')
+ ->addValue('MyContactFields.FavColor', 'Red')
+ ->addValue('MyContactFields.FavFood', 'Cherry')
+ ->execute()
+ ->first()['id'];
+
+ $contactId2 = Contact::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('first_name', 'MaryLou')
+ ->addValue('last_name', 'Tester')
+ ->addValue('MyContactFields.FavColor', 'Purple')
+ ->addValue('MyContactFields.FavFood', 'Grapes')
+ ->execute()
+ ->first()['id'];
+
+ $contact = Contact::get()
+ ->setCheckPermissions(FALSE)
+ ->addSelect('first_name')
+ ->addSelect('MyContactFields.FavColor')
+ ->addSelect('MyContactFields.FavFood')
+ ->addWhere('id', '=', $contactId1)
+ ->addWhere('MyContactFields.FavColor', '=', 'Red')
+ ->addWhere('MyContactFields.FavFood', '=', 'Cherry')
+ ->execute()
+ ->first();
+
+ $this->assertArrayHasKey('MyContactFields', $contact);
+ $contactFields = $contact['MyContactFields'];
+ $this->assertArrayHasKey('FavColor', $contactFields);
+ $this->assertEquals('Red', $contactFields['FavColor']);
+
+ Contact::update()
+ ->addWhere('id', '=', $contactId1)
+ ->addValue('MyContactFields.FavColor', 'Blue')
+ ->execute();
+
+ $contact = Contact::get()
+ ->setCheckPermissions(FALSE)
+ ->addSelect('MyContactFields.FavColor')
+ ->addWhere('id', '=', $contactId1)
+ ->execute()
+ ->first();
+
+ $contactFields = $contact['MyContactFields'];
+ $this->assertEquals('Blue', $contactFields['FavColor']);
+
+ $search = Contact::get()
+ ->setCheckPermissions(FALSE)
+ ->addClause('OR', ['MyContactFields.FavColor', '=', 'Blue'], ['MyContactFields.FavFood', '=', 'Grapes'])
+ ->addSelect('id')
+ ->addOrderBy('id')
+ ->execute()
+ ->indexBy('id');
+
+ $this->assertEquals([$contactId1, $contactId2], array_keys((array) $search));
+
+ $search = Contact::get()
+ ->setCheckPermissions(FALSE)
+ ->addClause('NOT', ['MyContactFields.FavColor', '=', 'Purple'], ['MyContactFields.FavFood', '=', 'Grapes'])
+ ->addSelect('id')
+ ->addOrderBy('id')
+ ->execute()
+ ->indexBy('id');
+
+ $this->assertNotContains($contactId2, array_keys((array) $search));
+
+ $search = Contact::get()
+ ->setCheckPermissions(FALSE)
+ ->addClause('NOT', ['MyContactFields.FavColor', '=', 'Purple'], ['MyContactFields.FavFood', '=', 'Grapes'])
+ ->addSelect('id')
+ ->addOrderBy('id')
+ ->execute()
+ ->indexBy('id');
+
+ $this->assertContains($contactId1, array_keys((array) $search));
+ $this->assertNotContains($contactId2, array_keys((array) $search));
+
+ $search = Contact::get()
+ ->setCheckPermissions(FALSE)
+ ->setWhere([['NOT', ['OR', [['MyContactFields.FavColor', '=', 'Blue'], ['MyContactFields.FavFood', '=', 'Grapes']]]]])
+ ->addSelect('id')
+ ->addOrderBy('id')
+ ->execute()
+ ->indexBy('id');
+
+ $this->assertNotContains($contactId1, array_keys((array) $search));
+ $this->assertNotContains($contactId2, array_keys((array) $search));
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/ChainTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/ChainTest.php
new file mode 100644
index 00000000..bbd6a092
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/ChainTest.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace Civi\Test\Api4\Action;
+
+use Civi\Test\Api4\UnitTestCase;
+
+/**
+ * @group headless
+ */
+class ChainTest extends UnitTestCase {
+
+ public function testGetActionsWithFields() {
+ $actions = \Civi\Api4\Activity::getActions()
+ ->addChain('fields', \Civi\Api4\Activity::getFields()->setAction('$name'), 'name')
+ ->execute()
+ ->indexBy('name');
+
+ $this->assertEquals('Array', $actions['getActions']['fields']['params']['data_type']);
+ }
+
+ public function testGetEntityWithActions() {
+ $entities = \Civi\Api4\Entity::get()
+ ->addSelect('name')
+ ->setChain([
+ 'actions' => ['$name', 'getActions', ['select' => ['name']], 'name']
+ ])
+ ->execute()
+ ->indexBy('name');
+
+ $this->assertArrayHasKey('replace', $entities['Contact']['actions']);
+ $this->assertArrayHasKey('getLinks', $entities['Entity']['actions']);
+ $this->assertArrayNotHasKey('replace', $entities['Entity']['actions']);
+ }
+
+ public function testContactCreateWithGroup() {
+ $firstName = uniqid('cwtf');
+ $lastName = uniqid('cwtl');
+
+ $contact = \Civi\Api4\Contact::create()
+ ->addValue('first_name', $firstName)
+ ->addValue('last_name', $lastName)
+ ->addChain('group', \Civi\Api4\Group::create()->addValue('title', '$display_name'), 0)
+ ->addChain('add_to_group', \Civi\Api4\GroupContact::create()->addValue('contact_id', '$id')->addValue('group_id', '$group.id'), 0)
+ ->addChain('check_group', \Civi\Api4\GroupContact::get()->addWhere('group_id', '=', '$group.id'))
+ ->execute()
+ ->first();
+
+ $this->assertCount(1, $contact['check_group']);
+ $this->assertEquals($contact['id'], $contact['check_group'][0]['contact_id']);
+ $this->assertEquals($contact['group']['id'], $contact['check_group'][0]['group_id']);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/ComplexQueryTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/ComplexQueryTest.php
new file mode 100644
index 00000000..0a320100
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/ComplexQueryTest.php
@@ -0,0 +1,87 @@
+<?php
+
+namespace Civi\Test\Api4\Action;
+
+use Civi\Test\Api4\UnitTestCase;
+use Civi\Api4\Activity;
+
+/**
+ * @group headless
+ *
+ * This class tests a series of complex query situations described in the
+ * initial APIv4 specification
+ */
+class ComplexQueryTest extends UnitTestCase {
+
+ public function setUpHeadless() {
+ $relatedTables = [
+ 'civicrm_activity',
+ 'civicrm_activity_contact',
+ ];
+ $this->cleanup(['tablesToTruncate' => $relatedTables]);
+ $this->loadDataSet('DefaultDataSet');
+
+ return parent::setUpHeadless();
+ }
+
+ /**
+ * Fetch all phone call activities
+ * Expects at least one activity loaded from the data set.
+ */
+ public function testGetAllHousingSupportActivities() {
+ $results = Activity::get()
+ ->setCheckPermissions(FALSE)
+ ->addWhere('activity_type.name', '=', 'Phone Call')
+ ->execute();
+
+ $this->assertGreaterThan(0, count($results));
+ }
+
+ /**
+ * Fetch all activities with a blue tag; and return all tags on the activities
+ */
+ public function testGetAllTagsForBlueTaggedActivities() {
+
+ }
+
+ /**
+ * Fetch contacts named 'Bob' and all of their blue activities
+ */
+ public function testGetAllBlueActivitiesForBobs() {
+
+ }
+
+ /**
+ * Get all contacts in a zipcode and return their Home or Work email addresses
+ */
+ public function testGetHomeOrWorkEmailsForContactsWithZipcode() {
+
+ }
+
+ /**
+ * Fetch all activities where Bob is the assignee or source
+ */
+ public function testGetActivitiesWithBobAsAssigneeOrSource() {
+
+ }
+
+ /**
+ * Get all contacts which
+ * (a) have address in zipcode 94117 or 94118 or in city "San Francisco","LA"
+ * and
+ * (b) are not deceased and
+ * (c) have a custom-field "most_important_issue=Environment".
+ */
+ public function testAWholeLotOfConditions() {
+
+ }
+
+ /**
+ * Get participants who attended CiviCon 2012 but not CiviCon 2013.
+ * Return their name and email.
+ */
+ public function testGettingNameAndEmailOfAttendeesOfCiviCon2012Only() {
+
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/ContactApiKeyTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/ContactApiKeyTest.php
new file mode 100644
index 00000000..63d85d55
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/ContactApiKeyTest.php
@@ -0,0 +1,170 @@
+<?php
+
+namespace Civi\Test\Api4\Action;
+
+use Civi\Api4\Contact;
+
+/**
+ * @group headless
+ */
+class ContactApiKeyTest extends \Civi\Test\Api4\UnitTestCase {
+
+ public function testGetApiKey() {
+ \CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access CiviCRM'];
+ $key = uniqid();
+
+ $contact = Contact::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('first_name', 'Api')
+ ->addValue('last_name', 'Key0')
+ ->addValue('api_key', $key)
+ ->execute()
+ ->first();
+
+ $result = Contact::get()
+ ->setCheckPermissions(FALSE)
+ ->addWhere('id', '=', $contact['id'])
+ ->addSelect('api_key')
+ ->execute()
+ ->first();
+
+ $this->assertEquals($result['api_key'], $key);
+
+ $result = Contact::get()
+ ->addWhere('id', '=', $contact['id'])
+ ->addSelect('api_key')
+ ->execute()
+ ->first();
+
+ $this->assertTrue(empty($result['api_key']));
+ }
+
+ public function testCreateWithApiKey() {
+ \CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access CiviCRM', 'add contacts'];
+ $key = uniqid();
+
+ $error = '';
+ try {
+ Contact::create()
+ ->addValue('first_name', 'Api')
+ ->addValue('last_name', 'Key1')
+ ->addValue('api_key', $key)
+ ->execute()
+ ->first();
+ }
+ catch (\Exception $e) {
+ $error = $e->getMessage();
+ }
+ $this->assertContains('key', $error);
+ }
+
+ public function testUpdateApiKey() {
+ \CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access CiviCRM'];
+ $key = uniqid();
+
+ $contact = Contact::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('first_name', 'Api')
+ ->addValue('last_name', 'Key2')
+ ->addValue('api_key', $key)
+ ->execute()
+ ->first();
+
+ $error = '';
+ try {
+ // Try to update the key without permissions; nothing should happen
+ Contact::update()
+ ->addWhere('id', '=', $contact['id'])
+ ->addValue('api_key', "NotAllowed")
+ ->execute();
+ }
+ catch (\Exception $e) {
+ $error = $e->getMessage();
+ }
+
+ $result = Contact::get()
+ ->setCheckPermissions(FALSE)
+ ->addWhere('id', '=', $contact['id'])
+ ->addSelect('api_key')
+ ->execute()
+ ->first();
+
+ $this->assertContains('key', $error);
+
+ // Assert key is still the same
+ $this->assertEquals($result['api_key'], $key);
+
+ // Now we can update the key
+ \CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access CiviCRM', 'administer CiviCRM', 'edit all contacts'];
+
+ Contact::update()
+ ->addWhere('id', '=', $contact['id'])
+ ->addValue('api_key', "IGotThePower!")
+ ->execute();
+
+ $result = Contact::get()
+ ->addWhere('id', '=', $contact['id'])
+ ->addSelect('api_key')
+ ->execute()
+ ->first();
+
+ // Assert key was updated
+ $this->assertEquals($result['api_key'], "IGotThePower!");
+ }
+
+ public function testUpdateOwnApiKey() {
+ \CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access CiviCRM', 'edit own api keys', 'edit my contact'];
+ $key = uniqid();
+
+ $contact = Contact::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('first_name', 'Api')
+ ->addValue('last_name', 'Key3')
+ ->addValue('api_key', $key)
+ ->execute()
+ ->first();
+
+ $error = '';
+ try {
+ // Try to update the key without permissions; nothing should happen
+ Contact::update()
+ ->addWhere('id', '=', $contact['id'])
+ ->addValue('api_key', "NotAllowed")
+ ->execute();
+ }
+ catch (\Exception $e) {
+ $error = $e->getMessage();
+ }
+
+ $this->assertContains('key', $error);
+
+ $result = Contact::get()
+ ->setCheckPermissions(FALSE)
+ ->addWhere('id', '=', $contact['id'])
+ ->addSelect('api_key')
+ ->execute()
+ ->first();
+
+ // Assert key is still the same
+ $this->assertEquals($result['api_key'], $key);
+
+ // Now we can update the key
+ \CRM_Core_Session::singleton()->set('userID', $contact['id']);
+
+ Contact::update()
+ ->addWhere('id', '=', $contact['id'])
+ ->addValue('api_key', "MyId!")
+ ->execute();
+
+ $result = Contact::get()
+ ->setCheckPermissions(FALSE)
+ ->addWhere('id', '=', $contact['id'])
+ ->addSelect('api_key')
+ ->execute()
+ ->first();
+
+ // Assert key was updated
+ $this->assertEquals($result['api_key'], "MyId!");
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/CreateCustomValueTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/CreateCustomValueTest.php
new file mode 100644
index 00000000..762b4617
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/CreateCustomValueTest.php
@@ -0,0 +1,64 @@
+<?php
+
+namespace Civi\Test\Api4\Action;
+
+use Civi\Api4\CustomField;
+use Civi\Api4\CustomGroup;
+use Civi\Api4\OptionGroup;
+use Civi\Api4\OptionValue;
+
+/**
+ * @group headless
+ */
+class CreateCustomValueTest extends BaseCustomValueTest {
+
+ public function testGetWithCustomData() {
+ $optionValues = ['r' => 'Red', 'g' => 'Green', 'b' => 'Blue'];
+
+ $customGroup = CustomGroup::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('name', 'MyContactFields')
+ ->addValue('extends', 'Contact')
+ ->execute()
+ ->first();
+
+ CustomField::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('label', 'Color')
+ ->addValue('options', $optionValues)
+ ->addValue('custom_group_id', $customGroup['id'])
+ ->addValue('html_type', 'Select')
+ ->addValue('data_type', 'String')
+ ->execute();
+
+ $customField = CustomField::get()
+ ->setCheckPermissions(FALSE)
+ ->addWhere('label', '=', 'Color')
+ ->execute()
+ ->first();
+
+ $this->assertNotNull($customField['option_group_id']);
+ $optionGroupId = $customField['option_group_id'];
+
+ $optionGroup = OptionGroup::get()
+ ->setCheckPermissions(FALSE)
+ ->addWhere('id', '=', $optionGroupId)
+ ->execute()
+ ->first();
+
+ $this->assertEquals('Color', $optionGroup['title']);
+
+ $createdOptionValues = OptionValue::get()
+ ->setCheckPermissions(FALSE)
+ ->addWhere('option_group_id', '=', $optionGroupId)
+ ->execute()
+ ->getArrayCopy();
+
+ $values = array_column($createdOptionValues, 'value');
+ $labels = array_column($createdOptionValues, 'label');
+ $createdOptionValues = array_combine($values, $labels);
+
+ $this->assertEquals($optionValues, $createdOptionValues);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/CreateWithOptionGroupTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/CreateWithOptionGroupTest.php
new file mode 100644
index 00000000..b4d0af85
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/CreateWithOptionGroupTest.php
@@ -0,0 +1,190 @@
+<?php
+
+namespace Civi\Test\Api4\Action;
+
+use Civi\Api4\CustomField;
+use Civi\Api4\CustomGroup;
+use Civi\Api4\Contact;
+
+/**
+ * @group headless
+ */
+class CreateWithOptionGroupTest extends BaseCustomValueTest {
+
+ /**
+ * Remove the custom tables
+ */
+ public function setUp() {
+ $this->dropByPrefix('civicrm_value_financial');
+ $this->dropByPrefix('civicrm_value_favorite');
+ parent::setUp();
+ }
+
+ public function testGetWithCustomData() {
+ $group = uniqid('fava');
+ $colorField = uniqid('colora');
+ $foodField = uniqid('fooda');
+
+ $customGroupId = CustomGroup::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('name', $group)
+ ->addValue('extends', 'Contact')
+ ->execute()
+ ->first()['id'];
+
+ CustomField::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('label', $colorField)
+ ->addValue('name', $colorField)
+ ->addValue('options', ['r' => 'Red', 'g' => 'Green', 'b' => 'Blue'])
+ ->addValue('custom_group_id', $customGroupId)
+ ->addValue('html_type', 'Select')
+ ->addValue('data_type', 'String')
+ ->execute();
+
+ CustomField::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('label', $foodField)
+ ->addValue('name', $foodField)
+ ->addValue('options', ['1' => 'Corn', '2' => 'Potatoes', '3' => 'Cheese'])
+ ->addValue('custom_group_id', $customGroupId)
+ ->addValue('html_type', 'Select')
+ ->addValue('data_type', 'String')
+ ->execute();
+
+ $customGroupId = CustomGroup::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('name', 'FinancialStuff')
+ ->addValue('extends', 'Contact')
+ ->execute()
+ ->first()['id'];
+
+ CustomField::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('label', 'Salary')
+ ->addValue('custom_group_id', $customGroupId)
+ ->addValue('html_type', 'Number')
+ ->addValue('data_type', 'Money')
+ ->execute();
+
+ Contact::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('first_name', 'Jerome')
+ ->addValue('last_name', 'Tester')
+ ->addValue('contact_type', 'Individual')
+ ->addValue("$group.$colorField", 'r')
+ ->addValue("$group.$foodField", '1')
+ ->addValue('FinancialStuff.Salary', 50000)
+ ->execute();
+
+ $result = Contact::get()
+ ->setCheckPermissions(FALSE)
+ ->addSelect('first_name')
+ ->addSelect("$group.$colorField.label")
+ ->addSelect("$group.$foodField.label")
+ ->addSelect('FinancialStuff.Salary')
+ ->addWhere("$group.$foodField.label", 'IN', ['Corn', 'Potatoes'])
+ ->addWhere('FinancialStuff.Salary', '>', '10000')
+ ->execute()
+ ->first();
+
+ $this->assertArrayHasKey($group, $result);
+ $favoriteThings = $result[$group];
+ $favoriteFood = $favoriteThings[$foodField];
+ $favoriteColor = $favoriteThings[$colorField];
+ $financialStuff = $result['FinancialStuff'];
+ $this->assertEquals('Red', $favoriteColor['label']);
+ $this->assertEquals('Corn', $favoriteFood['label']);
+ $this->assertEquals(50000, $financialStuff['Salary']);
+ }
+
+ public function testWithCustomDataForMultipleContacts() {
+ $group = uniqid('favb');
+ $colorField = uniqid('colorb');
+ $foodField = uniqid('foodb');
+
+ $customGroupId = CustomGroup::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('name', $group)
+ ->addValue('extends', 'Contact')
+ ->execute()
+ ->first()['id'];
+
+ CustomField::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('label', $colorField)
+ ->addValue('name', $colorField)
+ ->addValue('options', ['r' => 'Red', 'g' => 'Green', 'b' => 'Blue'])
+ ->addValue('custom_group_id', $customGroupId)
+ ->addValue('html_type', 'Select')
+ ->addValue('data_type', 'String')
+ ->execute();
+
+ CustomField::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('label', $foodField)
+ ->addValue('name', $foodField)
+ ->addValue('options', ['1' => 'Corn', '2' => 'Potatoes', '3' => 'Cheese'])
+ ->addValue('custom_group_id', $customGroupId)
+ ->addValue('html_type', 'Select')
+ ->addValue('data_type', 'String')
+ ->execute();
+
+ $customGroupId = CustomGroup::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('name', 'FinancialStuff')
+ ->addValue('extends', 'Contact')
+ ->execute()
+ ->first()['id'];
+
+ CustomField::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('label', 'Salary')
+ ->addValue('custom_group_id', $customGroupId)
+ ->addValue('html_type', 'Number')
+ ->addValue('data_type', 'Money')
+ ->execute();
+
+ Contact::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('first_name', 'Red')
+ ->addValue('last_name', 'Corn')
+ ->addValue('contact_type', 'Individual')
+ ->addValue("$group.$colorField", 'r')
+ ->addValue("$group.$foodField", '1')
+ ->addValue('FinancialStuff.Salary', 10000)
+ ->execute();
+
+ Contact::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('first_name', 'Blue')
+ ->addValue('last_name', 'Cheese')
+ ->addValue('contact_type', 'Individual')
+ ->addValue("$group.$colorField", 'b')
+ ->addValue("$group.$foodField", '3')
+ ->addValue('FinancialStuff.Salary', 500000)
+ ->execute();
+
+ $result = Contact::get()
+ ->setCheckPermissions(FALSE)
+ ->addSelect('first_name')
+ ->addSelect('last_name')
+ ->addSelect("$group.$colorField.label")
+ ->addSelect("$group.$foodField.label")
+ ->addSelect('FinancialStuff.Salary')
+ ->addWhere("$group.$foodField.label", 'IN', ['Corn', 'Cheese'])
+ ->execute();
+
+ $blueCheese = NULL;
+ foreach ($result as $contact) {
+ if ($contact['first_name'] === 'Blue') {
+ $blueCheese = $contact;
+ }
+ }
+
+ $this->assertEquals('Blue', $blueCheese[$group][$colorField]['label']);
+ $this->assertEquals('Cheese', $blueCheese[$group][$foodField]['label']);
+ $this->assertEquals(500000, $blueCheese['FinancialStuff']['Salary']);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/CustomValuePerformanceTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/CustomValuePerformanceTest.php
new file mode 100644
index 00000000..3fa59ef4
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/CustomValuePerformanceTest.php
@@ -0,0 +1,95 @@
+<?php
+
+namespace Civi\Test\Api4\Action;
+
+use Civi\Api4\Contact;
+use Civi\Api4\CustomField;
+use Civi\Api4\CustomGroup;
+use Civi\Test\Api4\Traits\QueryCounterTrait;
+
+/**
+ * @group headless
+ */
+class CustomValuePerformanceTest extends BaseCustomValueTest {
+
+ use QueryCounterTrait;
+
+ public function testQueryCount() {
+
+ $this->markTestIncomplete();
+
+ $customGroupId = CustomGroup::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('name', 'MyContactFields')
+ ->addValue('title', 'MyContactFields')
+ ->addValue('extends', 'Contact')
+ ->execute()
+ ->first()['id'];
+
+ CustomField::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('label', 'FavColor')
+ ->addValue('custom_group_id', $customGroupId)
+ ->addValue('options', ['r' => 'Red', 'g' => 'Green', 'b' => 'Blue'])
+ ->addValue('html_type', 'Select')
+ ->addValue('data_type', 'String')
+ ->execute();
+
+ CustomField::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('label', 'FavAnimal')
+ ->addValue('custom_group_id', $customGroupId)
+ ->addValue('html_type', 'Text')
+ ->addValue('data_type', 'String')
+ ->execute();
+
+ CustomField::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('label', 'FavLetter')
+ ->addValue('custom_group_id', $customGroupId)
+ ->addValue('html_type', 'Text')
+ ->addValue('data_type', 'String')
+ ->execute();
+
+ CustomField::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('label', 'FavFood')
+ ->addValue('custom_group_id', $customGroupId)
+ ->addValue('html_type', 'Text')
+ ->addValue('data_type', 'String')
+ ->execute();
+
+ $this->beginQueryCount();
+
+ Contact::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('first_name', 'Red')
+ ->addValue('last_name', 'Tester')
+ ->addValue('contact_type', 'Individual')
+ ->addValue('MyContactFields.FavColor', 'r')
+ ->addValue('MyContactFields.FavAnimal', 'Sheep')
+ ->addValue('MyContactFields.FavLetter', 'z')
+ ->addValue('MyContactFields.FavFood', 'Coconuts')
+ ->execute();
+
+ Contact::get()
+ ->setCheckPermissions(FALSE)
+ ->addSelect('display_name')
+ ->addSelect('MyContactFields.FavColor.label')
+ ->addSelect('MyContactFields.FavColor.weight')
+ ->addSelect('MyContactFields.FavColor.is_default')
+ ->addSelect('MyContactFields.FavAnimal')
+ ->addSelect('MyContactFields.FavLetter')
+ ->addWhere('MyContactFields.FavColor', '=', 'r')
+ ->addWhere('MyContactFields.FavFood', '=', 'Coconuts')
+ ->addWhere('MyContactFields.FavAnimal', '=', 'Sheep')
+ ->addWhere('MyContactFields.FavLetter', '=', 'z')
+ ->execute()
+ ->first();
+
+ // FIXME: This count is artificially high due to the line
+ // $this->entity = Tables::getBriefName(Tables::getClassForTable($targetTable));
+ // In class Joinable. TODO: Investigate why.
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/CustomValueTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/CustomValueTest.php
new file mode 100644
index 00000000..da954bc7
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/CustomValueTest.php
@@ -0,0 +1,174 @@
+<?php
+
+namespace Civi\Test\Api4\Action;
+
+use Civi\Api4\CustomField;
+use Civi\Api4\CustomGroup;
+use Civi\Api4\CustomValue;
+use Civi\Api4\Contact;
+
+/**
+ * @group headless
+ */
+class CustomValueTest extends BaseCustomValueTest {
+
+ protected $contactID;
+
+ /**
+ * Test CustomValue::GetFields/Get/Create/Update/Replace/Delete
+ */
+ public function testCRUD() {
+ $optionValues = ['r' => 'Red', 'g' => 'Green', 'b' => 'Blue'];
+
+ $group = uniqid('groupc');
+ $colorField = uniqid('colorc');
+
+ $customGroup = CustomGroup::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('name', $group)
+ ->addValue('extends', 'Contact')
+ ->addValue('is_multiple', TRUE)
+ ->execute()
+ ->first();
+
+ CustomField::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('label', $colorField)
+ ->addValue('options', $optionValues)
+ ->addValue('custom_group_id', $customGroup['id'])
+ ->addValue('html_type', 'Select')
+ ->addValue('data_type', 'String')
+ ->execute();
+
+ $this->contactID = Contact::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('first_name', 'Johann')
+ ->addValue('last_name', 'Tester')
+ ->addValue('contact_type', 'Individual')
+ ->execute()
+ ->first()['id'];
+
+ // Retrieve and check the fields of CustomValue = Custom_$group
+ $fields = CustomValue::getFields($group)->execute();
+ $expectedResult = [
+ [
+ 'custom_field_id' => 1,
+ 'custom_group' => $group,
+ 'name' => $colorField,
+ 'title' => ts($colorField),
+ 'entity' => "Custom_$group",
+ 'data_type' => 'String',
+ 'fk_entity' => NULL,
+ ],
+ [
+ 'name' => 'id',
+ 'title' => ts('Custom Value ID'),
+ 'entity' => "Custom_$group",
+ 'data_type' => 'Integer',
+ 'fk_entity' => NULL,
+ ],
+ [
+ 'name' => 'entity_id',
+ 'title' => ts('Entity ID'),
+ 'entity' => "Custom_$group",
+ 'data_type' => 'Integer',
+ 'fk_entity' => 'Contact',
+ ],
+ ];
+
+ foreach ($expectedResult as $key => $field) {
+ foreach ($field as $attr => $value) {
+ $this->assertEquals($expectedResult[$key][$attr], $fields[$key][$attr]);
+ }
+ }
+
+ // CASE 1: Test CustomValue::create
+ // Create two records for a single contact and using CustomValue::get ensure that two records are created
+ CustomValue::create($group)
+ ->addValue($colorField, 'Green')
+ ->addValue("entity_id", $this->contactID)
+ ->execute();
+ CustomValue::create($group)
+ ->addValue($colorField, 'Red')
+ ->addValue("entity_id", $this->contactID)
+ ->execute();
+ // fetch custom values using API4 CustomValue::get
+ $result = CustomValue::get($group)->execute();
+
+ // check if two custom values are created
+ $this->assertEquals(2, count($result));
+ $expectedResult = [
+ [
+ 'id' => 1,
+ $colorField => 'Green',
+ 'entity_id' => $this->contactID,
+ ],
+ [
+ 'id' => 2,
+ $colorField => 'Red',
+ 'entity_id' => $this->contactID,
+ ],
+ ];
+ // match the data
+ foreach ($expectedResult as $key => $field) {
+ foreach ($field as $attr => $value) {
+ $this->assertEquals($expectedResult[$key][$attr], $result[$key][$attr]);
+ }
+ }
+
+ // CASE 2: Test CustomValue::update
+ // Update a records whose id is 1 and change the custom field (name = Color) value to 'White' from 'Green'
+ CustomValue::update($group)
+ ->addWhere("id", "=", 1)
+ ->addValue($colorField, 'White')
+ ->execute();
+
+ // ensure that the value is changed for id = 1
+ $color = CustomValue::get($group)
+ ->addWhere("id", "=", 1)
+ ->execute()
+ ->first()[$colorField];
+ $this->assertEquals('White', $color);
+
+ // CASE 3: Test CustomValue::replace
+ // create a second contact which will be used to replace the custom values, created earlier
+ $secondContactID = Contact::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('first_name', 'Adam')
+ ->addValue('last_name', 'Tester')
+ ->addValue('contact_type', 'Individual')
+ ->execute()
+ ->first()['id'];
+ // Replace all the records which was created earlier with entity_id = first contact
+ // with custom record [$colorField => 'Rainbow', 'entity_id' => $secondContactID]
+ CustomValue::replace($group)
+ ->setRecords([[$colorField => 'Rainbow', 'entity_id' => $secondContactID]])
+ ->addWhere('entity_id', '=', $this->contactID)
+ ->execute();
+
+ // Check the two records created earlier is replaced by new contact
+ $result = CustomValue::get($group)->execute();
+ $this->assertEquals(1, count($result));
+
+ $expectedResult = [
+ [
+ 'id' => 3,
+ $colorField => 'Rainbow',
+ 'entity_id' => $secondContactID,
+ ],
+ ];
+ foreach ($expectedResult as $key => $field) {
+ foreach ($field as $attr => $value) {
+ $this->assertEquals($expectedResult[$key][$attr], $result[$key][$attr]);
+ }
+ }
+
+ // CASE 4: Test CustomValue::delete
+ // There is only record left whose id = 3, delete that record on basis of criteria id = 3
+ CustomValue::delete($group)->addWhere("id", "=", 3)->execute();
+ $result = CustomValue::get($group)->execute();
+ // check that there are no custom values present
+ $this->assertEquals(0, count($result));
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/DateTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/DateTest.php
new file mode 100644
index 00000000..8cdbfc7c
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/DateTest.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace Civi\Test\Api4\Action;
+
+use Civi\Api4\Contact;
+use Civi\Api4\Relationship;
+use Civi\Test\Api4\UnitTestCase;
+
+/**
+ * @group headless
+ */
+class DateTest extends UnitTestCase {
+
+ public function testRelationshipDate() {
+ $c1 = Contact::create()
+ ->addValue('first_name', 'c')
+ ->addValue('last_name', 'one')
+ ->execute()
+ ->first()['id'];
+ $c2 = Contact::create()
+ ->addValue('first_name', 'c')
+ ->addValue('last_name', 'two')
+ ->execute()
+ ->first()['id'];
+ $r = Relationship::create()
+ ->addValue('contact_id_a', $c1)
+ ->addValue('contact_id_b', $c2)
+ ->addValue('relationship_type_id', 1)
+ ->addValue('start_date', 'now')
+ ->addValue('end_date', 'now + 1 week')
+ ->execute()
+ ->first()['id'];
+ $result = Relationship::get()
+ ->addWhere('start_date', '=', 'now')
+ ->addWhere('end_date', '>', 'now + 1 day')
+ ->execute()
+ ->indexBy('id');
+ $this->assertArrayHasKey($r, $result);
+ $result = Relationship::get()
+ ->addWhere('start_date', '<', 'now')
+ ->execute()
+ ->indexBy('id');
+ $this->assertArrayNotHasKey($r, $result);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/EvaluateConditionTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/EvaluateConditionTest.php
new file mode 100644
index 00000000..3726042f
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/EvaluateConditionTest.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Civi\Test\Api4\Action;
+
+use Civi\Api4\MockBasicEntity;
+use Civi\Test\Api4\UnitTestCase;
+
+/**
+ * @group headless
+ */
+class EvaluateConditionTest extends UnitTestCase {
+
+ public function testEvaluateCondition() {
+ $action = MockBasicEntity::get();
+ $reflection = new \ReflectionClass($action);
+ $method = $reflection->getMethod('evaluateCondition');
+ $method->setAccessible(TRUE);
+
+ $data = [
+ 'nada' => 0,
+ 'uno' => 1,
+ 'dos' => 2,
+ 'apple' => 'red',
+ 'banana' => 'yellow',
+ 'values' => ['one' => 1, 'two' => 2, 'three' => 3],
+ ];
+
+ $this->assertFalse($method->invoke($action, '$uno > $dos', $data));
+ $this->assertTrue($method->invoke($action, '$uno < $dos', $data));
+ $this->assertTrue($method->invoke($action, '$apple == "red" && $banana != "red"', $data));
+ $this->assertFalse($method->invoke($action, '$apple == "red" && $banana != "yellow"', $data));
+ $this->assertTrue($method->invoke($action, '$values.one == $uno', $data));
+ $this->assertTrue($method->invoke($action, '$values.one + $dos == $values.three', $data));
+ $this->assertTrue($method->invoke($action, 'empty($nada)', $data));
+ $this->assertFalse($method->invoke($action, 'empty($values)', $data));
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/ExtendFromIndividualTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/ExtendFromIndividualTest.php
new file mode 100644
index 00000000..2d3be50c
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/ExtendFromIndividualTest.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace Civi\Test\Api4\Action;
+
+use Civi\Api4\Contact;
+use Civi\Api4\CustomField;
+use Civi\Api4\CustomGroup;
+
+/**
+ * @group headless
+ */
+class ExtendFromIndividualTest extends BaseCustomValueTest {
+
+ public function testGetWithNonStandardExtends() {
+
+ $customGroup = CustomGroup::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('name', 'MyContactFields')
+ ->addValue('extends', 'Individual') // not Contact
+ ->execute()
+ ->first();
+
+ CustomField::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('label', 'FavColor')
+ ->addValue('custom_group_id', $customGroup['id'])
+ ->addValue('html_type', 'Text')
+ ->addValue('data_type', 'String')
+ ->execute();
+
+ $contactId = Contact::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('first_name', 'Johann')
+ ->addValue('last_name', 'Tester')
+ ->addValue('contact_type', 'Individual')
+ ->addValue('MyContactFields.FavColor', 'Red')
+ ->execute()
+ ->first()['id'];
+
+ $contact = Contact::get()
+ ->setCheckPermissions(FALSE)
+ ->addSelect('display_name')
+ ->addSelect('MyContactFields.FavColor')
+ ->addWhere('id', '=', $contactId)
+ ->execute()
+ ->first();
+
+ $this->assertArrayHasKey('MyContactFields', $contact);
+ $contactFields = $contact['MyContactFields'];
+ $favColor = $contactFields['FavColor'];
+ $this->assertEquals('Red', $favColor);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/FkJoinTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/FkJoinTest.php
new file mode 100644
index 00000000..c2b044e5
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/FkJoinTest.php
@@ -0,0 +1,75 @@
+<?php
+
+namespace Civi\Test\Api4\Action;
+
+use Civi\Test\Api4\UnitTestCase;
+use Civi\Api4\Activity;
+use Civi\Api4\Contact;
+
+/**
+ * @group headless
+ */
+class FkJoinTest extends UnitTestCase {
+
+ public function setUpHeadless() {
+ $relatedTables = [
+ 'civicrm_activity',
+ 'civicrm_phone',
+ 'civicrm_activity_contact',
+ ];
+ $this->cleanup(['tablesToTruncate' => $relatedTables]);
+ $this->loadDataSet('DefaultDataSet');
+
+ return parent::setUpHeadless();
+ }
+
+ /**
+ * Fetch all phone call activities. Expects a single activity
+ * loaded from the data set.
+ */
+ public function testThreeLevelJoin() {
+ $results = Activity::get()
+ ->setCheckPermissions(FALSE)
+ ->addWhere('activity_type.name', '=', 'Phone Call')
+ ->execute();
+
+ $this->assertCount(1, $results);
+ }
+
+ public function testActivityContactJoin() {
+ $results = Activity::get()
+ ->setCheckPermissions(FALSE)
+ ->addSelect('assignees.id')
+ ->addSelect('assignees.first_name')
+ ->addSelect('assignees.display_name')
+ ->addWhere('assignees.first_name', '=', 'Phoney')
+ ->execute();
+
+ $firstResult = $results->first();
+
+ $this->assertCount(1, $results);
+ $this->assertTrue(is_array($firstResult['assignees']));
+
+ $firstAssignee = array_shift($firstResult['assignees']);
+ $this->assertEquals($firstAssignee['first_name'], 'Phoney');
+ }
+
+ public function testContactPhonesJoin() {
+ $testContact = $this->getReference('test_contact_1');
+ $testPhone = $this->getReference('test_phone_1');
+
+ $results = Contact::get()
+ ->setCheckPermissions(FALSE)
+ ->addSelect('phones.phone')
+ ->addWhere('id', '=', $testContact['id'])
+ ->addWhere('phones.location_type.name', '=', 'Home')
+ ->execute()
+ ->first();
+
+ $this->assertArrayHasKey('phones', $results);
+ $this->assertCount(1, $results['phones']);
+ $firstPhone = array_shift($results['phones']);
+ $this->assertEquals($testPhone['phone'], $firstPhone['phone']);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/GetExtraFieldsTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/GetExtraFieldsTest.php
new file mode 100644
index 00000000..bc9b10e7
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/GetExtraFieldsTest.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace Civi\Test\Api4\Action;
+
+use Civi\Test\Api4\UnitTestCase;
+use Civi\Api4\Contact;
+
+/**
+ * @group headless
+ */
+class GetExtraFieldsTest extends UnitTestCase {
+
+ public function testBAOFieldsWillBeReturned() {
+ $returnedFields = Contact::getFields()
+ ->execute()
+ ->getArrayCopy();
+
+ $baseFields = \CRM_Contact_BAO_Contact::fields();
+ $baseFieldNames = array_column($baseFields, 'name');
+ $returnedFieldNames = array_column($returnedFields, 'name');
+ $notReturned = array_diff($baseFieldNames, $returnedFieldNames);
+
+ $this->assertEmpty($notReturned);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/GetFromArrayTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/GetFromArrayTest.php
new file mode 100644
index 00000000..bee6fbf3
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/GetFromArrayTest.php
@@ -0,0 +1,163 @@
+<?php
+
+namespace Civi\Test\Api4\Action;
+
+use Civi\Test\Api4\UnitTestCase;
+use Civi\Api4\MockArrayEntity;
+
+/**
+ * @group headless
+ */
+class GetFromArrayTest extends UnitTestCase {
+
+ public function testArrayGetWithLimit() {
+ $result = MockArrayEntity::get()
+ ->setOffset(2)
+ ->setLimit(2)
+ ->execute();
+ $this->assertEquals(3, $result[0]['field1']);
+ $this->assertEquals(4, $result[1]['field1']);
+ $this->assertEquals(2, count($result));
+ }
+
+ public function testArrayGetWithSort() {
+ $result = MockArrayEntity::get()
+ ->addOrderBy('field1', 'DESC')
+ ->execute();
+ $this->assertEquals([5, 4, 3, 2, 1], array_column((array) $result, 'field1'));
+
+ $result = MockArrayEntity::get()
+ ->addOrderBy('field5', 'DESC')
+ ->addOrderBy('field2', 'ASC')
+ ->execute();
+ $this->assertEquals([3, 2, 5, 4, 1], array_column((array) $result, 'field1'));
+
+ $result = MockArrayEntity::get()
+ ->addOrderBy('field3', 'ASC')
+ ->addOrderBy('field2', 'ASC')
+ ->execute();
+ $this->assertEquals([3, 1, 2, 5, 4], array_column((array) $result, 'field1'));
+ }
+
+ public function testArrayGetWithSelect() {
+ $result = MockArrayEntity::get()
+ ->addSelect('field1')
+ ->addSelect('field3')
+ ->setLimit(4)
+ ->execute();
+ $this->assertEquals([
+ [
+ 'field1' => 1,
+ 'field3' => NULL,
+ ],
+ [
+ 'field1' => 2,
+ 'field3' => 0,
+ ],
+ [
+ 'field1' => 3,
+ ],
+ [
+ 'field1' => 4,
+ 'field3' => 1,
+ ],
+ ], (array) $result);
+ }
+
+ public function testArrayGetWithWhere() {
+ $result = MockArrayEntity::get()
+ ->addWhere('field2', '=', 'yack')
+ ->execute();
+ $this->assertEquals([2], array_column((array) $result, 'field1'));
+
+ $result = MockArrayEntity::get()
+ ->addWhere('field5', '!=', 'banana')
+ ->addWhere('field3', 'IS NOT NULL')
+ ->execute();
+ $this->assertEquals([4, 5], array_column((array) $result, 'field1'));
+
+ $result = MockArrayEntity::get()
+ ->addWhere('field1', '>=', '4')
+ ->execute();
+ $this->assertEquals([4, 5], array_column((array) $result, 'field1'));
+
+ $result = MockArrayEntity::get()
+ ->addWhere('field1', '<', '2')
+ ->execute();
+ $this->assertEquals([1], array_column((array) $result, 'field1'));
+
+ $result = MockArrayEntity::get()
+ ->addWhere('field2', 'LIKE', '%ra%')
+ ->execute();
+ $this->assertEquals([1, 3], array_column((array) $result, 'field1'));
+
+ $result = MockArrayEntity::get()
+ ->addWhere('field3', 'IS NULL')
+ ->execute();
+ $this->assertEquals([1, 3], array_column((array) $result, 'field1'));
+
+ $result = MockArrayEntity::get()
+ ->addWhere('field3', '=', '0')
+ ->execute();
+ $this->assertEquals([2], array_column((array) $result, 'field1'));
+
+ $result = MockArrayEntity::get()
+ ->addWhere('field2', 'LIKE', '%ra')
+ ->execute();
+ $this->assertEquals([1], array_column((array) $result, 'field1'));
+
+ $result = MockArrayEntity::get()
+ ->addWhere('field2', 'LIKE', 'ra')
+ ->execute();
+ $this->assertEquals(0, count($result));
+
+ $result = MockArrayEntity::get()
+ ->addWhere('field2', 'NOT LIKE', '%ra%')
+ ->execute();
+ $this->assertEquals([2, 4, 5], array_column((array) $result, 'field1'));
+
+ $result = MockArrayEntity::get()
+ ->addWhere('field6', '=', '0')
+ ->execute();
+ $this->assertEquals([3, 4, 5], array_column((array) $result, 'field1'));
+
+ $result = MockArrayEntity::get()
+ ->addWhere('field6', '=', 0)
+ ->execute();
+ $this->assertEquals([3, 4, 5], array_column((array) $result, 'field1'));
+
+ $result = MockArrayEntity::get()
+ ->addWhere('field1', 'BETWEEN', [3, 5])
+ ->execute();
+ $this->assertEquals([3, 4, 5], array_column((array) $result, 'field1'));
+
+ $result = MockArrayEntity::get()
+ ->addWhere('field1', 'NOT BETWEEN', [3, 4])
+ ->execute();
+ $this->assertEquals([1, 2, 5], array_column((array) $result, 'field1'));
+ }
+
+ public function testArrayGetWithNestedWhereClauses() {
+ $result = MockArrayEntity::get()
+ ->addClause('OR', ['field2', 'LIKE', '%ra'], ['field2', 'LIKE', 'x ray'])
+ ->execute();
+ $this->assertEquals([1, 3], array_column((array) $result, 'field1'));
+
+ $result = MockArrayEntity::get()
+ ->addClause('OR', ['field2', '=', 'zebra'], ['field2', '=', 'yack'])
+ ->addClause('OR', ['field5', '!=', 'apple'], ['field3', 'IS NULL'])
+ ->execute();
+ $this->assertEquals([1, 2], array_column((array) $result, 'field1'));
+
+ $result = MockArrayEntity::get()
+ ->addClause('NOT', ['field2', '!=', 'yack'])
+ ->execute();
+ $this->assertEquals([2], array_column((array) $result, 'field1'));
+
+ $result = MockArrayEntity::get()
+ ->addClause('OR', ['field1', '=', 2], ['AND', [['field5', '=', 'apple'], ['field3', '=', 1]]])
+ ->execute();
+ $this->assertEquals([2, 4, 5], array_column((array) $result, 'field1'));
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/IndexTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/IndexTest.php
new file mode 100644
index 00000000..17356c92
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/IndexTest.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace Civi\Test\Api4\Action;
+
+use Civi\Test\Api4\UnitTestCase;
+
+/**
+ * @group headless
+ */
+class IndexTest extends UnitTestCase {
+
+ public function testIndex() {
+ // Results indexed by name
+ $resultByName = civicrm_api4('Activity', 'getActions', [], 'name');
+ $this->assertInstanceOf('Civi\Api4\Generic\Result', $resultByName);
+ $this->assertEquals('get', $resultByName['get']['name']);
+
+ // Get result at index 0
+ $firstResult = civicrm_api4('Activity', 'getActions', [], 0);
+ $this->assertInstanceOf('Civi\Api4\Generic\Result', $firstResult);
+ $this->assertArrayHasKey('name', $firstResult);
+
+ $this->assertEquals($resultByName->first(), (array) $firstResult);
+ }
+
+ public function testBadIndexInt() {
+ $error = '';
+ try {
+ civicrm_api4('Activity', 'getActions', [], 99);
+ }
+ catch (\API_Exception $e) {
+ $error = $e->getMessage();
+ }
+ $this->assertContains('not found', $error);
+ }
+
+ public function testBadIndexString() {
+ $error = '';
+ try {
+ civicrm_api4('Activity', 'getActions', [], 'xyz');
+ }
+ catch (\API_Exception $e) {
+ $error = $e->getMessage();
+ }
+ $this->assertContains('not found', $error);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/NullValueTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/NullValueTest.php
new file mode 100644
index 00000000..dc4f656a
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/NullValueTest.php
@@ -0,0 +1,55 @@
+<?php
+
+namespace Civi\Test\Api4\Action;
+
+use Civi\Api4\Contact;
+use Civi\Test\Api4\UnitTestCase;
+
+/**
+ * @group headless
+ */
+class NullValueTest extends UnitTestCase {
+
+ public function setUpHeadless() {
+ $format = '{contact.first_name}{ }{contact.last_name}';
+ \Civi::settings()->set('display_name_format', $format);
+ return parent::setUpHeadless();
+ }
+
+ public function testStringNull() {
+ $contact = Contact::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('first_name', 'Joseph')
+ ->addValue('last_name', 'null')
+ ->addValue('contact_type', 'Individual')
+ ->execute()
+ ->first();
+
+ $this->assertSame('Null', $contact['last_name']);
+ $this->assertSame('Joseph Null', $contact['display_name']);
+ }
+
+ public function testSettingToNull() {
+ $contact = Contact::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('first_name', 'ILoveMy')
+ ->addValue('last_name', 'LastName')
+ ->addValue('contact_type', 'Individual')
+ ->execute()
+ ->first();
+
+ $this->assertSame('ILoveMy LastName', $contact['display_name']);
+ $contactId = $contact['id'];
+
+ $contact = Contact::update()
+ ->setCheckPermissions(FALSE)
+ ->addWhere('id', '=', $contactId)
+ ->addValue('last_name', NULL)
+ ->execute()
+ ->first();
+
+ $this->assertSame(NULL, $contact['last_name']);
+ $this->assertSame('ILoveMy', $contact['display_name']);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/ReplaceTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/ReplaceTest.php
new file mode 100644
index 00000000..097e12b0
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/ReplaceTest.php
@@ -0,0 +1,171 @@
+<?php
+
+namespace Civi\Test\Api4\Action;
+
+use Civi\Api4\CustomField;
+use Civi\Api4\CustomGroup;
+use Civi\Api4\CustomValue;
+use Civi\Api4\Email;
+use Civi\Test\Api4\Traits\TableDropperTrait;
+use Civi\Test\Api4\UnitTestCase;
+use Civi\Api4\Contact;
+
+/**
+ * @group headless
+ */
+class ReplaceTest extends UnitTestCase {
+ use TableDropperTrait;
+
+ /**
+ * Set up baseline for testing
+ */
+ public function setUp() {
+ $tablesToTruncate = [
+ 'civicrm_custom_group',
+ 'civicrm_custom_field',
+ 'civicrm_email',
+ ];
+ $this->dropByPrefix('civicrm_value_replacetest');
+ $this->cleanup(['tablesToTruncate' => $tablesToTruncate]);
+ parent::setUp();
+ }
+
+ public function testEmailReplace() {
+ $cid1 = Contact::create()
+ ->addValue('first_name', 'Lotsa')
+ ->addValue('last_name', 'Emails')
+ ->execute()
+ ->first()['id'];
+ $cid2 = Contact::create()
+ ->addValue('first_name', 'Notso')
+ ->addValue('last_name', 'Many')
+ ->execute()
+ ->first()['id'];
+ $e0 = Email::create()
+ ->setValues(['contact_id' => $cid2, 'email' => 'nosomany@example.com', 'location_type_id' => 1])
+ ->execute()
+ ->first()['id'];
+ $e1 = Email::create()
+ ->setValues(['contact_id' => $cid1, 'email' => 'first@example.com', 'location_type_id' => 1])
+ ->execute()
+ ->first()['id'];
+ $e2 = Email::create()
+ ->setValues(['contact_id' => $cid1, 'email' => 'second@example.com', 'location_type_id' => 1])
+ ->execute()
+ ->first()['id'];
+ $replacement = [
+ ['email' => 'firstedited@example.com', 'id' => $e1],
+ ['contact_id' => $cid1, 'email' => 'third@example.com', 'location_type_id' => 1]
+ ];
+ $replaced = Email::replace()
+ ->setRecords($replacement)
+ ->addWhere('contact_id', '=', $cid1)
+ ->execute();
+ // Should have saved 2 records
+ $this->assertEquals(2, $replaced->count());
+ // Should have deleted email2
+ $this->assertEquals([$e2], $replaced->deleted);
+ // Verify contact now has the new email records
+ $results = Email::get()
+ ->addWhere('contact_id', '=', $cid1)
+ ->execute()
+ ->indexBy('id');
+ $this->assertEquals('firstedited@example.com', $results[$e1]['email']);
+ $this->assertEquals(2, $results->count());
+ $this->assertArrayNotHasKey($e2, (array) $results);
+ $this->assertArrayNotHasKey($e0, (array) $results);
+ unset($results[$e1]);
+ foreach ($results as $result) {
+ $this->assertEquals('third@example.com', $result['email']);
+ }
+ // Validate our other contact's email did not get deleted
+ $c2email = Email::get()
+ ->addWhere('contact_id', '=', $cid2)
+ ->execute()
+ ->first();
+ $this->assertEquals('nosomany@example.com', $c2email['email']);
+ }
+
+ public function testCustomValueReplace() {
+ $customGroup = CustomGroup::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('name', 'replaceTest')
+ ->addValue('extends', 'Contact')
+ ->addValue('is_multiple', TRUE)
+ ->execute()
+ ->first();
+
+ CustomField::create()
+ ->addValue('label', 'Custom1')
+ ->addValue('custom_group_id', $customGroup['id'])
+ ->addValue('html_type', 'String')
+ ->addValue('data_type', 'String')
+ ->execute();
+
+ CustomField::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('label', 'Custom2')
+ ->addValue('custom_group_id', $customGroup['id'])
+ ->addValue('html_type', 'String')
+ ->addValue('data_type', 'String')
+ ->execute();
+
+ $cid1 = Contact::create()
+ ->addValue('first_name', 'Lotsa')
+ ->addValue('last_name', 'Data')
+ ->execute()
+ ->first()['id'];
+ $cid2 = Contact::create()
+ ->addValue('first_name', 'Notso')
+ ->addValue('last_name', 'Much')
+ ->execute()
+ ->first()['id'];
+
+ // Contact 2 gets one row
+ CustomValue::create('replaceTest')
+ ->setCheckPermissions(FALSE)
+ ->addValue('Custom1', "2 1")
+ ->addValue('Custom2', "2 1")
+ ->addValue('entity_id', $cid2)
+ ->execute();
+
+ // Create 3 rows for contact 1
+ foreach ([1, 2, 3] as $i) {
+ CustomValue::create('replaceTest')
+ ->setCheckPermissions(FALSE)
+ ->addValue('Custom1', "1 $i")
+ ->addValue('Custom2', "1 $i")
+ ->addValue('entity_id', $cid1)
+ ->execute();
+ }
+
+ $cid1Records = CustomValue::get('replaceTest')
+ ->setCheckPermissions(FALSE)
+ ->addWhere('entity_id', '=', $cid1)
+ ->execute();
+
+ $this->assertCount(3, $cid1Records);
+ $this->assertCount(1, CustomValue::get('replaceTest')->setCheckPermissions(FALSE)->addWhere('entity_id', '=', $cid2)->execute());
+
+ $result = CustomValue::replace('replaceTest')
+ ->addWhere('entity_id', '=', $cid1)
+ ->addRecord(['Custom1' => 'new one', 'Custom2' => 'new two'])
+ ->addRecord(['id' => $cid1Records[0]['id'], 'Custom1' => 'changed one', 'Custom2' => 'changed two'])
+ ->execute();
+
+ $this->assertCount(2, $result);
+ $this->assertCount(2, $result->deleted);
+
+ $newRecords = CustomValue::get('replaceTest')
+ ->setCheckPermissions(FALSE)
+ ->addWhere('entity_id', '=', $cid1)
+ ->execute()
+ ->indexBy('id');
+
+ $this->assertEquals('new one', $newRecords->last()['Custom1']);
+ $this->assertEquals('new two', $newRecords->last()['Custom2']);
+ $this->assertEquals('changed one', $newRecords[$cid1Records[0]['id']]['Custom1']);
+ $this->assertEquals('changed two', $newRecords[$cid1Records[0]['id']]['Custom2']);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/UpdateContactTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/UpdateContactTest.php
new file mode 100644
index 00000000..71265598
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/UpdateContactTest.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace Civi\Test\Api4\Action;
+
+use Civi\Api4\Contact;
+use Civi\Test\Api4\UnitTestCase;
+
+/**
+ * Class UpdateContactTest
+ * @package Civi\Test\Api4\Action
+ * @group headless
+ */
+class UpdateContactTest extends UnitTestCase {
+
+ public function testUpdateWillWork() {
+ $contactId = Contact::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('first_name', 'Johann')
+ ->addValue('last_name', 'Tester')
+ ->addValue('contact_type', 'Individual')
+ ->execute()
+ ->first()['id'];
+
+ $contact = Contact::update()
+ ->setCheckPermissions(FALSE)
+ ->addWhere('id', '=', $contactId)
+ ->addValue('first_name', 'Testy')
+ ->execute()
+ ->first();
+ $this->assertEquals('Testy', $contact['first_name']);
+ $this->assertEquals('Tester', $contact['last_name']);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/UpdateCustomValueTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/UpdateCustomValueTest.php
new file mode 100644
index 00000000..99a9f011
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Action/UpdateCustomValueTest.php
@@ -0,0 +1,56 @@
+<?php
+
+namespace Civi\Test\Api4\Action;
+
+use Civi\Api4\Contact;
+use Civi\Api4\CustomField;
+use Civi\Api4\CustomGroup;
+use \CRM_Core_BAO_CustomValueTable as CustomValueTable;
+
+/**
+ * @group headless
+ */
+class UpdateCustomValueTest extends BaseCustomValueTest {
+
+ public function testGetWithCustomData() {
+
+ $customGroup = CustomGroup::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('name', 'MyContactFields')
+ ->addValue('extends', 'Contact')
+ ->execute()
+ ->first();
+
+ CustomField::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('label', 'FavColor')
+ ->addValue('custom_group_id', $customGroup['id'])
+ ->addValue('html_type', 'Text')
+ ->addValue('data_type', 'String')
+ ->execute();
+
+ $contactId = Contact::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('first_name', 'Red')
+ ->addValue('last_name', 'Tester')
+ ->addValue('contact_type', 'Individual')
+ ->addValue('MyContactFields.FavColor', 'Red')
+ ->execute()
+ ->first()['id'];
+
+ Contact::update()
+ ->setCheckPermissions(FALSE)
+ ->addWhere('id', '=', $contactId)
+ ->addValue('first_name', 'Red')
+ ->addValue('last_name', 'Tester')
+ ->addValue('contact_type', 'Individual')
+ ->addValue('MyContactFields.FavColor', 'Blue')
+ ->execute();
+
+ $result = CustomValueTable::getEntityValues($contactId, 'Contact');
+
+ $this->assertEquals(1, count($result));
+ $this->assertContains('Blue', $result);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/DataSets/ConformanceTest.json b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/DataSets/ConformanceTest.json
new file mode 100644
index 00000000..fcaf8966
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/DataSets/ConformanceTest.json
@@ -0,0 +1,28 @@
+{
+ "Contact": [
+ {
+ "first_name": "Janice",
+ "last_name": "Voss",
+ "contact_type": "Individual"
+ }
+ ],
+ "CustomGroup": [
+ {
+ "name": "MyFavoriteThings",
+ "extends": "Contact"
+ }
+ ],
+ "Event": [
+ {
+ "start_date": "20401010000000",
+ "title": "The Singularity",
+ "event_type_id": "major_historical_event"
+ }
+ ],
+ "Group": [
+ {
+ "name": "the_group",
+ "title": "The Group"
+ }
+ ]
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/DataSets/DefaultDataSet.json b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/DataSets/DefaultDataSet.json
new file mode 100644
index 00000000..7d4a91bc
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/DataSets/DefaultDataSet.json
@@ -0,0 +1,45 @@
+{
+ "Contact": [
+ {
+ "first_name": "Phoney",
+ "last_name": "Contact",
+ "contact_type": "Individual",
+ "@ref": "test_contact_1"
+ },
+ {
+ "first_name": "Second",
+ "last_name": "Test",
+ "contact_type": "Individual",
+ "@ref": "test_contact_2"
+ }
+ ],
+ "Activity": [
+ {
+ "subject": "Test Phone Activity",
+ "activity_type": "Phone Call",
+ "source_contact_id": "@ref test_contact_1.id"
+ },
+ {
+ "subject": "Another Activity",
+ "activity_type": "Meeting",
+ "source_contact_id": "@ref test_contact_1.id",
+ "assignee_contact_id": [
+ "@ref test_contact_1.id",
+ "@ref test_contact_2.id"
+ ]
+ }
+ ],
+ "Phone": [
+ {
+ "contact_id": "@ref test_contact_1.id",
+ "phone": "+35355439483",
+ "location_type_id": "1",
+ "@ref": "test_phone_1"
+ },
+ {
+ "contact_id": "@ref test_contact_1.id",
+ "phone": "+3538733439483",
+ "location_type_id": "2"
+ }
+ ]
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/DataSets/MultiContactMultiEmail.json b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/DataSets/MultiContactMultiEmail.json
new file mode 100644
index 00000000..ce3fbcaf
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/DataSets/MultiContactMultiEmail.json
@@ -0,0 +1,42 @@
+{
+ "Contact": [
+ {
+ "first_name": "First",
+ "last_name": "Contact",
+ "contact_type": "Individual",
+ "@ref": "test_contact_1"
+ },
+ {
+ "first_name": "Second",
+ "last_name": "Contact",
+ "contact_type": "Individual",
+ "@ref": "test_contact_2"
+ }
+ ],
+ "Email": [
+ {
+ "contact_id": "@ref test_contact_1.id",
+ "email": "test_contact_one_home@fakedomain.com",
+ "location_type_id": 1,
+ "@ref": "test_email_1"
+ },
+ {
+ "contact_id": "@ref test_contact_1.id",
+ "email": "test_contact_one_work@fakedomain.com",
+ "location_type_id": 2,
+ "@ref": "test_email_2"
+ },
+ {
+ "contact_id": "@ref test_contact_2.id",
+ "email": "test_contact_two_home@fakedomain.com",
+ "location_type_id": 1,
+ "@ref": "test_email_3"
+ },
+ {
+ "contact_id": "@ref test_contact_2.id",
+ "email": "test_contact_two_work@fakedomain.com",
+ "location_type_id": 2,
+ "@ref": "test_email_4"
+ }
+ ]
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/DataSets/SingleContact.json b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/DataSets/SingleContact.json
new file mode 100644
index 00000000..73e7369e
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/DataSets/SingleContact.json
@@ -0,0 +1,81 @@
+{
+ "Contact": [
+ {
+ "first_name": "Single",
+ "last_name": "Contact",
+ "contact_type": "Individual",
+ "preferred_communication_method": "1",
+ "@ref": "test_contact_1"
+ }
+ ],
+ "Activity": [
+ {
+ "subject": "Won A Nobel Prize",
+ "activity_type": "Meeting",
+ "source_contact_id": "@ref test_contact_1.id",
+ "@ref": "test_activity_1"
+ },
+ {
+ "subject": "Cleaned The House",
+ "activity_type": "Meeting",
+ "source_contact_id": "@ref test_contact_1.id",
+ "assignee_contact_id": [
+ "@ref test_contact_1.id"
+ ],
+ "@ref": "test_activity_2"
+ }
+ ],
+ "Phone": [
+ {
+ "contact_id": "@ref test_contact_1.id",
+ "phone": "+1111111111111",
+ "location_type_id": 1
+ },
+ {
+ "contact_id": "@ref test_contact_1.id",
+ "phone": "+2222222222222",
+ "location_type_id": 2
+ }
+ ],
+ "Email": [
+ {
+ "contact_id": "@ref test_contact_1.id",
+ "email": "test_contact_home@fakedomain.com",
+ "location_type_id": 1
+ },
+ {
+ "contact_id": "@ref test_contact_1.id",
+ "email": "test_contact_work@fakedomain.com",
+ "location_type_id": 2
+ }
+ ],
+ "Address": [
+ {
+ "contact_id": "@ref test_contact_1.id",
+ "street_address": "123 Sesame St.",
+ "location_type_id": 1
+ }
+ ],
+ "Website": [
+ {
+ "contact_id": "@ref test_contact_1.id",
+ "url": "http://test.com",
+ "website_id": 1
+ }
+ ],
+ "OpenID": [
+ {
+ "contact_id": "@ref test_contact_1.id",
+ "openid": "123",
+ "allowed_to_login": 1,
+ "location_type_id": 1
+ }
+ ],
+ "IM": [
+ {
+ "contact_id": "@ref test_contact_1.id",
+ "name": "123",
+ "location_type_id": 1
+ }
+ ]
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Entity/ConformanceTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Entity/ConformanceTest.php
new file mode 100644
index 00000000..79fe5b30
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Entity/ConformanceTest.php
@@ -0,0 +1,226 @@
+<?php
+
+namespace Civi\Test\Api4\Entity;
+
+use Civi\Api4\Generic\AbstractEntity;
+use Civi\Api4\Entity;
+use Civi\Test\Api4\Service\TestCreationParameterProvider;
+use Civi\Test\Api4\Traits\TableDropperTrait;
+use Civi\Test\Api4\UnitTestCase;
+
+/**
+ * @group headless
+ */
+class ConformanceTest extends UnitTestCase {
+
+ use TableDropperTrait;
+ use \Civi\Test\Api4\Traits\OptionCleanupTrait {
+ setUp as setUpOptionCleanup;
+ }
+
+ /**
+ * @var TestCreationParameterProvider
+ */
+ protected $creationParamProvider;
+
+ /**
+ * Set up baseline for testing
+ */
+ public function setUp() {
+ $tablesToTruncate = [
+ 'civicrm_custom_group',
+ 'civicrm_custom_field',
+ 'civicrm_group',
+ 'civicrm_event',
+ 'civicrm_participant',
+ ];
+ $this->dropByPrefix('civicrm_value_myfavorite');
+ $this->cleanup(['tablesToTruncate' => $tablesToTruncate]);
+ $this->setUpOptionCleanup();
+ $this->loadDataSet('ConformanceTest');
+ $this->creationParamProvider = \Civi::container()->get('test.param_provider');
+ parent::setUp();
+ // calculateTaxAmount() for contribution triggers a deprecation notice
+ \PHPUnit_Framework_Error_Deprecated::$enabled = FALSE;
+ }
+
+ public function getEntities() {
+ $result = [];
+ $entities = Entity::get()->setCheckPermissions(FALSE)->execute();
+ foreach ($entities as $entity) {
+ if ($entity['name'] != 'Entity') {
+ $result[] = [$entity['name']];
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Fixme: This should use getEntities as a dataProvider but that fails for some reason
+ */
+ public function testConformance() {
+ $entities = $this->getEntities();
+ $this->assertNotEmpty($entities);
+
+ foreach ($entities as $data) {
+ $entity = $data[0];
+ $entityClass = 'Civi\Api4\\' . $entity;
+
+ $this->checkActions($entityClass);
+ $this->checkFields($entityClass, $entity);
+ $id = $this->checkCreation($entity, $entityClass);
+ $this->checkGet($entityClass, $id, $entity);
+ $this->checkUpdateFailsFromCreate($entityClass, $id);
+ $this->checkWrongParamType($entityClass);
+ $this->checkDeleteWithNoId($entityClass);
+ $this->checkDeletion($entityClass, $id);
+ $this->checkPostDelete($entityClass, $id, $entity);
+ }
+ }
+
+ /**
+ * @param string $entityClass
+ * @param $entity
+ */
+ protected function checkFields($entityClass, $entity) {
+ $fields = $entityClass::getFields()
+ ->setCheckPermissions(FALSE)
+ ->setIncludeCustom(FALSE)
+ ->execute()
+ ->indexBy('name');
+
+ $errMsg = sprintf('%s is missing required ID field', $entity);
+ $subset = ['data_type' => 'Integer'];
+
+ $this->assertArraySubset($subset, $fields['id'], $errMsg);
+ }
+
+ /**
+ * @param string $entityClass
+ */
+ protected function checkActions($entityClass) {
+ $actions = $entityClass::getActions()
+ ->setCheckPermissions(FALSE)
+ ->execute()
+ ->indexBy('name');
+
+ $this->assertNotEmpty($actions->getArrayCopy());
+ }
+
+ /**
+ * @param string $entity
+ * @param AbstractEntity|string $entityClass
+ *
+ * @return mixed
+ */
+ protected function checkCreation($entity, $entityClass) {
+ $requiredParams = $this->creationParamProvider->getRequired($entity);
+ $createResult = $entityClass::create()
+ ->setValues($requiredParams)
+ ->setCheckPermissions(FALSE)
+ ->execute()
+ ->first();
+
+ $this->assertArrayHasKey('id', $createResult, "create missing ID");
+ $id = $createResult['id'];
+
+ $this->assertGreaterThanOrEqual(1, $id, "$entity ID not positive");
+
+ return $id;
+ }
+
+ /**
+ * @param AbstractEntity|string $entityClass
+ * @param int $id
+ */
+ protected function checkUpdateFailsFromCreate($entityClass, $id) {
+ $exceptionThrown = '';
+ try {
+ $entityClass::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('id', $id)
+ ->execute();
+ }
+ catch (\API_Exception $e) {
+ $exceptionThrown = $e->getMessage();
+ }
+ $this->assertContains('id', $exceptionThrown);
+ }
+
+ /**
+ * @param AbstractEntity|string $entityClass
+ * @param int $id
+ * @param string $entity
+ */
+ protected function checkGet($entityClass, $id, $entity) {
+ $getResult = $entityClass::get()
+ ->setCheckPermissions(FALSE)
+ ->addWhere('id', '=', $id)
+ ->execute();
+
+ $errMsg = sprintf('Failed to fetch a %s after creation', $entity);
+ $this->assertEquals(1, count($getResult), $errMsg);
+ }
+
+ /**
+ * @param AbstractEntity|string $entityClass
+ */
+ protected function checkDeleteWithNoId($entityClass) {
+ $exceptionThrown = '';
+ try {
+ $entityClass::delete()
+ ->execute();
+ }
+ catch (\API_Exception $e) {
+ $exceptionThrown = $e->getMessage();
+ }
+ $this->assertContains('required', $exceptionThrown);
+ }
+
+ /**
+ * @param AbstractEntity|string $entityClass
+ */
+ protected function checkWrongParamType($entityClass) {
+ $exceptionThrown = '';
+ try {
+ $entityClass::get()
+ ->setCheckPermissions('nada')
+ ->execute();
+ }
+ catch (\API_Exception $e) {
+ $exceptionThrown = $e->getMessage();
+ }
+ $this->assertContains('checkPermissions', $exceptionThrown);
+ $this->assertContains('type', $exceptionThrown);
+ }
+
+ /**
+ * @param AbstractEntity|string $entityClass
+ * @param int $id
+ */
+ protected function checkDeletion($entityClass, $id) {
+ $deleteResult = $entityClass::delete()
+ ->setCheckPermissions(FALSE)
+ ->addWhere('id', '=', $id)
+ ->execute();
+
+ // should get back an array of deleted id
+ $this->assertEquals([$id], (array) $deleteResult);
+ }
+
+ /**
+ * @param AbstractEntity|string $entityClass
+ * @param int $id
+ * @param string $entity
+ */
+ protected function checkPostDelete($entityClass, $id, $entity) {
+ $getDeletedResult = $entityClass::get()
+ ->setCheckPermissions(FALSE)
+ ->addWhere('id', '=', $id)
+ ->execute();
+
+ $errMsg = sprintf('Entity "%s" was not deleted', $entity);
+ $this->assertEquals(0, count($getDeletedResult), $errMsg);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Entity/ContactJoinTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Entity/ContactJoinTest.php
new file mode 100644
index 00000000..392e0466
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Entity/ContactJoinTest.php
@@ -0,0 +1,103 @@
+<?php
+
+namespace Civi\Test\Api4\Entity;
+
+use Civi\Api4\Contact;
+use Civi\Api4\OptionValue;
+use Civi\Test\Api4\UnitTestCase;
+
+/**
+ * @group headless
+ */
+class ContactJoinTest extends UnitTestCase {
+
+ public function setUpHeadless() {
+ $relatedTables = [
+ 'civicrm_address',
+ 'civicrm_email',
+ 'civicrm_phone',
+ 'civicrm_openid',
+ 'civicrm_im',
+ 'civicrm_website',
+ 'civicrm_activity',
+ 'civicrm_activity_contact',
+ ];
+
+ $this->cleanup(['tablesToTruncate' => $relatedTables]);
+ $this->loadDataSet('SingleContact');
+
+ return parent::setUpHeadless();
+ }
+
+ public function testContactJoin() {
+
+ $contact = $this->getReference('test_contact_1');
+ $entitiesToTest = ['Address', 'OpenID', 'IM', 'Website', 'Email', 'Phone'];
+
+ foreach ($entitiesToTest as $entity) {
+ $results = civicrm_api4($entity, 'get', [
+ 'where' => [['contact_id', '=', $contact['id']]],
+ 'select' => ['contact.display_name', 'contact.id'],
+ ]);
+ foreach ($results as $result) {
+ $this->assertEquals($contact['id'], $result['contact']['id']);
+ $this->assertEquals($contact['display_name'], $result['contact']['display_name']);
+ }
+ }
+ }
+
+ public function testJoinToPCMWillReturnArray() {
+ $contact = Contact::create()->setValues([
+ 'preferred_communication_method' => [1, 2, 3],
+ 'contact_type' => 'Individual',
+ 'first_name' => 'Test',
+ 'last_name' => 'PCM',
+ ])->execute()->first();
+
+ $fetchedContact = Contact::get()
+ ->addWhere('id', '=', $contact['id'])
+ ->addSelect('preferred_communication_method')
+ ->execute()
+ ->first();
+
+ $this->assertCount(3, $fetchedContact["preferred_communication_method"]);
+ }
+
+ public function testJoinToPCMOptionValueWillShowLabel() {
+ $options = OptionValue::get()
+ ->addWhere('option_group.name', '=', 'preferred_communication_method')
+ ->execute()
+ ->getArrayCopy();
+
+ $optionValues = array_column($options, 'value');
+ $labels = array_column($options, 'label');
+
+ $contact = Contact::create()->setValues([
+ 'preferred_communication_method' => $optionValues,
+ 'contact_type' => 'Individual',
+ 'first_name' => 'Test',
+ 'last_name' => 'PCM',
+ ])->execute()->first();
+
+ $contact2 = Contact::create()->setValues([
+ 'preferred_communication_method' => $optionValues,
+ 'contact_type' => 'Individual',
+ 'first_name' => 'Test',
+ 'last_name' => 'PCM2',
+ ])->execute()->first();
+
+ $contactIds = array_column([$contact, $contact2], 'id');
+
+ $fetchedContact = Contact::get()
+ ->addWhere('id', 'IN', $contactIds)
+ ->addSelect('preferred_communication_method.label')
+ ->execute()
+ ->first();
+
+ $preferredMethod = $fetchedContact['preferred_communication_method'];
+ $returnedLabels = array_column($preferredMethod, 'label');
+
+ $this->assertEquals($labels, $returnedLabels);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Entity/EntityTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Entity/EntityTest.php
new file mode 100644
index 00000000..8f7ecd00
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Entity/EntityTest.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace Civi\Test\Api4\Entity;
+
+use Civi\Api4\Entity;
+use Civi\Test\Api4\UnitTestCase;
+
+/**
+ * @group headless
+ */
+class EntityTest extends UnitTestCase {
+
+ public function testEntityGet() {
+ $result = Entity::get()
+ ->setCheckPermissions(FALSE)
+ ->execute()
+ ->indexBy('name');
+ $this->assertArrayHasKey('Entity', $result,
+ "Entity::get missing itself");
+ $this->assertArrayHasKey('Participant', $result,
+ "Entity::get missing Participant");
+ }
+
+ public function testEntity() {
+ $result = Entity::getActions()
+ ->setCheckPermissions(FALSE)
+ ->execute()
+ ->indexBy('name');
+ $this->assertNotContains(
+ 'create',
+ array_keys((array) $result),
+ "Entity entity has more than basic actions");
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Entity/ParticipantTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Entity/ParticipantTest.php
new file mode 100644
index 00000000..8236efb0
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Entity/ParticipantTest.php
@@ -0,0 +1,200 @@
+<?php
+
+namespace Civi\Test\Api4\Entity;
+
+use Civi\Api4\Participant;
+use Civi\Test\Api4\UnitTestCase;
+
+/**
+ * @group headless
+ */
+class ParticipantTest extends UnitTestCase {
+
+ public function setUp() {
+ parent::setUp();
+ $cleanup_params = [
+ 'tablesToTruncate' => [
+ 'civicrm_event',
+ 'civicrm_participant',
+ ],
+ ];
+ $this->cleanup($cleanup_params);
+ }
+
+ public function testGetActions() {
+ $result = Participant::getActions()
+ ->setCheckPermissions(FALSE)
+ ->execute()
+ ->indexBy('name');
+
+ $getParams = $result['get']['params'];
+ $whereDescription = 'Criteria for selecting items.';
+
+ $this->assertEquals(TRUE, $getParams['checkPermissions']['default']);
+ $this->assertEquals($whereDescription, $getParams['where']['description']);
+ }
+
+ public function testGet() {
+ $rows = $this->getRowCount('civicrm_participant');
+ if ($rows > 0) {
+ $this->markTestSkipped('Participant table must be empty');
+ }
+
+ // With no records:
+ $result = Participant::get()->setCheckPermissions(FALSE)->execute();
+ $this->assertEquals(0, $result->count(), "count of empty get is not 0");
+
+ // Check that the $result knows what the inputs were
+ $this->assertEquals('Participant', $result->entity);
+ $this->assertEquals('get', $result->action);
+ $this->assertEquals(4, $result->version);
+
+ // Create some test related records before proceeding
+ $participantCount = 20;
+ $contactCount = 7;
+ $eventCount = 5;
+
+ // All events will either have this number or one less because of the
+ // rotating participation creation method.
+ $expectedFirstEventCount = ceil($participantCount / $eventCount);
+
+ $dummy = [
+ 'contacts' => $this->createEntity([
+ 'type' => 'Individual',
+ 'count' => $contactCount,
+ 'seq' => 1]),
+ 'events' => $this->createEntity([
+ 'type' => 'Event',
+ 'count' => $eventCount,
+ 'seq' => 1]),
+ 'sources' => ['Paddington', 'Springfield', 'Central'],
+ ];
+
+ // - create dummy participants record
+ for ($i = 0; $i < $participantCount; $i++) {
+ $dummy['participants'][$i] = $this->sample([
+ 'type' => 'Participant',
+ 'overrides' => [
+ 'event_id' => $dummy['events'][$i % $eventCount]['id'],
+ 'contact_id' => $dummy['contacts'][$i % $contactCount]['id'],
+ 'source' => $dummy['sources'][$i % 3], // 3 = number of sources
+ ]])['sample_params'];
+
+ Participant::create()
+ ->setValues($dummy['participants'][$i])
+ ->setCheckPermissions(FALSE)
+ ->execute();
+ }
+ $sqlCount = $this->getRowCount('civicrm_participant');
+ $this->assertEquals($participantCount, $sqlCount, "Unexpected count");
+
+ $firstEventId = $dummy['events'][0]['id'];
+ $secondEventId = $dummy['events'][1]['id'];
+ $firstContactId = $dummy['contacts'][0]['id'];
+
+ $firstOnlyResult = Participant::get()
+ ->setCheckPermissions(FALSE)
+ ->addClause('AND', ['event_id', '=', $firstEventId])
+ ->execute();
+
+ $this->assertEquals($expectedFirstEventCount, count($firstOnlyResult),
+ "count of first event is not $expectedFirstEventCount");
+
+ // get first two events using different methods
+ $firstTwo = Participant::get()
+ ->setCheckPermissions(FALSE)
+ ->addWhere('event_id', 'IN', [$firstEventId, $secondEventId])
+ ->execute();
+
+ $firstResult = $result->first();
+
+ // verify counts
+ // count should either twice the first event count or one less
+ $this->assertLessThanOrEqual(
+ $expectedFirstEventCount * 2,
+ count($firstTwo),
+ "count is too high"
+ );
+
+ $this->assertGreaterThanOrEqual(
+ $expectedFirstEventCount * 2 - 1,
+ count($firstTwo),
+ "count is too low"
+ );
+
+ $firstParticipantResult = Participant::get()
+ ->setCheckPermissions(FALSE)
+ ->addWhere('event_id', '=', $firstEventId)
+ ->addWhere('contact_id', '=', $firstContactId)
+ ->execute();
+
+ $this->assertEquals(1, count($firstParticipantResult), "more than one registration");
+
+ $firstParticipantId = $firstParticipantResult->first()['id'];
+
+ // get a result which excludes $first_participant
+ $otherParticipantResult = Participant::get()
+ ->setCheckPermissions(FALSE)
+ ->setSelect(['id'])
+ ->addClause('NOT', [
+ ['event_id', '=', $firstEventId],
+ ['contact_id', '=', $firstContactId],
+ ]
+ )
+ ->execute()
+ ->indexBy('id');
+
+ // check alternate syntax for NOT
+ $otherParticipantResult2 = Participant::get()
+ ->setCheckPermissions(FALSE)
+ ->setSelect(['id'])
+ ->addClause('NOT', 'AND', [
+ ['event_id', '=', $firstEventId],
+ ['contact_id', '=', $firstContactId],
+ ]
+ )
+ ->execute()
+ ->indexBy('id');
+
+ $this->assertEquals($otherParticipantResult, $otherParticipantResult2);
+
+ $this->assertEquals($participantCount - 1,
+ count($otherParticipantResult),
+ "failed to exclude a single record on complex criteria");
+ // check the record we have excluded is the right one:
+
+ $this->assertFalse(
+ $otherParticipantResult->offsetExists($firstParticipantId),
+ 'excluded wrong record');
+
+ // retrieve a participant record and update some records
+ $patchRecord = [
+ 'source' => "not " . $firstResult['source'],
+ ];
+
+ Participant::update()
+ ->addWhere('event_id', '=', $firstEventId)
+ ->setCheckPermissions(FALSE)
+ ->setLimit(20)
+ ->setValues($patchRecord)
+ ->setCheckPermissions(FALSE)
+ ->execute();
+
+ // - delete some records
+ $secondEventId = $dummy['events'][1]['id'];
+ $deleteResult = Participant::delete()
+ ->addWhere('event_id', '=', $secondEventId)
+ ->setCheckPermissions(FALSE)
+ ->execute();
+ $expectedDeletes = [2, 7, 12, 17];
+ $this->assertEquals($expectedDeletes, (array) $deleteResult,
+ "didn't delete every second record as expected");
+
+ $sqlCount = $this->getRowCount('civicrm_participant');
+ $this->assertEquals(
+ $participantCount - count($expectedDeletes),
+ $sqlCount,
+ "records not gone from database after delete");
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Mock/Api4/Action/MockArrayEntity/Get.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Mock/Api4/Action/MockArrayEntity/Get.php
new file mode 100644
index 00000000..f276baf6
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Mock/Api4/Action/MockArrayEntity/Get.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace Civi\Api4\Action\MockArrayEntity;
+
+/**
+ * This class demonstrates how the getRecords method of Basic\Get can be overridden.
+ */
+class Get extends \Civi\Api4\Generic\BasicGetAction {
+
+ public function getRecords() {
+ return [
+ [
+ 'field1' => 1,
+ 'field2' => 'zebra',
+ 'field3' => NULL,
+ 'field4' => [1, 2, 3],
+ 'field5' => 'apple',
+ ],
+ [
+ 'field1' => 2,
+ 'field2' => 'yack',
+ 'field3' => 0,
+ 'field4' => [2, 3, 4],
+ 'field5' => 'banana',
+ 'field6' => '',
+ ],
+ [
+ 'field1' => 3,
+ 'field2' => 'x ray',
+ 'field4' => [3, 4, 5],
+ 'field5' => 'banana',
+ 'field6' => 0,
+ ],
+ [
+ 'field1' => 4,
+ 'field2' => 'wildebeest',
+ 'field3' => 1,
+ 'field4' => [4, 5, 6],
+ 'field5' => 'apple',
+ 'field6' => '0',
+ ],
+ [
+ 'field1' => 5,
+ 'field2' => 'vole',
+ 'field3' => 1,
+ 'field4' => [4, 5, 6],
+ 'field5' => 'apple',
+ 'field6' => 0,
+ ],
+ ];
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Mock/Api4/MockArrayEntity.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Mock/Api4/MockArrayEntity.php
new file mode 100644
index 00000000..371df50e
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Mock/Api4/MockArrayEntity.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace Civi\Api4;
+use Civi\Api4\Generic\BasicGetFieldsAction;
+
+/**
+ * MockArrayEntity entity.
+ *
+ * @method Generic\BasicGetAction get()
+ *
+ * @package Civi\Api4
+ */
+class MockArrayEntity extends Generic\AbstractEntity {
+
+ public static function getFields() {
+ return new BasicGetFieldsAction(static::class, __FUNCTION__, function() {
+ return [];
+ });
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Mock/Api4/MockBasicEntity.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Mock/Api4/MockBasicEntity.php
new file mode 100644
index 00000000..aed11e45
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Mock/Api4/MockBasicEntity.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace Civi\Api4;
+
+/**
+ * MockBasicEntity entity.
+ *
+ * @package Civi\Api4
+ */
+class MockBasicEntity extends Generic\AbstractEntity {
+
+ const STORAGE_CLASS = '\\Civi\\Test\\Api4\\Mock\\MockEntityDataStorage';
+
+ /**
+ * @return Generic\BasicGetFieldsAction
+ */
+ public static function getFields() {
+ return new Generic\BasicGetFieldsAction(static::class, __FUNCTION__, function() {
+ return [
+ [
+ 'name' => 'id',
+ 'type' => 'Integer',
+ ],
+ [
+ 'name' => 'group',
+ 'options' => [
+ 'one' => 'One',
+ 'two' => 'Two',
+ ]
+ ],
+ [
+ 'name' => 'color',
+ ],
+ [
+ 'name' => 'shape',
+ ],
+ [
+ 'name' => 'size',
+ ],
+ [
+ 'name' => 'weight',
+ ],
+ ];
+ });
+ }
+
+ /**
+ * @return Generic\BasicGetAction
+ */
+ public static function get() {
+ return new Generic\BasicGetAction('MockBasicEntity', __FUNCTION__, [self::STORAGE_CLASS, 'get']);
+ }
+
+ /**
+ * @return Generic\BasicCreateAction
+ */
+ public static function create() {
+ return new Generic\BasicCreateAction(static::class, __FUNCTION__, [self::STORAGE_CLASS, 'write']);
+ }
+
+ /**
+ * @return Generic\BasicUpdateAction
+ */
+ public static function update() {
+ return new Generic\BasicUpdateAction(self::getEntityName(), __FUNCTION__, 'id', [self::STORAGE_CLASS, 'write']);
+ }
+
+ /**
+ * @return Generic\BasicBatchAction
+ */
+ public static function delete() {
+ return new Generic\BasicBatchAction('MockBasicEntity', __FUNCTION__, 'id', [self::STORAGE_CLASS, 'delete']);
+ }
+
+ /**
+ * @return Generic\BasicBatchAction
+ */
+ public static function batchFrobnicate() {
+ return new Generic\BasicBatchAction('MockBasicEntity', __FUNCTION__, ['id', 'number'], function ($item) {
+ return [
+ 'id' => $item['id'],
+ 'frobnication' => $item['number'] * $item['number'],
+ ];
+ });
+ }
+
+ /**
+ * @return Generic\BasicReplaceAction
+ */
+ public static function replace() {
+ return new Generic\BasicReplaceAction('MockBasicEntity', __FUNCTION__);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Mock/MockEntityDataStorage.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Mock/MockEntityDataStorage.php
new file mode 100644
index 00000000..fbb10465
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Mock/MockEntityDataStorage.php
@@ -0,0 +1,31 @@
+<?php
+
+namespace Civi\Test\Api4\Mock;
+
+/**
+ * Simple data backend for mock basic api.
+ */
+class MockEntityDataStorage {
+
+ private static $data = [];
+
+ private static $nextId = 1;
+
+ public static function get() {
+ return self::$data;
+ }
+
+ public static function write($record) {
+ if (empty($record['id'])) {
+ $record['id'] = self::$nextId++;
+ }
+ self::$data[$record['id']] = $record;
+ return $record;
+ }
+
+ public static function delete($record) {
+ unset(self::$data[$record['id']]);
+ return $record;
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Mock/MockV4ReflectionBase.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Mock/MockV4ReflectionBase.php
new file mode 100644
index 00000000..e46272d2
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Mock/MockV4ReflectionBase.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Civi\Test\Api4\Mock;
+
+/**
+ * Class TestV4ReflectionBase
+ *
+ * This is the base class.
+ *
+ * @internal
+ */
+class MockV4ReflectionBase {
+ /**
+ * This is the foo property.
+ *
+ * In general, you can do nothing with it.
+ *
+ * @var array
+ */
+ public $foo = [];
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Mock/MockV4ReflectionChild.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Mock/MockV4ReflectionChild.php
new file mode 100644
index 00000000..83966b5c
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Mock/MockV4ReflectionChild.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Civi\Test\Api4\Mock;
+
+/**
+ * @inheritDoc
+ */
+class MockV4ReflectionChild extends MockV4ReflectionBase {
+ /**
+ * @inheritDoc
+ *
+ * In the child class, foo has been barred.
+ */
+ public $foo = ['bar' => 1];
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Mock/MockV4ReflectionGrandchild.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Mock/MockV4ReflectionGrandchild.php
new file mode 100644
index 00000000..a2a93a4f
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Mock/MockV4ReflectionGrandchild.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace Civi\Test\Api4\Mock;
+
+
+/**
+ * Grandchild class
+ *
+ * This is an extended description.
+ *
+ * There is a line break in this description.
+ *
+ * @inheritdoc
+ */
+class MockV4ReflectionGrandchild extends MockV4ReflectionChild {
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Query/Api4SelectQueryComplexJoinTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Query/Api4SelectQueryComplexJoinTest.php
new file mode 100644
index 00000000..0e68843a
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Query/Api4SelectQueryComplexJoinTest.php
@@ -0,0 +1,85 @@
+<?php
+
+namespace Civi\Test\Api4\Query;
+
+use Civi\Api4\Query\Api4SelectQuery;
+use Civi\Test\Api4\UnitTestCase;
+
+/**
+ * @group headless
+ */
+class Api4SelectQueryComplexJoinTest extends UnitTestCase {
+
+ public function setUpHeadless() {
+ $relatedTables = [
+ 'civicrm_address',
+ 'civicrm_email',
+ 'civicrm_phone',
+ 'civicrm_openid',
+ 'civicrm_im',
+ 'civicrm_website',
+ 'civicrm_activity',
+ 'civicrm_activity_contact',
+ ];
+ $this->cleanup(['tablesToTruncate' => $relatedTables]);
+ $this->loadDataSet('SingleContact');
+ return parent::setUpHeadless();
+ }
+
+ public function testWithComplexRelatedEntitySelect() {
+ $query = new Api4SelectQuery('Contact', FALSE);
+ $query->select[] = 'id';
+ $query->select[] = 'display_name';
+ $query->select[] = 'phones.phone';
+ $query->select[] = 'emails.email';
+ $query->select[] = 'emails.location_type.name';
+ $query->select[] = 'created_activities.contact_id';
+ $query->select[] = 'created_activities.activity.subject';
+ $query->select[] = 'created_activities.activity.activity_type.name';
+ $query->where[] = ['first_name', '=', 'Single'];
+ $query->where[] = ['id', '=', $this->getReference('test_contact_1')['id']];
+ $results = $query->run();
+
+ $testActivities = [
+ $this->getReference('test_activity_1'),
+ $this->getReference('test_activity_2'),
+ ];
+ $activitySubjects = array_column($testActivities, 'subject');
+
+ $this->assertCount(1, $results);
+ $firstResult = array_shift($results);
+ $this->assertArrayHasKey('created_activities', $firstResult);
+ $firstCreatedActivity = array_shift($firstResult['created_activities']);
+ $this->assertArrayHasKey('activity', $firstCreatedActivity);
+ $firstActivity = $firstCreatedActivity['activity'];
+ $this->assertContains($firstActivity['subject'], $activitySubjects);
+ $this->assertArrayHasKey('activity_type', $firstActivity);
+ $activityType = $firstActivity['activity_type'];
+ $this->assertArrayHasKey('name', $activityType);
+ }
+
+ public function testWithSelectOfOrphanDeepValues() {
+ $query = new Api4SelectQuery('Contact', FALSE);
+ $query->select[] = 'id';
+ $query->select[] = 'first_name';
+ $query->select[] = 'emails.location_type.name'; // emails not selected
+ $results = $query->run();
+ $firstResult = array_shift($results);
+
+ $this->assertEmpty($firstResult['emails']);
+ }
+
+ public function testOrderDoesNotMatter() {
+ $query = new Api4SelectQuery('Contact', FALSE);
+ $query->select[] = 'id';
+ $query->select[] = 'first_name';
+ $query->select[] = 'emails.location_type.name'; // before emails selection
+ $query->select[] = 'emails.email';
+ $query->where[] = ['emails.email', 'IS NOT NULL'];
+ $results = $query->run();
+ $firstResult = array_shift($results);
+
+ $this->assertNotEmpty($firstResult['emails'][0]['location_type']['name']);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Query/Api4SelectQueryTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Query/Api4SelectQueryTest.php
new file mode 100644
index 00000000..ca9a5851
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Query/Api4SelectQueryTest.php
@@ -0,0 +1,91 @@
+<?php
+
+namespace Civi\Test\Api4\Query;
+
+use Civi\Api4\Query\Api4SelectQuery;
+use Civi\Test\Api4\UnitTestCase;
+
+/**
+ * @group headless
+ */
+class Api4SelectQueryTest extends UnitTestCase {
+
+ public function setUpHeadless() {
+ $relatedTables = [
+ 'civicrm_address',
+ 'civicrm_email',
+ 'civicrm_phone',
+ 'civicrm_openid',
+ 'civicrm_im',
+ 'civicrm_website',
+ 'civicrm_activity',
+ 'civicrm_activity_contact',
+ ];
+ $this->cleanup(['tablesToTruncate' => $relatedTables]);
+ $this->loadDataSet('DefaultDataSet');
+ $displayNameFormat = '{contact.first_name}{ }{contact.last_name}';
+ \Civi::settings()->set('display_name_format', $displayNameFormat);
+
+ return parent::setUpHeadless();
+ }
+
+ public function testWithSingleWhereJoin() {
+ $phoneNum = $this->getReference('test_phone_1')['phone'];
+
+ $query = new Api4SelectQuery('Contact', FALSE);
+ $query->where[] = ['phones.phone', '=', $phoneNum];
+ $results = $query->run();
+
+ $this->assertCount(1, $results);
+ }
+
+ public function testOneToManyJoin() {
+ $phoneNum = $this->getReference('test_phone_1')['phone'];
+
+ $query = new Api4SelectQuery('Contact', FALSE);
+ $query->select[] = 'id';
+ $query->select[] = 'first_name';
+ $query->select[] = 'phones.phone';
+ $query->where[] = ['phones.phone', '=', $phoneNum];
+ $results = $query->run();
+
+ $this->assertCount(1, $results);
+ $firstResult = array_shift($results);
+ $this->assertArrayHasKey('phones', $firstResult);
+ $firstPhone = array_shift($firstResult['phones']);
+ $this->assertEquals($phoneNum, $firstPhone['phone']);
+ }
+
+ public function testManyToOneJoin() {
+ $phoneNum = $this->getReference('test_phone_1')['phone'];
+ $contact = $this->getReference('test_contact_1');
+
+ $query = new Api4SelectQuery('Phone', FALSE);
+ $query->select[] = 'id';
+ $query->select[] = 'phone';
+ $query->select[] = 'contact.display_name';
+ $query->select[] = 'contact.first_name';
+ $query->where[] = ['phone', '=', $phoneNum];
+ $results = $query->run();
+
+ $this->assertCount(1, $results);
+ $firstResult = array_shift($results);
+ $this->assertArrayHasKey('contact', $firstResult);
+ $resultContact = $firstResult['contact'];
+ $this->assertEquals($contact['display_name'], $resultContact['display_name']);
+ }
+
+ public function testOneToManyMultipleJoin() {
+ $query = new Api4SelectQuery('Contact', FALSE);
+ $query->select[] = 'id';
+ $query->select[] = 'first_name';
+ $query->select[] = 'phones.phone';
+ $query->where[] = ['first_name', '=', 'Phoney'];
+ $results = $query->run();
+ $result = array_pop($results);
+
+ $this->assertEquals('Phoney', $result['first_name']);
+ $this->assertCount(2, $result['phones']);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Query/OneToOneJoinTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Query/OneToOneJoinTest.php
new file mode 100644
index 00000000..ef05f657
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Query/OneToOneJoinTest.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace Civi\Test\Api4\Query;
+
+use Civi\Api4\Contact;
+use Civi\Api4\OptionGroup;
+use Civi\Api4\OptionValue;
+use Civi\Test\Api4\UnitTestCase;
+
+/**
+ * Class OneToOneJoinTest
+ * @package Civi\Test\Api4\Query
+ * @group headless
+ */
+class OneToOneJoinTest extends UnitTestCase {
+
+ public function testOneToOneJoin() {
+ $armenianContact = Contact::create()
+ ->addValue('first_name', 'Contact')
+ ->addValue('last_name', 'One')
+ ->addValue('contact_type', 'Individual')
+ ->addValue('preferred_language', 'hy_AM')
+ ->execute()
+ ->first();
+
+ $basqueContact = Contact::create()
+ ->addValue('first_name', 'Contact')
+ ->addValue('last_name', 'Two')
+ ->addValue('contact_type', 'Individual')
+ ->addValue('preferred_language', 'eu_ES')
+ ->execute()
+ ->first();
+
+ $contacts = Contact::get()
+ ->addWhere('id', 'IN', [$armenianContact['id'], $basqueContact['id']])
+ ->addSelect('preferred_language.label')
+ ->addSelect('last_name')
+ ->execute()
+ ->indexBy('last_name')
+ ->getArrayCopy();
+
+ $this->assertEquals($contacts['One']['preferred_language']['label'], 'Armenian');
+ $this->assertEquals($contacts['Two']['preferred_language']['label'], 'Basque');
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Query/OptionValueJoinTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Query/OptionValueJoinTest.php
new file mode 100644
index 00000000..75d4b977
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Query/OptionValueJoinTest.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace Civi\Test\Api4\Query;
+
+use Civi\Api4\Query\Api4SelectQuery;
+use Civi\Test\Api4\UnitTestCase;
+
+/**
+ * @group headless
+ */
+class OptionValueJoinTest extends UnitTestCase {
+
+ public function setUpHeadless() {
+ $relatedTables = [
+ 'civicrm_address',
+ 'civicrm_email',
+ 'civicrm_phone',
+ 'civicrm_openid',
+ 'civicrm_im',
+ 'civicrm_website',
+ 'civicrm_activity',
+ 'civicrm_activity_contact',
+ ];
+
+ $this->cleanup(['tablesToTruncate' => $relatedTables]);
+ $this->loadDataSet('SingleContact');
+
+ return parent::setUpHeadless();
+ }
+
+ public function testCommunicationMethodJoin() {
+ $query = new Api4SelectQuery('Contact', FALSE);
+ $query->select[] = 'first_name';
+ $query->select[] = 'preferred_communication_method.label';
+ $query->where[] = ['preferred_communication_method', 'IS NOT NULL'];
+ $results = $query->run();
+ $first = array_shift($results);
+ $firstPreferredMethod = array_shift($first['preferred_communication_method']);
+
+ $this->assertEquals(
+ 'Phone',
+ $firstPreferredMethod['label']
+ );
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Query/SelectQueryMultiJoinTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Query/SelectQueryMultiJoinTest.php
new file mode 100644
index 00000000..860bd785
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Query/SelectQueryMultiJoinTest.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace Civi\Test\Api4\Query;
+
+use Civi\Api4\Contact;
+use Civi\Api4\Email;
+use Civi\Test\Api4\UnitTestCase;
+
+/**
+ * Class SelectQueryMultiJoinTest
+ * @package Civi\Test\Api4\Query
+ * @group headless
+ */
+class SelectQueryMultiJoinTest extends UnitTestCase {
+ public function setUpHeadless() {
+ $this->cleanup(['tablesToTruncate' => ['civicrm_contact', 'civicrm_email']]);
+ $this->loadDataSet('MultiContactMultiEmail');
+ return parent::setUpHeadless();
+ }
+
+ public function testOneToManySelect() {
+ $results = Contact::get()
+ ->addSelect('emails.email')
+ ->execute()
+ ->indexBy('id')
+ ->getArrayCopy();
+
+ $firstContactId = $this->getReference('test_contact_1')['id'];
+ $secondContactId = $this->getReference('test_contact_2')['id'];
+
+ $firstContact = $results[$firstContactId];
+ $secondContact = $results[$secondContactId];
+ $firstContactEmails = array_column($firstContact['emails'], 'email');
+ $secondContactEmails = array_column($secondContact['emails'], 'email');
+
+ $expectedFirstEmails = [
+ 'test_contact_one_home@fakedomain.com',
+ 'test_contact_one_work@fakedomain.com',
+ ];
+ $expectedSecondEmails = [
+ 'test_contact_two_home@fakedomain.com',
+ 'test_contact_two_work@fakedomain.com',
+ ];
+
+ $this->assertEquals($expectedFirstEmails, $firstContactEmails);
+ $this->assertEquals($expectedSecondEmails, $secondContactEmails);
+ }
+
+ public function testManyToOneSelect() {
+ $results = Email::get()
+ ->addSelect('contact.display_name')
+ ->execute()
+ ->indexBy('id')
+ ->getArrayCopy();
+
+ $firstEmail = $this->getReference('test_email_1');
+ $secondEmail = $this->getReference('test_email_2');
+ $thirdEmail = $this->getReference('test_email_3');
+ $fourthEmail = $this->getReference('test_email_4');
+ $firstContactEmailIds = [$firstEmail['id'], $secondEmail['id']];
+ $secondContactEmailIds = [$thirdEmail['id'], $fourthEmail['id']];
+
+ foreach ($results as $id => $email) {
+ $displayName = $email['contact']['display_name'];
+ if (in_array($id, $firstContactEmailIds)) {
+ $this->assertEquals('First Contact', $displayName);
+ }
+ elseif (in_array($id, $secondContactEmailIds)) {
+ $this->assertEquals('Second Contact', $displayName);
+ }
+ }
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Service/Schema/SchemaMapRealTableTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Service/Schema/SchemaMapRealTableTest.php
new file mode 100644
index 00000000..183d34dc
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Service/Schema/SchemaMapRealTableTest.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Civi\Test\Api4\Service\Schema;
+
+use Civi\Test\Api4\UnitTestCase;
+
+/**
+ * @group headless
+ */
+class SchemaMapRealTableTest extends UnitTestCase {
+ public function testAutoloadWillPopulateTablesByDefault() {
+ $map = \Civi::container()->get('schema_map');
+ $this->assertNotEmpty($map->getTables());
+ }
+
+ public function testSimplePathWillExist() {
+ $map = \Civi::container()->get('schema_map');
+ $path = $map->getPath('civicrm_contact', 'emails');
+ $this->assertCount(1, $path);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Service/Schema/SchemaMapperTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Service/Schema/SchemaMapperTest.php
new file mode 100644
index 00000000..04952f7b
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Service/Schema/SchemaMapperTest.php
@@ -0,0 +1,90 @@
+<?php
+
+namespace Civi\Test\Api4\Service\Schema;
+
+use Civi\Api4\Service\Schema\Joinable\Joinable;
+use Civi\Api4\Service\Schema\SchemaMap;
+use Civi\Api4\Service\Schema\Table;
+use Civi\Test\Api4\UnitTestCase;
+
+/**
+ * @group headless
+ */
+class SchemaMapperTest extends UnitTestCase {
+
+ public function testWillHaveNoPathWithNoTables() {
+ $map = new SchemaMap();
+ $this->assertEmpty($map->getPath('foo', 'bar'));
+ }
+
+ public function testWillHavePathWithSingleJump() {
+ $phoneTable = new Table('civicrm_phone');
+ $locationTable = new Table('civicrm_location_type');
+ $link = new Joinable('civicrm_location_type', 'id', 'location');
+ $phoneTable->addTableLink('location_type_id', $link);
+
+ $map = new SchemaMap();
+ $map->addTables([$phoneTable, $locationTable]);
+
+ $this->assertNotEmpty($map->getPath('civicrm_phone', 'location'));
+ }
+
+ public function testWillHavePathWithDoubleJump() {
+ $activity = new Table('activity');
+ $activityContact = new Table('activity_contact');
+ $middleLink = new Joinable('activity_contact', 'activity_id');
+ $contactLink = new Joinable('contact', 'id');
+ $activity->addTableLink('id', $middleLink);
+ $activityContact->addTableLink('contact_id', $contactLink);
+
+ $map = new SchemaMap();
+ $map->addTables([$activity, $activityContact]);
+
+ $this->assertNotEmpty($map->getPath('activity', 'contact'));
+ }
+
+ public function testPathWithTripleJoin() {
+ $first = new Table('first');
+ $second = new Table('second');
+ $third = new Table('third');
+ $first->addTableLink('id', new Joinable('second', 'id'));
+ $second->addTableLink('id', new Joinable('third', 'id'));
+ $third->addTableLink('id', new Joinable('fourth', 'id'));
+
+ $map = new SchemaMap();
+ $map->addTables([$first, $second, $third]);
+
+ $this->assertNotEmpty($map->getPath('first', 'fourth'));
+ }
+
+ public function testCircularReferenceWillNotBreakIt() {
+ $contactTable = new Table('contact');
+ $carTable = new Table('car');
+ $carLink = new Joinable('car', 'id');
+ $ownerLink = new Joinable('contact', 'id');
+ $contactTable->addTableLink('car_id', $carLink);
+ $carTable->addTableLink('owner_id', $ownerLink);
+
+ $map = new SchemaMap();
+ $map->addTables([$contactTable, $carTable]);
+
+ $this->assertEmpty($map->getPath('contact', 'foo'));
+ }
+
+ public function testCannotGoOverJoinLimit() {
+ $first = new Table('first');
+ $second = new Table('second');
+ $third = new Table('third');
+ $fourth = new Table('fourth');
+ $first->addTableLink('id', new Joinable('second', 'id'));
+ $second->addTableLink('id', new Joinable('third', 'id'));
+ $third->addTableLink('id', new Joinable('fourth', 'id'));
+ $fourth->addTableLink('id', new Joinable('fifth', 'id'));
+
+ $map = new SchemaMap();
+ $map->addTables([$first, $second, $third, $fourth]);
+
+ $this->assertEmpty($map->getPath('first', 'fifth'));
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Service/TestCreationParameterProvider.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Service/TestCreationParameterProvider.php
new file mode 100644
index 00000000..ef10cea9
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Service/TestCreationParameterProvider.php
@@ -0,0 +1,148 @@
+<?php
+
+namespace Civi\Test\Api4\Service;
+
+use Civi\Api4\Service\Spec\FieldSpec;
+use Civi\Api4\Service\Spec\SpecGatherer;
+use \CRM_Utils_String as StringHelper;
+
+class TestCreationParameterProvider {
+
+ /**
+ * @var SpecGatherer
+ */
+ protected $gatherer;
+
+ /**
+ * @param SpecGatherer $gatherer
+ */
+ public function __construct(SpecGatherer $gatherer) {
+ $this->gatherer = $gatherer;
+ }
+
+ /**
+ * @param $entity
+ *
+ * @return array
+ */
+ public function getRequired($entity) {
+ $createSpec = $this->gatherer->getSpec($entity, 'create', FALSE);
+ $requiredFields = array_merge($createSpec->getRequiredFields(), $createSpec->getConditionalRequiredFields());
+
+ if ($entity === 'Contact') {
+ $requiredFields[] = $createSpec->getFieldByName('first_name');
+ $requiredFields[] = $createSpec->getFieldByName('last_name');
+ }
+
+ $requiredParams = [];
+ foreach ($requiredFields as $requiredField) {
+ $value = $this->getRequiredValue($requiredField);
+ $requiredParams[$requiredField->getName()] = $value;
+ }
+
+ unset($requiredParams['id']);
+
+ return $requiredParams;
+ }
+
+ /**
+ * Attempt to get a value using field option, defaults, FKEntity, or a random
+ * value based on the data type.
+ *
+ * @param FieldSpec $field
+ *
+ * @return mixed
+ * @throws \Exception
+ */
+ private function getRequiredValue(FieldSpec $field) {
+
+ if ($field->getOptions()) {
+ return $this->getOption($field);
+ }
+ elseif ($field->getDefaultValue()) {
+ return $field->getDefaultValue();
+ }
+ elseif ($field->getFkEntity()) {
+ return $this->getFkID($field);
+ }
+
+ $randomValue = $this->getRandomValue($field->getDataType());
+
+ if ($randomValue) {
+ return $randomValue;
+ }
+
+ throw new \Exception('Could not provide default value');
+ }
+
+ /**
+ * @param FieldSpec $field
+ *
+ * @return mixed
+ */
+ private function getOption(FieldSpec $field) {
+ $options = $field->getOptions();
+ $useKeyNames = ['data_type', 'html_type'];
+ $shouldUseKey = in_array($field->getName(), $useKeyNames);
+ $isIdField = substr($field->getName(), -3) === '_id';
+
+ if ($isIdField || $shouldUseKey) {
+ return array_rand($options); // return key (ID)
+ }
+ else {
+ return $options[array_rand($options)];
+ }
+ }
+
+ /**
+ * @param FieldSpec $field
+ *
+ * @return mixed
+ * @throws \Exception
+ */
+ private function getFkID(FieldSpec $field) {
+ $fkEntity = $field->getFkEntity();
+ $params = ['checkPermissions' => FALSE];
+ // Be predictable about what type of contact we select
+ if ($fkEntity === 'Contact') {
+ $params['where'] = [['contact_type', '=', 'Individual']];
+ }
+ $entityList = civicrm_api4($fkEntity, 'get', $params);
+ if ($entityList->count() < 1) {
+ $msg = sprintf('At least one %s is required in test', $fkEntity);
+ throw new \Exception($msg);
+ }
+
+ return $entityList->last()['id'];
+ }
+
+ /**
+ * @param $dataType
+ *
+ * @return int|null|string
+ */
+ private function getRandomValue($dataType) {
+ switch ($dataType) {
+ case 'Boolean':
+ return TRUE;
+
+ case 'Integer':
+ return rand(1, 2000);
+
+ case 'String':
+ return StringHelper::createRandom(10, implode('', range('a', 'z')));
+
+ case 'Text':
+ return StringHelper::createRandom(100, implode('', range('a', 'z')));
+
+ case 'Money':
+ return sprintf('%d.%2d', rand(0, 2000), rand(1, 99));
+
+ case 'Date':
+ return '20100102';
+ }
+
+ return NULL;
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Spec/RequestSpecTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Spec/RequestSpecTest.php
new file mode 100644
index 00000000..45aa41b2
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Spec/RequestSpecTest.php
@@ -0,0 +1,42 @@
+<?php
+
+namespace Civi\Test\Api4\Spec;
+
+use Civi\Api4\Service\Spec\FieldSpec;
+use Civi\Api4\Service\Spec\RequestSpec;
+use Civi\Test\Api4\UnitTestCase;
+
+/**
+ * @group headless
+ */
+class RequestSpecTest extends UnitTestCase {
+
+ public function testRequiredFieldFetching() {
+ $spec = new RequestSpec('Contact', 'get');
+ $requiredField = new FieldSpec('name', 'Contact');
+ $requiredField->setRequired(TRUE);
+ $nonRequiredField = new FieldSpec('age', 'Contact', 'Integer');
+ $nonRequiredField->setRequired(FALSE);
+ $spec->addFieldSpec($requiredField);
+ $spec->addFieldSpec($nonRequiredField);
+
+ $requiredFields = $spec->getRequiredFields();
+
+ $this->assertCount(1, $requiredFields);
+ $this->assertEquals('name', array_shift($requiredFields)->getName());
+ }
+
+ public function testGettingFieldNames() {
+ $spec = new RequestSpec('Contact', 'get');
+ $nameField = new FieldSpec('name', 'Contact');
+ $ageField = new FieldSpec('age', 'Contact', 'Integer');
+ $spec->addFieldSpec($nameField);
+ $spec->addFieldSpec($ageField);
+
+ $fieldNames = $spec->getFieldNames();
+
+ $this->assertCount(2, $fieldNames);
+ $this->assertEquals(['name', 'age'], $fieldNames);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Spec/SpecFormatterTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Spec/SpecFormatterTest.php
new file mode 100644
index 00000000..bdb1c459
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Spec/SpecFormatterTest.php
@@ -0,0 +1,90 @@
+<?php
+
+namespace Civi\Test\Api4\Spec;
+
+use Civi\Api4\Service\Spec\CustomFieldSpec;
+use Civi\Api4\Service\Spec\FieldSpec;
+use Civi\Api4\Service\Spec\RequestSpec;
+use Civi\Api4\Service\Spec\SpecFormatter;
+use Civi\Test\Api4\UnitTestCase;
+
+/**
+ * @group headless
+ */
+class SpecFormatterTest extends UnitTestCase {
+
+ public function testSpecToArray() {
+ $spec = new RequestSpec('Contact', 'get');
+ $fieldName = 'last_name';
+ $field = new FieldSpec($fieldName, 'Contact');
+ $spec->addFieldSpec($field);
+ $arraySpec = SpecFormatter::specToArray($spec->getFields());
+
+ $this->assertEquals('String', $arraySpec[$fieldName]['data_type']);
+ }
+
+ /**
+ * @dataProvider arrayFieldSpecProvider
+ *
+ * @param array $fieldData
+ * @param string $expectedName
+ * @param string $expectedType
+ */
+ public function testArrayToField($fieldData, $expectedName, $expectedType) {
+ $field = SpecFormatter::arrayToField($fieldData, 'TestEntity');
+
+ $this->assertEquals($expectedName, $field->getName());
+ $this->assertEquals($expectedType, $field->getDataType());
+ }
+
+ public function testCustomFieldWillBeReturned() {
+ $customGroupId = 1432;
+ $customFieldId = 3333;
+ $name = 'MyFancyField';
+
+ $data = [
+ 'custom_group_id' => $customGroupId,
+ 'custom_group' => ['name' => 'my_group'],
+ 'id' => $customFieldId,
+ 'name' => $name,
+ 'data_type' => 'String',
+ 'html_type' => 'MultiSelect',
+ ];
+
+ /** @var CustomFieldSpec $field */
+ $field = SpecFormatter::arrayToField($data, 'TestEntity');
+
+ $this->assertInstanceOf(CustomFieldSpec::class, $field);
+ $this->assertEquals('my_group', $field->getCustomGroupName());
+ $this->assertEquals($customFieldId, $field->getCustomFieldId());
+ $this->assertEquals(\CRM_Core_DAO::SERIALIZE_SEPARATOR_BOOKEND, $field->getSerialize());
+ }
+
+ /**
+ * @return array
+ */
+ public function arrayFieldSpecProvider() {
+ return [
+ [
+ [
+ 'name' => 'Foo',
+ 'title' => 'Bar',
+ 'type' => \CRM_Utils_Type::T_STRING
+ ],
+ 'Foo',
+ 'String'
+ ],
+ [
+ [
+ 'name' => 'MyField',
+ 'title' => 'Bar',
+ 'type' => \CRM_Utils_Type::T_STRING,
+ 'data_type' => 'Boolean' // this should take precedence
+ ],
+ 'MyField',
+ 'Boolean'
+ ],
+ ];
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Spec/SpecGathererTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Spec/SpecGathererTest.php
new file mode 100644
index 00000000..bf5b92b9
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Spec/SpecGathererTest.php
@@ -0,0 +1,96 @@
+<?php
+
+namespace Civi\Test\Api4\Spec;
+
+use Civi\Api4\Service\Spec\FieldSpec;
+use Civi\Api4\Service\Spec\Provider\SpecProviderInterface;
+use Civi\Api4\Service\Spec\RequestSpec;
+use Civi\Api4\Service\Spec\SpecGatherer;
+use Civi\Test\Api4\Traits\OptionCleanupTrait;
+use Civi\Test\Api4\UnitTestCase;
+use Civi\Api4\CustomField;
+use Civi\Api4\CustomGroup;
+use Civi\Test\Api4\Traits\TableDropperTrait;
+use Prophecy\Argument;
+
+/**
+ * @group headless
+ */
+class SpecGathererTest extends UnitTestCase {
+
+ use TableDropperTrait;
+ use OptionCleanupTrait;
+
+ public function setUpHeadless() {
+ $this->dropByPrefix('civicrm_value_favorite');
+ $this->cleanup([
+ 'tablesToTruncate' => [
+ 'civicrm_custom_group',
+ 'civicrm_custom_field'
+ ],
+ ]);
+ return parent::setUpHeadless();
+ }
+
+ public function testBasicFieldsGathering() {
+ $gatherer = new SpecGatherer();
+ $specs = $gatherer->getSpec('Contact', 'get', FALSE);
+ $contactDAO = _civicrm_api3_get_DAO('Contact');
+ $contactFields = $contactDAO::fields();
+ $specFieldNames = $specs->getFieldNames();
+ $contactFieldNames = array_column($contactFields, 'name');
+
+ $this->assertEmpty(array_diff_key($contactFieldNames, $specFieldNames));
+ }
+
+ public function testWithSpecProvider() {
+ $gather = new SpecGatherer();
+
+ $provider = $this->prophesize(SpecProviderInterface::class);
+ $provider->applies('Contact', 'create')->willReturn(TRUE);
+ $provider->modifySpec(Argument::any())->will(function ($args) {
+ /** @var RequestSpec $spec */
+ $spec = $args[0];
+ $spec->addFieldSpec(new FieldSpec('foo', 'Contact'));
+ });
+ $gather->addSpecProvider($provider->reveal());
+
+ $spec = $gather->getSpec('Contact', 'create', FALSE);
+ $fieldNames = $spec->getFieldNames();
+
+ $this->assertContains('foo', $fieldNames);
+ }
+
+ public function testPseudoConstantOptionsWillBeAdded() {
+ $customGroupId = CustomGroup::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('name', 'FavoriteThings')
+ ->addValue('extends', 'Contact')
+ ->execute()
+ ->first()['id'];
+
+ $options = ['r' => 'Red', 'g' => 'Green', 'p' => 'Pink'];
+
+ CustomField::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('label', 'FavColor')
+ ->addValue('custom_group_id', $customGroupId)
+ ->addValue('options', $options)
+ ->addValue('html_type', 'Select')
+ ->addValue('data_type', 'String')
+ ->execute();
+
+ $gatherer = new SpecGatherer();
+ $spec = $gatherer->getSpec('Contact', 'get', TRUE);
+
+ $regularField = $spec->getFieldByName('contact_type');
+ $this->assertNotEmpty($regularField->getOptions());
+ $this->assertContains('Individual', $regularField->getOptions());
+
+ $customField = $spec->getFieldByName('FavoriteThings.FavColor');
+ $this->assertNotEmpty($customField->getOptions());
+ $this->assertContains('Green', $customField->getOptions());
+ $this->assertEquals('Pink', $customField->getOptions()['p']);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Traits/OptionCleanupTrait.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Traits/OptionCleanupTrait.php
new file mode 100644
index 00000000..06f43235
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Traits/OptionCleanupTrait.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace Civi\Test\Api4\Traits;
+
+trait OptionCleanupTrait {
+
+ protected $optionGroupMaxId;
+ protected $optionValueMaxId;
+
+ public function setUp() {
+ $this->optionGroupMaxId = \CRM_Core_DAO::singleValueQuery('SELECT MAX(id) FROM civicrm_option_group');
+ $this->optionValueMaxId = \CRM_Core_DAO::singleValueQuery('SELECT MAX(id) FROM civicrm_option_value');
+ }
+
+ public function tearDown() {
+ if ($this->optionValueMaxId) {
+ \CRM_Core_DAO::executeQuery('DELETE FROM civicrm_option_value WHERE id > ' . $this->optionValueMaxId);
+ }
+ if ($this->optionGroupMaxId) {
+ \CRM_Core_DAO::executeQuery('DELETE FROM civicrm_option_group WHERE id > ' . $this->optionGroupMaxId);
+ }
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Traits/QueryCounterTrait.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Traits/QueryCounterTrait.php
new file mode 100644
index 00000000..c7e10f1b
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Traits/QueryCounterTrait.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace Civi\Test\Api4\Traits;
+
+use \CRM_Utils_Array as ArrayHelper;
+
+trait QueryCounterTrait {
+
+ /**
+ * @var int
+ */
+ protected $startCount = 0;
+
+ /**
+ * Start the query counter
+ */
+ protected function beginQueryCount() {
+ $this->startCount = $this->getCurrentGlobalQueryCount();
+ }
+
+ /**
+ * @return int
+ * The number of queries since the counter was started
+ */
+ protected function getQueryCount() {
+ return $this->getCurrentGlobalQueryCount() - $this->startCount;
+ }
+
+ /**
+ * @return int
+ * @throws \Exception
+ */
+ private function getCurrentGlobalQueryCount() {
+ global $_DB_DATAOBJECT;
+
+ if (!$_DB_DATAOBJECT) {
+ throw new \Exception('Database object not set so cannot count queries');
+ }
+
+ return ArrayHelper::value('RESULTSEQ', $_DB_DATAOBJECT, 0);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Traits/TableDropperTrait.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Traits/TableDropperTrait.php
new file mode 100644
index 00000000..6e543473
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Traits/TableDropperTrait.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Civi\Test\Api4\Traits;
+
+trait TableDropperTrait {
+ /**
+ * @param $prefix
+ */
+ protected function dropByPrefix($prefix) {
+ $sql = "SELECT CONCAT( 'DROP TABLE ', GROUP_CONCAT(table_name) , ';' ) " .
+ "AS statement FROM information_schema.tables " .
+ "WHERE table_name LIKE '%s%%' AND table_schema = DATABASE();";
+ $sql = sprintf($sql, $prefix);
+ $dropTableQuery = \CRM_Core_DAO::executeQuery($sql);
+ $dropTableQuery->fetch();
+ $dropTableQuery = $dropTableQuery->statement;
+
+ if ($dropTableQuery) {
+ \CRM_Core_DAO::executeQuery($dropTableQuery);
+ }
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Traits/TestDataLoaderTrait.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Traits/TestDataLoaderTrait.php
new file mode 100644
index 00000000..1db22090
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Traits/TestDataLoaderTrait.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace Civi\Test\Api4\Traits;
+
+/**
+ * This probably should be a separate class
+ */
+trait TestDataLoaderTrait {
+
+ /**
+ * @var array
+ * References to entities used for loading test data
+ */
+ protected $references;
+
+ /**
+ * Creates entities from a JSON data set
+ *
+ * @param $path
+ */
+ protected function loadDataSet($path) {
+ if (!file_exists($path)) {
+ $path = __DIR__ . '/../DataSets/' . $path . '.json';
+ }
+
+ $dataSet = json_decode(file_get_contents($path), TRUE);
+ foreach ($dataSet as $entityName => $entities) {
+ foreach ($entities as $entityValues) {
+
+ $entityValues = $this->replaceReferences($entityValues);
+
+ $params = ['values' => $entityValues, 'checkPermissions' => FALSE];
+ $result = civicrm_api4($entityName, 'create', $params);
+ if (isset($entityValues['@ref'])) {
+ $this->references[$entityValues['@ref']] = $result->first();
+ }
+ }
+ }
+ }
+
+ /**
+ * @param $name
+ *
+ * @return null|mixed
+ */
+ protected function getReference($name) {
+ return isset($this->references[$name]) ? $this->references[$name] : NULL;
+ }
+
+ /**
+ * @param array $entityValues
+ *
+ * @return array
+ */
+ private function replaceReferences($entityValues) {
+ foreach ($entityValues as $name => $value) {
+ if (is_array($value)) {
+ $entityValues[$name] = $this->replaceReferences($value);
+ }
+ elseif (substr($value, 0, 4) === '@ref') {
+ $referenceName = substr($value, 5);
+ list ($reference, $property) = explode('.', $referenceName);
+ $entityValues[$name] = $this->references[$reference][$property];
+ }
+ }
+ return $entityValues;
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/UnitTestCase.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/UnitTestCase.php
new file mode 100644
index 00000000..4f872d12
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/UnitTestCase.php
@@ -0,0 +1,235 @@
+<?php
+
+namespace Civi\Test\Api4;
+
+use Civi\Test\Api4\Traits\TestDataLoaderTrait;
+use Civi\Test\HeadlessInterface;
+use Civi\Test\TransactionalInterface;
+
+/**
+ * @group headless
+ */
+class UnitTestCase extends \PHPUnit_Framework_TestCase implements HeadlessInterface, TransactionalInterface {
+
+ use TestDataLoaderTrait;
+
+ /**
+ * @see CiviUnitTestCase
+ *
+ * @param string $name
+ * @param array $data
+ * @param string $dataName
+ */
+ public function __construct($name = NULL, array $data = [], $dataName = '') {
+ parent::__construct($name, $data, $dataName);
+ error_reporting(E_ALL & ~E_NOTICE);
+ }
+
+ public function setUpHeadless() {
+ return \Civi\Test::headless()->installMe(__DIR__)->apply();
+ }
+
+ /**
+ * Tears down the fixture, for example, closes a network connection.
+ *
+ * This method is called after a test is executed.
+ */
+ public function tearDown() {
+ parent::tearDown();
+ }
+
+ /**
+ * Quick clean by emptying tables created for the test.
+ *
+ * @param array $params
+ */
+ public function cleanup($params) {
+ $params += [
+ 'tablesToTruncate' => [],
+ ];
+ \CRM_Core_DAO::executeQuery("SET FOREIGN_KEY_CHECKS = 0;");
+ foreach ($params['tablesToTruncate'] as $table) {
+ \Civi::log()->info('truncating: ' . $table);
+ $sql = "TRUNCATE TABLE $table";
+ \CRM_Core_DAO::executeQuery($sql);
+ }
+ \CRM_Core_DAO::executeQuery("SET FOREIGN_KEY_CHECKS = 1;");
+ }
+
+ /**
+ * Quick record counter
+ *
+ * @param string $table_name
+ * @returns int record count
+ */
+ public function getRowCount($table_name) {
+ $sql = "SELECT count(id) FROM $table_name";
+ return (int) \CRM_Core_DAO::singleValueQuery($sql);
+ }
+
+ /**
+ * Create sample entities (using V3 for now).
+ *
+ * @param array $params (type, seq, overrides, count)
+ * @return array (either single, or array of array if count >1)
+ */
+ public static function createEntity($params) {
+ $params += [
+ 'count' => 1,
+ 'seq' => 0,
+ ];
+ $entities = [];
+ $entity = NULL;
+ for ($i = 0; $i < $params['count']; $i++) {
+ $params['seq']++;
+ $data = self::sample($params);
+ $api_params = ['sequential' => 1] + $data['sample_params'];
+ $result = civicrm_api3($data['entity'], 'create', $api_params);
+ if ($result['is_error']) {
+ throw new \Exception("creating $data[entity] failed");
+ }
+ $entity = $result['values'][0];
+ if (!($entity['id'] > 0)) {
+ throw new \Exception("created entity is malformed");
+ }
+ $entities[] = $entity;
+ }
+ return $params['count'] == 1 ? $entity : $entities;
+ }
+
+ /**
+ * Helper function for creating sample entities.
+ *
+ * Depending on the supplied sequence integer, plucks values from the dummy data.
+ * Constructs a foreign entity when an ID is required but isn't supplied in the overrides.
+ *
+ * Inspired by CiviUnitTestCase::
+ * @todo - extract this function to own class and share with CiviUnitTestCase?
+ * @param array $params
+ * - type: string roughly matching entity type
+ * - seq: (optional) int sequence number for the values of this type
+ * - overrides: (optional) array of fill in parameters
+ *
+ * @return array
+ * - entity: string API entity type (usually the type supplied except for contact subtypes)
+ * - sample_params: array API sample_params properties of sample entity
+ */
+ public static function sample($params) {
+ $params += [
+ 'seq' => 0,
+ 'overrides' => [],
+ ];
+ $type = $params['type'];
+ // sample data - if field is array then chosed based on `seq`
+ $sample_params = [];
+ if (in_array($type, ['Individual', 'Organization', 'Household'])) {
+ $sample_params['contact_type'] = $type;
+ $entity = 'Contact';
+ }
+ else {
+ $entity = $type;
+ }
+ // use the seq to pluck a set of params out
+ foreach (self::sampleData($type) as $key => $value) {
+ if (is_array($value)) {
+ $sample_params[$key] = $value[$params['seq'] % count($value)];
+ }
+ else {
+ $sample_params[$key] = $value;
+ }
+ }
+ if ($type == 'Individual') {
+ $sample_params['email'] = strtolower(
+ $sample_params['first_name'] . '_' . $sample_params['last_name'] . '@civicrm.org'
+ );
+ $sample_params['prefix_id'] = 3;
+ $sample_params['suffix_id'] = 3;
+ }
+ if (!count($sample_params)) {
+ throw new \Exception("unknown sample type: $type");
+ }
+ $sample_params = $params['overrides'] + $sample_params;
+ // make foreign enitiies if they haven't been supplied
+ foreach ($sample_params as $key => $value) {
+ if (substr($value, 0, 6) === 'dummy.') {
+ $foreign_entity = self::createEntity([
+ 'type' => substr($value, 6),
+ 'seq' => $params['seq']]);
+ $sample_params[$key] = $foreign_entity['id'];
+ }
+ }
+ return compact("entity", "sample_params");
+ }
+
+ /**
+ * Provider of sample data.
+ *
+ * @return array
+ * Array values represent a set of allowable items.
+ * Strings in the form "dummy.Entity" require creating a foreign entity first.
+ */
+ public static function sampleData($type) {
+ $data = [
+ 'Individual' => [
+ // The number of values in each list need to be coprime numbers to not have duplicates
+ 'first_name' => ['Anthony', 'Joe', 'Terrence', 'Lucie', 'Albert', 'Bill', 'Kim'],
+ 'middle_name' => ['J.', 'M.', 'P', 'L.', 'K.', 'A.', 'B.', 'C.', 'D', 'E.', 'Z.'],
+ 'last_name' => ['Anderson', 'Miller', 'Smith', 'Collins', 'Peterson'],
+ 'contact_type' => 'Individual',
+ ],
+ 'Organization' => [
+ 'organization_name' => [
+ 'Unit Test Organization',
+ 'Acme',
+ 'Roberts and Sons',
+ 'Cryo Space Labs',
+ 'Sharper Pens',
+ ],
+ ],
+ 'Household' => [
+ 'household_name' => ['Unit Test household'],
+ ],
+ 'Event' => [
+ 'title' => 'Annual CiviCRM meet',
+ 'summary' => 'If you have any CiviCRM related issues or want to track where CiviCRM is heading, Sign up now',
+ 'description' => 'This event is intended to give brief idea about progess of CiviCRM and giving solutions to common user issues',
+ 'event_type_id' => 1,
+ 'is_public' => 1,
+ 'start_date' => 20081021,
+ 'end_date' => 20081023,
+ 'is_online_registration' => 1,
+ 'registration_start_date' => 20080601,
+ 'registration_end_date' => 20081015,
+ 'max_participants' => 100,
+ 'event_full_text' => 'Sorry! We are already full',
+ 'is_monetary' => 0,
+ 'is_active' => 1,
+ 'is_show_location' => 0,
+ ],
+ 'Participant' => [
+ 'event_id' => 'dummy.Event',
+ 'contact_id' => 'dummy.Individual',
+ 'status_id' => 2,
+ 'role_id' => 1,
+ 'register_date' => 20070219,
+ 'source' => 'Wimbeldon',
+ 'event_level' => 'Payment',
+ ],
+ 'Contribution' => [
+ 'contact_id' => 'dummy.Individual',
+ 'financial_type_id' => 1, // donation, 2 = member, 3 = campaign contribution, 4=event
+ 'total_amount' => 7.3,
+ ],
+ 'Activity' => [
+ //'activity_type_id' => 1,
+ 'subject' => 'unit testing',
+ 'source_contact_id' => 'dummy.Individual',
+ ],
+ ];
+ if ($type == 'Contact') {
+ $type = 'Individual';
+ }
+ return $data[$type];
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Utils/ArrayInsertionServiceTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Utils/ArrayInsertionServiceTest.php
new file mode 100644
index 00000000..462f07cc
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Utils/ArrayInsertionServiceTest.php
@@ -0,0 +1,67 @@
+<?php
+
+namespace Civi\Test\Api4\Utils;
+
+use Civi\Api4\Utils\ArrayInsertionUtil;
+use Civi\Test\Api4\UnitTestCase;
+
+/**
+ * @group headless
+ */
+class ArrayInsertionServiceTest extends UnitTestCase {
+
+ public function testInsertWillWork() {
+ $arr = [];
+ $path = ['foo' => FALSE, 'bar' => FALSE];
+ $inserter = new ArrayInsertionUtil();
+ $inserter::insert($arr, $path, ['LALA']);
+
+ $expected = [
+ 'foo' => [
+ 'bar' => 'LALA'
+ ],
+ ];
+
+ $this->assertEquals($expected, $arr);
+ }
+
+ public function testInsertionOfContactEmailLocation() {
+ $contacts = [
+ [
+ 'id' => 1,
+ 'first_name' => 'Jim'
+ ],
+ [
+ 'id' => 2,
+ 'first_name' => 'Karen'
+ ],
+ ];
+ $emails = [
+ [
+ 'email' => 'jim@jim.com',
+ 'id' => 2,
+ '_parent_id' => 1
+ ],
+ ];
+ $locationTypes = [
+ [
+ 'name' => 'Home',
+ 'id' => 3,
+ '_parent_id' => 2
+ ],
+ ];
+
+ $emailPath = ['emails' => TRUE];
+ $locationPath = ['emails' => TRUE, 'location' => FALSE];
+ $inserter = new ArrayInsertionUtil();
+
+ foreach ($contacts as &$contact) {
+ $inserter::insert($contact, $emailPath, $emails);
+ $inserter::insert($contact, $locationPath, $locationTypes);
+ }
+
+ $locationType = $contacts[0]['emails'][0]['location']['name'];
+ $this->assertEquals('Home', $locationType);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Utils/ReflectionUtilsTest.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Utils/ReflectionUtilsTest.php
new file mode 100644
index 00000000..d94de337
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/Utils/ReflectionUtilsTest.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace Civi\Test\Api4\Utils;
+
+use Civi\Api4\Utils\ReflectionUtils;
+use Civi\Test\Api4\Mock\MockV4ReflectionGrandchild;
+use Civi\Test\Api4\UnitTestCase;
+
+/**
+ * @group headless
+ */
+class ReflectionUtilsTest extends UnitTestCase {
+
+ /**
+ * Test that class annotations are returned across @inheritDoc
+ */
+ public function testGetDocBlockForClass() {
+ $grandChild = new MockV4ReflectionGrandchild();
+ $reflection = new \ReflectionClass($grandChild);
+ $doc = ReflectionUtils::getCodeDocs($reflection);
+
+ $this->assertEquals(TRUE, $doc['internal']);
+ $this->assertEquals('Grandchild class', $doc['description']);
+
+ $expectedComment = 'This is an extended description.
+
+There is a line break in this description.
+
+This is the base class.';
+
+ $this->assertEquals($expectedComment, $doc['comment']);
+ }
+
+ /**
+ * Test that property annotations are returned across @inheritDoc
+ */
+ public function testGetDocBlockForProperty() {
+ $grandChild = new MockV4ReflectionGrandchild();
+ $reflection = new \ReflectionClass($grandChild);
+ $doc = ReflectionUtils::getCodeDocs($reflection->getProperty('foo'), 'Property');
+
+ $this->assertEquals('This is the foo property.', $doc['description']);
+ $this->assertEquals("In the child class, foo has been barred.\n\nIn general, you can do nothing with it.", $doc['comment']);
+ }
+
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/bootstrap.php b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/bootstrap.php
new file mode 100644
index 00000000..1c28e611
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/phpunit/bootstrap.php
@@ -0,0 +1,56 @@
+<?php
+
+define('CIVICRM_CONTAINER_CACHE', 'never');
+ini_set('memory_limit', '2G');
+ini_set('safe_mode', 0);
+$bootCode = cv('php:boot --level=classloader', 'phpcode');
+eval($bootCode);
+
+preg_match('/require_once\s*\'(.*)\'/', $bootCode, $matches);
+$loader = require sprintf('%s/vendor/autoload.php', $matches[1]);
+$loader->addPsr4('Civi\\Test\\Api4\\', __DIR__);
+$loader->addPsr4('Civi\\Api4\\', __DIR__ . '/Mock/Api4');
+
+/**
+ * Call the "cv" command.
+ *
+ * @param string $cmd
+ * The rest of the command to send.
+ * @param string $decode
+ * Ex: 'json' or 'phpcode'.
+ * @return string
+ * Response output (if the command executed normally).
+ * @throws \RuntimeException
+ * If the command terminates abnormally.
+ */
+function cv($cmd, $decode = 'json') {
+ $cmd = 'cv ' . $cmd;
+ $descriptorSpec = [0 => ["pipe", "r"], 1 => ["pipe", "w"], 2 => STDERR];
+ $oldOutput = getenv('CV_OUTPUT');
+ putenv("CV_OUTPUT=json");
+ $process = proc_open($cmd, $descriptorSpec, $pipes, __DIR__);
+ putenv("CV_OUTPUT=$oldOutput");
+ fclose($pipes[0]);
+ $result = stream_get_contents($pipes[1]);
+ fclose($pipes[1]);
+ if (proc_close($process) !== 0) {
+ throw new RuntimeException("Command failed ($cmd):\n$result");
+ }
+ switch ($decode) {
+ case 'raw':
+ return $result;
+
+ case 'phpcode':
+ // If the last output is /*PHPCODE*/, then we managed to complete execution.
+ if (substr(trim($result), 0, 12) !== "/*BEGINPHP*/" || substr(trim($result), -10) !== "/*ENDPHP*/") {
+ throw new \RuntimeException("Command failed ($cmd):\n$result");
+ }
+ return $result;
+
+ case 'json':
+ return json_decode($result, 1);
+
+ default:
+ throw new RuntimeException("Bad decoder format ($decode)");
+ }
+}
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/services.xml b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/services.xml
new file mode 100644
index 00000000..220ba910
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/tests/services.xml
@@ -0,0 +1,10 @@
+<container xmlns="http://symfony.com/schema/dic/services"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
+
+ <services>
+ <service id="test.param_provider" class="Civi\Test\Api4\Service\TestCreationParameterProvider">
+ <argument type="service" id="spec_gatherer"/>
+ </service>
+ </services>
+</container>
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/xml/Menu/api4.xml b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/xml/Menu/api4.xml
new file mode 100644
index 00000000..1973f234
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/xml/Menu/api4.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<menu>
+ <item>
+ <path>civicrm/ajax/api4</path>
+ <page_callback>CRM_Api4_Page_AJAX</page_callback>
+ <access_arguments>access CiviCRM</access_arguments>
+ </item>
+ <item>
+ <path>civicrm/api4</path>
+ <page_callback>CRM_Api4_Page_Api4Explorer</page_callback>
+ <title>CiviCRM</title>
+ <access_arguments>access CiviCRM</access_arguments>
+ </item>
+</menu>