From f815f0f0a097e44e8565052b9a662ae7cd589368 Mon Sep 17 00:00:00 2001 From: Kevin Cristiano <kcristiano@kcristiano.com> Date: Thu, 8 Oct 2020 07:29:34 -0400 Subject: [PATCH] civicrm release --- civicrm.php | 18 +- civicrm/CRM/ACL/DAO/ACL.php | 4 +- civicrm/CRM/ACL/DAO/ACLCache.php | 4 +- civicrm/CRM/ACL/DAO/EntityRole.php | 4 +- civicrm/CRM/Activity/BAO/Activity.php | 1 - civicrm/CRM/Activity/BAO/Query.php | 2 +- civicrm/CRM/Activity/DAO/Activity.php | 10 +- civicrm/CRM/Activity/DAO/ActivityContact.php | 4 +- civicrm/CRM/Activity/Import/Form/MapField.php | 4 +- civicrm/CRM/Activity/Import/Form/Preview.php | 4 +- civicrm/CRM/Activity/Import/Parser.php | 12 +- civicrm/CRM/Admin/Form/Job.php | 2 +- civicrm/CRM/Admin/Form/ScheduleReminders.php | 2 +- civicrm/CRM/Admin/Form/Setting/UF.php | 3 +- civicrm/CRM/Api4/Page/Api4Explorer.php | 3 +- civicrm/CRM/Batch/DAO/Batch.php | 4 +- civicrm/CRM/Batch/DAO/EntityBatch.php | 4 +- civicrm/CRM/Campaign/DAO/Campaign.php | 4 +- civicrm/CRM/Campaign/DAO/CampaignGroup.php | 4 +- civicrm/CRM/Campaign/DAO/Survey.php | 4 +- civicrm/CRM/Campaign/Form/Campaign.php | 2 +- civicrm/CRM/Case/DAO/Case.php | 4 +- civicrm/CRM/Case/DAO/CaseActivity.php | 4 +- civicrm/CRM/Case/DAO/CaseContact.php | 4 +- civicrm/CRM/Case/DAO/CaseType.php | 4 +- civicrm/CRM/Case/Form/CaseView.php | 6 + civicrm/CRM/Contact/BAO/Contact.php | 7 +- civicrm/CRM/Contact/BAO/ContactType.php | 55 +- civicrm/CRM/Contact/BAO/Query.php | 16 +- civicrm/CRM/Contact/DAO/ACLContactCache.php | 4 +- civicrm/CRM/Contact/DAO/Contact.php | 10 +- civicrm/CRM/Contact/DAO/ContactType.php | 4 +- civicrm/CRM/Contact/DAO/DashboardContact.php | 4 +- civicrm/CRM/Contact/DAO/Group.php | 4 +- civicrm/CRM/Contact/DAO/GroupContact.php | 4 +- civicrm/CRM/Contact/DAO/GroupContactCache.php | 4 +- civicrm/CRM/Contact/DAO/GroupNesting.php | 4 +- civicrm/CRM/Contact/DAO/GroupOrganization.php | 4 +- civicrm/CRM/Contact/DAO/Relationship.php | 4 +- civicrm/CRM/Contact/DAO/RelationshipCache.php | 4 +- civicrm/CRM/Contact/DAO/RelationshipType.php | 4 +- civicrm/CRM/Contact/DAO/SavedSearch.php | 4 +- .../CRM/Contact/DAO/SubscriptionHistory.php | 4 +- .../Contact/Form/Search/Custom/Proximity.php | 22 +- civicrm/CRM/Contribute/BAO/Contribution.php | 92 +- civicrm/CRM/Contribute/BAO/Query.php | 4 +- civicrm/CRM/Contribute/DAO/Contribution.php | 8 +- .../CRM/Contribute/DAO/ContributionPage.php | 5 +- .../Contribute/DAO/ContributionProduct.php | 4 +- .../CRM/Contribute/DAO/ContributionRecur.php | 4 +- .../CRM/Contribute/DAO/ContributionSoft.php | 4 +- civicrm/CRM/Contribute/DAO/Premium.php | 4 +- .../CRM/Contribute/DAO/PremiumsProduct.php | 4 +- civicrm/CRM/Contribute/DAO/Product.php | 4 +- civicrm/CRM/Contribute/DAO/Widget.php | 4 +- .../Contribute/Form/Contribution/Confirm.php | 96 +- .../CRM/Contribute/Form/Contribution/Main.php | 3 +- .../Form/ContributionPage/Amount.php | 12 +- .../Form/ContributionPage/Settings.php | 2 +- .../Form/ContributionPage/Widget.php | 2 +- .../CRM/Contribute/Import/Form/MapField.php | 4 +- .../CRM/Contribute/Import/Form/Preview.php | 4 +- civicrm/CRM/Contribute/Import/Parser.php | 14 +- civicrm/CRM/Core/BAO/CustomValueTable.php | 8 +- civicrm/CRM/Core/BAO/MessageTemplate.php | 51 +- civicrm/CRM/Core/BAO/PrevNextCache.php | 26 - civicrm/CRM/Core/BAO/SchemaHandler.php | 56 +- civicrm/CRM/Core/BAO/UFGroup.php | 6 +- civicrm/CRM/Core/CodeGen/DAO.php | 48 +- civicrm/CRM/Core/CodeGen/Specification.php | 1 + civicrm/CRM/Core/CodeGen/Util/Template.php | 1 + civicrm/CRM/Core/DAO.php | 53 +- civicrm/CRM/Core/DAO/ActionLog.php | 4 +- civicrm/CRM/Core/DAO/ActionMapping.php | 4 +- civicrm/CRM/Core/DAO/ActionSchedule.php | 7 +- civicrm/CRM/Core/DAO/Address.php | 4 +- civicrm/CRM/Core/DAO/AddressFormat.php | 4 +- civicrm/CRM/Core/DAO/Cache.php | 4 +- civicrm/CRM/Core/DAO/Component.php | 4 +- civicrm/CRM/Core/DAO/Country.php | 4 +- civicrm/CRM/Core/DAO/County.php | 4 +- civicrm/CRM/Core/DAO/CustomField.php | 4 +- civicrm/CRM/Core/DAO/CustomGroup.php | 4 +- civicrm/CRM/Core/DAO/Dashboard.php | 4 +- civicrm/CRM/Core/DAO/Discount.php | 4 +- civicrm/CRM/Core/DAO/Domain.php | 4 +- civicrm/CRM/Core/DAO/Email.php | 4 +- civicrm/CRM/Core/DAO/EntityFile.php | 4 +- civicrm/CRM/Core/DAO/EntityTag.php | 4 +- civicrm/CRM/Core/DAO/Extension.php | 4 +- civicrm/CRM/Core/DAO/File.php | 4 +- civicrm/CRM/Core/DAO/IM.php | 4 +- civicrm/CRM/Core/DAO/Job.php | 4 +- civicrm/CRM/Core/DAO/JobLog.php | 4 +- civicrm/CRM/Core/DAO/LocBlock.php | 4 +- civicrm/CRM/Core/DAO/LocationType.php | 4 +- civicrm/CRM/Core/DAO/Log.php | 4 +- civicrm/CRM/Core/DAO/MailSettings.php | 4 +- civicrm/CRM/Core/DAO/Managed.php | 4 +- civicrm/CRM/Core/DAO/Mapping.php | 4 +- civicrm/CRM/Core/DAO/MappingField.php | 4 +- civicrm/CRM/Core/DAO/Menu.php | 4 +- civicrm/CRM/Core/DAO/MessageTemplate.php | 4 +- civicrm/CRM/Core/DAO/Navigation.php | 4 +- civicrm/CRM/Core/DAO/Note.php | 4 +- civicrm/CRM/Core/DAO/OpenID.php | 4 +- civicrm/CRM/Core/DAO/OptionGroup.php | 4 +- civicrm/CRM/Core/DAO/OptionValue.php | 4 +- civicrm/CRM/Core/DAO/Phone.php | 10 +- civicrm/CRM/Core/DAO/PreferencesDate.php | 4 +- civicrm/CRM/Core/DAO/PrevNextCache.php | 4 +- civicrm/CRM/Core/DAO/PrintLabel.php | 4 +- civicrm/CRM/Core/DAO/RecurringEntity.php | 4 +- civicrm/CRM/Core/DAO/Setting.php | 4 +- civicrm/CRM/Core/DAO/StateProvince.php | 4 +- civicrm/CRM/Core/DAO/StatusPreference.php | 4 +- civicrm/CRM/Core/DAO/SystemLog.php | 4 +- civicrm/CRM/Core/DAO/Tag.php | 4 +- civicrm/CRM/Core/DAO/Timezone.php | 4 +- civicrm/CRM/Core/DAO/UFField.php | 4 +- civicrm/CRM/Core/DAO/UFGroup.php | 4 +- civicrm/CRM/Core/DAO/UFJoin.php | 4 +- civicrm/CRM/Core/DAO/UFMatch.php | 4 +- civicrm/CRM/Core/DAO/Website.php | 4 +- civicrm/CRM/Core/DAO/WordReplacement.php | 4 +- civicrm/CRM/Core/DAO/Worldregion.php | 4 +- civicrm/CRM/Core/Error.php | 19 +- .../Core/Exception/PrematureExitException.php | 7 + civicrm/CRM/Core/Lock.php | 3 +- civicrm/CRM/Core/OptionGroup.php | 10 +- civicrm/CRM/Core/Payment/AuthorizeNet.php | 1 - civicrm/CRM/Core/Payment/BaseIPN.php | 14 +- civicrm/CRM/Core/Payment/Dummy.php | 1 - civicrm/CRM/Core/Payment/PayJunction.php | 1 - civicrm/CRM/Core/Payment/PayPalImpl.php | 9 +- civicrm/CRM/Core/Payment/Realex.php | 1 - civicrm/CRM/Core/Payment/eWAY.php | 1 - civicrm/CRM/Core/PrevNextCache/Sql.php | 3 +- civicrm/CRM/Core/PseudoConstant.php | 2 +- civicrm/CRM/Core/Region.php | 49 - civicrm/CRM/Core/Report/Excel.php | 6 +- civicrm/CRM/Custom/Import/Parser.php | 2 +- civicrm/CRM/Cxn/DAO/Cxn.php | 4 +- civicrm/CRM/Dedupe/BAO/Rule.php | 2 +- civicrm/CRM/Dedupe/DAO/Exception.php | 4 +- civicrm/CRM/Dedupe/DAO/Rule.php | 4 +- civicrm/CRM/Dedupe/DAO/RuleGroup.php | 4 +- civicrm/CRM/Dedupe/MergeHandler.php | 61 + civicrm/CRM/Dedupe/Merger.php | 72 +- civicrm/CRM/Event/BAO/Event.php | 2 + civicrm/CRM/Event/BAO/Participant.php | 8 +- civicrm/CRM/Event/Cart/DAO/Cart.php | 4 +- civicrm/CRM/Event/Cart/DAO/EventInCart.php | 4 +- civicrm/CRM/Event/DAO/Event.php | 5 +- civicrm/CRM/Event/DAO/Participant.php | 4 +- civicrm/CRM/Event/DAO/ParticipantPayment.php | 4 +- .../CRM/Event/DAO/ParticipantStatusType.php | 4 +- civicrm/CRM/Event/Form/ManageEvent/Fee.php | 8 +- civicrm/CRM/Event/Form/Registration.php | 60 +- .../CRM/Event/Form/Registration/Register.php | 7 +- civicrm/CRM/Event/Form/Task/Batch.php | 41 +- civicrm/CRM/Event/Import/Form/MapField.php | 4 +- civicrm/CRM/Event/Import/Form/Preview.php | 4 +- civicrm/CRM/Event/Import/Parser.php | 12 +- civicrm/CRM/Export/BAO/ExportProcessor.php | 16 +- civicrm/CRM/Financial/BAO/Payment.php | 12 +- civicrm/CRM/Financial/DAO/Currency.php | 4 +- .../Financial/DAO/EntityFinancialAccount.php | 4 +- .../CRM/Financial/DAO/EntityFinancialTrxn.php | 4 +- .../CRM/Financial/DAO/FinancialAccount.php | 4 +- civicrm/CRM/Financial/DAO/FinancialItem.php | 4 +- civicrm/CRM/Financial/DAO/FinancialTrxn.php | 4 +- civicrm/CRM/Financial/DAO/FinancialType.php | 4 +- .../CRM/Financial/DAO/PaymentProcessor.php | 4 +- .../Financial/DAO/PaymentProcessorType.php | 4 +- civicrm/CRM/Financial/DAO/PaymentToken.php | 4 +- civicrm/CRM/Friend/DAO/Friend.php | 4 +- civicrm/CRM/Grant/BAO/Query.php | 4 +- civicrm/CRM/Grant/DAO/Grant.php | 4 +- civicrm/CRM/Group/Form/Edit.php | 3 +- civicrm/CRM/Logging/Differ.php | 3 +- civicrm/CRM/Logging/ReportDetail.php | 3 +- civicrm/CRM/Logging/Reverter.php | 3 +- civicrm/CRM/Logging/Schema.php | 6 +- civicrm/CRM/Mailing/DAO/BouncePattern.php | 4 +- civicrm/CRM/Mailing/DAO/BounceType.php | 4 +- civicrm/CRM/Mailing/DAO/Mailing.php | 4 +- civicrm/CRM/Mailing/DAO/MailingAB.php | 4 +- civicrm/CRM/Mailing/DAO/MailingComponent.php | 4 +- civicrm/CRM/Mailing/DAO/MailingGroup.php | 4 +- civicrm/CRM/Mailing/DAO/MailingJob.php | 4 +- civicrm/CRM/Mailing/DAO/Recipients.php | 4 +- civicrm/CRM/Mailing/DAO/Spool.php | 4 +- civicrm/CRM/Mailing/DAO/TrackableURL.php | 4 +- civicrm/CRM/Mailing/Event/DAO/Bounce.php | 4 +- civicrm/CRM/Mailing/Event/DAO/Confirm.php | 4 +- civicrm/CRM/Mailing/Event/DAO/Delivered.php | 4 +- civicrm/CRM/Mailing/Event/DAO/Forward.php | 4 +- civicrm/CRM/Mailing/Event/DAO/Opened.php | 4 +- civicrm/CRM/Mailing/Event/DAO/Queue.php | 4 +- civicrm/CRM/Mailing/Event/DAO/Reply.php | 4 +- civicrm/CRM/Mailing/Event/DAO/Subscribe.php | 4 +- .../Mailing/Event/DAO/TrackableURLOpen.php | 4 +- civicrm/CRM/Mailing/Event/DAO/Unsubscribe.php | 4 +- civicrm/CRM/Mailing/Page/Browse.php | 5 - civicrm/CRM/Member/BAO/Membership.php | 51 +- civicrm/CRM/Member/DAO/Membership.php | 4 +- civicrm/CRM/Member/DAO/MembershipBlock.php | 4 +- civicrm/CRM/Member/DAO/MembershipLog.php | 4 +- civicrm/CRM/Member/DAO/MembershipPayment.php | 4 +- civicrm/CRM/Member/DAO/MembershipStatus.php | 4 +- civicrm/CRM/Member/DAO/MembershipType.php | 4 +- civicrm/CRM/Member/Form.php | 6 +- civicrm/CRM/Member/Form/Membership.php | 2 +- civicrm/CRM/Member/Form/MembershipRenewal.php | 14 +- civicrm/CRM/Member/Import/Form/MapField.php | 4 +- civicrm/CRM/Member/Import/Form/Preview.php | 4 +- civicrm/CRM/Member/Import/Parser.php | 14 +- civicrm/CRM/PCP/DAO/PCP.php | 4 +- civicrm/CRM/PCP/DAO/PCPBlock.php | 4 +- civicrm/CRM/Pledge/BAO/Query.php | 4 +- civicrm/CRM/Pledge/DAO/Pledge.php | 4 +- civicrm/CRM/Pledge/DAO/PledgeBlock.php | 4 +- civicrm/CRM/Pledge/DAO/PledgePayment.php | 4 +- civicrm/CRM/Price/BAO/LineItem.php | 39 +- civicrm/CRM/Price/BAO/PriceField.php | 10 +- civicrm/CRM/Price/DAO/LineItem.php | 4 +- civicrm/CRM/Price/DAO/PriceField.php | 4 +- civicrm/CRM/Price/DAO/PriceFieldValue.php | 4 +- civicrm/CRM/Price/DAO/PriceSet.php | 4 +- civicrm/CRM/Price/DAO/PriceSetEntity.php | 4 +- civicrm/CRM/Queue/DAO/QueueItem.php | 4 +- civicrm/CRM/Report/DAO/ReportInstance.php | 4 +- civicrm/CRM/Report/Form.php | 12 +- .../Report/Form/Contribute/Bookkeeping.php | 69 +- .../CRM/Report/Form/Contribute/History.php | 2 +- .../Report/Form/Event/ParticipantListing.php | 2 +- civicrm/CRM/Report/Form/Mailing/Summary.php | 6 + civicrm/CRM/SMS/DAO/Provider.php | 4 +- civicrm/CRM/SMS/Form/Group.php | 2 +- civicrm/CRM/SMS/Form/Schedule.php | 2 +- civicrm/CRM/UF/Form/AdvanceSetting.php | 2 +- civicrm/CRM/Upgrade/Form.php | 13 - .../Upgrade/Incremental/MessageTemplates.php | 18 +- .../Upgrade/Incremental/php/FiveThirty.php | 114 ++ .../CRM/Upgrade/Incremental/php/FourFive.php | 3 +- .../Upgrade/Incremental/sql/5.29.0.mysql.tpl | 1 - .../Upgrade/Incremental/sql/5.30.0.mysql.tpl | 1 + .../Incremental/sql/5.30.alpha1.mysql.tpl | 4 + .../Incremental/sql/5.30.beta1.mysql.tpl | 1 + civicrm/CRM/Upgrade/Page/Upgrade.php | 46 +- civicrm/CRM/Utils/Array.php | 20 +- civicrm/CRM/Utils/Check/Component.php | 12 +- civicrm/CRM/Utils/Check/Component/Env.php | 16 +- civicrm/CRM/Utils/Check/Message.php | 25 +- civicrm/CRM/Utils/Date.php | 2 +- civicrm/CRM/Utils/File.php | 1 + civicrm/CRM/Utils/Mail/EmailProcessor.php | 8 +- civicrm/CRM/Utils/SQL.php | 35 + civicrm/CRM/Utils/SQL/TempTable.php | 16 +- civicrm/CRM/Utils/SameSite.php | 224 ++++ civicrm/CRM/Utils/System.php | 16 +- civicrm/CRM/Utils/System/Backdrop.php | 5 +- civicrm/CRM/Utils/System/Base.php | 6 + civicrm/CRM/Utils/System/Drupal.php | 5 +- civicrm/CRM/Utils/System/Drupal6.php | 5 +- civicrm/CRM/Utils/System/WordPress.php | 161 ++- civicrm/CRM/Utils/Token.php | 4 +- civicrm/CRM/Utils/VersionCheck.php | 9 +- .../Civi/Api4/Generic/AbstractQueryAction.php | 6 +- .../Api4/Generic/BasicGetFieldsAction.php | 9 + civicrm/Civi/Api4/Generic/DAOGetAction.php | 10 +- .../Generic/Traits/ArrayQueryActionTrait.php | 9 + .../Generic/Traits/CustomValueActionTrait.php | 21 +- civicrm/Civi/Api4/Query/Api4SelectQuery.php | 36 +- civicrm/Civi/Api4/Service/Spec/FieldSpec.php | 23 + .../Civi/Api4/Service/Spec/SpecFormatter.php | 1 + civicrm/Civi/Api4/Utils/CoreUtil.php | 9 + civicrm/Civi/Install/Requirements.php | 19 +- civicrm/Civi/Test.php | 3 +- civicrm/ang/api4Explorer/Explorer.js | 64 +- civicrm/ang/crmMailingAB/services.js | 1 - civicrm/api/class.api.php | 24 +- civicrm/api/v3/Contribution.php | 22 +- civicrm/api/v3/LineItem.php | 24 +- civicrm/api/v3/MailingEventSubscribe.php | 4 +- civicrm/api/v3/MessageTemplate.php | 5 + civicrm/api/v3/Order.php | 8 +- civicrm/api/v3/System.php | 18 +- civicrm/api/v3/utils.php | 33 +- civicrm/bin/regen.sh | 2 +- civicrm/civicrm-version.php | 2 +- civicrm/composer.json | 6 +- civicrm/composer.lock | 62 +- .../CRM/Event/Cart/StateMachine/Checkout.php | 9 +- civicrm/ext/financialacls/LICENSE.txt | 667 ++++++++++ civicrm/ext/financialacls/README.md | 44 + .../ext/financialacls/financialacls.civix.php | 477 +++++++ civicrm/ext/financialacls/financialacls.php | 218 ++++ .../ext/financialacls/images/screenshot.png | Bin 0 -> 11775 bytes civicrm/ext/financialacls/info.xml | 33 + civicrm/ext/financialacls/phpunit.xml.dist | 18 + .../tests/phpunit/LineItemTest.php | 114 ++ .../financialacls/tests/phpunit/bootstrap.php | 63 + civicrm/ext/search/CRM/Search/Page/Ang.php | 197 +++ civicrm/ext/search/CRM/Search/Upgrader.php | 34 + .../ext/search/CRM/Search/Upgrader/Base.php | 391 ++++++ civicrm/ext/search/README.md | 7 + civicrm/ext/search/ang/search.ang.php | 17 + civicrm/ext/search/ang/search.module.js | 95 ++ .../search/ang/search/SaveSmartGroup.ctrl.js | 54 + .../search/ang/search/crmSearch.component.js | 580 +++++++++ civicrm/ext/search/ang/search/crmSearch.html | 20 + .../search/ang/search/crmSearch/controls.html | 25 + .../search/ang/search/crmSearch/criteria.html | 48 + .../search/ang/search/crmSearch/debug.html | 10 + .../search/ang/search/crmSearch/pager.html | 35 + .../search/ang/search/crmSearch/results.html | 32 + .../ang/search/crmSearchActions.component.js | 62 + .../search/ang/search/crmSearchActions.html | 10 + .../crmSearchActionDelete.ctrl.js | 24 + .../crmSearchActionDelete.html | 10 + .../crmSearchActionUpdate.ctrl.js | 63 + .../crmSearchActionUpdate.html | 16 + .../ang/search/crmSearchClause.directive.js | 75 ++ .../search/ang/search/crmSearchClause.html | 41 + .../ang/search/crmSearchFunction.component.js | 28 + .../search/ang/search/crmSearchFunction.html | 4 + .../ang/search/crmSearchValue.directive.js | 115 ++ .../ext/search/ang/search/saveSmartGroup.html | 26 + civicrm/ext/search/css/search.css | 153 +++ civicrm/ext/search/info.xml | 30 + civicrm/ext/search/search.civix.php | 477 +++++++ civicrm/ext/search/search.php | 128 ++ .../search/templates/CRM/Search/Page/Ang.tpl | 0 civicrm/ext/search/xml/Menu/search.xml | 8 + civicrm/install/civicrm.php | 2 - civicrm/js/model/crm.uf.js | 4 +- civicrm/packages/DB/DataObject.php | 4 +- civicrm/packages/VERSIONS.php | 1 - civicrm/release-notes.md | 11 + civicrm/release-notes/5.30.0.md | 579 +++++++++ civicrm/settings/Mailing.setting.php | 2 +- .../CheckDbWellFormed.civi-setup.php | 19 +- .../CoreRequirementsAdapter.civi-setup.php | 1 + .../FlushDrupal8.civi-setup.php | 4 +- .../InstallSettingsFile.civi-setup.php | 7 + civicrm/setup/src/Setup/DbUtil.php | 51 +- civicrm/sql/civicrm.mysql | 2 +- civicrm/sql/civicrm_data.mysql | 109 +- civicrm/sql/civicrm_generated.mysql | 4 +- .../templates/CRM/Admin/Form/Setting/Mail.hlp | 14 + .../CRM/Case/Form/Activity/OpenCase.tpl | 10 +- civicrm/templates/CRM/Case/Form/CaseView.tpl | 4 +- .../CRM/Contribute/Form/Contribution/Main.tpl | 3 + .../CRM/Contribute/Form/Search/Common.tpl | 2 +- .../Event/Form/ManageEvent/Registration.hlp | 2 +- .../Event/Form/ParticipantFeeSelection.tpl | 4 +- .../CRM/Event/Form/Registration/Register.tpl | 3 + .../templates/CRM/Mailing/Form/Settings.hlp | 28 - civicrm/templates/CRM/Pledge/Form/Pledge.tpl | 8 +- civicrm/templates/CRM/UF/Form/Group.hlp | 2 +- .../CRM/common/civicrm.settings.php.template | 7 +- civicrm/vendor/autoload.php | 2 +- .../vendor/composer/autoload_namespaces.php | 1 + civicrm/vendor/composer/autoload_real.php | 14 +- civicrm/vendor/composer/autoload_static.php | 16 +- civicrm/vendor/composer/include_paths.php | 3 +- civicrm/vendor/composer/installed.json | 62 + civicrm/{packages => vendor/pear/db}/DB.php | 20 +- .../pear/db}/DB/common.php | 11 +- .../{packages => vendor/pear/db}/DB/dbase.php | 4 +- civicrm/vendor/pear/db/DB/fbsql.php | 769 +++++++++++ civicrm/vendor/pear/db/DB/ibase.php | 1082 +++++++++++++++ civicrm/vendor/pear/db/DB/ifx.php | 683 ++++++++++ civicrm/vendor/pear/db/DB/msql.php | 831 ++++++++++++ .../{packages => vendor/pear/db}/DB/mssql.php | 2 +- .../{packages => vendor/pear/db}/DB/mysql.php | 6 +- .../pear/db}/DB/mysqli.php | 8 +- civicrm/vendor/pear/db/DB/oci8.php | 1155 +++++++++++++++++ .../{packages => vendor/pear/db}/DB/odbc.php | 2 +- .../{packages => vendor/pear/db}/DB/pgsql.php | 10 +- civicrm/vendor/pear/db/DB/sqlite.php | 963 ++++++++++++++ .../pear/db}/DB/storage.php | 4 +- civicrm/vendor/pear/db/DB/sybase.php | 942 ++++++++++++++ civicrm/vendor/pear/db/PATCHES.txt | 7 + civicrm/vendor/pear/db/composer.json | 43 + civicrm/xml/schema/Activity/Activity.xml | 90 +- .../schema/Activity/ActivityAssignment.xml | 115 -- .../xml/schema/Activity/ActivityTarget.xml | 61 - civicrm/xml/schema/Batch/Batch.xml | 9 - civicrm/xml/schema/Batch/EntityBatch.xml | 1 + civicrm/xml/schema/Case/Case.xml | 62 - civicrm/xml/schema/Case/CaseActivity.xml | 27 - civicrm/xml/schema/Contact/Contact.xml | 68 +- civicrm/xml/schema/Contact/Individual.xml | 34 - civicrm/xml/schema/Contact/SavedSearch.xml | 17 - .../xml/schema/Contribute/Contribution.xml | 47 +- .../schema/Contribute/ContributionPage.xml | 15 +- .../schema/Contribute/ContributionProduct.xml | 11 - .../schema/Contribute/ContributionRecur.xml | 24 - .../xml/schema/Contribute/PremiumsProduct.xml | 8 - civicrm/xml/schema/Core/ActionSchedule.xml | 3 + civicrm/xml/schema/Core/Cache.xml | 8 - civicrm/xml/schema/Core/CustomField.xml | 8 - civicrm/xml/schema/Core/CustomGroup.xml | 8 - civicrm/xml/schema/Core/Dashboard.xml | 15 - civicrm/xml/schema/Core/Discount.xml | 20 +- civicrm/xml/schema/Core/Domain.xml | 47 - civicrm/xml/schema/Core/EntityFile.xml | 1 + civicrm/xml/schema/Core/EntityTag.xml | 24 +- civicrm/xml/schema/Core/Job.xml | 9 - civicrm/xml/schema/Core/Mapping.xml | 10 - civicrm/xml/schema/Core/MappingField.xml | 8 - civicrm/xml/schema/Core/Phone.xml | 16 +- civicrm/xml/schema/Core/UFField.xml | 32 - civicrm/xml/schema/Core/UFGroup.xml | 28 - civicrm/xml/schema/Core/UFMatch.xml | 17 - civicrm/xml/schema/Dedupe/RuleGroup.xml | 17 - civicrm/xml/schema/Event/Event.xml | 24 +- .../schema/Financial/EntityFinancialTrxn.xml | 9 - .../xml/schema/Financial/FinancialAccount.xml | 7 - .../xml/schema/Financial/FinancialTrxn.xml | 46 - .../xml/schema/Financial/FinancialType.xml | 10 - .../xml/schema/Financial/PaymentProcessor.xml | 8 - civicrm/xml/schema/Grant/Grant.xml | 9 - civicrm/xml/schema/Member/MembershipType.xml | 22 - civicrm/xml/schema/PCP/PCP.xml | 23 - civicrm/xml/schema/PCP/PCPBlock.xml | 7 - civicrm/xml/schema/Pledge/Pledge.xml | 17 - civicrm/xml/schema/Price/LineItem.xml | 9 - civicrm/xml/schema/Price/PriceField.xml | 8 - civicrm/xml/schema/Price/PriceSet.xml | 17 - civicrm/xml/templates/civicrm_data.tpl | 1 + civicrm/xml/templates/dao.tpl | 7 +- .../contribution_invoice_receipt_html.tpl | 45 +- .../event_online_receipt_html.tpl | 2 +- .../event_online_receipt_text.tpl | 2 +- .../participant_confirm_html.tpl | 2 +- .../participant_confirm_text.tpl | 2 +- civicrm/xml/version.xml | 2 +- wp-rest/Controller/AuthorizeIPN.php | 1 + wp-rest/Controller/Cxn.php | 1 + wp-rest/Controller/Open.php | 1 + wp-rest/Controller/PayPalIPN.php | 1 + wp-rest/Controller/PxIPN.php | 1 + wp-rest/Controller/Soap.php | 1 + wp-rest/Controller/Url.php | 1 + wp-rest/Controller/Widget.php | 1 + 449 files changed, 13979 insertions(+), 2332 deletions(-) create mode 100644 civicrm/CRM/Upgrade/Incremental/php/FiveThirty.php delete mode 100644 civicrm/CRM/Upgrade/Incremental/sql/5.29.0.mysql.tpl create mode 100644 civicrm/CRM/Upgrade/Incremental/sql/5.30.0.mysql.tpl create mode 100644 civicrm/CRM/Upgrade/Incremental/sql/5.30.alpha1.mysql.tpl create mode 100644 civicrm/CRM/Upgrade/Incremental/sql/5.30.beta1.mysql.tpl create mode 100644 civicrm/CRM/Utils/SameSite.php create mode 100644 civicrm/ext/financialacls/LICENSE.txt create mode 100644 civicrm/ext/financialacls/README.md create mode 100644 civicrm/ext/financialacls/financialacls.civix.php create mode 100644 civicrm/ext/financialacls/financialacls.php create mode 100644 civicrm/ext/financialacls/images/screenshot.png create mode 100644 civicrm/ext/financialacls/info.xml create mode 100644 civicrm/ext/financialacls/phpunit.xml.dist create mode 100644 civicrm/ext/financialacls/tests/phpunit/LineItemTest.php create mode 100644 civicrm/ext/financialacls/tests/phpunit/bootstrap.php create mode 100644 civicrm/ext/search/CRM/Search/Page/Ang.php create mode 100644 civicrm/ext/search/CRM/Search/Upgrader.php create mode 100644 civicrm/ext/search/CRM/Search/Upgrader/Base.php create mode 100644 civicrm/ext/search/README.md create mode 100644 civicrm/ext/search/ang/search.ang.php create mode 100644 civicrm/ext/search/ang/search.module.js create mode 100644 civicrm/ext/search/ang/search/SaveSmartGroup.ctrl.js create mode 100644 civicrm/ext/search/ang/search/crmSearch.component.js create mode 100644 civicrm/ext/search/ang/search/crmSearch.html create mode 100644 civicrm/ext/search/ang/search/crmSearch/controls.html create mode 100644 civicrm/ext/search/ang/search/crmSearch/criteria.html create mode 100644 civicrm/ext/search/ang/search/crmSearch/debug.html create mode 100644 civicrm/ext/search/ang/search/crmSearch/pager.html create mode 100644 civicrm/ext/search/ang/search/crmSearch/results.html create mode 100644 civicrm/ext/search/ang/search/crmSearchActions.component.js create mode 100644 civicrm/ext/search/ang/search/crmSearchActions.html create mode 100644 civicrm/ext/search/ang/search/crmSearchActions/crmSearchActionDelete.ctrl.js create mode 100644 civicrm/ext/search/ang/search/crmSearchActions/crmSearchActionDelete.html create mode 100644 civicrm/ext/search/ang/search/crmSearchActions/crmSearchActionUpdate.ctrl.js create mode 100644 civicrm/ext/search/ang/search/crmSearchActions/crmSearchActionUpdate.html create mode 100644 civicrm/ext/search/ang/search/crmSearchClause.directive.js create mode 100644 civicrm/ext/search/ang/search/crmSearchClause.html create mode 100644 civicrm/ext/search/ang/search/crmSearchFunction.component.js create mode 100644 civicrm/ext/search/ang/search/crmSearchFunction.html create mode 100644 civicrm/ext/search/ang/search/crmSearchValue.directive.js create mode 100644 civicrm/ext/search/ang/search/saveSmartGroup.html create mode 100644 civicrm/ext/search/css/search.css create mode 100644 civicrm/ext/search/info.xml create mode 100644 civicrm/ext/search/search.civix.php create mode 100644 civicrm/ext/search/search.php create mode 100644 civicrm/ext/search/templates/CRM/Search/Page/Ang.tpl create mode 100644 civicrm/ext/search/xml/Menu/search.xml create mode 100644 civicrm/release-notes/5.30.0.md create mode 100644 civicrm/templates/CRM/Admin/Form/Setting/Mail.hlp delete mode 100644 civicrm/templates/CRM/Mailing/Form/Settings.hlp rename civicrm/{packages => vendor/pear/db}/DB.php (99%) rename civicrm/{packages => vendor/pear/db}/DB/common.php (99%) rename civicrm/{packages => vendor/pear/db}/DB/dbase.php (99%) create mode 100644 civicrm/vendor/pear/db/DB/fbsql.php create mode 100644 civicrm/vendor/pear/db/DB/ibase.php create mode 100644 civicrm/vendor/pear/db/DB/ifx.php create mode 100644 civicrm/vendor/pear/db/DB/msql.php rename civicrm/{packages => vendor/pear/db}/DB/mssql.php (99%) rename civicrm/{packages => vendor/pear/db}/DB/mysql.php (99%) rename civicrm/{packages => vendor/pear/db}/DB/mysqli.php (99%) create mode 100644 civicrm/vendor/pear/db/DB/oci8.php rename civicrm/{packages => vendor/pear/db}/DB/odbc.php (99%) rename civicrm/{packages => vendor/pear/db}/DB/pgsql.php (99%) create mode 100644 civicrm/vendor/pear/db/DB/sqlite.php rename civicrm/{packages => vendor/pear/db}/DB/storage.php (99%) create mode 100644 civicrm/vendor/pear/db/DB/sybase.php create mode 100644 civicrm/vendor/pear/db/PATCHES.txt create mode 100644 civicrm/vendor/pear/db/composer.json delete mode 100644 civicrm/xml/schema/Activity/ActivityAssignment.xml delete mode 100644 civicrm/xml/schema/Activity/ActivityTarget.xml diff --git a/civicrm.php b/civicrm.php index ec634adb08..5463d699eb 100644 --- a/civicrm.php +++ b/civicrm.php @@ -2,7 +2,7 @@ /* Plugin Name: CiviCRM Description: CiviCRM - Growing and Sustaining Relationships -Version: 5.29.1 +Version: 5.30.0 Requires at least: 4.9 Requires PHP: 7.1 Author: CiviCRM LLC @@ -56,7 +56,7 @@ if ( ! defined( 'ABSPATH' ) ) exit; // Set version here: when it changes, will force JS to reload -define( 'CIVICRM_PLUGIN_VERSION', '5.29.1' ); +define( 'CIVICRM_PLUGIN_VERSION', '5.30.0' ); // Store reference to this file if (!defined('CIVICRM_PLUGIN_FILE')) { @@ -1304,7 +1304,7 @@ class CiviCRM_For_WordPress { ); foreach ($setupPaths as $setupPath) { $loader = implode(DIRECTORY_SEPARATOR, [$civicrmCore, $setupPath, 'civicrm-setup-autoload.php']); - if (file_exists($civicrmCore . DIRECTORY_SEPARATOR . '.use-civicrm-setup') && file_exists($loader)) { + if (file_exists($loader)) { require_once $loader; require_once implode(DIRECTORY_SEPARATOR, [$civicrmCore, 'CRM', 'Core', 'ClassLoader.php']); CRM_Core_ClassLoader::singleton()->register(); @@ -1326,17 +1326,7 @@ class CiviCRM_For_WordPress { } } - // Uses CIVICRM_PLUGIN_DIR instead of WP_PLUGIN_DIR - $installFile = - CIVICRM_PLUGIN_DIR . - 'civicrm' . DIRECTORY_SEPARATOR . - 'install' . DIRECTORY_SEPARATOR . - 'index.php'; - - // Notice: Undefined variable: siteDir in: - // CIVICRM_PLUGIN_DIR/civicrm/install/index.php on line 456 - include ( $installFile ); - + wp_die( __( 'Installer unavailable. Failed to locate CiviCRM libraries.', 'civicrm' ) ); } diff --git a/civicrm/CRM/ACL/DAO/ACL.php b/civicrm/CRM/ACL/DAO/ACL.php index df4002df2b..fa06cf96c5 100644 --- a/civicrm/CRM/ACL/DAO/ACL.php +++ b/civicrm/CRM/ACL/DAO/ACL.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/ACL/ACL.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:d91adfb9ffb0a514d42f6828f1b95033) + * (GenCodeChecksum:f75eaa0ee87675c14a224ec22b2c30a7) */ /** * Database access object for the ACL entity. */ class CRM_ACL_DAO_ACL extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.6'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/ACL/DAO/ACLCache.php b/civicrm/CRM/ACL/DAO/ACLCache.php index 9e7ede4882..7e3ac8429f 100644 --- a/civicrm/CRM/ACL/DAO/ACLCache.php +++ b/civicrm/CRM/ACL/DAO/ACLCache.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/ACL/ACLCache.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:0427e4f02009f7a66cf34ae18ef397aa) + * (GenCodeChecksum:cbf36d56ce734a5f7ceeb2071b68ebf8) */ /** * Database access object for the ACLCache entity. */ class CRM_ACL_DAO_ACLCache extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.6'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/ACL/DAO/EntityRole.php b/civicrm/CRM/ACL/DAO/EntityRole.php index 0afa11cd52..9130d307ae 100644 --- a/civicrm/CRM/ACL/DAO/EntityRole.php +++ b/civicrm/CRM/ACL/DAO/EntityRole.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/ACL/EntityRole.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:2e8133b4c385feaa934b2d14ea40f971) + * (GenCodeChecksum:d985c951ef9a8872008576b41c1f2b9c) */ /** * Database access object for the EntityRole entity. */ class CRM_ACL_DAO_EntityRole extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.6'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Activity/BAO/Activity.php b/civicrm/CRM/Activity/BAO/Activity.php index 3c8a0eb155..40f6fdc906 100644 --- a/civicrm/CRM/Activity/BAO/Activity.php +++ b/civicrm/CRM/Activity/BAO/Activity.php @@ -2164,7 +2164,6 @@ AND cl.modified_id = c.id ], ]; $fields = array_merge($activityFields, $exportableFields); - $fields['activity_type_id']['title'] = ts('Activity Type ID'); $fields['activity_priority_id'] = $fields['priority_id']; if ($name === 'Case') { diff --git a/civicrm/CRM/Activity/BAO/Query.php b/civicrm/CRM/Activity/BAO/Query.php index 20dd823853..d432467e8d 100644 --- a/civicrm/CRM/Activity/BAO/Query.php +++ b/civicrm/CRM/Activity/BAO/Query.php @@ -189,7 +189,7 @@ class CRM_Activity_BAO_Query { // We no longer expect "subject" as a specific criteria (as of CRM-19447), // but we still use activity_subject in Activity.Get API case 'activity_subject': - $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause($fieldSpec['where'], $op, $value, CRM_Utils_Type::typeToString($fieldSpec['type'])); + $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause($fieldSpec['where'], $op, $value, $query->getDataTypeForRealField($name)); $query->_qill[$grouping][] = $query->getQillForField($fieldSpec['is_pseudofield_for'] ?? $fieldSpec['name'], $value, $op, $fieldSpec); break; diff --git a/civicrm/CRM/Activity/DAO/Activity.php b/civicrm/CRM/Activity/DAO/Activity.php index 141c390fa4..03ad751cd6 100644 --- a/civicrm/CRM/Activity/DAO/Activity.php +++ b/civicrm/CRM/Activity/DAO/Activity.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Activity/Activity.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:99193964db864f9ec91b02b2bbc2d8f4) + * (GenCodeChecksum:cbcbcbb6720f015deae4097b01196c9a) */ /** * Database access object for the Activity entity. */ class CRM_Activity_DAO_Activity extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.1'; /** * Static instance to hold the table name. @@ -286,7 +288,7 @@ class CRM_Activity_DAO_Activity extends CRM_Core_DAO { 'activity_type_id' => [ 'name' => 'activity_type_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Activity Type'), + 'title' => ts('Activity Type ID'), 'description' => ts('FK to civicrm_option_value.id, that has to be valid, registered activity type.'), 'required' => TRUE, 'import' => TRUE, @@ -300,6 +302,7 @@ class CRM_Activity_DAO_Activity extends CRM_Core_DAO { 'localizable' => 0, 'html' => [ 'type' => 'Select', + 'label' => ts("Activity Type"), ], 'pseudoconstant' => [ 'optionGroupName' => 'activity_type', @@ -389,7 +392,7 @@ class CRM_Activity_DAO_Activity extends CRM_Core_DAO { 'phone_id' => [ 'name' => 'phone_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Phone (called)'), + 'title' => ts('Phone (called) ID'), 'description' => ts('Phone ID of the number called (optional - used if an existing phone number is selected).'), 'where' => 'civicrm_activity.phone_id', 'table_name' => 'civicrm_activity', @@ -399,6 +402,7 @@ class CRM_Activity_DAO_Activity extends CRM_Core_DAO { 'FKClassName' => 'CRM_Core_DAO_Phone', 'html' => [ 'type' => 'EntityRef', + 'label' => ts("Phone (called)"), ], 'add' => '2.0', ], diff --git a/civicrm/CRM/Activity/DAO/ActivityContact.php b/civicrm/CRM/Activity/DAO/ActivityContact.php index f7bd52c8e5..61676f395b 100644 --- a/civicrm/CRM/Activity/DAO/ActivityContact.php +++ b/civicrm/CRM/Activity/DAO/ActivityContact.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Activity/ActivityContact.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:b458f04c2195968dbc9d49f35f7abb70) + * (GenCodeChecksum:777598f3625dfeaf37a81de282808c60) */ /** * Database access object for the ActivityContact entity. */ class CRM_Activity_DAO_ActivityContact extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '4.4'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Activity/Import/Form/MapField.php b/civicrm/CRM/Activity/Import/Form/MapField.php index 51cb5aa42b..a0688d9cff 100644 --- a/civicrm/CRM/Activity/Import/Form/MapField.php +++ b/civicrm/CRM/Activity/Import/Form/MapField.php @@ -350,7 +350,7 @@ class CRM_Activity_Import_Form_MapField extends CRM_Import_Form_MapField { } $fileName = $this->controller->exportValue('DataSource', 'uploadFile'); - $seperator = $this->controller->exportValue('DataSource', 'fieldSeparator'); + $separator = $this->controller->exportValue('DataSource', 'fieldSeparator'); $skipColumnHeader = $this->controller->exportValue('DataSource', 'skipColumnHeader'); $mapperKeys = []; @@ -431,7 +431,7 @@ class CRM_Activity_Import_Form_MapField extends CRM_Import_Form_MapField { } $parser = new CRM_Activity_Import_Parser_Activity($mapperKeysMain, $mapperLocType, $mapperPhoneType); - $parser->run($fileName, $seperator, $mapper, $skipColumnHeader, + $parser->run($fileName, $separator, $mapper, $skipColumnHeader, CRM_Import_Parser::MODE_PREVIEW ); diff --git a/civicrm/CRM/Activity/Import/Form/Preview.php b/civicrm/CRM/Activity/Import/Form/Preview.php index 539c7162ab..718f30626e 100644 --- a/civicrm/CRM/Activity/Import/Form/Preview.php +++ b/civicrm/CRM/Activity/Import/Form/Preview.php @@ -92,7 +92,7 @@ class CRM_Activity_Import_Form_Preview extends CRM_Import_Form_Preview { */ public function postProcess() { $fileName = $this->controller->exportValue('DataSource', 'uploadFile'); - $seperator = $this->controller->exportValue('DataSource', 'fieldSeparator'); + $separator = $this->controller->exportValue('DataSource', 'fieldSeparator'); $skipColumnHeader = $this->controller->exportValue('DataSource', 'skipColumnHeader'); $invalidRowCount = $this->get('invalidRowCount'); $conflictRowCount = $this->get('conflictRowCount'); @@ -132,7 +132,7 @@ class CRM_Activity_Import_Form_Preview extends CRM_Import_Form_Preview { } $mapperFields[] = implode(' - ', $header); } - $parser->run($fileName, $seperator, + $parser->run($fileName, $separator, $mapperFields, $skipColumnHeader, CRM_Import_Parser::MODE_IMPORT, diff --git a/civicrm/CRM/Activity/Import/Parser.php b/civicrm/CRM/Activity/Import/Parser.php index d74f955ca0..9de96fd44d 100644 --- a/civicrm/CRM/Activity/Import/Parser.php +++ b/civicrm/CRM/Activity/Import/Parser.php @@ -28,7 +28,7 @@ abstract class CRM_Activity_Import_Parser extends CRM_Import_Parser { * Separator being used. * @var string */ - protected $_seperator; + protected $_separator; /** * Total number of lines in file. @@ -45,7 +45,7 @@ abstract class CRM_Activity_Import_Parser extends CRM_Import_Parser { /** * @param string $fileName - * @param string $seperator + * @param string $separator * @param $mapper * @param bool $skipColumnHeader * @param int $mode @@ -58,7 +58,7 @@ abstract class CRM_Activity_Import_Parser extends CRM_Import_Parser { */ public function run( $fileName, - $seperator = ',', + $separator = ',', &$mapper, $skipColumnHeader = FALSE, $mode = self::MODE_PREVIEW, @@ -75,7 +75,7 @@ abstract class CRM_Activity_Import_Parser extends CRM_Import_Parser { $this->_haveColumnHeader = $skipColumnHeader; - $this->_seperator = $seperator; + $this->_separator = $separator; $fd = fopen($fileName, "r"); if (!$fd) { @@ -106,7 +106,7 @@ abstract class CRM_Activity_Import_Parser extends CRM_Import_Parser { while (!feof($fd)) { $this->_lineCount++; - $values = fgetcsv($fd, 8192, $seperator); + $values = fgetcsv($fd, 8192, $separator); if (!$values) { continue; } @@ -327,7 +327,7 @@ abstract class CRM_Activity_Import_Parser extends CRM_Import_Parser { public function set($store, $mode = self::MODE_SUMMARY) { $store->set('fileSize', $this->_fileSize); $store->set('lineCount', $this->_lineCount); - $store->set('seperator', $this->_seperator); + $store->set('separator', $this->_separator); $store->set('fields', $this->getSelectValues()); $store->set('fieldTypes', $this->getSelectTypes()); diff --git a/civicrm/CRM/Admin/Form/Job.php b/civicrm/CRM/Admin/Form/Job.php index 0f69059f7b..65bb09710e 100644 --- a/civicrm/CRM/Admin/Form/Job.php +++ b/civicrm/CRM/Admin/Form/Job.php @@ -97,7 +97,7 @@ class CRM_Admin_Form_Job extends CRM_Admin_Form { $this->add('select', 'run_frequency', ts('Run frequency'), CRM_Core_SelectValues::getJobFrequency()); // CRM-17686 - $this->add('datepicker', 'scheduled_run_date', ts('Scheduled Run Date'), NULL, FALSE, ['minDate' => time()]); + $this->add('datepicker', 'scheduled_run_date', ts('Scheduled Run Date'), NULL, FALSE, ['minDate' => date('Y-m-d')]); $this->add('textarea', 'parameters', ts('Command parameters'), "cols=50 rows=6" diff --git a/civicrm/CRM/Admin/Form/ScheduleReminders.php b/civicrm/CRM/Admin/Form/ScheduleReminders.php index c881ef7793..f9ab1c98a6 100644 --- a/civicrm/CRM/Admin/Form/ScheduleReminders.php +++ b/civicrm/CRM/Admin/Form/ScheduleReminders.php @@ -250,7 +250,7 @@ class CRM_Admin_Form_ScheduleReminders extends CRM_Admin_Form { $this->addEntityRef('recipient_manual_id', ts('Manual Recipients'), ['multiple' => TRUE, 'create' => TRUE]); $this->add('select', 'group_id', ts('Group'), - CRM_Core_PseudoConstant::nestedGroup('Mailing'), FALSE, ['class' => 'crm-select2 huge'] + CRM_Core_PseudoConstant::nestedGroup(), FALSE, ['class' => 'crm-select2 huge'] ); // multilingual only options diff --git a/civicrm/CRM/Admin/Form/Setting/UF.php b/civicrm/CRM/Admin/Form/Setting/UF.php index 62c12870f9..208b2f3cf1 100644 --- a/civicrm/CRM/Admin/Form/Setting/UF.php +++ b/civicrm/CRM/Admin/Form/Setting/UF.php @@ -61,7 +61,8 @@ class CRM_Admin_Form_Setting_UF extends CRM_Admin_Form_Setting { $config->dsn != $config->userFrameworkDSN || !empty($drupal_prefix) ) ) { - $dsnArray = DB::parseDSN($config->dsn); + $dsn = CRM_Utils_SQL::autoSwitchDSN($config->dsn); + $dsnArray = DB::parseDSN($dsn); $tableNames = CRM_Core_DAO::getTableNames(); asort($tableNames); $tablePrefixes = '$databases[\'default\'][\'default\'][\'prefix\']= array('; diff --git a/civicrm/CRM/Api4/Page/Api4Explorer.php b/civicrm/CRM/Api4/Page/Api4Explorer.php index 3daba07338..6de079bb8f 100644 --- a/civicrm/CRM/Api4/Page/Api4Explorer.php +++ b/civicrm/CRM/Api4/Page/Api4Explorer.php @@ -11,6 +11,7 @@ */ use Civi\Api4\Service\Schema\Joinable\Joinable; +use Civi\Api4\Utils\CoreUtil; /** * @@ -30,7 +31,7 @@ class CRM_Api4_Page_Api4Explorer extends CRM_Core_Page { }); } $vars = [ - 'operators' => \CRM_Core_DAO::acceptedSQLOperators(), + 'operators' => CoreUtil::getOperators(), 'basePath' => Civi::resources()->getUrl('civicrm'), 'schema' => (array) \Civi\Api4\Entity::get()->setChain(['fields' => ['$name', 'getFields']])->execute(), 'links' => $entityLinks, diff --git a/civicrm/CRM/Batch/DAO/Batch.php b/civicrm/CRM/Batch/DAO/Batch.php index 995e7cd961..f2571b674f 100644 --- a/civicrm/CRM/Batch/DAO/Batch.php +++ b/civicrm/CRM/Batch/DAO/Batch.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Batch/Batch.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:062be2c63f724430f3a380c015fd6a02) + * (GenCodeChecksum:8169fc2f338afc4a163214c0018030be) */ /** * Database access object for the Batch entity. */ class CRM_Batch_DAO_Batch extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '3.3'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Batch/DAO/EntityBatch.php b/civicrm/CRM/Batch/DAO/EntityBatch.php index 3bffdf0ada..6c697fb370 100644 --- a/civicrm/CRM/Batch/DAO/EntityBatch.php +++ b/civicrm/CRM/Batch/DAO/EntityBatch.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Batch/EntityBatch.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:645843a03d3f8e9194ddd2bd79abf5c7) + * (GenCodeChecksum:6b6bd1337d9011c2a262de0e62c1e8e1) */ /** * Database access object for the EntityBatch entity. */ class CRM_Batch_DAO_EntityBatch extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '3.3'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Campaign/DAO/Campaign.php b/civicrm/CRM/Campaign/DAO/Campaign.php index 44b832de21..742996a6bd 100644 --- a/civicrm/CRM/Campaign/DAO/Campaign.php +++ b/civicrm/CRM/Campaign/DAO/Campaign.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Campaign/Campaign.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:6b53e3fb452807fce31fc81bb2b09b38) + * (GenCodeChecksum:cfa77579eb9b91b31b6c5618b52c6e87) */ /** * Database access object for the Campaign entity. */ class CRM_Campaign_DAO_Campaign extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '3.3'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Campaign/DAO/CampaignGroup.php b/civicrm/CRM/Campaign/DAO/CampaignGroup.php index 4dbb8b7f2d..97d4abd5af 100644 --- a/civicrm/CRM/Campaign/DAO/CampaignGroup.php +++ b/civicrm/CRM/Campaign/DAO/CampaignGroup.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Campaign/CampaignGroup.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:a4fa6a19ef72119a316417521e209cd4) + * (GenCodeChecksum:74c02a4708ef706860d023c1635b98c4) */ /** * Database access object for the CampaignGroup entity. */ class CRM_Campaign_DAO_CampaignGroup extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '3.3'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Campaign/DAO/Survey.php b/civicrm/CRM/Campaign/DAO/Survey.php index 62493531bf..9c9626b6be 100644 --- a/civicrm/CRM/Campaign/DAO/Survey.php +++ b/civicrm/CRM/Campaign/DAO/Survey.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Campaign/Survey.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:80b92f90300f8e432ddd68e95d22b622) + * (GenCodeChecksum:e955546c8081852591bc08b1fdee4213) */ /** * Database access object for the Survey entity. */ class CRM_Campaign_DAO_Survey extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '3.2'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Campaign/Form/Campaign.php b/civicrm/CRM/Campaign/Form/Campaign.php index 509b1278f4..b47cee77e7 100644 --- a/civicrm/CRM/Campaign/Form/Campaign.php +++ b/civicrm/CRM/Campaign/Form/Campaign.php @@ -222,7 +222,7 @@ class CRM_Campaign_Form_Campaign extends CRM_Core_Form { $this->add('wysiwyg', 'goal_general', ts('Campaign Goals'), ['rows' => 2, 'cols' => 40]); $this->add('text', 'goal_revenue', ts('Revenue Goal'), ['size' => 8, 'maxlength' => 12]); $this->addRule('goal_revenue', ts('Please enter a valid money value (e.g. %1).', - [1 => CRM_Utils_Money::format('99.99', ' ')] + [1 => CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency(99.99)] ), 'money'); // is this Campaign active diff --git a/civicrm/CRM/Case/DAO/Case.php b/civicrm/CRM/Case/DAO/Case.php index 8f54a68d23..946c992f4b 100644 --- a/civicrm/CRM/Case/DAO/Case.php +++ b/civicrm/CRM/Case/DAO/Case.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Case/Case.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:ec6804103883c17fff63061e06d3e591) + * (GenCodeChecksum:8b18140da75bbf971a143c205f2af1cd) */ /** * Database access object for the Case entity. */ class CRM_Case_DAO_Case extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.8'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Case/DAO/CaseActivity.php b/civicrm/CRM/Case/DAO/CaseActivity.php index a57d2a9bd1..9efe7b4af3 100644 --- a/civicrm/CRM/Case/DAO/CaseActivity.php +++ b/civicrm/CRM/Case/DAO/CaseActivity.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Case/CaseActivity.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:d2ef4c48307f130ba531ff5c4bfb38d6) + * (GenCodeChecksum:974d18e84d3416c98293bedd66c3384c) */ /** * Database access object for the CaseActivity entity. */ class CRM_Case_DAO_CaseActivity extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.8'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Case/DAO/CaseContact.php b/civicrm/CRM/Case/DAO/CaseContact.php index df46165d15..788b1e58f9 100644 --- a/civicrm/CRM/Case/DAO/CaseContact.php +++ b/civicrm/CRM/Case/DAO/CaseContact.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Case/CaseContact.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:4a71399455a593008da4849db726f7b9) + * (GenCodeChecksum:b315f42d7c886c123c9e87c9713c4911) */ /** * Database access object for the CaseContact entity. */ class CRM_Case_DAO_CaseContact extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '2.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Case/DAO/CaseType.php b/civicrm/CRM/Case/DAO/CaseType.php index 039a958244..dbafcb30a8 100644 --- a/civicrm/CRM/Case/DAO/CaseType.php +++ b/civicrm/CRM/Case/DAO/CaseType.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Case/CaseType.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:c65e3b1aa03fb9ad3122915d612fefb8) + * (GenCodeChecksum:cde81a56b0e8201eac521b92ded6fb45) */ /** * Database access object for the CaseType entity. */ class CRM_Case_DAO_CaseType extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '4.5'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Case/Form/CaseView.php b/civicrm/CRM/Case/Form/CaseView.php index 646d48cfd0..7c9724a53c 100644 --- a/civicrm/CRM/Case/Form/CaseView.php +++ b/civicrm/CRM/Case/Form/CaseView.php @@ -272,6 +272,9 @@ class CRM_Case_Form_CaseView extends CRM_Core_Form { ['class' => 'crm-select2 crm-action-menu fa-list-ol'] ); } + // This button is hidden but gets clicked by javascript at + // https://github.com/civicrm/civicrm-core/blob/bd28ecf8121a85bc069cad3ab912a0c3dff8fdc5/templates/CRM/Case/Form/CaseView.js#L194 + // by the onChange handler for the above timeline_id select. $this->addElement('submit', $this->getButtonName('next'), ' ', ['class' => 'hiddenElement']); $this->buildMergeCaseForm(); @@ -517,6 +520,9 @@ class CRM_Case_Form_CaseView extends CRM_Core_Form { FALSE, ['class' => 'crm-select2 huge'] ); + // This button is hidden but gets clicked by javascript at + // https://github.com/civicrm/civicrm-core/blob/bd28ecf8121a85bc069cad3ab912a0c3dff8fdc5/templates/CRM/Case/Form/CaseView.js#L55 + // when the mergeCasesDialog is saved. $this->addElement('submit', $this->getButtonName('next', 'merge_case'), ts('Merge'), diff --git a/civicrm/CRM/Contact/BAO/Contact.php b/civicrm/CRM/Contact/BAO/Contact.php index 6a105253d6..5f42dce4fa 100644 --- a/civicrm/CRM/Contact/BAO/Contact.php +++ b/civicrm/CRM/Contact/BAO/Contact.php @@ -1562,9 +1562,13 @@ WHERE civicrm_contact.id = " . CRM_Utils_Type::escape($id, 'Integer'); ], ]; + $phoneFields = CRM_Core_DAO_Phone::export(); + // This adds phone_type to the exportable fields and make it available for export. + // with testing the same can be done to the other entities. + CRM_Core_DAO::appendPseudoConstantsToFields($phoneFields); $locationFields = array_merge($locationType, CRM_Core_DAO_Address::export(), - CRM_Core_DAO_Phone::export(), + $phoneFields, CRM_Core_DAO_Email::export(), $IMProvider, CRM_Core_DAO_IM::export(TRUE), @@ -1605,7 +1609,6 @@ WHERE civicrm_contact.id = " . CRM_Utils_Type::escape($id, 'Integer'); ); } } - $fields['current_employer_id']['title'] = ts('Current Employer ID'); //fix for CRM-791 if ($export) { $fields = array_merge($fields, [ diff --git a/civicrm/CRM/Contact/BAO/ContactType.php b/civicrm/CRM/Contact/BAO/ContactType.php index 4284efa467..18b62ab86e 100644 --- a/civicrm/CRM/Contact/BAO/ContactType.php +++ b/civicrm/CRM/Contact/BAO/ContactType.php @@ -45,9 +45,11 @@ class CRM_Contact_BAO_ContactType extends CRM_Contact_DAO_ContactType { * @param string $contactType * * @return bool + * + * @throws \API_Exception */ public static function isActive($contactType) { - $contact = self::contactTypeInfo(FALSE); + $contact = self::contactTypeInfo(); return array_key_exists($contactType, $contact); } @@ -113,51 +115,25 @@ class CRM_Contact_BAO_ContactType extends CRM_Contact_DAO_ContactType { /** * Retrieve all subtypes Information. * - * @todo - call getAllContactTypes & return filtered results. - * * @param array $contactType - * .. * @param bool $all - * @param bool $ignoreCache * * @return array - * Array of sub type information + * Array of sub type information, subset of getAllContactTypes. + * + * @throws \API_Exception */ - public static function subTypeInfo($contactType = NULL, $all = FALSE, $ignoreCache = FALSE) { - $argString = $all ? 'CRM_CT_STI_1_' : 'CRM_CT_STI_0_'; - if (!empty($contactType)) { - $contactType = (array) $contactType; - $argString .= implode('_', $contactType); - } - if (!Civi::cache('contactTypes')->has($argString) || $ignoreCache) { - $ctWHERE = ''; - if (!empty($contactType)) { - $ctWHERE = " AND parent.name IN ('" . implode("','", $contactType) . "')"; - } - - $sql = " -SELECT subtype.*, parent.name as parent, parent.label as parent_label -FROM civicrm_contact_type subtype -INNER JOIN civicrm_contact_type parent ON subtype.parent_id = parent.id -WHERE subtype.name IS NOT NULL AND subtype.parent_id IS NOT NULL {$ctWHERE} -"; - if ($all === FALSE) { - $sql .= " AND subtype.is_active = 1 AND parent.is_active = 1 ORDER BY parent.id"; - } - $dao = CRM_Core_DAO::executeQuery($sql, [], - FALSE, 'CRM_Contact_DAO_ContactType' - ); - $values = []; - while ($dao->fetch()) { - $value = []; - CRM_Core_DAO::storeValues($dao, $value); - $value['parent'] = $dao->parent; - $value['parent_label'] = $dao->parent_label; - $values[$dao->name] = $value; + public static function subTypeInfo($contactType = NULL, $all = FALSE) { + $contactTypes = self::getAllContactTypes(); + foreach ($contactTypes as $index => $type) { + if (empty($type['parent']) || + (!$all && !$type['is_active']) + || ($contactType && $type['parent'] !== $contactType) + ) { + unset($contactTypes[$index]); } - Civi::cache('contactTypes')->set($argString, $values); } - return Civi::cache('contactTypes')->get($argString); + return $contactTypes; } /** @@ -173,6 +149,7 @@ WHERE subtype.name IS NOT NULL AND subtype.parent_id IS NOT NULL {$ctWHERE} * @return array * all subtypes OR list of subtypes associated to * a given basic contact type + * @throws \API_Exception */ public static function subTypes($contactType = NULL, $all = FALSE, $columnName = 'name', $ignoreCache = FALSE) { if ($columnName === 'name') { diff --git a/civicrm/CRM/Contact/BAO/Query.php b/civicrm/CRM/Contact/BAO/Query.php index a6b2545d8b..3fe9a97fc2 100644 --- a/civicrm/CRM/Contact/BAO/Query.php +++ b/civicrm/CRM/Contact/BAO/Query.php @@ -1121,6 +1121,9 @@ class CRM_Contact_BAO_Query { } $field = $this->_fields[$elementName] ?? NULL; + if (isset($this->_pseudoConstantsSelect[$field['name']])) { + $this->_pseudoConstantsSelect[$name . '-' . $field['name']] = $this->_pseudoConstantsSelect[$field['name']]; + } // hack for profile, add location id if (!$field) { @@ -2186,7 +2189,7 @@ class CRM_Contact_BAO_Query { $name, $op, $value, $grouping, 'CRM_Contact_DAO_Contact', $field, - $field['title'], + $field['html']['label'] ?? $field['title'], CRM_Utils_Type::typeToString($dataType) ); if ($name === 'gender_id') { @@ -6938,6 +6941,17 @@ AND displayRelType.is_active = 1 return $field; } + /** + * Get the field datatype, using the type in the database rather than the pseudofield, if a pseudofield. + * + * @param string $fieldName + * + * @return string + */ + public function getDataTypeForRealField($fieldName) { + return CRM_Utils_Type::typeToString($this->getMetadataForRealField($fieldName)['type']); + } + /** * If we have a field that is better rendered via the pseudoconstant handled them here. * diff --git a/civicrm/CRM/Contact/DAO/ACLContactCache.php b/civicrm/CRM/Contact/DAO/ACLContactCache.php index b1bf5bdd03..6b319f9636 100644 --- a/civicrm/CRM/Contact/DAO/ACLContactCache.php +++ b/civicrm/CRM/Contact/DAO/ACLContactCache.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Contact/ACLContactCache.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:b99253b18e66856d4b6d914f2669fa30) + * (GenCodeChecksum:97d9be5e13ece64b6c9ad1722d9bca68) */ /** * Database access object for the ACLContactCache entity. */ class CRM_Contact_DAO_ACLContactCache extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '3.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Contact/DAO/Contact.php b/civicrm/CRM/Contact/DAO/Contact.php index 08668b2739..f91dc3264b 100644 --- a/civicrm/CRM/Contact/DAO/Contact.php +++ b/civicrm/CRM/Contact/DAO/Contact.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Contact/Contact.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:da59563e28c0229228f7c2baa20caabf) + * (GenCodeChecksum:f58884560d4f49764182cd97f1bbbcdf) */ /** * Database access object for the Contact entity. */ class CRM_Contact_DAO_Contact extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.1'; /** * Static instance to hold the table name. @@ -1214,7 +1216,7 @@ class CRM_Contact_DAO_Contact extends CRM_Core_DAO { 'gender_id' => [ 'name' => 'gender_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Gender'), + 'title' => ts('Gender ID'), 'description' => ts('FK to gender ID'), 'import' => TRUE, 'where' => 'civicrm_contact.gender_id', @@ -1227,6 +1229,7 @@ class CRM_Contact_DAO_Contact extends CRM_Core_DAO { 'localizable' => 0, 'html' => [ 'type' => 'Select', + 'label' => ts("Gender"), ], 'pseudoconstant' => [ 'optionGroupName' => 'gender', @@ -1399,7 +1402,7 @@ class CRM_Contact_DAO_Contact extends CRM_Core_DAO { 'current_employer_id' => [ 'name' => 'employer_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Current Employer'), + 'title' => ts('Current Employer ID'), 'description' => ts('OPTIONAL FK to civicrm_contact record.'), 'where' => 'civicrm_contact.employer_id', 'export' => TRUE, @@ -1411,6 +1414,7 @@ class CRM_Contact_DAO_Contact extends CRM_Core_DAO { 'FKClassName' => 'CRM_Contact_DAO_Contact', 'html' => [ 'type' => 'EntityRef', + 'label' => ts("Current Employer"), ], 'add' => '2.1', ], diff --git a/civicrm/CRM/Contact/DAO/ContactType.php b/civicrm/CRM/Contact/DAO/ContactType.php index 0237e0943d..41d8b87803 100644 --- a/civicrm/CRM/Contact/DAO/ContactType.php +++ b/civicrm/CRM/Contact/DAO/ContactType.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Contact/ContactType.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:34f2ee23181ddce3050484b6e30b0e72) + * (GenCodeChecksum:0f7546e10f09f7637d50f7a34c632cb5) */ /** * Database access object for the ContactType entity. */ class CRM_Contact_DAO_ContactType extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '3.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Contact/DAO/DashboardContact.php b/civicrm/CRM/Contact/DAO/DashboardContact.php index 360c5caf71..16c0b3b593 100644 --- a/civicrm/CRM/Contact/DAO/DashboardContact.php +++ b/civicrm/CRM/Contact/DAO/DashboardContact.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Contact/DashboardContact.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:c6084fad33e2fed9634eee46cfcfd4ee) + * (GenCodeChecksum:67153c09e74eda2febf15986f9c04439) */ /** * Database access object for the DashboardContact entity. */ class CRM_Contact_DAO_DashboardContact extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '3.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Contact/DAO/Group.php b/civicrm/CRM/Contact/DAO/Group.php index 3c051ec4f4..cdae401986 100644 --- a/civicrm/CRM/Contact/DAO/Group.php +++ b/civicrm/CRM/Contact/DAO/Group.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Contact/Group.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:a8a3812a392b7544441cdbe1f9500f21) + * (GenCodeChecksum:6a2a222c5fa5b461727bb95379723b08) */ /** * Database access object for the Group entity. */ class CRM_Contact_DAO_Group extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Contact/DAO/GroupContact.php b/civicrm/CRM/Contact/DAO/GroupContact.php index 0605b3a09f..52c66fd638 100644 --- a/civicrm/CRM/Contact/DAO/GroupContact.php +++ b/civicrm/CRM/Contact/DAO/GroupContact.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Contact/GroupContact.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:11947c2ac6a20781c2daa15b0f894983) + * (GenCodeChecksum:143ba4d95cae73fc81c8e932970cbc1f) */ /** * Database access object for the GroupContact entity. */ class CRM_Contact_DAO_GroupContact extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Contact/DAO/GroupContactCache.php b/civicrm/CRM/Contact/DAO/GroupContactCache.php index 19c2f738cf..3ee02aa7e2 100644 --- a/civicrm/CRM/Contact/DAO/GroupContactCache.php +++ b/civicrm/CRM/Contact/DAO/GroupContactCache.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Contact/GroupContactCache.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:78446b0939b21d995dd17476d11030e7) + * (GenCodeChecksum:a169b776ec9bfc8864a05750d4ae6b95) */ /** * Database access object for the GroupContactCache entity. */ class CRM_Contact_DAO_GroupContactCache extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '2.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Contact/DAO/GroupNesting.php b/civicrm/CRM/Contact/DAO/GroupNesting.php index 2f6a61b8f5..90624a6e98 100644 --- a/civicrm/CRM/Contact/DAO/GroupNesting.php +++ b/civicrm/CRM/Contact/DAO/GroupNesting.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Contact/GroupNesting.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:7e5dfd763c2f4b39ff7f3ea962269bae) + * (GenCodeChecksum:0ca7da77b0229b439c9c3a4c4c2e4326) */ /** * Database access object for the GroupNesting entity. */ class CRM_Contact_DAO_GroupNesting extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '2.0'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Contact/DAO/GroupOrganization.php b/civicrm/CRM/Contact/DAO/GroupOrganization.php index 49f1bd8616..407ea31d83 100644 --- a/civicrm/CRM/Contact/DAO/GroupOrganization.php +++ b/civicrm/CRM/Contact/DAO/GroupOrganization.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Contact/GroupOrganization.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:59e2d9b6cb5577d3f8a72422fb33f643) + * (GenCodeChecksum:dfe8edf8f786790af95f09f456d1cbe7) */ /** * Database access object for the GroupOrganization entity. */ class CRM_Contact_DAO_GroupOrganization extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '2.0'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Contact/DAO/Relationship.php b/civicrm/CRM/Contact/DAO/Relationship.php index 5110814fe3..14b10a5280 100644 --- a/civicrm/CRM/Contact/DAO/Relationship.php +++ b/civicrm/CRM/Contact/DAO/Relationship.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Contact/Relationship.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:b1e83f9c102db77881c4b9c730f38337) + * (GenCodeChecksum:25faea8225f483ae95cf29af08a8542d) */ /** * Database access object for the Relationship entity. */ class CRM_Contact_DAO_Relationship extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Contact/DAO/RelationshipCache.php b/civicrm/CRM/Contact/DAO/RelationshipCache.php index ddb541bb70..cfa5a75bc2 100644 --- a/civicrm/CRM/Contact/DAO/RelationshipCache.php +++ b/civicrm/CRM/Contact/DAO/RelationshipCache.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Contact/RelationshipCache.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:d511533f30d5d2d8deac82664a6288d1) + * (GenCodeChecksum:b40781c15c3351a766a6083522f0e5e4) */ /** * Database access object for the RelationshipCache entity. */ class CRM_Contact_DAO_RelationshipCache extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '5.29'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Contact/DAO/RelationshipType.php b/civicrm/CRM/Contact/DAO/RelationshipType.php index 06ec7aff10..e703e2fe84 100644 --- a/civicrm/CRM/Contact/DAO/RelationshipType.php +++ b/civicrm/CRM/Contact/DAO/RelationshipType.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Contact/RelationshipType.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:205fa87e1f397fbf810ffbbd74c1853a) + * (GenCodeChecksum:6e9767fcd0fc6eba8fcd408588fe0755) */ /** * Database access object for the RelationshipType entity. */ class CRM_Contact_DAO_RelationshipType extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Contact/DAO/SavedSearch.php b/civicrm/CRM/Contact/DAO/SavedSearch.php index 0d3dcbbedd..a5fb2d4603 100644 --- a/civicrm/CRM/Contact/DAO/SavedSearch.php +++ b/civicrm/CRM/Contact/DAO/SavedSearch.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Contact/SavedSearch.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:4093fd1cfa684e54d609528d8df160ca) + * (GenCodeChecksum:4b2f292a8196a5dc4a73afc078cd11cb) */ /** * Database access object for the SavedSearch entity. */ class CRM_Contact_DAO_SavedSearch extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Contact/DAO/SubscriptionHistory.php b/civicrm/CRM/Contact/DAO/SubscriptionHistory.php index d476dd3ebb..421ea33815 100644 --- a/civicrm/CRM/Contact/DAO/SubscriptionHistory.php +++ b/civicrm/CRM/Contact/DAO/SubscriptionHistory.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Contact/SubscriptionHistory.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:4aa1411720f09f9ad8e2feb2a8d2a833) + * (GenCodeChecksum:7c033b0631f14da30172883b14686574) */ /** * Database access object for the SubscriptionHistory entity. */ class CRM_Contact_DAO_SubscriptionHistory extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Contact/Form/Search/Custom/Proximity.php b/civicrm/CRM/Contact/Form/Search/Custom/Proximity.php index 34026c06de..8d211aafcf 100644 --- a/civicrm/CRM/Contact/Form/Search/Custom/Proximity.php +++ b/civicrm/CRM/Contact/Form/Search/Custom/Proximity.php @@ -19,9 +19,6 @@ */ class CRM_Contact_Form_Search_Custom_Proximity extends CRM_Contact_Form_Search_Custom_Base implements CRM_Contact_Form_Search_Interface { - protected $_latitude = NULL; - protected $_longitude = NULL; - protected $_distance = NULL; protected $_aclFrom = NULL; protected $_aclWhere = NULL; @@ -39,21 +36,6 @@ class CRM_Contact_Form_Search_Custom_Proximity extends CRM_Contact_Form_Search_C unset($this->_formValues['uf_group_id']); unset($this->_formValues['component_mode']); unset($this->_formValues['operator']); - - if (!empty($this->_formValues)) { - // add the country and state - self::addGeocodingData($this->_formValues); - $this->_latitude = $this->_formValues['geo_code_1']; - $this->_longitude = $this->_formValues['geo_code_2']; - - if ($this->_formValues['prox_distance_unit'] == "miles") { - $conversionFactor = 1609.344; - } - else { - $conversionFactor = 1000; - } - $this->_distance = $this->_formValues['distance'] * $conversionFactor; - } $this->_group = $this->_formValues['group'] ?? NULL; $this->_tag = $this->_formValues['tag'] ?? NULL; @@ -192,6 +174,10 @@ class CRM_Contact_Form_Search_Custom_Proximity extends CRM_Contact_Form_Search_C $isCountOnly = TRUE; } + if (empty($this->_formValues['geo_code_1']) || empty($this->_formValues['geo_code_2'])) { + self::addGeocodingData($this->_formValues); + } + $searchParams = [ ['prox_distance_unit', '=', $this->_formValues['prox_distance_unit'], 0, 0], ['prox_distance', '=', $this->_formValues['distance'], 0, 0], diff --git a/civicrm/CRM/Contribute/BAO/Contribution.php b/civicrm/CRM/Contribute/BAO/Contribution.php index e5d83dd6cf..238c4f196d 100644 --- a/civicrm/CRM/Contribute/BAO/Contribution.php +++ b/civicrm/CRM/Contribute/BAO/Contribution.php @@ -847,8 +847,6 @@ class CRM_Contribute_BAO_Contribution extends CRM_Contribute_DAO_Contribution { CRM_Core_BAO_CustomField::getFieldsForImport('Contribution', FALSE, FALSE, FALSE, $checkPermission) ); - $fields['financial_type_id']['title'] = ts('Financial Type ID'); - self::$_exportableFields = $fields; } @@ -2613,7 +2611,7 @@ LEFT JOIN civicrm_contribution contribution ON ( componentPayment.contribution_ * @return bool|array * @throws CiviCRM_API3_Exception */ - protected static function repeatTransaction(&$contribution, &$input, $contributionParams) { + protected static function repeatTransaction(&$contribution, $input, $contributionParams) { if (!empty($contribution->id)) { return FALSE; } @@ -4286,14 +4284,7 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac $params['tax_amount'] = array_sum($taxAmountArray); $params['total_amount'] = $params['total_amount'] + $params['tax_amount']; } - else { - // update line item of contrbution - if (isset($params['financial_type_id']) && array_key_exists($params['financial_type_id'], $taxRates) && $isLineItem) { - $taxRate = $taxRates[$params['financial_type_id']]; - $taxAmount = CRM_Contribute_BAO_Contribution_Utils::calculateTaxAmount($params['line_total'], $taxRate); - $params['tax_amount'] = round($taxAmount['tax_amount'], 2); - } - } + return $params; } @@ -4452,10 +4443,18 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac * @throws \CRM_Core_Exception * @throws \CiviCRM_API3_Exception */ - public static function completeOrder($input, &$ids, $objects, $isPostPaymentCreate = FALSE) { + public static function completeOrder($input, $ids, $objects, $isPostPaymentCreate = FALSE) { $transaction = new CRM_Core_Transaction(); $contribution = $objects['contribution']; - $primaryContributionID = $contribution->id ?? $objects['first_contribution']->id; + // @todo see if we even need this - it's used further down to create an activity + // but the BAO layer should create that - we just need to add a test to cover it & can + // maybe remove $ids altogether. + $contributionContactID = $ids['related_contact']; + $participantID = $ids['participant']; + $recurringContributionID = $ids['contributionRecur']; + + // Unset ids just to make it clear it's not used again. + unset($ids); // The previous details are used when calculating line items so keep it before any code that 'does something' if (!empty($contribution->id)) { $input['prevContribution'] = CRM_Contribute_BAO_Contribution::getValues(['id' => $contribution->id]); @@ -4476,9 +4475,6 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac 'financial_type_id', ]; - $participant = $objects['participant'] ?? NULL; - $recurContrib = $objects['contributionRecur'] ?? NULL; - $recurringContributionID = (empty($recurContrib->id)) ? NULL : $recurContrib->id; $event = $objects['event'] ?? NULL; $paymentProcessorId = ''; @@ -4499,7 +4495,7 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac ], array_intersect_key($input, array_fill_keys($inputContributionWhiteList, 1) )); - $contributionParams['payment_processor'] = $input['payment_processor'] = $paymentProcessorId; + $contributionParams['payment_processor'] = $paymentProcessorId; // If paymentProcessor is not set then the payment_instrument_id would not be correct. // not clear when or if this would occur if you encounter this please fix here & add a unit test. @@ -4524,11 +4520,7 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac } else { if (empty($input['IAmAHorribleNastyBeyondExcusableHackInTheCRMEventFORMTaskClassThatNeedsToBERemoved'])) { - if ($event->is_email_confirm) { - // @todo this should be set by the function that sends the mail after sending. - $contributionParams['receipt_date'] = $changeDate; - } - $participantParams['id'] = $participant->id; + $participantParams['id'] = $participantID; $participantParams['status_id'] = 'Registered'; civicrm_api3('Participant', 'create', $participantParams); } @@ -4542,16 +4534,10 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac } // Add new soft credit against current $contribution. - if (!empty($objects['contributionRecur']) && $objects['contributionRecur']->id) { - CRM_Contribute_BAO_ContributionRecur::addrecurSoftCredit($objects['contributionRecur']->id, $contribution->id); + if ($recurringContributionID) { + CRM_Contribute_BAO_ContributionRecur::addrecurSoftCredit($recurringContributionID, $contribution->id); } - if (empty($contribution->_relatedObjects['participant']) && !empty($contribution->_relatedObjects['membership'])) { - // @fixme Can we remove this if altogether? - we removed the participant if / else and left relatedObjects['participant'] to ensure behaviour didn't change but it is probably not required. - // @todo - use getRelatedMemberships instead - $contribution->trxn_id = $input['trxn_id'] ?? NULL; - $contribution->receive_date = CRM_Utils_Date::isoToMysql($contribution->receive_date); - } $contribution->contribution_status_id = $contributionParams['contribution_status_id']; CRM_Core_Error::debug_log_message('Contribution record updated successfully'); @@ -4566,9 +4552,9 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac if ($input['component'] == 'contribute') { //CRM-4027 $targetContactID = NULL; - if (!empty($ids['related_contact'])) { + if ($contributionContactID) { $targetContactID = $contribution->contact_id; - $contribution->contact_id = $ids['related_contact']; + $contribution->contact_id = $contributionContactID; } CRM_Activity_BAO_Activity::addActivity($contribution, NULL, $targetContactID); } @@ -4714,40 +4700,6 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac return $ids; } - /** - * This function is used to record partial payments for contribution - * - * @param array $contribution - * - * @param array $params - * - * @return CRM_Financial_DAO_FinancialTrxn - */ - public static function recordPartialPayment($contribution, $params) { - CRM_Core_Error::deprecatedFunctionWarning('use payment create api'); - $balanceTrxnParams['to_financial_account_id'] = self::getToFinancialAccount($contribution, $params); - $balanceTrxnParams['from_financial_account_id'] = CRM_Financial_BAO_FinancialAccount::getFinancialAccountForFinancialTypeByRelationship($contribution['financial_type_id'], 'Accounts Receivable Account is'); - $balanceTrxnParams['total_amount'] = $params['total_amount']; - $balanceTrxnParams['contribution_id'] = $params['contribution_id']; - $balanceTrxnParams['trxn_date'] = CRM_Utils_Array::value('trxn_date', $params, CRM_Utils_Array::value('contribution_receive_date', $params, date('YmdHis'))); - $balanceTrxnParams['fee_amount'] = $params['fee_amount'] ?? NULL; - $balanceTrxnParams['net_amount'] = $params['total_amount'] ?? NULL; - $balanceTrxnParams['currency'] = $contribution['currency']; - $balanceTrxnParams['trxn_id'] = $params['contribution_trxn_id'] ?? NULL; - $balanceTrxnParams['status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_FinancialTrxn', 'status_id', 'Completed'); - $balanceTrxnParams['payment_instrument_id'] = CRM_Utils_Array::value('payment_instrument_id', $params, $contribution['payment_instrument_id']); - $balanceTrxnParams['check_number'] = $params['check_number'] ?? NULL; - $balanceTrxnParams['is_payment'] = 1; - - if (!empty($params['payment_processor'])) { - // I can't find evidence this is passed in - I was gonna just remove it but decided to deprecate as I see self::getToFinancialAccount - // also anticipates it. - CRM_Core_Error::deprecatedFunctionWarning('passing payment_processor is deprecated - use payment_processor_id'); - $balanceTrxnParams['payment_processor_id'] = $params['payment_processor']; - } - return CRM_Core_BAO_FinancialTrxn::create($balanceTrxnParams); - } - /** * Get the description (source field) for the recurring contribution. * @@ -4820,7 +4772,7 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac if (empty($item['financial_type_id'])) { $item['financial_type_id'] = $params['financial_type_id']; } - $lineItemAmount += $item['line_total'] + CRM_Utils_Array::value('tax_amount', $item, 0.00); + $lineItemAmount += $item['line_total'] + ($item['tax_amount'] ?? 0.00); } } @@ -4828,11 +4780,7 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac $params['total_amount'] = $lineItemAmount; } else { - $currency = CRM_Utils_Array::value('currency', $params, ''); - - if (empty($currency)) { - $currency = CRM_Core_Config::singleton()->defaultCurrency; - } + $currency = $params['currency'] ?? CRM_Core_Config::singleton()->defaultCurrency; if (!CRM_Utils_Money::equals($totalAmount, $lineItemAmount, $currency)) { throw new CRM_Contribute_Exception_CheckLineItemsException(); diff --git a/civicrm/CRM/Contribute/BAO/Query.php b/civicrm/CRM/Contribute/BAO/Query.php index 2925909aff..f91512a0ea 100644 --- a/civicrm/CRM/Contribute/BAO/Query.php +++ b/civicrm/CRM/Contribute/BAO/Query.php @@ -915,10 +915,10 @@ class CRM_Contribute_BAO_Query extends CRM_Core_BAO_Query { $form->addFormFieldsFromMetadata(); $form->add('text', 'contribution_amount_low', ts('From'), ['size' => 8, 'maxlength' => 8]); - $form->addRule('contribution_amount_low', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::format('9.99', ' ')]), 'money'); + $form->addRule('contribution_amount_low', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency('9.99')]), 'money'); $form->add('text', 'contribution_amount_high', ts('To'), ['size' => 8, 'maxlength' => 8]); - $form->addRule('contribution_amount_high', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::format('99.99', ' ')]), 'money'); + $form->addRule('contribution_amount_high', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency('99.99')]), 'money'); // Adding select option for curreny type -- CRM-4711 $form->add('select', 'contribution_currency_type', diff --git a/civicrm/CRM/Contribute/DAO/Contribution.php b/civicrm/CRM/Contribute/DAO/Contribution.php index e64cf1675e..45f51b756c 100644 --- a/civicrm/CRM/Contribute/DAO/Contribution.php +++ b/civicrm/CRM/Contribute/DAO/Contribution.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Contribute/Contribution.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:5e4c6a77803e361625cafbd2f6e58bbe) + * (GenCodeChecksum:cc3bcdbce84066823084f71e30f6990b) */ /** * Database access object for the Contribution entity. */ class CRM_Contribute_DAO_Contribution extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.3'; /** * Static instance to hold the table name. @@ -325,7 +327,7 @@ class CRM_Contribute_DAO_Contribution extends CRM_Core_DAO { 'financial_type_id' => [ 'name' => 'financial_type_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Financial Type'), + 'title' => ts('Financial Type ID'), 'description' => ts('FK to Financial Type for (total_amount - non_deductible_amount).'), 'where' => 'civicrm_contribution.financial_type_id', 'export' => TRUE, @@ -336,6 +338,7 @@ class CRM_Contribute_DAO_Contribution extends CRM_Core_DAO { 'FKClassName' => 'CRM_Financial_DAO_FinancialType', 'html' => [ 'type' => 'Select', + 'label' => ts("Financial Type"), ], 'pseudoconstant' => [ 'table' => 'civicrm_financial_type', @@ -359,6 +362,7 @@ class CRM_Contribute_DAO_Contribution extends CRM_Core_DAO { 'FKClassName' => 'CRM_Contribute_DAO_ContributionPage', 'html' => [ 'type' => 'Select', + 'label' => ts("Contribution Page"), ], 'pseudoconstant' => [ 'table' => 'civicrm_contribution_page', diff --git a/civicrm/CRM/Contribute/DAO/ContributionPage.php b/civicrm/CRM/Contribute/DAO/ContributionPage.php index 7970a2907f..80ec08d3ac 100644 --- a/civicrm/CRM/Contribute/DAO/ContributionPage.php +++ b/civicrm/CRM/Contribute/DAO/ContributionPage.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Contribute/ContributionPage.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:63bc96be1f5552249e75545940c13bed) + * (GenCodeChecksum:902bfa164280b9ba21a7cb5a38aceba8) */ /** * Database access object for the ContributionPage entity. */ class CRM_Contribute_DAO_ContributionPage extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.3'; /** * Static instance to hold the table name. @@ -467,6 +469,7 @@ class CRM_Contribute_DAO_ContributionPage extends CRM_Core_DAO { 'entity' => 'ContributionPage', 'bao' => 'CRM_Contribute_BAO_ContributionPage', 'localizable' => 0, + 'serialize' => self::SERIALIZE_SEPARATOR_TRIMMED, 'html' => [ 'type' => 'Select', ], diff --git a/civicrm/CRM/Contribute/DAO/ContributionProduct.php b/civicrm/CRM/Contribute/DAO/ContributionProduct.php index cebd77a3fb..e5a0f05370 100644 --- a/civicrm/CRM/Contribute/DAO/ContributionProduct.php +++ b/civicrm/CRM/Contribute/DAO/ContributionProduct.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Contribute/ContributionProduct.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:dd95e7a994c6dfe314f149b5ba7d48db) + * (GenCodeChecksum:4e76d9dc75f5bc1b1141645c8ee5e2e4) */ /** * Database access object for the ContributionProduct entity. */ class CRM_Contribute_DAO_ContributionProduct extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.4'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Contribute/DAO/ContributionRecur.php b/civicrm/CRM/Contribute/DAO/ContributionRecur.php index a3254572ca..2ccd7486d6 100644 --- a/civicrm/CRM/Contribute/DAO/ContributionRecur.php +++ b/civicrm/CRM/Contribute/DAO/ContributionRecur.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Contribute/ContributionRecur.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:62019becf87dee13b7e9e174eb3c286e) + * (GenCodeChecksum:decf43c002d0e4ded0fe5f2a2e2f7bd0) */ /** * Database access object for the ContributionRecur entity. */ class CRM_Contribute_DAO_ContributionRecur extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.6'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Contribute/DAO/ContributionSoft.php b/civicrm/CRM/Contribute/DAO/ContributionSoft.php index ba2c9e21c3..3a1a9b7af7 100644 --- a/civicrm/CRM/Contribute/DAO/ContributionSoft.php +++ b/civicrm/CRM/Contribute/DAO/ContributionSoft.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Contribute/ContributionSoft.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:a1c2918874b74f3f9bbbe0464aea1563) + * (GenCodeChecksum:caa58722ef865c7342fdff08f24d86ee) */ /** * Database access object for the ContributionSoft entity. */ class CRM_Contribute_DAO_ContributionSoft extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '2.2'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Contribute/DAO/Premium.php b/civicrm/CRM/Contribute/DAO/Premium.php index a6ef840350..7e0020c134 100644 --- a/civicrm/CRM/Contribute/DAO/Premium.php +++ b/civicrm/CRM/Contribute/DAO/Premium.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Contribute/Premium.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:df0bfae4e0916c02a621471db14f3151) + * (GenCodeChecksum:cd1826e777cea80450636ef175aaab7f) */ /** * Database access object for the Premium entity. */ class CRM_Contribute_DAO_Premium extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.4'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Contribute/DAO/PremiumsProduct.php b/civicrm/CRM/Contribute/DAO/PremiumsProduct.php index 79d9071c30..b153adb274 100644 --- a/civicrm/CRM/Contribute/DAO/PremiumsProduct.php +++ b/civicrm/CRM/Contribute/DAO/PremiumsProduct.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Contribute/PremiumsProduct.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:a250114a018b2fd5e9d259f00d18a5c3) + * (GenCodeChecksum:4831cb4c7e0611db0f4312f6522d2c20) */ /** * Database access object for the PremiumsProduct entity. */ class CRM_Contribute_DAO_PremiumsProduct extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.4'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Contribute/DAO/Product.php b/civicrm/CRM/Contribute/DAO/Product.php index 5632c65d43..8433e1820a 100644 --- a/civicrm/CRM/Contribute/DAO/Product.php +++ b/civicrm/CRM/Contribute/DAO/Product.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Contribute/Product.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:6e0a8b319985de1fe01120f4d91003a8) + * (GenCodeChecksum:dea1c7db61776456a70f752fe9f93f06) */ /** * Database access object for the Product entity. */ class CRM_Contribute_DAO_Product extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.4'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Contribute/DAO/Widget.php b/civicrm/CRM/Contribute/DAO/Widget.php index 116d447ed5..5da47438da 100644 --- a/civicrm/CRM/Contribute/DAO/Widget.php +++ b/civicrm/CRM/Contribute/DAO/Widget.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Contribute/Widget.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:b0e7180701d83e1d83d53bf2e43c0c51) + * (GenCodeChecksum:e24eaf675b793969d408fbc0f847a9ed) */ /** * Database access object for the Widget entity. */ class CRM_Contribute_DAO_Widget extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '2.0'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Contribute/Form/Contribution/Confirm.php b/civicrm/CRM/Contribute/Form/Contribution/Confirm.php index 228e285dfd..93a210a553 100644 --- a/civicrm/CRM/Contribute/Form/Contribution/Confirm.php +++ b/civicrm/CRM/Contribute/Form/Contribution/Confirm.php @@ -102,7 +102,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr $pledgeParams['create_date'] = $pledgeParams['start_date'] = $pledgeParams['scheduled_date'] = date("Ymd"); if (!empty($params['start_date'])) { $pledgeParams['frequency_day'] = intval(date("d", strtotime($params['start_date']))); - $pledgeParams['start_date'] = $pledgeParams['scheduled_date'] = date('Ymd', strtotime(CRM_Utils_Array::value('start_date', $params))); + $pledgeParams['start_date'] = $pledgeParams['scheduled_date'] = date('Ymd', strtotime($params['start_date'])); } $pledgeParams['status_id'] = $contribution->contribution_status_id; $pledgeParams['max_reminders'] = $form->_values['max_reminders']; @@ -155,19 +155,19 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr $paymentProcessorOutcome, $receiptDate, $recurringContributionID) { $contributionParams = [ 'financial_type_id' => $financialTypeID, - 'receive_date' => (CRM_Utils_Array::value('receive_date', $params)) ? CRM_Utils_Date::processDate($params['receive_date']) : date('YmdHis'), + 'receive_date' => !empty($params['receive_date']) ? CRM_Utils_Date::processDate($params['receive_date']) : date('YmdHis'), 'tax_amount' => $params['tax_amount'] ?? NULL, 'amount_level' => $params['amount_level'] ?? NULL, 'invoice_id' => $params['invoiceID'], 'currency' => $params['currencyID'], - 'is_pay_later' => CRM_Utils_Array::value('is_pay_later', $params, 0), + 'is_pay_later' => $params['is_pay_later'] ?? 0, //configure cancel reason, cancel date and thankyou date //from 'contribution' type profile if included - 'cancel_reason' => CRM_Utils_Array::value('cancel_reason', $params, 0), + 'cancel_reason' => $params['cancel_reason'] ?? 0, 'cancel_date' => isset($params['cancel_date']) ? CRM_Utils_Date::format($params['cancel_date']) : NULL, 'thankyou_date' => isset($params['thankyou_date']) ? CRM_Utils_Date::format($params['thankyou_date']) : NULL, //setting to make available to hook - although seems wrong to set on form for BAO hook availability - 'skipLineItem' => CRM_Utils_Array::value('skipLineItem', $params, 0), + 'skipLineItem' => $params['skipLineItem'] ?? 0, ]; if ($paymentProcessorOutcome) { @@ -301,7 +301,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr $this->_params['is_pay_later'] = $this->get('is_pay_later'); $this->assign('is_pay_later', $this->_params['is_pay_later']); if ($this->_params['is_pay_later']) { - $this->assign('pay_later_receipt', CRM_Utils_Array::value('pay_later_receipt', $this->_values)); + $this->assign('pay_later_receipt', $this->_values['pay_later_receipt'] ?? NULL); } // if onbehalf-of-organization if (!empty($this->_values['onbehalf_profile_id']) && !empty($this->_params['onbehalf']['organization_name'])) { @@ -460,7 +460,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr // assign contribution page id to the template so we can add css class for it $this->assign('contributionPageID', $this->_id); - $this->assign('is_for_organization', CRM_Utils_Array::value('is_for_organization', $this->_params)); + $this->assign('is_for_organization', $this->_params['is_for_organization'] ?? NULL); $this->set('params', $this->_params); } @@ -485,7 +485,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr $fieldTypes[] = CRM_Core_BAO_UFGroup::getContactType($this->_values['honoree_profile_id']); $this->buildCustom($this->_values['honoree_profile_id'], 'honoreeProfileFields', TRUE, 'honor', $fieldTypes); } - $this->assign('receiptFromEmail', CRM_Utils_Array::value('receipt_from_email', $this->_values)); + $this->assign('receiptFromEmail', $this->_values['receipt_from_email'] ?? NULL); $amount_block_is_active = $this->get('amount_block_is_active'); $this->assign('amount_block_is_active', $amount_block_is_active); @@ -738,7 +738,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr $this->assign('product_name', $productDAO->name); $this->assign('price', $productDAO->price); $this->assign('sku', $productDAO->sku); - $this->assign('option', CRM_Utils_Array::value('options_' . $premiumParams['selectProduct'], $premiumParams)); + $this->assign('option', $premiumParams['options_' . $premiumParams['selectProduct']] ?? NULL); $periodType = $productDAO->period_type; @@ -887,7 +887,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr $isEmailReceipt = !empty($form->_values['is_email_receipt']); $isSeparateMembershipPayment = !empty($params['separate_membership_payment']); - $pledgeID = !empty($params['pledge_id']) ? $params['pledge_id'] : CRM_Utils_Array::value('pledge_id', $form->_values); + $pledgeID = !empty($params['pledge_id']) ? $params['pledge_id'] : $form->_values['pledge_id'] ?? NULL; if (!$isSeparateMembershipPayment && !empty($form->_values['pledge_block_id']) && (!empty($params['is_pledge']) || $pledgeID)) { $isPledge = TRUE; @@ -1089,10 +1089,10 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr $recurParams['is_email_receipt'] = $params['is_email_receipt'] ?? NULL; // we need to add a unique trxn_id to avoid a unique key error // in paypal IPN we reset this when paypal sends us the real trxn id, CRM-2991 - $recurParams['trxn_id'] = CRM_Utils_Array::value('trxn_id', $params, $params['invoiceID']); + $recurParams['trxn_id'] = $params['trxn_id'] ?? $params['invoiceID']; $recurParams['financial_type_id'] = $contributionType->id; - $campaignId = CRM_Utils_Array::value('campaign_id', $params, CRM_Utils_Array::value('campaign_id', $form->_values)); + $campaignId = $params['campaign_id'] ?? $form->_values['campaign_id'] ?? NULL; $recurParams['campaign_id'] = $campaignId; $recurring = CRM_Contribute_BAO_ContributionRecur::add($recurParams); if (is_a($recurring, 'CRM_Core_Error')) { @@ -1343,7 +1343,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr $membershipTypes = CRM_Member_BAO_Membership::buildMembershipTypeValues($this, $membershipTypeIDs); $membershipType = empty($membershipTypes) ? [] : reset($membershipTypes); - $this->assign('membership_name', CRM_Utils_Array::value('name', $membershipType)); + $this->assign('membership_name', $membershipType['name']); $this->_values['membership_name'] = $membershipType['name'] ?? NULL; $isPaidMembership = FALSE; @@ -1360,7 +1360,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr $financialTypeID = $this->_values['financial_type_id']; } else { - $financialTypeID = CRM_Utils_Array::value('financial_type_id', $membershipType, CRM_Utils_Array::value('financial_type_id', $membershipParams)); + $financialTypeID = $membershipType['financial_type_id'] ?? $membershipParams['financial_type_id'] ?? NULL; } if (!empty($this->_params['membership_source'])) { @@ -1412,10 +1412,10 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr $isProcessSeparateMembershipTransaction, $financialTypeID, $unprocessedLineItems) { $membershipContribution = NULL; - $isTest = CRM_Utils_Array::value('is_test', $membershipParams, FALSE); + $isTest = $membershipParams['is_test'] ?? FALSE; $errors = $paymentResults = []; $form->_values['isMembership'] = TRUE; - $isRecurForFirstTransaction = CRM_Utils_Array::value('is_recur', $form->_params, CRM_Utils_Array::value('is_recur', $membershipParams)); + $isRecurForFirstTransaction = $form->_params['is_recur'] ?? $membershipParams['is_recur'] ?? NULL; $totalAmount = $membershipParams['amount']; @@ -1465,7 +1465,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr unset($membershipParams['is_recur']); } list($membershipContribution, $secondPaymentResult) = $this->processSecondaryFinancialTransaction($contactID, $form, array_merge($membershipParams, ['skipLineItem' => 1]), - $isTest, $unprocessedLineItems, CRM_Utils_Array::value('minimum_fee', $membershipDetails, 0), CRM_Utils_Array::value('financial_type_id', $membershipDetails)); + $isTest, $unprocessedLineItems, $membershipDetails['minimum_fee'] ?? 0, $membershipDetails['financial_type_id'] ?? NULL); $paymentResults[] = ['contribution_id' => $membershipContribution->id, 'result' => $secondPaymentResult]; $totalAmount = $membershipContribution->total_amount; } @@ -1486,7 +1486,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr } //@todo it should no longer be possible for it to get to this point & membership to not be an array if (is_array($membershipTypeIDs) && !empty($membershipContributionID)) { - $typesTerms = CRM_Utils_Array::value('types_terms', $membershipParams, []); + $typesTerms = $membershipParams['types_terms'] ?? []; $membershipLines = $nonMembershipLines = []; foreach ($unprocessedLineItems as $priceSetID => $lines) { @@ -1509,7 +1509,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr $membershipLineItems = $unprocessedLineItems; } $i++; - $numTerms = CRM_Utils_Array::value($memType, $typesTerms, 1); + $numTerms = $typesTerms[$memType] ?? 1; $contributionRecurID = $form->_params['contributionRecurID'] ?? NULL; $membershipSource = NULL; @@ -1537,11 +1537,21 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr $pending = $membershipContribution->contribution_status_id == CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Pending'); } else { - $pending = $this->getIsPending(); + // The concept of contributeMode is deprecated. + // the is_monetary concept probably should be too as it can be calculated from + // the existence of 'amount' & seems fragile. + if (((isset($this->_contributeMode)) || !empty($this->_params['is_pay_later']) + ) && + (($this->_values['is_monetary'] && $this->_amount > 0.0)) + ) { + $pending = TRUE; + } + $pending = FALSE; } + list($membership, $renewalMode, $dates) = CRM_Member_BAO_Membership::processMembership( $contactID, $memType, $isTest, - date('YmdHis'), CRM_Utils_Array::value('cms_contactID', $membershipParams), + date('YmdHis'), $membershipParams['cms_contactID'] ?? NULL, $customFieldsFormatted, $numTerms, $membershipID, $pending, $contributionRecurID, $membershipSource, $isPayLater, $campaignId, [], $membershipContribution, @@ -1648,7 +1658,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr if (!empty($membershipContribution) && !is_a($membershipContribution, 'CRM_Core_Error')) { if (empty($form->_paymentProcessor)) { // @todo this can maybe go now we are setting payment_processor_id = 0 more reliably. - $paymentProcessorIDs = explode(CRM_Core_DAO::VALUE_SEPARATOR, CRM_Utils_Array::value('payment_processor', $this->_values)); + $paymentProcessorIDs = explode(CRM_Core_DAO::VALUE_SEPARATOR, $this->_values['payment_processor'] ?? NULL); $this->_paymentProcessor['id'] = $paymentProcessorIDs[0]; } $result = ['payment_status_id' => 1, 'contribution' => $membershipContribution]; @@ -1731,10 +1741,9 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr 'contact_id' => $contactID, 'line_item' => $lineItems, 'is_test' => $isTest, - 'campaign_id' => CRM_Utils_Array::value('campaign_id', $tempParams, CRM_Utils_Array::value('campaign_id', - $form->_values)), + 'campaign_id' => $tempParams['campaign_id'] ?? $form->_values['campaign_id'] ?? NULL, 'contribution_page_id' => $form->_id, - 'source' => CRM_Utils_Array::value('source', $tempParams, CRM_Utils_Array::value('description', $tempParams)), + 'source' => $tempParams['source'] ?? $tempParams['description'] ?? NULL, ]; $isMonetary = !empty($form->_values['is_monetary']); if ($isMonetary) { @@ -1787,27 +1796,6 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr return [$membershipContribution, $result]; } - /** - * Is the payment a pending payment. - * - * We are moving towards always creating as pending and updating at the end (based on payment), so this should be - * an interim refactoring. It was shared with another unrelated form & some parameters may not apply to this form. - * - * @return bool - */ - protected function getIsPending() { - // The concept of contributeMode is deprecated. - // the is_monetary concept probably should be too as it can be calculated from - // the existence of 'amount' & seems fragile. - if (((isset($this->_contributeMode)) || !empty($this->_params['is_pay_later']) - ) && - (($this->_values['is_monetary'] && $this->_amount > 0.0)) - ) { - return TRUE; - } - return FALSE; - } - /** * Are we going to do 2 financial transactions. * @@ -1875,7 +1863,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr !empty($this->_membershipBlock['is_separate_payment']) && !empty($this->_values['fee'][$priceField->id]) && ($this->_values['fee'][$priceField->id]['name'] == "other_amount") - && CRM_Utils_Array::value("price_{$paramWeDoNotUnderstand}", $this->_params) < 1 + && ($this->_params["price_{$paramWeDoNotUnderstand}"] ?? NULL) < 1 && empty($this->_params["price_{$priceField->id}"]) ) { $this->_params['amount'] = NULL; @@ -1888,8 +1876,8 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr //@todo - merge with section above if (!empty($this->_membershipBlock['is_separate_payment']) && !empty($this->_values['fee'][$priceField->id]) - && CRM_Utils_Array::value('name', $this->_values['fee'][$priceField->id]) == 'contribution_amount' - && CRM_Utils_Array::value("price_{$priceField->id}", $this->_params) == '-1' + && ($this->_values['fee'][$priceField->id]['name'] ?? NULL) == 'contribution_amount' + && ($this->_params["price_{$priceField->id}"] ?? NULL) == '-1' ) { $this->_params['amount'] = NULL; } @@ -1976,7 +1964,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr } $form->set('memberPriceFieldIDS', $membershipPriceFieldIDs); $form->setRecurringMembershipParams(); - $form->processFormSubmission(CRM_Utils_Array::value('contact_id', $params)); + $form->processFormSubmission($params['contact_id'] ?? NULL); } /** @@ -2208,7 +2196,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr $this->_contactID = $contactID; //get email primary first if exist - $subscriptionEmail = ['email' => CRM_Utils_Array::value('email-Primary', $params)]; + $subscriptionEmail = ['email' => $params['email-Primary'] ?? NULL]; if (!$subscriptionEmail['email']) { $subscriptionEmail['email'] = $params["email-{$this->_bltID}"] ?? NULL; } @@ -2305,7 +2293,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr $contactID, $this->wrangleFinancialTypeID($this->_values['financial_type_id']), ($this->_mode == 'test') ? 1 : 0, - CRM_Utils_Array::value('is_recur', $paymentParams) + $paymentParams['is_recur'] ?? NULL ); if (empty($result['is_payment_failure'])) { @@ -2471,12 +2459,12 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr * @throws \Exception */ protected function completeTransaction($result, $contributionID) { - if (CRM_Utils_Array::value('payment_status_id', $result) == 1) { + if (($result['payment_status_id'] ?? NULL) == 1) { try { civicrm_api3('contribution', 'completetransaction', [ 'id' => $contributionID, 'trxn_id' => $result['trxn_id'] ?? NULL, - 'payment_processor_id' => CRM_Utils_Array::value('payment_processor_id', $result, $this->_paymentProcessor['id']), + 'payment_processor_id' => $result['payment_processor_id'] ?? $this->_paymentProcessor['id'], 'is_transactional' => FALSE, 'fee_amount' => $result['fee_amount'] ?? NULL, 'receive_date' => $result['receive_date'] ?? NULL, diff --git a/civicrm/CRM/Contribute/Form/Contribution/Main.php b/civicrm/CRM/Contribute/Form/Contribution/Main.php index d215092d92..7753b2a135 100644 --- a/civicrm/CRM/Contribute/Form/Contribution/Main.php +++ b/civicrm/CRM/Contribute/Form/Contribution/Main.php @@ -330,8 +330,7 @@ class CRM_Contribute_Form_Contribution_Main extends CRM_Contribute_Form_Contribu } } - $contactID = $this->getContactID(); - if ($this->getContactID() === 0) { + if ($contactID === 0) { $this->addCidZeroOptions(); } diff --git a/civicrm/CRM/Contribute/Form/ContributionPage/Amount.php b/civicrm/CRM/Contribute/Form/ContributionPage/Amount.php index b90b5ea054..16fdf63245 100644 --- a/civicrm/CRM/Contribute/Form/ContributionPage/Amount.php +++ b/civicrm/CRM/Contribute/Form/ContributionPage/Amount.php @@ -48,10 +48,10 @@ class CRM_Contribute_Form_ContributionPage_Amount extends CRM_Contribute_Form_Co // do u want to allow a free form text field for amount $this->addElement('checkbox', 'is_allow_other_amount', ts('Allow other amounts'), NULL, ['onclick' => "minMax(this);showHideAmountBlock( this, 'is_allow_other_amount' );"]); $this->add('text', 'min_amount', ts('Minimum Amount'), ['size' => 8, 'maxlength' => 8]); - $this->addRule('min_amount', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::format('9.99', ' ')]), 'money'); + $this->addRule('min_amount', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency('9.99')]), 'money'); $this->add('text', 'max_amount', ts('Maximum Amount'), ['size' => 8, 'maxlength' => 8]); - $this->addRule('max_amount', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::format('99.99', ' ')]), 'money'); + $this->addRule('max_amount', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency('99.99')]), 'money'); //CRM-12055 $this->add('text', 'amount_label', ts('Contribution Amounts Label')); @@ -67,7 +67,7 @@ class CRM_Contribute_Form_ContributionPage_Amount extends CRM_Contribute_Form_Co // value $this->add('text', "value[$i]", ts('Value'), CRM_Core_DAO::getAttribute('CRM_Core_DAO_OptionValue', 'value')); - $this->addRule("value[$i]", ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::format('99.99', ' ')]), 'money'); + $this->addRule("value[$i]", ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency('99.99')]), 'money'); // default $default[] = $this->createElement('radio', NULL, NULL, NULL, $i); @@ -262,7 +262,7 @@ class CRM_Contribute_Form_ContributionPage_Amount extends CRM_Contribute_Form_Co // CRM-4038: fix value display foreach ($defaults['value'] as & $amount) { - $amount = trim(CRM_Utils_Money::format($amount, ' ')); + $amount = trim(CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency($amount)); } } } @@ -366,7 +366,7 @@ class CRM_Contribute_Form_ContributionPage_Amount extends CRM_Contribute_Form_Co } } else { - if ($fields['amount_block_is_active'] && empty($fields['payment_processor'])) { + if (!empty($fields['amount_block_is_active']) && empty($fields['payment_processor'])) { $errors['payment_processor'] = ts('You have listed fixed contribution options or selected a price set, but no payment option has been selected. Please select at least one payment processor and/or enable the pay later option.'); } } @@ -533,7 +533,7 @@ class CRM_Contribute_Form_ContributionPage_Amount extends CRM_Contribute_Form_Co if (array_key_exists('payment_processor', $params) && !CRM_Utils_System::isNull($params['payment_processor']) ) { - $params['payment_processor'] = implode(CRM_Core_DAO::VALUE_SEPARATOR, array_keys($params['payment_processor'])); + $params['payment_processor'] = array_keys($params['payment_processor']); } else { $params['payment_processor'] = 'null'; diff --git a/civicrm/CRM/Contribute/Form/ContributionPage/Settings.php b/civicrm/CRM/Contribute/Form/ContributionPage/Settings.php index dc96867c0a..07e498ae48 100644 --- a/civicrm/CRM/Contribute/Form/ContributionPage/Settings.php +++ b/civicrm/CRM/Contribute/Form/ContributionPage/Settings.php @@ -162,7 +162,7 @@ class CRM_Contribute_Form_ContributionPage_Settings extends CRM_Contribute_Form_ // collect goal amount $this->add('text', 'goal_amount', ts('Goal Amount'), ['size' => 8, 'maxlength' => 12]); - $this->addRule('goal_amount', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::format('99.99', ' ')]), 'money'); + $this->addRule('goal_amount', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency('99.99')]), 'money'); // is confirmation page enabled? $this->addElement('checkbox', 'is_confirm_enabled', ts('Use a confirmation page?')); diff --git a/civicrm/CRM/Contribute/Form/ContributionPage/Widget.php b/civicrm/CRM/Contribute/Form/ContributionPage/Widget.php index d59c1f6c45..7c6ee1bb99 100644 --- a/civicrm/CRM/Contribute/Form/ContributionPage/Widget.php +++ b/civicrm/CRM/Contribute/Form/ContributionPage/Widget.php @@ -40,7 +40,7 @@ class CRM_Contribute_Form_ContributionPage_Widget extends CRM_Contribute_Form_Co $this->assign('cpageId', $this->_id); - $this->assign('widgetExternUrl', CRM_Utils_System::externUrl('extern/widget', "cpageId={$this->_id}&widgetId={$this->_widget->id}&format=3")); + $this->assign('widgetExternUrl', CRM_Utils_System::externUrl('extern/widget', "cpageId={$this->_id}&widgetId=" . ($this->_widget->id ?? '') . "&format=3")); $config = CRM_Core_Config::singleton(); $title = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_ContributionPage', diff --git a/civicrm/CRM/Contribute/Import/Form/MapField.php b/civicrm/CRM/Contribute/Import/Form/MapField.php index cb886752c7..9a354792cb 100644 --- a/civicrm/CRM/Contribute/Import/Form/MapField.php +++ b/civicrm/CRM/Contribute/Import/Form/MapField.php @@ -424,7 +424,7 @@ class CRM_Contribute_Import_Form_MapField extends CRM_Import_Form_MapField { } $fileName = $this->controller->exportValue('DataSource', 'uploadFile'); - $seperator = $this->controller->exportValue('DataSource', 'fieldSeparator'); + $separator = $this->controller->exportValue('DataSource', 'fieldSeparator'); $skipColumnHeader = $this->controller->exportValue('DataSource', 'skipColumnHeader'); $mapper = $mapperKeys = $mapperKeysMain = $mapperSoftCredit = $softCreditFields = $mapperPhoneType = $mapperSoftCreditType = []; @@ -511,7 +511,7 @@ class CRM_Contribute_Import_Form_MapField extends CRM_Import_Form_MapField { } $parser = new CRM_Contribute_Import_Parser_Contribution($mapperKeysMain, $mapperSoftCredit, $mapperPhoneType); - $parser->run($fileName, $seperator, $mapper, $skipColumnHeader, + $parser->run($fileName, $separator, $mapper, $skipColumnHeader, CRM_Import_Parser::MODE_PREVIEW, $this->get('contactType') ); diff --git a/civicrm/CRM/Contribute/Import/Form/Preview.php b/civicrm/CRM/Contribute/Import/Form/Preview.php index b14ea26c39..e45de43a25 100644 --- a/civicrm/CRM/Contribute/Import/Form/Preview.php +++ b/civicrm/CRM/Contribute/Import/Form/Preview.php @@ -94,7 +94,7 @@ class CRM_Contribute_Import_Form_Preview extends CRM_Import_Form_Preview { */ public function postProcess() { $fileName = $this->controller->exportValue('DataSource', 'uploadFile'); - $seperator = $this->controller->exportValue('DataSource', 'fieldSeparator'); + $separator = $this->controller->exportValue('DataSource', 'fieldSeparator'); $skipColumnHeader = $this->controller->exportValue('DataSource', 'skipColumnHeader'); $invalidRowCount = $this->get('invalidRowCount'); $conflictRowCount = $this->get('conflictRowCount'); @@ -128,7 +128,7 @@ class CRM_Contribute_Import_Form_Preview extends CRM_Import_Form_Preview { } $mapperFields[] = implode(' - ', $header); } - $parser->run($fileName, $seperator, + $parser->run($fileName, $separator, $mapperFields, $skipColumnHeader, CRM_Import_Parser::MODE_IMPORT, diff --git a/civicrm/CRM/Contribute/Import/Parser.php b/civicrm/CRM/Contribute/Import/Parser.php index 87637bb629..77ec4824cb 100644 --- a/civicrm/CRM/Contribute/Import/Parser.php +++ b/civicrm/CRM/Contribute/Import/Parser.php @@ -34,10 +34,10 @@ abstract class CRM_Contribute_Import_Parser extends CRM_Import_Parser { protected $_fileSize; /** - * Seperator being used + * Separator being used * @var string */ - protected $_seperator; + protected $_separator; /** * Total number of lines in file @@ -104,7 +104,7 @@ abstract class CRM_Contribute_Import_Parser extends CRM_Import_Parser { /** * @param string $fileName - * @param string $seperator + * @param string $separator * @param $mapper * @param bool $skipColumnHeader * @param int $mode @@ -118,7 +118,7 @@ abstract class CRM_Contribute_Import_Parser extends CRM_Import_Parser { */ public function run( $fileName, - $seperator = ',', + $separator = ',', &$mapper, $skipColumnHeader = FALSE, $mode = self::MODE_PREVIEW, @@ -149,7 +149,7 @@ abstract class CRM_Contribute_Import_Parser extends CRM_Import_Parser { $this->_haveColumnHeader = $skipColumnHeader; - $this->_seperator = $seperator; + $this->_separator = $separator; $fd = fopen($fileName, "r"); if (!$fd) { @@ -182,7 +182,7 @@ abstract class CRM_Contribute_Import_Parser extends CRM_Import_Parser { while (!feof($fd)) { $this->_lineCount++; - $values = fgetcsv($fd, 8192, $seperator); + $values = fgetcsv($fd, 8192, $separator); if (!$values) { continue; } @@ -506,7 +506,7 @@ abstract class CRM_Contribute_Import_Parser extends CRM_Import_Parser { public function set($store, $mode = self::MODE_SUMMARY) { $store->set('fileSize', $this->_fileSize); $store->set('lineCount', $this->_lineCount); - $store->set('seperator', $this->_seperator); + $store->set('separator', $this->_separator); $store->set('fields', $this->getSelectValues()); $store->set('fieldTypes', $this->getSelectTypes()); diff --git a/civicrm/CRM/Core/BAO/CustomValueTable.php b/civicrm/CRM/Core/BAO/CustomValueTable.php index 8bfc4fb8ad..fa3396a3b3 100644 --- a/civicrm/CRM/Core/BAO/CustomValueTable.php +++ b/civicrm/CRM/Core/BAO/CustomValueTable.php @@ -540,7 +540,7 @@ AND $cond } if (!isset($params['entityID']) || !CRM_Utils_Type::validate($params['entityID'], 'Integer', FALSE)) { - return CRM_Core_Error::createAPIError(ts('entity_id needs to be set and of type Integer')); + throw new CRM_Core_Exception(ts('entity_id needs to be set and of type Integer')); } // first collect all the id/value pairs. The format is: @@ -550,7 +550,7 @@ AND $cond if ($customFieldInfo = CRM_Core_BAO_CustomField::getKeyID($n, TRUE)) { $fieldID = (int ) $customFieldInfo[0]; if (CRM_Utils_Type::escape($fieldID, 'Integer', FALSE) === NULL) { - return CRM_Core_Error::createAPIError(ts('field ID needs to be of type Integer for index %1', + throw new CRM_Core_Exception(ts('field ID needs to be of type Integer for index %1', [1 => $fieldID] )); } @@ -620,7 +620,7 @@ AND cf.id IN ( $fieldIDList ) } // Ensure that value is of the right data type elseif (CRM_Utils_Type::escape($fieldValue['value'], $dataType, FALSE) === NULL) { - return CRM_Core_Error::createAPIError(ts('value: %1 is not of the right field data type: %2', + throw new CRM_Core_Exception(ts('value: %1 is not of the right field data type: %2', [ 1 => $fieldValue['value'], 2 => $dao->data_type, @@ -668,7 +668,7 @@ AND cf.id IN ( $fieldIDList ) return ['is_error' => 0, 'result' => 1]; } - return CRM_Core_Error::createAPIError(ts('Unknown error')); + throw new CRM_Core_Exception(ts('Unknown error')); } /** diff --git a/civicrm/CRM/Core/BAO/MessageTemplate.php b/civicrm/CRM/Core/BAO/MessageTemplate.php index fbbb96709e..4dedb8c66e 100644 --- a/civicrm/CRM/Core/BAO/MessageTemplate.php +++ b/civicrm/CRM/Core/BAO/MessageTemplate.php @@ -402,6 +402,8 @@ class CRM_Core_BAO_MessageTemplate extends CRM_Core_DAO_MessageTemplate { 'isTest' => FALSE, // filename of optional PDF version to add as attachment (do not include path) 'PDFFilename' => NULL, + // Disable Smarty? + 'disableSmarty' => FALSE, ]; $params = array_merge($defaults, $params); @@ -511,14 +513,17 @@ class CRM_Core_BAO_MessageTemplate extends CRM_Core_DAO_MessageTemplate { $contact = $contact[$contactID]; } - $mailContent['subject'] = CRM_Utils_Token::replaceDomainTokens($mailContent['subject'], $domain, FALSE, $tokens['subject'], TRUE); - $mailContent['text'] = CRM_Utils_Token::replaceDomainTokens($mailContent['text'], $domain, FALSE, $tokens['text'], TRUE); - $mailContent['html'] = CRM_Utils_Token::replaceDomainTokens($mailContent['html'], $domain, TRUE, $tokens['html'], TRUE); + // When using Smarty we need to pass the $escapeSmarty parameter. + $escapeSmarty = !$params['disableSmarty']; + + $mailContent['subject'] = CRM_Utils_Token::replaceDomainTokens($mailContent['subject'], $domain, FALSE, $tokens['subject'], $escapeSmarty); + $mailContent['text'] = CRM_Utils_Token::replaceDomainTokens($mailContent['text'], $domain, FALSE, $tokens['text'], $escapeSmarty); + $mailContent['html'] = CRM_Utils_Token::replaceDomainTokens($mailContent['html'], $domain, TRUE, $tokens['html'], $escapeSmarty); if ($contactID) { - $mailContent['subject'] = CRM_Utils_Token::replaceContactTokens($mailContent['subject'], $contact, FALSE, $tokens['subject'], FALSE, TRUE); - $mailContent['text'] = CRM_Utils_Token::replaceContactTokens($mailContent['text'], $contact, FALSE, $tokens['text'], FALSE, TRUE); - $mailContent['html'] = CRM_Utils_Token::replaceContactTokens($mailContent['html'], $contact, FALSE, $tokens['html'], FALSE, TRUE); + $mailContent['subject'] = CRM_Utils_Token::replaceContactTokens($mailContent['subject'], $contact, FALSE, $tokens['subject'], FALSE, $escapeSmarty); + $mailContent['text'] = CRM_Utils_Token::replaceContactTokens($mailContent['text'], $contact, FALSE, $tokens['text'], FALSE, $escapeSmarty); + $mailContent['html'] = CRM_Utils_Token::replaceContactTokens($mailContent['html'], $contact, FALSE, $tokens['html'], FALSE, $escapeSmarty); $contactArray = [$contactID => $contact]; CRM_Utils_Hook::tokenValues($contactArray, @@ -535,20 +540,30 @@ class CRM_Core_BAO_MessageTemplate extends CRM_Core_DAO_MessageTemplate { $mailContent['html'] = CRM_Utils_Token::replaceHookTokens($mailContent['html'], $contact, $categories, TRUE); } - // strip whitespace from ends and turn into a single line - $mailContent['subject'] = "{strip}{$mailContent['subject']}{/strip}"; + // Normally Smarty is run, but it can be disabled using the disableSmarty + // parameter, which may be useful for non-core uses of MessageTemplate.send + // In particular it helps with the mosaicomsgtpl extension. + if (!$params['disableSmarty']) { + // strip whitespace from ends and turn into a single line + $mailContent['subject'] = "{strip}{$mailContent['subject']}{/strip}"; - // parse the three elements with Smarty - $smarty = CRM_Core_Smarty::singleton(); - foreach ($params['tplParams'] as $name => $value) { - $smarty->assign($name, $value); + // parse the three elements with Smarty + $smarty = CRM_Core_Smarty::singleton(); + foreach ($params['tplParams'] as $name => $value) { + $smarty->assign($name, $value); + } + foreach ([ + 'subject', + 'text', + 'html', + ] as $elem) { + $mailContent[$elem] = $smarty->fetch("string:{$mailContent[$elem]}"); + } } - foreach ([ - 'subject', - 'text', - 'html', - ] as $elem) { - $mailContent[$elem] = $smarty->fetch("string:{$mailContent[$elem]}"); + else { + // Since we're not relying on Smarty for this function, we DIY. + // strip whitespace from ends and turn into a single line + $mailContent['subject'] = trim(preg_replace('/[\r\n]+/', ' ', $mailContent['subject'])); } // send the template, honouring the target user’s preferences (if any) diff --git a/civicrm/CRM/Core/BAO/PrevNextCache.php b/civicrm/CRM/Core/BAO/PrevNextCache.php index 4eae51e91a..f84454ae72 100644 --- a/civicrm/CRM/Core/BAO/PrevNextCache.php +++ b/civicrm/CRM/Core/BAO/PrevNextCache.php @@ -121,32 +121,6 @@ WHERE cachekey = %3 AND CRM_Core_DAO::executeQuery($sql, $params); } - /** - * Delete pair from the previous next cache table to remove it from further merge consideration. - * - * The pair may have been flipped, so make sure we delete using both orders - * - * @param int $id1 - * @param int $id2 - * @param string $cacheKey - */ - public static function deletePair($id1, $id2, $cacheKey = NULL) { - $sql = "DELETE FROM civicrm_prevnext_cache WHERE entity_table = 'civicrm_contact'"; - - $pair = "(entity_id1 = %2 AND entity_id2 = %3) OR (entity_id1 = %3 AND entity_id2 = %2)"; - $sql .= " AND ( {$pair} )"; - $params[2] = [$id1, 'Integer']; - $params[3] = [$id2, 'Integer']; - - if (isset($cacheKey)) { - $sql .= " AND cachekey LIKE %4"; - // used % to address any row with conflict-cacheKey e.g "merge Individual_8_0_conflicts" - $params[4] = ["{$cacheKey}%", 'String']; - } - - CRM_Core_DAO::executeQuery($sql, $params); - } - /** * Mark contacts as being in conflict. * diff --git a/civicrm/CRM/Core/BAO/SchemaHandler.php b/civicrm/CRM/Core/BAO/SchemaHandler.php index 5523858d6a..a6c9845a22 100644 --- a/civicrm/CRM/Core/BAO/SchemaHandler.php +++ b/civicrm/CRM/Core/BAO/SchemaHandler.php @@ -592,7 +592,8 @@ MODIFY {$columnName} varchar( $length ) */ public static function checkFKExists($table_name, $constraint_name) { $config = CRM_Core_Config::singleton(); - $dbUf = DB::parseDSN($config->dsn); + $dsn = CRM_Utils_SQL::autoSwitchDSN($config->dsn); + $dbUf = DB::parseDSN($dsn); $query = " SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = %1 @@ -796,30 +797,51 @@ MODIFY {$columnName} varchar( $length ) * * @param bool $revert * Being able to revert if primarily for unit testing. + * @param array $patterns + * Defaults to ['civicrm\_%'] but can be overridden to specify any pattern. eg ['civicrm\_%', 'civi%\_%', 'veda%\_%']. + * @param array $databaseList + * Allows you to specify an alternative database to the configured CiviCRM database. * * @return bool */ - public static function migrateUtf8mb4($revert = FALSE) { + public static function migrateUtf8mb4($revert = FALSE, $patterns = ['civicrm\_%'], $databaseList = NULL) { $newCharSet = $revert ? 'utf8' : 'utf8mb4'; $newCollation = $revert ? 'utf8_unicode_ci' : 'utf8mb4_unicode_ci'; $newBinaryCollation = $revert ? 'utf8_bin' : 'utf8mb4_bin'; $tables = []; $dao = new CRM_Core_DAO(); - $database = $dao->_database; - CRM_Core_DAO::executeQuery("ALTER DATABASE $database CHARACTER SET = $newCharSet COLLATE = $newCollation"); - $dao = CRM_Core_DAO::executeQuery("SHOW TABLE STATUS WHERE Engine = 'InnoDB' AND Name LIKE 'civicrm\_%'"); - while ($dao->fetch()) { - $tables[$dao->Name] = [ - 'Engine' => $dao->Engine, - ]; - } - $dsn = defined('CIVICRM_LOGGING_DSN') ? DB::parseDSN(CIVICRM_LOGGING_DSN) : DB::parseDSN(CIVICRM_DSN); - $logging_database = $dsn['database']; - $dao = CRM_Core_DAO::executeQuery("SHOW TABLE STATUS FROM `$logging_database` WHERE Engine <> 'MyISAM' AND Name LIKE 'log\_civicrm\_%'"); - while ($dao->fetch()) { - $tables["$logging_database.{$dao->Name}"] = [ - 'Engine' => $dao->Engine, - ]; + $databases = $databaseList ?? [$dao->_database]; + + $tableNameLikePatterns = []; + $logTableNameLikePatterns = []; + + foreach ($patterns as $pattern) { + $pattern = CRM_Utils_Type::escape($pattern, 'String'); + $tableNameLikePatterns[] = "Name LIKE '{$pattern}'"; + $logTableNameLikePatterns[] = "Name LIKE 'log\_{$pattern}'"; + } + + foreach ($databases as $database) { + CRM_Core_DAO::executeQuery("ALTER DATABASE $database CHARACTER SET = $newCharSet COLLATE = $newCollation"); + $dao = CRM_Core_DAO::executeQuery("SHOW TABLE STATUS FROM `{$database}` WHERE Engine = 'InnoDB' AND (" . implode(' OR ', $tableNameLikePatterns) . ")"); + while ($dao->fetch()) { + $tables["{$database}.{$dao->Name}"] = [ + 'Engine' => $dao->Engine, + ]; + } + } + // If we specified a list of databases assume the user knows what they are doing. + // If they specify the database they should also specify the pattern. + if (!$databaseList) { + $dsn = defined('CIVICRM_LOGGING_DSN') ? CRM_Utils_SQL::autoSwitchDSN(CIVICRM_LOGGING_DSN) : CRM_Utils_SQL::autoSwitchDSN(CIVICRM_DSN); + $dsn = DB::parseDSN($dsn); + $logging_database = $dsn['database']; + $dao = CRM_Core_DAO::executeQuery("SHOW TABLE STATUS FROM `{$logging_database}` WHERE Engine <> 'MyISAM' AND (" . implode(' OR ', $logTableNameLikePatterns) . ")"); + while ($dao->fetch()) { + $tables["{$logging_database}.{$dao->Name}"] = [ + 'Engine' => $dao->Engine, + ]; + } } foreach ($tables as $table => $param) { $query = "ALTER TABLE $table"; diff --git a/civicrm/CRM/Core/BAO/UFGroup.php b/civicrm/CRM/Core/BAO/UFGroup.php index 34490de330..aca664795e 100644 --- a/civicrm/CRM/Core/BAO/UFGroup.php +++ b/civicrm/CRM/Core/BAO/UFGroup.php @@ -1316,8 +1316,8 @@ class CRM_Core_BAO_UFGroup extends CRM_Core_DAO_UFGroup { 'Multi-Select State/Province', 'Multi-Select Country', ])) { - $valSeperator = CRM_Core_DAO::VALUE_SEPARATOR; - $selectedOptions = explode($valSeperator, $params[$index]); + $valSeparator = CRM_Core_DAO::VALUE_SEPARATOR; + $selectedOptions = explode($valSeparator, $params[$index]); foreach ($selectedOptions as $key => $multiOption) { if ($multiOption) { @@ -2982,7 +2982,7 @@ AND ( entity_id IS NULL OR entity_id <= 0 ) /** * Update the profile type 'group_type' as per profile fields including group types and group subtype values. - * Build and store string like: group_type1,group_type2[VALUE_SEPERATOR]group_type1Type:1:2:3,group_type2Type:1:2 + * Build and store string like: group_type1,group_type2[VALUE_SEPARATOR]group_type1Type:1:2:3,group_type2Type:1:2 * * FIELDS GROUP_TYPE * BirthDate + Email Individual,Contact diff --git a/civicrm/CRM/Core/CodeGen/DAO.php b/civicrm/CRM/Core/CodeGen/DAO.php index 69cc8d0e47..9865d9e5f8 100644 --- a/civicrm/CRM/Core/CodeGen/DAO.php +++ b/civicrm/CRM/Core/CodeGen/DAO.php @@ -26,6 +26,10 @@ class CRM_Core_CodeGen_DAO extends CRM_Core_CodeGen_BaseTask { */ private $tsFunctionName; + private $useHelper = ''; + + private $ext = "'civicrm'"; + /** * CRM_Core_CodeGen_DAO constructor. * @@ -37,6 +41,12 @@ class CRM_Core_CodeGen_DAO extends CRM_Core_CodeGen_BaseTask { parent::__construct($config); $this->name = $name; $this->tsFunctionName = $tsFunctionName; + // If this DAO belongs to an extension, add `use` statement and define EXT constant. + if (strpos($tsFunctionName, '::ts')) { + $this->tsFunctionName = 'E::ts'; + $this->useHelper = 'use \\' . explode('::', $tsFunctionName)[0] . ' as E;'; + $this->ext = 'E::LONG_NAME'; + } } /** @@ -68,16 +78,8 @@ class CRM_Core_CodeGen_DAO extends CRM_Core_CodeGen_BaseTask { return; } - $template = new CRM_Core_CodeGen_Util_Template('php'); - $template->assign('table', $this->tables[$this->name]); - if (empty($this->tables[$this->name]['index'])) { - $template->assign('indicesPhp', var_export([], 1)); - } - else { - $template->assign('indicesPhp', var_export($this->tables[$this->name]['index'], 1)); - } + $template = $this->getTemplate(); $template->assign('genCodeChecksum', $this->getTableChecksum()); - $template->assign('tsFunctionName', $this->tsFunctionName); $template->run('dao.tpl', $this->getAbsFileName()); } @@ -88,21 +90,31 @@ class CRM_Core_CodeGen_DAO extends CRM_Core_CodeGen_BaseTask { */ public function getRaw() { if (!$this->raw) { - $template = new CRM_Core_CodeGen_Util_Template('php'); - $template->assign('table', $this->tables[$this->name]); - if (empty($this->tables[$this->name]['index'])) { - $template->assign('indicesPhp', var_export([], 1)); - } - else { - $template->assign('indicesPhp', var_export($this->tables[$this->name]['index'], 1)); - } + $template = $this->getTemplate(); $template->assign('genCodeChecksum', 'NEW'); - $template->assign('tsFunctionName', $this->tsFunctionName); $this->raw = $template->fetch('dao.tpl'); } return $this->raw; } + /** + * @return CRM_Core_CodeGen_Util_Template + */ + private function getTemplate() { + $template = new CRM_Core_CodeGen_Util_Template('php'); + $template->assign('table', $this->tables[$this->name]); + if (empty($this->tables[$this->name]['index'])) { + $template->assign('indicesPhp', var_export([], 1)); + } + else { + $template->assign('indicesPhp', var_export($this->tables[$this->name]['index'], 1)); + } + $template->assign('tsFunctionName', $this->tsFunctionName); + $template->assign('ext', $this->ext); + $template->assign('useHelper', $this->useHelper); + return $template; + } + /** * Get relative file name. * diff --git a/civicrm/CRM/Core/CodeGen/Specification.php b/civicrm/CRM/Core/CodeGen/Specification.php index 0f4678d74a..4d2be52ad4 100644 --- a/civicrm/CRM/Core/CodeGen/Specification.php +++ b/civicrm/CRM/Core/CodeGen/Specification.php @@ -212,6 +212,7 @@ class CRM_Core_CodeGen_Specification { 'objectName' => $klass, 'title' => $tableXML->title ?? self::nameToTitle($klass), 'icon' => $tableXML->icon ?? NULL, + 'add' => $tableXML->add ?? NULL, 'labelName' => substr($name, 8), 'className' => $this->classNames[$name], 'bao' => ($useBao ? str_replace('DAO', 'BAO', $this->classNames[$name]) : $this->classNames[$name]), diff --git a/civicrm/CRM/Core/CodeGen/Util/Template.php b/civicrm/CRM/Core/CodeGen/Util/Template.php index ee0e09bd4b..5febab786d 100644 --- a/civicrm/CRM/Core/CodeGen/Util/Template.php +++ b/civicrm/CRM/Core/CodeGen/Util/Template.php @@ -78,6 +78,7 @@ class CRM_Core_CodeGen_Util_Template { '=> true,' => '=> TRUE,', '=> false,' => '=> FALSE,', 'static ::' => 'static::', + 'use\\' => 'use \\', ]; $contents = str_replace(array_keys($replacements), array_values($replacements), $contents); $contents = preg_replace('#(\s*)\\/\\*\\*#', "\n\$1/**", $contents); diff --git a/civicrm/CRM/Core/DAO.php b/civicrm/CRM/Core/DAO.php index a1cd6f7498..d62fba1401 100644 --- a/civicrm/CRM/Core/DAO.php +++ b/civicrm/CRM/Core/DAO.php @@ -161,9 +161,10 @@ class CRM_Core_DAO extends DB_DataObject { public static function init($dsn) { Civi::$statics[__CLASS__]['init'] = 1; $options = &PEAR::getStaticProperty('DB_DataObject', 'options'); + $dsn = CRM_Utils_SQL::autoSwitchDSN($dsn); $options['database'] = $dsn; $options['quote_identifiers'] = TRUE; - if (self::isSSLDSN($dsn)) { + if (CRM_Utils_SQL::isSSLDSN($dsn)) { // There are two different options arrays. $other_options = &PEAR::getStaticProperty('DB', 'options'); $other_options['ssl'] = TRUE; @@ -552,7 +553,8 @@ class CRM_Core_DAO extends DB_DataObject { // Exclude fields yet not added by pending upgrades $dbVer = \CRM_Core_BAO_Domain::version(); - if ($fields && version_compare($dbVer, \CRM_Utils_System::version()) < 0) { + $daoExt = defined(static::class . '::EXT') ? constant(static::class . '::EXT') : NULL; + if ($fields && $daoExt === 'civicrm' && version_compare($dbVer, \CRM_Utils_System::version()) < 0) { $fields = array_filter($fields, function($field) use ($dbVer) { $add = $field['add'] ?? '1.0.0'; if (substr_count($add, '.') < 2) { @@ -1148,6 +1150,23 @@ class CRM_Core_DAO extends DB_DataObject { return $result; } + /** + * Checks if this DAO's table ought to exist. + * + * If there are pending DB updates, this function compares the CiviCRM version of the table to the current schema version. + * + * @return bool + * @throws CRM_Core_Exception + */ + public static function tableHasBeenAdded() { + if (CRM_Utils_System::version() === CRM_Core_BAO_Domain::version()) { + return TRUE; + } + $daoExt = defined(static::class . '::EXT') ? constant(static::class . '::EXT') : NULL; + $daoVersion = defined(static::class . '::TABLE_ADDED') ? constant(static::class . '::TABLE_ADDED') : '1.0'; + return !($daoExt === 'civicrm' && version_compare(CRM_Core_BAO_Domain::version(), $daoVersion, '<')); + } + /** * Check if there is a given table in the database. * @@ -1181,15 +1200,12 @@ LIKE %1 /** * @param $version - * + * @deprecated * @return bool */ public function checkVersion($version) { - $query = " -SELECT version -FROM civicrm_domain -"; - $dbVersion = CRM_Core_DAO::singleValueQuery($query); + CRM_Core_Error::deprecatedFunctionWarning('CRM_Core_BAO_Domain::version'); + $dbVersion = CRM_Core_BAO_Domain::version(); return trim($version) == trim($dbVersion); } @@ -1524,6 +1540,7 @@ FROM civicrm_domain } if ($trapException) { + CRM_Core_Error::deprecatedFunctionWarning('calling functions should handle exceptions'); $errorScope = CRM_Core_TemporaryErrorScope::ignoreException(); } @@ -1539,6 +1556,7 @@ FROM civicrm_domain } if (is_a($result, 'DB_Error')) { + CRM_Core_Error::deprecatedFunctionWarning('calling functions should handle exceptions'); return $result; } @@ -2812,7 +2830,7 @@ SELECT contact_id /** * @see http://issues.civicrm.org/jira/browse/CRM-9150 * support for other syntaxes is discussed in ticket but being put off for now - * @return array + * @return string[] */ public static function acceptedSQLOperators() { return [ @@ -3111,21 +3129,4 @@ SELECT contact_id } } - /** - * Does the DSN indicate the connection should use ssl. - * - * @param string $dsn - * - * @return bool - */ - public static function isSSLDSN(string $dsn):bool { - // Note that ssl= below is not an official PEAR::DB option. It doesn't know - // what to do with it. We made it up because it's not required - // to have client-side certificates to use ssl, so here you can specify - // you want that by putting ssl=1 in the DSN string. - // - // Cast to bool in case of error which we interpret as no ssl. - return (bool) preg_match('/[\?&](key|cert|ca|capath|cipher|ssl)=/', $dsn); - } - } diff --git a/civicrm/CRM/Core/DAO/ActionLog.php b/civicrm/CRM/Core/DAO/ActionLog.php index b4399596f4..f725a2558a 100644 --- a/civicrm/CRM/Core/DAO/ActionLog.php +++ b/civicrm/CRM/Core/DAO/ActionLog.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/ActionLog.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:1cc48318ce597a6cac7ff0e2a4518ff5) + * (GenCodeChecksum:2ccef6f7cc6a43d833301e93a2a0d61f) */ /** * Database access object for the ActionLog entity. */ class CRM_Core_DAO_ActionLog extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '3.4'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/ActionMapping.php b/civicrm/CRM/Core/DAO/ActionMapping.php index 95f2c276ab..8a61ce5bbc 100644 --- a/civicrm/CRM/Core/DAO/ActionMapping.php +++ b/civicrm/CRM/Core/DAO/ActionMapping.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/ActionMapping.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:be879c0936430ca0cebf0c8bf1c3dc7a) + * (GenCodeChecksum:037a3f26719a4774957814f28c499e60) */ /** * Database access object for the ActionMapping entity. */ class CRM_Core_DAO_ActionMapping extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '3.4'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/ActionSchedule.php b/civicrm/CRM/Core/DAO/ActionSchedule.php index 6ac1166411..666f773ba8 100644 --- a/civicrm/CRM/Core/DAO/ActionSchedule.php +++ b/civicrm/CRM/Core/DAO/ActionSchedule.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/ActionSchedule.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:4493f5d18b2b452d4a6b9ee4e15a15bf) + * (GenCodeChecksum:d05639de89f460efbb3474dcaf5acd27) */ /** * Database access object for the ActionSchedule entity. */ class CRM_Core_DAO_ActionSchedule extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '3.4'; /** * Static instance to hold the table name. @@ -407,6 +409,7 @@ class CRM_Core_DAO_ActionSchedule extends CRM_Core_DAO { 'entity' => 'ActionSchedule', 'bao' => 'CRM_Core_BAO_ActionSchedule', 'localizable' => 0, + 'serialize' => self::SERIALIZE_SEPARATOR_TRIMMED, 'add' => '3.4', ], 'entity_status' => [ @@ -421,6 +424,7 @@ class CRM_Core_DAO_ActionSchedule extends CRM_Core_DAO { 'entity' => 'ActionSchedule', 'bao' => 'CRM_Core_BAO_ActionSchedule', 'localizable' => 0, + 'serialize' => self::SERIALIZE_SEPARATOR_TRIMMED, 'add' => '3.4', ], 'start_action_offset' => [ @@ -615,6 +619,7 @@ class CRM_Core_DAO_ActionSchedule extends CRM_Core_DAO { 'entity' => 'ActionSchedule', 'bao' => 'CRM_Core_BAO_ActionSchedule', 'localizable' => 0, + 'serialize' => self::SERIALIZE_COMMA, 'add' => '3.4', ], 'recipient_listing' => [ diff --git a/civicrm/CRM/Core/DAO/Address.php b/civicrm/CRM/Core/DAO/Address.php index f74252f6e2..7151847db2 100644 --- a/civicrm/CRM/Core/DAO/Address.php +++ b/civicrm/CRM/Core/DAO/Address.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/Address.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:8dc9868de21595676938b96deb836379) + * (GenCodeChecksum:a6b8f21dd3839de1ce1273e0910f0c8c) */ /** * Database access object for the Address entity. */ class CRM_Core_DAO_Address extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/AddressFormat.php b/civicrm/CRM/Core/DAO/AddressFormat.php index 436733d4bc..762b90b6a1 100644 --- a/civicrm/CRM/Core/DAO/AddressFormat.php +++ b/civicrm/CRM/Core/DAO/AddressFormat.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/AddressFormat.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:bd005804e8f957b74c4eafacea35792d) + * (GenCodeChecksum:1ce11647576d05acfc364969eddfcce4) */ /** * Database access object for the AddressFormat entity. */ class CRM_Core_DAO_AddressFormat extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '3.2'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/Cache.php b/civicrm/CRM/Core/DAO/Cache.php index 71704bd66a..01bc33ea91 100644 --- a/civicrm/CRM/Core/DAO/Cache.php +++ b/civicrm/CRM/Core/DAO/Cache.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/Cache.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:695d7ebea0e7103ff58dfbbff111dde6) + * (GenCodeChecksum:af1401f844c699c6ad35366a32a8db03) */ /** * Database access object for the Cache entity. */ class CRM_Core_DAO_Cache extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '2.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/Component.php b/civicrm/CRM/Core/DAO/Component.php index bcc2c8008a..f9b7b077f9 100644 --- a/civicrm/CRM/Core/DAO/Component.php +++ b/civicrm/CRM/Core/DAO/Component.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/Component.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:1f7be99fb2d2adfebdf3faa2013666f4) + * (GenCodeChecksum:6c3fd2c8e875746c0ceffa499624f77c) */ /** * Database access object for the Component entity. */ class CRM_Core_DAO_Component extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '2.0'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/Country.php b/civicrm/CRM/Core/DAO/Country.php index eb77528b78..24d458b99c 100644 --- a/civicrm/CRM/Core/DAO/Country.php +++ b/civicrm/CRM/Core/DAO/Country.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/Country.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:d40139ce11551ce445103a7fe9d3d46e) + * (GenCodeChecksum:cdd80b394924586274cf4b91183d3637) */ /** * Database access object for the Country entity. */ class CRM_Core_DAO_Country extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/County.php b/civicrm/CRM/Core/DAO/County.php index 38167c8050..97d2fae58e 100644 --- a/civicrm/CRM/Core/DAO/County.php +++ b/civicrm/CRM/Core/DAO/County.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/County.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:095ad13ad08bd1d3c12be5094239df32) + * (GenCodeChecksum:96f94dbbafff9a4e1f0ff276799fcbbd) */ /** * Database access object for the County entity. */ class CRM_Core_DAO_County extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/CustomField.php b/civicrm/CRM/Core/DAO/CustomField.php index e866fd532c..698d34a495 100644 --- a/civicrm/CRM/Core/DAO/CustomField.php +++ b/civicrm/CRM/Core/DAO/CustomField.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/CustomField.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:1acb9b3538bd3005b99e6af6d9ec062f) + * (GenCodeChecksum:4ded3c0d1a8e34502a5957ee74c4480a) */ /** * Database access object for the CustomField entity. */ class CRM_Core_DAO_CustomField extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/CustomGroup.php b/civicrm/CRM/Core/DAO/CustomGroup.php index 8d38cd71d5..12dd47769e 100644 --- a/civicrm/CRM/Core/DAO/CustomGroup.php +++ b/civicrm/CRM/Core/DAO/CustomGroup.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/CustomGroup.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:4f58c488f8213dab928db357bc88f478) + * (GenCodeChecksum:494d883be861157d8067e6a6c50c23f6) */ /** * Database access object for the CustomGroup entity. */ class CRM_Core_DAO_CustomGroup extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/Dashboard.php b/civicrm/CRM/Core/DAO/Dashboard.php index a54895fe9e..a9d40d3243 100644 --- a/civicrm/CRM/Core/DAO/Dashboard.php +++ b/civicrm/CRM/Core/DAO/Dashboard.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/Dashboard.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:8cc83b4a4fa0fe28868e33887824c4e5) + * (GenCodeChecksum:2d134bfa6938d2e8a8d8e25e99769823) */ /** * Database access object for the Dashboard entity. */ class CRM_Core_DAO_Dashboard extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '3.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/Discount.php b/civicrm/CRM/Core/DAO/Discount.php index 914c2d9147..ed2f53e1e1 100644 --- a/civicrm/CRM/Core/DAO/Discount.php +++ b/civicrm/CRM/Core/DAO/Discount.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/Discount.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:b240911ddcf71cc294fe02614887711c) + * (GenCodeChecksum:a23716379d3cccf678a9d8e423690e7c) */ /** * Database access object for the Discount entity. */ class CRM_Core_DAO_Discount extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '2.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/Domain.php b/civicrm/CRM/Core/DAO/Domain.php index b90d697812..82ccf0f5a8 100644 --- a/civicrm/CRM/Core/DAO/Domain.php +++ b/civicrm/CRM/Core/DAO/Domain.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/Domain.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:33e01928bc64aa36fb39bbbeea1579dc) + * (GenCodeChecksum:57a526de0b2bc02fed832a22dc50ad80) */ /** * Database access object for the Domain entity. */ class CRM_Core_DAO_Domain extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/Email.php b/civicrm/CRM/Core/DAO/Email.php index 92ff31e6dd..7c0a55b812 100644 --- a/civicrm/CRM/Core/DAO/Email.php +++ b/civicrm/CRM/Core/DAO/Email.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/Email.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:6e34e9759f2a50eb22dea1fceb15090c) + * (GenCodeChecksum:866b627595adac9091080a4e4ab146bc) */ /** * Database access object for the Email entity. */ class CRM_Core_DAO_Email extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/EntityFile.php b/civicrm/CRM/Core/DAO/EntityFile.php index efef635fb8..0d32257d1e 100644 --- a/civicrm/CRM/Core/DAO/EntityFile.php +++ b/civicrm/CRM/Core/DAO/EntityFile.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/EntityFile.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:b9ac4293489f424584b9983d1304987f) + * (GenCodeChecksum:f2d4dfec2466ad664b4949983b1c7e58) */ /** * Database access object for the EntityFile entity. */ class CRM_Core_DAO_EntityFile extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.5'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/EntityTag.php b/civicrm/CRM/Core/DAO/EntityTag.php index 7ef0feff7e..eb2be19b66 100644 --- a/civicrm/CRM/Core/DAO/EntityTag.php +++ b/civicrm/CRM/Core/DAO/EntityTag.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/EntityTag.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:3f4e098850d5540fb6ddd3e04d0084fe) + * (GenCodeChecksum:58f15f695b38fa4cacfdf82d2734e0f0) */ /** * Database access object for the EntityTag entity. */ class CRM_Core_DAO_EntityTag extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/Extension.php b/civicrm/CRM/Core/DAO/Extension.php index 3a58bb43c8..c666e3ab02 100644 --- a/civicrm/CRM/Core/DAO/Extension.php +++ b/civicrm/CRM/Core/DAO/Extension.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/Extension.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:84798b0aba44e90cbdde1d65736c616d) + * (GenCodeChecksum:08d2151b75e68f334bd88475b58fab7b) */ /** * Database access object for the Extension entity. */ class CRM_Core_DAO_Extension extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '4.2'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/File.php b/civicrm/CRM/Core/DAO/File.php index 51b0b79200..dc6508d4a6 100644 --- a/civicrm/CRM/Core/DAO/File.php +++ b/civicrm/CRM/Core/DAO/File.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/File.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:5c8f93d416373dd32add73f0bbaa59cc) + * (GenCodeChecksum:aa0883a815a43dd250612348f3ec470e) */ /** * Database access object for the File entity. */ class CRM_Core_DAO_File extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.5'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/IM.php b/civicrm/CRM/Core/DAO/IM.php index bee4ce0022..958b4dda19 100644 --- a/civicrm/CRM/Core/DAO/IM.php +++ b/civicrm/CRM/Core/DAO/IM.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/IM.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:a01a1aec350383201dbc1c3f59e9b709) + * (GenCodeChecksum:da6b080a31b208a71635d272fabab7ec) */ /** * Database access object for the IM entity. */ class CRM_Core_DAO_IM extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/Job.php b/civicrm/CRM/Core/DAO/Job.php index 39d72b9754..ebdb62818e 100644 --- a/civicrm/CRM/Core/DAO/Job.php +++ b/civicrm/CRM/Core/DAO/Job.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/Job.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:8f64b00207fd2c2f98c378c01e29b911) + * (GenCodeChecksum:6e3a5de515fda550b1b5aeb493c50f0b) */ /** * Database access object for the Job entity. */ class CRM_Core_DAO_Job extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '4.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/JobLog.php b/civicrm/CRM/Core/DAO/JobLog.php index 59e75d9fab..b59a98b1f5 100644 --- a/civicrm/CRM/Core/DAO/JobLog.php +++ b/civicrm/CRM/Core/DAO/JobLog.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/JobLog.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:d5266d899de027b3411970632fd2d6ef) + * (GenCodeChecksum:eca8e7af1026dbfaf7beecb95ce02361) */ /** * Database access object for the JobLog entity. */ class CRM_Core_DAO_JobLog extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '4.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/LocBlock.php b/civicrm/CRM/Core/DAO/LocBlock.php index fcac92b27c..154a2e776c 100644 --- a/civicrm/CRM/Core/DAO/LocBlock.php +++ b/civicrm/CRM/Core/DAO/LocBlock.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/LocBlock.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:fd6b47fdf91519058cc607ab736a8934) + * (GenCodeChecksum:8459c5a6d25e5c70e44de49b109a82fa) */ /** * Database access object for the LocBlock entity. */ class CRM_Core_DAO_LocBlock extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '2.0'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/LocationType.php b/civicrm/CRM/Core/DAO/LocationType.php index b75a338c7d..70648f67db 100644 --- a/civicrm/CRM/Core/DAO/LocationType.php +++ b/civicrm/CRM/Core/DAO/LocationType.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/LocationType.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:06904ebbc78a929b34c17ba1bb580350) + * (GenCodeChecksum:325ccb933339bc909efc7d6b60c7186b) */ /** * Database access object for the LocationType entity. */ class CRM_Core_DAO_LocationType extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/Log.php b/civicrm/CRM/Core/DAO/Log.php index c9541016bf..182d42f5d4 100644 --- a/civicrm/CRM/Core/DAO/Log.php +++ b/civicrm/CRM/Core/DAO/Log.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/Log.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:a102ecbdf345a1df748ea11fdd997b9e) + * (GenCodeChecksum:5dfdb2863ba1bc7b84288a522cdaef51) */ /** * Database access object for the Log entity. */ class CRM_Core_DAO_Log extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.5'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/MailSettings.php b/civicrm/CRM/Core/DAO/MailSettings.php index 343c7e6bfd..acc8278c19 100644 --- a/civicrm/CRM/Core/DAO/MailSettings.php +++ b/civicrm/CRM/Core/DAO/MailSettings.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/MailSettings.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:455750fee6f1acbe50aeb19387f12158) + * (GenCodeChecksum:b43716d8c8e362738d8d3420e8fbe03d) */ /** * Database access object for the MailSettings entity. */ class CRM_Core_DAO_MailSettings extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '2.2'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/Managed.php b/civicrm/CRM/Core/DAO/Managed.php index 4d4cbafdfd..212817ec78 100644 --- a/civicrm/CRM/Core/DAO/Managed.php +++ b/civicrm/CRM/Core/DAO/Managed.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/Managed.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:939c0ed6422323450f5b9b00f8da85ad) + * (GenCodeChecksum:e6146e35f8c8321e600a4198cbd6949e) */ /** * Database access object for the Managed entity. */ class CRM_Core_DAO_Managed extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '4.2'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/Mapping.php b/civicrm/CRM/Core/DAO/Mapping.php index 5b7121f90f..59505d1d5f 100644 --- a/civicrm/CRM/Core/DAO/Mapping.php +++ b/civicrm/CRM/Core/DAO/Mapping.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/Mapping.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:ca067ae4e0d6b7c01e9c24da7cff585a) + * (GenCodeChecksum:c97b13ea2aaccdf8ba13b6552ccb59f2) */ /** * Database access object for the Mapping entity. */ class CRM_Core_DAO_Mapping extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.2'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/MappingField.php b/civicrm/CRM/Core/DAO/MappingField.php index c8e0f48dda..cd5f06e741 100644 --- a/civicrm/CRM/Core/DAO/MappingField.php +++ b/civicrm/CRM/Core/DAO/MappingField.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/MappingField.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:c29cb464b46084c28cedf37c3757ffd1) + * (GenCodeChecksum:3702a3c3cb9cd696eb829d15f4676439) */ /** * Database access object for the MappingField entity. */ class CRM_Core_DAO_MappingField extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.2'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/Menu.php b/civicrm/CRM/Core/DAO/Menu.php index a0f1b6c6ea..98f3f4ad14 100644 --- a/civicrm/CRM/Core/DAO/Menu.php +++ b/civicrm/CRM/Core/DAO/Menu.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/Menu.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:8eaee41a6e8192bd26fab7f4ef443c0c) + * (GenCodeChecksum:8be7941b4dccb08266109e3e1599159f) */ /** * Database access object for the Menu entity. */ class CRM_Core_DAO_Menu extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/MessageTemplate.php b/civicrm/CRM/Core/DAO/MessageTemplate.php index 54b4664182..5c7965c83f 100644 --- a/civicrm/CRM/Core/DAO/MessageTemplate.php +++ b/civicrm/CRM/Core/DAO/MessageTemplate.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/MessageTemplate.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:542e52d6f757c9e8adfa2a5ed07b6475) + * (GenCodeChecksum:68dd4ac3c9f098e3577dbed8d5a2a105) */ /** * Database access object for the MessageTemplate entity. */ class CRM_Core_DAO_MessageTemplate extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.6'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/Navigation.php b/civicrm/CRM/Core/DAO/Navigation.php index 81125e4f02..b567777810 100644 --- a/civicrm/CRM/Core/DAO/Navigation.php +++ b/civicrm/CRM/Core/DAO/Navigation.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/Navigation.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:39aa4b4d2c380d3a08c4931c3e497006) + * (GenCodeChecksum:34c3d3b834400b49f1b8c6c99a08c99e) */ /** * Database access object for the Navigation entity. */ class CRM_Core_DAO_Navigation extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '3.0'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/Note.php b/civicrm/CRM/Core/DAO/Note.php index c5cd339ea7..748d1076f1 100644 --- a/civicrm/CRM/Core/DAO/Note.php +++ b/civicrm/CRM/Core/DAO/Note.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/Note.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:0e43e07bfbf7de4e7fa48f4d24a923fd) + * (GenCodeChecksum:86e72396a497a58c1568d0d081435e75) */ /** * Database access object for the Note entity. */ class CRM_Core_DAO_Note extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/OpenID.php b/civicrm/CRM/Core/DAO/OpenID.php index 02db0f94b5..1a61aba156 100644 --- a/civicrm/CRM/Core/DAO/OpenID.php +++ b/civicrm/CRM/Core/DAO/OpenID.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/OpenID.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:ed6e574083475c9caf6e8f5028510263) + * (GenCodeChecksum:4d60933113e2b5330dd8194e7ebe6ae4) */ /** * Database access object for the OpenID entity. */ class CRM_Core_DAO_OpenID extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '2.0'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/OptionGroup.php b/civicrm/CRM/Core/DAO/OptionGroup.php index 28611f23ac..9feb9fd8f1 100644 --- a/civicrm/CRM/Core/DAO/OptionGroup.php +++ b/civicrm/CRM/Core/DAO/OptionGroup.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/OptionGroup.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:8a0474f902f423a6055399c438cd406c) + * (GenCodeChecksum:d0011ad2bb6c090eeb86d25916c5624b) */ /** * Database access object for the OptionGroup entity. */ class CRM_Core_DAO_OptionGroup extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.5'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/OptionValue.php b/civicrm/CRM/Core/DAO/OptionValue.php index ed5bf88c5f..7f0c3644f2 100644 --- a/civicrm/CRM/Core/DAO/OptionValue.php +++ b/civicrm/CRM/Core/DAO/OptionValue.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/OptionValue.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:370198cee250edd2f29743a5b5667e9a) + * (GenCodeChecksum:e51b16ecfe5f8302c8610b7f5dfd55e5) */ /** * Database access object for the OptionValue entity. */ class CRM_Core_DAO_OptionValue extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.5'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/Phone.php b/civicrm/CRM/Core/DAO/Phone.php index 444c0b3a53..9a6b668620 100644 --- a/civicrm/CRM/Core/DAO/Phone.php +++ b/civicrm/CRM/Core/DAO/Phone.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/Phone.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:3c1f1743128af72b009c6cf4effc1da2) + * (GenCodeChecksum:efdb60e03b54f246e73588b6eb99611d) */ /** * Database access object for the Phone entity. */ class CRM_Core_DAO_Phone extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.1'; /** * Static instance to hold the table name. @@ -172,7 +174,7 @@ class CRM_Core_DAO_Phone extends CRM_Core_DAO { 'location_type_id' => [ 'name' => 'location_type_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Phone Location Type'), + 'title' => ts('Phone Location Type ID'), 'description' => ts('Which Location does this phone belong to.'), 'where' => 'civicrm_phone.location_type_id', 'table_name' => 'civicrm_phone', @@ -181,6 +183,7 @@ class CRM_Core_DAO_Phone extends CRM_Core_DAO { 'localizable' => 0, 'html' => [ 'type' => 'Select', + 'label' => ts("Phone Location Type"), ], 'pseudoconstant' => [ 'table' => 'civicrm_location_type', @@ -289,7 +292,7 @@ class CRM_Core_DAO_Phone extends CRM_Core_DAO { 'phone_type_id' => [ 'name' => 'phone_type_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Phone Type'), + 'title' => ts('Phone Type ID'), 'description' => ts('Which type of phone does this number belongs.'), 'where' => 'civicrm_phone.phone_type_id', 'export' => TRUE, @@ -299,6 +302,7 @@ class CRM_Core_DAO_Phone extends CRM_Core_DAO { 'localizable' => 0, 'html' => [ 'type' => 'Select', + 'label' => ts("Phone Type"), ], 'pseudoconstant' => [ 'optionGroupName' => 'phone_type', diff --git a/civicrm/CRM/Core/DAO/PreferencesDate.php b/civicrm/CRM/Core/DAO/PreferencesDate.php index 4f50074007..b1d08e7cd0 100644 --- a/civicrm/CRM/Core/DAO/PreferencesDate.php +++ b/civicrm/CRM/Core/DAO/PreferencesDate.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/PreferencesDate.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:e950a7985ee12a8b5ec6e926e8a49b20) + * (GenCodeChecksum:21383b05b8c8e98ed1721aab06031907) */ /** * Database access object for the PreferencesDate entity. */ class CRM_Core_DAO_PreferencesDate extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '2.0'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/PrevNextCache.php b/civicrm/CRM/Core/DAO/PrevNextCache.php index cb58112789..a39549db9e 100644 --- a/civicrm/CRM/Core/DAO/PrevNextCache.php +++ b/civicrm/CRM/Core/DAO/PrevNextCache.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/PrevNextCache.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:3e1ec2353f61b542cca9c3c9efc73e80) + * (GenCodeChecksum:8086ffe55554b0fba698136fd6dee894) */ /** * Database access object for the PrevNextCache entity. */ class CRM_Core_DAO_PrevNextCache extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '3.4'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/PrintLabel.php b/civicrm/CRM/Core/DAO/PrintLabel.php index c0d4463fe2..129e80804d 100644 --- a/civicrm/CRM/Core/DAO/PrintLabel.php +++ b/civicrm/CRM/Core/DAO/PrintLabel.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/PrintLabel.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:b98543c0a9d27b4f6aef0ab432bb37e2) + * (GenCodeChecksum:484a16ebc1b881e7718bfcf139024ee7) */ /** * Database access object for the PrintLabel entity. */ class CRM_Core_DAO_PrintLabel extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '4.4'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/RecurringEntity.php b/civicrm/CRM/Core/DAO/RecurringEntity.php index e6463e239d..d22aa96ac7 100644 --- a/civicrm/CRM/Core/DAO/RecurringEntity.php +++ b/civicrm/CRM/Core/DAO/RecurringEntity.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/RecurringEntity.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:4d0ee948935c36ddf9c0a04ed30ec44f) + * (GenCodeChecksum:b78474c715335f7689a9a5fcdcb5718e) */ /** * Database access object for the RecurringEntity entity. */ class CRM_Core_DAO_RecurringEntity extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '4.6'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/Setting.php b/civicrm/CRM/Core/DAO/Setting.php index 70ac634cfd..160f781cda 100644 --- a/civicrm/CRM/Core/DAO/Setting.php +++ b/civicrm/CRM/Core/DAO/Setting.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/Setting.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:196dea902e132f8ba6ffb8b108ee1996) + * (GenCodeChecksum:c1fda2807e8265021ffaa490325a7e4f) */ /** * Database access object for the Setting entity. */ class CRM_Core_DAO_Setting extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '4.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/StateProvince.php b/civicrm/CRM/Core/DAO/StateProvince.php index eed42f3859..80a91c2e1f 100644 --- a/civicrm/CRM/Core/DAO/StateProvince.php +++ b/civicrm/CRM/Core/DAO/StateProvince.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/StateProvince.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:22bfd6264c17c80a3638b9860cb97767) + * (GenCodeChecksum:2dced9a7a3e6be3d05ea7b7babe4b113) */ /** * Database access object for the StateProvince entity. */ class CRM_Core_DAO_StateProvince extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/StatusPreference.php b/civicrm/CRM/Core/DAO/StatusPreference.php index 471513595b..b06b206f24 100644 --- a/civicrm/CRM/Core/DAO/StatusPreference.php +++ b/civicrm/CRM/Core/DAO/StatusPreference.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/StatusPreference.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:48ca8492c6febf737281831d72147a38) + * (GenCodeChecksum:1fa80acc24bcb14df3947cba2daa930f) */ /** * Database access object for the StatusPreference entity. */ class CRM_Core_DAO_StatusPreference extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '4.7'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/SystemLog.php b/civicrm/CRM/Core/DAO/SystemLog.php index 22d57c06a4..c18ee55ebc 100644 --- a/civicrm/CRM/Core/DAO/SystemLog.php +++ b/civicrm/CRM/Core/DAO/SystemLog.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/SystemLog.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:c90dd839d7756d95a85e38cadbcb40c9) + * (GenCodeChecksum:46d7f317ffb5d01d9cb22898ce38abb3) */ /** * Database access object for the SystemLog entity. */ class CRM_Core_DAO_SystemLog extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '4.5'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/Tag.php b/civicrm/CRM/Core/DAO/Tag.php index 09e7ec787d..65ae7a378b 100644 --- a/civicrm/CRM/Core/DAO/Tag.php +++ b/civicrm/CRM/Core/DAO/Tag.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/Tag.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:71590516eaae029ef71f6f4b3d7a800d) + * (GenCodeChecksum:7a2eb010fd96445604104b6ada9c0b99) */ /** * Database access object for the Tag entity. */ class CRM_Core_DAO_Tag extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/Timezone.php b/civicrm/CRM/Core/DAO/Timezone.php index d2cd010c88..5f9abdfc6c 100644 --- a/civicrm/CRM/Core/DAO/Timezone.php +++ b/civicrm/CRM/Core/DAO/Timezone.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/Timezone.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:fc76675346aa5a97d5ce798b9968ee58) + * (GenCodeChecksum:fb1089cb65c1587b1242b9d250c664f7) */ /** * Database access object for the Timezone entity. */ class CRM_Core_DAO_Timezone extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.8'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/UFField.php b/civicrm/CRM/Core/DAO/UFField.php index 1d5821d996..a112e1c6f3 100644 --- a/civicrm/CRM/Core/DAO/UFField.php +++ b/civicrm/CRM/Core/DAO/UFField.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/UFField.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:a77a2bfe4b288e20b6929fd3a7532850) + * (GenCodeChecksum:e717ec384cfe13629b4fc440af2a99d5) */ /** * Database access object for the UFField entity. */ class CRM_Core_DAO_UFField extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/UFGroup.php b/civicrm/CRM/Core/DAO/UFGroup.php index 5005250997..ae79d22da8 100644 --- a/civicrm/CRM/Core/DAO/UFGroup.php +++ b/civicrm/CRM/Core/DAO/UFGroup.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/UFGroup.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:acf9d6ef6307d473c8a32775aa8e8e66) + * (GenCodeChecksum:1cac6e6b80a630f69ce25f709a20e4a3) */ /** * Database access object for the UFGroup entity. */ class CRM_Core_DAO_UFGroup extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/UFJoin.php b/civicrm/CRM/Core/DAO/UFJoin.php index 0477c90288..98639423b1 100644 --- a/civicrm/CRM/Core/DAO/UFJoin.php +++ b/civicrm/CRM/Core/DAO/UFJoin.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/UFJoin.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:70a227711539c0b5fdea5349d18e6b7f) + * (GenCodeChecksum:191143bced279d48cf34cdf6cf85a5fb) */ /** * Database access object for the UFJoin entity. */ class CRM_Core_DAO_UFJoin extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.3'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/UFMatch.php b/civicrm/CRM/Core/DAO/UFMatch.php index 6712ed2c00..fa08f60b22 100644 --- a/civicrm/CRM/Core/DAO/UFMatch.php +++ b/civicrm/CRM/Core/DAO/UFMatch.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/UFMatch.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:4d249265c27fb6dbe50e0589ddfed7d2) + * (GenCodeChecksum:4fca2151c2ecbd762ac9e2f067f0030f) */ /** * Database access object for the UFMatch entity. */ class CRM_Core_DAO_UFMatch extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/Website.php b/civicrm/CRM/Core/DAO/Website.php index 678e898f99..e9415359c1 100644 --- a/civicrm/CRM/Core/DAO/Website.php +++ b/civicrm/CRM/Core/DAO/Website.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/Website.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:2b048fb746a5762a6e132fe7b1e8dd07) + * (GenCodeChecksum:9e449b1f3a997b63c79066bd5cd782ae) */ /** * Database access object for the Website entity. */ class CRM_Core_DAO_Website extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '3.2'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/WordReplacement.php b/civicrm/CRM/Core/DAO/WordReplacement.php index 42c2051b31..65ac087b55 100644 --- a/civicrm/CRM/Core/DAO/WordReplacement.php +++ b/civicrm/CRM/Core/DAO/WordReplacement.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/WordReplacement.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:d7e1414f899a3bcabd20806508741f55) + * (GenCodeChecksum:f4afc331da543068dba6d243d98b8e39) */ /** * Database access object for the WordReplacement entity. */ class CRM_Core_DAO_WordReplacement extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '4.4'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/DAO/Worldregion.php b/civicrm/CRM/Core/DAO/Worldregion.php index 86b7409323..1dd866013d 100644 --- a/civicrm/CRM/Core/DAO/Worldregion.php +++ b/civicrm/CRM/Core/DAO/Worldregion.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Core/Worldregion.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:8045c0ab7f128626a594fbb634e2f169) + * (GenCodeChecksum:0312ba4169a285839ec54d655ff5ceb3) */ /** * Database access object for the Worldregion entity. */ class CRM_Core_DAO_Worldregion extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.8'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Core/Error.php b/civicrm/CRM/Core/Error.php index 04f7e8cd39..98d1a29ded 100644 --- a/civicrm/CRM/Core/Error.php +++ b/civicrm/CRM/Core/Error.php @@ -586,6 +586,9 @@ class CRM_Core_Error extends PEAR_ErrorStack { if ($config->userSystem->is_drupal and function_exists('watchdog')) { watchdog('civicrm', '%message', ['%message' => $message], $priority ?? WATCHDOG_DEBUG); } + elseif ($config->userSystem->is_drupal and CIVICRM_UF == 'Drupal8') { + \Drupal::logger('civicrm')->log($priority ?? \Drupal\Core\Logger\RfcLogLevel::DEBUG, '%message', ['%message' => $message]); + } } return $str; @@ -597,13 +600,15 @@ class CRM_Core_Error extends PEAR_ErrorStack { * @param string $string */ public static function debug_query($string) { - if (defined('CIVICRM_DEBUG_LOG_QUERY')) { - if (CIVICRM_DEBUG_LOG_QUERY === 'backtrace') { - CRM_Core_Error::backtrace($string, TRUE); - } - elseif (CIVICRM_DEBUG_LOG_QUERY) { - CRM_Core_Error::debug_var('Query', $string, TRUE, TRUE, 'sql_log', PEAR_LOG_DEBUG); - } + if (!defined('CIVICRM_DEBUG_LOG_QUERY')) { + // TODO: When its updated to support getenv(), call CRM_Utils_Constant::value('CIVICRM_DEBUG_LOG_QUERY', FALSE) + define('CIVICRM_DEBUG_LOG_QUERY', getenv('CIVICRM_DEBUG_LOG_QUERY')); + } + if (CIVICRM_DEBUG_LOG_QUERY === 'backtrace') { + CRM_Core_Error::backtrace($string, TRUE); + } + elseif (CIVICRM_DEBUG_LOG_QUERY) { + CRM_Core_Error::debug_var('Query', $string, TRUE, TRUE, 'sql_log', PEAR_LOG_DEBUG); } } diff --git a/civicrm/CRM/Core/Exception/PrematureExitException.php b/civicrm/CRM/Core/Exception/PrematureExitException.php index e0eee738b9..f46bbfff8e 100644 --- a/civicrm/CRM/Core/Exception/PrematureExitException.php +++ b/civicrm/CRM/Core/Exception/PrematureExitException.php @@ -25,6 +25,13 @@ */ class CRM_Core_Exception_PrematureExitException extends RuntimeException { + /** + * Contextual data. + * + * @var array + */ + public $errorData; + /** * Construct the exception. Note: The message is NOT binary safe. * diff --git a/civicrm/CRM/Core/Lock.php b/civicrm/CRM/Core/Lock.php index da1366b424..894b913ab2 100644 --- a/civicrm/CRM/Core/Lock.php +++ b/civicrm/CRM/Core/Lock.php @@ -111,7 +111,8 @@ class CRM_Core_Lock implements \Civi\Core\Lock\LockInterface { */ public function __construct($name, $timeout = NULL, $serverWideLock = FALSE) { $config = CRM_Core_Config::singleton(); - $dsnArray = DB::parseDSN($config->dsn); + $dsn = CRM_Utils_SQL::autoSwitchDSN($config->dsn); + $dsnArray = DB::parseDSN($dsn); $database = $dsnArray['database']; $domainID = CRM_Core_Config::domainID(); if ($serverWideLock) { diff --git a/civicrm/CRM/Core/OptionGroup.php b/civicrm/CRM/Core/OptionGroup.php index 89018c1a21..727f2b2214 100644 --- a/civicrm/CRM/Core/OptionGroup.php +++ b/civicrm/CRM/Core/OptionGroup.php @@ -106,7 +106,12 @@ class CRM_Core_OptionGroup { $orderBy = 'weight' ) { $cache = CRM_Utils_Cache::singleton(); - $cacheKey = self::createCacheKey($name, $flip, $grouping, $localize, $condition, $labelColumnName, $onlyActive, $keyColumnName, $orderBy); + if (in_array($name, self::$_domainIDGroups)) { + $cacheKey = self::createCacheKey($name, $flip, $grouping, $localize, $condition, $labelColumnName, $onlyActive, $keyColumnName, $orderBy, CRM_Core_Config::domainID()); + } + else { + $cacheKey = self::createCacheKey($name, $flip, $grouping, $localize, $condition, $labelColumnName, $onlyActive, $keyColumnName, $orderBy); + } if (!$fresh) { // Fetch from static var @@ -186,8 +191,7 @@ WHERE v.option_group_id = g.id * @return string */ protected static function createCacheKey($id) { - $cacheKey = "CRM_OG_" . preg_replace('/[^a-zA-Z0-9]/', '', $id) . '_' . md5(serialize(func_get_args())); - return $cacheKey; + return "CRM_OG_" . preg_replace('/[^a-zA-Z0-9]/', '', $id) . '_' . md5(serialize(func_get_args())); } /** diff --git a/civicrm/CRM/Core/Payment/AuthorizeNet.php b/civicrm/CRM/Core/Payment/AuthorizeNet.php index 5a9fcbc992..0cbbc2b939 100644 --- a/civicrm/CRM/Core/Payment/AuthorizeNet.php +++ b/civicrm/CRM/Core/Payment/AuthorizeNet.php @@ -193,7 +193,6 @@ class CRM_Core_Payment_AuthorizeNet extends CRM_Core_Payment { default: // Success $params['trxn_id'] = !empty($response_fields[6]) ? $response_fields[6] : $this->getTestTrxnID(); - $params['gross_amount'] = $response_fields[9]; break; } diff --git a/civicrm/CRM/Core/Payment/BaseIPN.php b/civicrm/CRM/Core/Payment/BaseIPN.php index 27b0074814..fdfce40a0f 100644 --- a/civicrm/CRM/Core/Payment/BaseIPN.php +++ b/civicrm/CRM/Core/Payment/BaseIPN.php @@ -135,12 +135,6 @@ class CRM_Core_Payment_BaseIPN { if (!$this->loadObjects($input, $ids, $objects, $required, $paymentProcessorID)) { return FALSE; } - //the process is that the loadObjects is kind of hacked by loading the objects for the original contribution and then somewhat inconsistently using them for the - //current contribution. Here we ensure that the original contribution is available to the complete transaction function - //we don't want to fix this in the payment processor classes because we would have to fix all of them - so better to fix somewhere central - if (isset($objects['contributionRecur'])) { - $objects['first_contribution'] = $objects['contribution']; - } return TRUE; } @@ -473,8 +467,12 @@ class CRM_Core_Payment_BaseIPN { * @throws \CRM_Core_Exception * @throws \CiviCRM_API3_Exception */ - public function completeTransaction(&$input, &$ids, &$objects) { - CRM_Contribute_BAO_Contribution::completeOrder($input, $ids, $objects); + public function completeTransaction($input, $ids, $objects) { + CRM_Contribute_BAO_Contribution::completeOrder($input, [ + 'related_contact' => $ids['related_contact'] ?? NULL, + 'participant' => !empty($objects['participant']) ? $objects['participant']->id : NULL, + 'contributionRecur' => !empty($objects['contributionRecur']) ? $objects['contributionRecur']->id : NULL, + ], $objects); } /** diff --git a/civicrm/CRM/Core/Payment/Dummy.php b/civicrm/CRM/Core/Payment/Dummy.php index 4757fb296c..7961d56e94 100644 --- a/civicrm/CRM/Core/Payment/Dummy.php +++ b/civicrm/CRM/Core/Payment/Dummy.php @@ -97,7 +97,6 @@ class CRM_Core_Payment_Dummy extends CRM_Core_Payment { $params['trxn_id'] = $this->getTrxnID();; - $params['gross_amount'] = $propertyBag->getAmount(); // Add a fee_amount so we can make sure fees are handled properly in underlying classes. $params['fee_amount'] = 1.50; $params['description'] = $this->getPaymentDescription($params); diff --git a/civicrm/CRM/Core/Payment/PayJunction.php b/civicrm/CRM/Core/Payment/PayJunction.php index 4fc8ec9005..ce1b33442c 100644 --- a/civicrm/CRM/Core/Payment/PayJunction.php +++ b/civicrm/CRM/Core/Payment/PayJunction.php @@ -151,7 +151,6 @@ class CRM_Core_Payment_PayJunction extends CRM_Core_Payment { // Success $params['trxn_result_code'] = $pjpgResponse['dc_response_code']; $params['trxn_id'] = $pjpgResponse['dc_transaction_id']; - $params['gross_amount'] = $params['amount']; return $params; } diff --git a/civicrm/CRM/Core/Payment/PayPalImpl.php b/civicrm/CRM/Core/Payment/PayPalImpl.php index 38a6eb300d..2820e426c8 100644 --- a/civicrm/CRM/Core/Payment/PayPalImpl.php +++ b/civicrm/CRM/Core/Payment/PayPalImpl.php @@ -349,7 +349,6 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment { /* Success */ $params['trxn_id'] = $result['transactionid']; - $params['gross_amount'] = $result['amt']; $params['fee_amount'] = $result['feeamt']; $params['net_amount'] = $result['settleamt'] ?? NULL; if ($params['net_amount'] == 0 && $params['fee_amount'] != 0) { @@ -972,6 +971,9 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment { $sub = empty($params['is_recur']) ? 'cgi-bin/webscr' : 'subscriptions'; $paypalURL = "{$url}{$sub}?$uri"; + // Allow each CMS to do a pre-flight check before redirecting to PayPal. + CRM_Core_Config::singleton()->userSystem->prePostRedirect(); + CRM_Utils_System::redirect($paypalURL); } @@ -1130,8 +1132,13 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment { // The contribution form passes a 'button' but the event form might still set one of these fields. // @todo more standardisation & get paypal fully out of the form layer. $possibleExpressFields = [ + // @todo - we think these top 2 are likely not required & it's still here + // on a precautionary basis. + // see https://github.com/civicrm/civicrm-core/pull/18680 '_qf_Register_upload_express_x', '_qf_Payment_upload_express_x', + '_qf_Register_upload_express', + '_qf_Payment_upload_express', '_qf_Main_upload_express', ]; if (array_intersect_key($params, array_fill_keys($possibleExpressFields, 1))) { diff --git a/civicrm/CRM/Core/Payment/Realex.php b/civicrm/CRM/Core/Payment/Realex.php index 6fa22c9ccd..03ad040db4 100644 --- a/civicrm/CRM/Core/Payment/Realex.php +++ b/civicrm/CRM/Core/Payment/Realex.php @@ -175,7 +175,6 @@ class CRM_Core_Payment_Realex extends CRM_Core_Payment { $params['trxn_id'] = $response['PASREF']; $params['trxn_result_code'] = serialize($extras); $params['currencyID'] = $this->_getParam('currency'); - $params['gross_amount'] = $this->_getParam('amount'); $params['fee_amount'] = 0; return $params; diff --git a/civicrm/CRM/Core/Payment/eWAY.php b/civicrm/CRM/Core/Payment/eWAY.php index 9c37727053..e8ec19da0d 100644 --- a/civicrm/CRM/Core/Payment/eWAY.php +++ b/civicrm/CRM/Core/Payment/eWAY.php @@ -340,7 +340,6 @@ class CRM_Core_Payment_eWAY extends CRM_Core_Payment { $beaglestatus = ': ' . $beaglestatus; } $params['trxn_result_code'] = $eWAYResponse->Status() . $beaglestatus; - $params['gross_amount'] = $eWAYResponse->Amount(); $params['trxn_id'] = $eWAYResponse->TransactionNumber(); return $params; diff --git a/civicrm/CRM/Core/PrevNextCache/Sql.php b/civicrm/CRM/Core/PrevNextCache/Sql.php index 5870c4424d..7f8696732f 100644 --- a/civicrm/CRM/Core/PrevNextCache/Sql.php +++ b/civicrm/CRM/Core/PrevNextCache/Sql.php @@ -40,8 +40,9 @@ class CRM_Core_PrevNextCache_Sql implements CRM_Core_PrevNextCache_Interface { $insertSQL = " INSERT INTO civicrm_prevnext_cache (cachekey, entity_id1, data) "; - $result = CRM_Core_DAO::executeQuery($insertSQL . $sql, $sqlParams, FALSE, NULL, FALSE, TRUE, TRUE); + $result = CRM_Core_DAO::executeQuery($insertSQL . $sql, $sqlParams, FALSE); if (is_a($result, 'DB_Error')) { + CRM_Core_Error::deprecatedFunctionWarning('errors are not expected to be returned'); throw new CRM_Core_Exception($result->message); } return TRUE; diff --git a/civicrm/CRM/Core/PseudoConstant.php b/civicrm/CRM/Core/PseudoConstant.php index 758db214ac..c1f5af9e77 100644 --- a/civicrm/CRM/Core/PseudoConstant.php +++ b/civicrm/CRM/Core/PseudoConstant.php @@ -857,7 +857,7 @@ WHERE id = %1"; * @param bool $excludeHidden * @return array */ - public static function nestedGroup($checkPermissions = TRUE, $groupType = NULL, $excludeHidden = TRUE) { + public static function nestedGroup(bool $checkPermissions = TRUE, $groupType = NULL, bool $excludeHidden = TRUE) { $groups = $checkPermissions ? self::group($groupType, $excludeHidden) : self::allGroup($groupType, $excludeHidden); return CRM_Contact_BAO_Group::getGroupsHierarchy($groups, NULL, ' ', TRUE); } diff --git a/civicrm/CRM/Core/Region.php b/civicrm/CRM/Core/Region.php index 242387ca65..06cece6bc9 100644 --- a/civicrm/CRM/Core/Region.php +++ b/civicrm/CRM/Core/Region.php @@ -48,10 +48,6 @@ class CRM_Core_Region { * @param string $name */ public function __construct($name) { - // Templates injected into regions should normally be file names, but sometimes inline notation is handy. - require_once 'CRM/Core/Smarty/resources/String.php'; - civicrm_smarty_register_string_resource(); - $this->_name = $name; $this->_snippets = []; @@ -223,7 +219,6 @@ class CRM_Core_Region { break; default: - require_once 'CRM/Core/Exception.php'; throw new CRM_Core_Exception(ts('Snippet type %1 is unrecognized', [1 => $snippet['type']])); } @@ -254,48 +249,4 @@ class CRM_Core_Region { return 0; } - /** - * Add block of static HTML to a region. - * - * @param string $markup - * HTML. - * - * public function addMarkup($markup) { - * return $this->add(array( - * 'type' => 'markup', - * 'markup' => $markup, - * )); - * } - * - * /** - * Add a Smarty template file to a region. - * - * Note: File is not evaluated until the page is rendered - * - * @param string $template - * Path to the Smarty template file. - * - * public function addTemplate($template) { - * return $this->add(array( - * 'type' => 'template', - * 'template' => $template, - * )); - * } - * - * /** - * Use a callback function to extend a region. - * - * @param mixed $callback - * @param array $arguments - * Optional, array of parameters for callback; if omitted, the default arguments are ($snippetSpec, $html). - * - * public function addCallback($callback, $arguments = FALSE) { - * return $this->add(array( - * 'type' => 'callback', - * 'callback' => $callback, - * 'arguments' => $arguments, - * )); - * } - */ - } diff --git a/civicrm/CRM/Core/Report/Excel.php b/civicrm/CRM/Core/Report/Excel.php index 52782e8132..ef1c396e0c 100644 --- a/civicrm/CRM/Core/Report/Excel.php +++ b/civicrm/CRM/Core/Report/Excel.php @@ -35,7 +35,7 @@ class CRM_Core_Report_Excel { public static function makeCSVTable($header, $rows, $outputHeader = TRUE) { $config = CRM_Core_Config::singleton(); - $seperator = $config->fieldSeparator; + $separator = $config->fieldSeparator; $add_character = "\015\012"; if ($outputHeader) { @@ -68,7 +68,7 @@ class CRM_Core_Report_Excel { } } - $str = implode($seperator, $strArray); + $str = implode($separator, $strArray); $value = &$str; } @@ -76,7 +76,7 @@ class CRM_Core_Report_Excel { } if ($colNo < $fields_cnt - 1) { - $schema_insert .= $seperator; + $schema_insert .= $separator; } $colNo++; } diff --git a/civicrm/CRM/Custom/Import/Parser.php b/civicrm/CRM/Custom/Import/Parser.php index bc934cb0c1..0ee0ccf2e0 100644 --- a/civicrm/CRM/Custom/Import/Parser.php +++ b/civicrm/CRM/Custom/Import/Parser.php @@ -312,7 +312,7 @@ abstract class CRM_Custom_Import_Parser extends CRM_Contact_Import_Parser { public function set($store, $mode = self::MODE_SUMMARY) { $store->set('fileSize', $this->_fileSize); $store->set('lineCount', $this->_lineCount); - $store->set('seperator', $this->_separator); + $store->set('separator', $this->_separator); $store->set('fields', $this->getSelectValues()); $store->set('fieldTypes', $this->getSelectTypes()); diff --git a/civicrm/CRM/Cxn/DAO/Cxn.php b/civicrm/CRM/Cxn/DAO/Cxn.php index f8190512c9..b0b70337bd 100644 --- a/civicrm/CRM/Cxn/DAO/Cxn.php +++ b/civicrm/CRM/Cxn/DAO/Cxn.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Cxn/Cxn.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:bafb13cb7901c4f15b5348e2bf9a27b5) + * (GenCodeChecksum:059dd4994211085d728a9fc8b7d80803) */ /** * Database access object for the Cxn entity. */ class CRM_Cxn_DAO_Cxn extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '4.6'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Dedupe/BAO/Rule.php b/civicrm/CRM/Dedupe/BAO/Rule.php index 0c40a52c58..b695d18b6e 100644 --- a/civicrm/CRM/Dedupe/BAO/Rule.php +++ b/civicrm/CRM/Dedupe/BAO/Rule.php @@ -136,7 +136,7 @@ class CRM_Dedupe_BAO_Rule extends CRM_Dedupe_DAO_Rule { $from = "{$this->rule_table} t1"; $str = 'NULL'; if (isset($this->params[$this->rule_table][$this->rule_field])) { - $str = CRM_Utils_Type::escape($this->params[$this->rule_table][$this->rule_field], 'String'); + $str = trim(CRM_Utils_Type::escape($this->params[$this->rule_table][$this->rule_field], 'String')); } if ($this->rule_length) { $where[] = "SUBSTR(t1.{$this->rule_field}, 1, {$this->rule_length}) = SUBSTR('$str', 1, {$this->rule_length})"; diff --git a/civicrm/CRM/Dedupe/DAO/Exception.php b/civicrm/CRM/Dedupe/DAO/Exception.php index 2d479b45da..cdbb87cc90 100644 --- a/civicrm/CRM/Dedupe/DAO/Exception.php +++ b/civicrm/CRM/Dedupe/DAO/Exception.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Dedupe/Exception.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:ea7ca2395c6d2d927c35a7241d621cdd) + * (GenCodeChecksum:f4bc21b42b1b5c9cfb0ffa7d3eb46e65) */ /** * Database access object for the Exception entity. */ class CRM_Dedupe_DAO_Exception extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '3.3'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Dedupe/DAO/Rule.php b/civicrm/CRM/Dedupe/DAO/Rule.php index df7521454e..dea3d38759 100644 --- a/civicrm/CRM/Dedupe/DAO/Rule.php +++ b/civicrm/CRM/Dedupe/DAO/Rule.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Dedupe/Rule.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:e218ab6aaed8b58ef6dc189845a36755) + * (GenCodeChecksum:56abeb7ada5e3dfde910bc5033ca047d) */ /** * Database access object for the Rule entity. */ class CRM_Dedupe_DAO_Rule extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.8'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Dedupe/DAO/RuleGroup.php b/civicrm/CRM/Dedupe/DAO/RuleGroup.php index 7caf59d70b..366ff64246 100644 --- a/civicrm/CRM/Dedupe/DAO/RuleGroup.php +++ b/civicrm/CRM/Dedupe/DAO/RuleGroup.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Dedupe/RuleGroup.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:3c21b8b33d8d561b1104318fba0db930) + * (GenCodeChecksum:87a385df0b5bca8150117411f2c31a4a) */ /** * Database access object for the RuleGroup entity. */ class CRM_Dedupe_DAO_RuleGroup extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.8'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Dedupe/MergeHandler.php b/civicrm/CRM/Dedupe/MergeHandler.php index 891bc96c90..6b86b2f9b9 100644 --- a/civicrm/CRM/Dedupe/MergeHandler.php +++ b/civicrm/CRM/Dedupe/MergeHandler.php @@ -33,6 +33,30 @@ class CRM_Dedupe_MergeHandler { */ protected $toRemoveID; + /** + * Migration info array. + * + * This is a nasty bunch of data used in mysterious ways. We want to work to make it more + * sensible but for now we store it. + * + * @var array + */ + protected $migrationInfo = []; + + /** + * @return array + */ + public function getMigrationInfo(): array { + return $this->migrationInfo; + } + + /** + * @param array $migrationInfo + */ + public function setMigrationInfo(array $migrationInfo) { + $this->migrationInfo = $migrationInfo; + } + /** * @return mixed */ @@ -129,4 +153,41 @@ class CRM_Dedupe_MergeHandler { return \Civi::$statics[__CLASS__]['dynamic']; } + /** + * Get the location blocks designated to be moved during the merge. + * + * Note this is a refactoring step and future refactors should develop a more coherent array + * + * @return array + * The format is ['address' => [0 => ['is_replace' => TRUE]], 'email' => [0...],[1....] + * where the entity is address, the internal indexing for the addresses on both contact is 1 + * and the intent to replace the existing address is TRUE. + */ + public function getLocationBlocksToMerge(): array { + $locBlocks = []; + foreach ($this->getMigrationInfo() as $key => $value) { + $isLocationField = (substr($key, 0, 14) === 'move_location_' and $value != NULL); + if (!$isLocationField) { + continue; + } + $locField = explode('_', $key); + $fieldName = $locField[2]; + $fieldCount = $locField[3]; + + // Set up the operation type (add/overwrite) + // Ignore operation for websites + // @todo Tidy this up + $operation = 0; + if ($fieldName !== 'website') { + $operation = $this->getMigrationInfo()['location_blocks'][$fieldName][$fieldCount]['operation'] ?? NULL; + } + // default operation is overwrite. + if (!$operation) { + $operation = 2; + } + $locBlocks[$fieldName][$fieldCount]['is_replace'] = $operation === 2; + } + return $locBlocks; + } + } diff --git a/civicrm/CRM/Dedupe/Merger.php b/civicrm/CRM/Dedupe/Merger.php index 74016a80a2..69827f1619 100644 --- a/civicrm/CRM/Dedupe/Merger.php +++ b/civicrm/CRM/Dedupe/Merger.php @@ -1198,7 +1198,7 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m $rows["move_$field"] = [ 'main' => self::getFieldValueAndLabel($field, $main)['label'], 'other' => self::getFieldValueAndLabel($field, $other)['label'], - 'title' => $fields[$field]['title'], + 'title' => $fields[$field]['html']['label'] ?? $fields[$field]['title'], ]; $value = self::getFieldValueAndLabel($field, $other)['value']; @@ -1401,7 +1401,9 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m $removeTables = array_merge($moveTables, $relTables[substr($key, 5)]['tables']); } } - self::mergeLocations($mainId, $otherId, $migrationInfo); + $mergeHandler = new CRM_Dedupe_MergeHandler((int) $mainId, (int) $otherId); + $mergeHandler->setMigrationInfo($migrationInfo); + self::mergeLocations($mergeHandler); // **** Do contact related migrations // @todo - move all custom field processing to the move class & eventually have an @@ -1409,7 +1411,7 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m $customFieldBAO = new CRM_Core_BAO_CustomField(); $customFieldBAO->move($otherId, $mainId, $submittedCustomFields); // add the related tables and unset the ones that don't sport any of the duplicate contact's info - $mergeHandler = new CRM_Dedupe_MergeHandler((int) $mainId, (int) $otherId); + CRM_Dedupe_Merger::moveContactBelongings($mergeHandler, $moveTables, $tableOperations); unset($moveTables, $tableOperations); @@ -1788,43 +1790,19 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m * The use of the new hook is tested, including the fact it is called before contributions are merged, as this * is likely to be significant data in merge hooks. * - * @param int $mainId - * @param int $otherId - * - * @param array $migrationInfo - * Migration info for the merge. This is passed to the hook as informational only. + * @param \CRM_Dedupe_MergeHandler $mergeHandler */ - public static function mergeLocations($mainId, $otherId, $migrationInfo) { - foreach ($migrationInfo as $key => $value) { - $isLocationField = (substr($key, 0, 14) === 'move_location_' and $value != NULL); - if (!$isLocationField) { - continue; - } - $locField = explode('_', $key); - $fieldName = $locField[2]; - $fieldCount = $locField[3]; - - // Set up the operation type (add/overwrite) - // Ignore operation for websites - // @todo Tidy this up - $operation = 0; - if ($fieldName !== 'website') { - $operation = $migrationInfo['location_blocks'][$fieldName][$fieldCount]['operation'] ?? NULL; - } - // default operation is overwrite. - if (!$operation) { - $operation = 2; - } - $locBlocks[$fieldName][$fieldCount]['operation'] = $operation; - } + public static function mergeLocations($mergeHandler) { + $locBlocks = $mergeHandler->getLocationBlocksToMerge(); $blocksDAO = []; + $migrationInfo = $mergeHandler->getMigrationInfo(); // @todo Handle OpenID (not currently in API). if (!empty($locBlocks)) { $locationBlocks = self::getLocationBlockInfo(); - $primaryBlockIds = CRM_Contact_BAO_Contact::getLocBlockIds($mainId, ['is_primary' => 1]); - $billingBlockIds = CRM_Contact_BAO_Contact::getLocBlockIds($mainId, ['is_billing' => 1]); + $primaryBlockIds = CRM_Contact_BAO_Contact::getLocBlockIds($mergeHandler->getToKeepID(), ['is_primary' => 1]); + $billingBlockIds = CRM_Contact_BAO_Contact::getLocBlockIds($mergeHandler->getToKeepID(), ['is_billing' => 1]); foreach ($locBlocks as $name => $block) { $blocksDAO[$name] = ['delete' => [], 'update' => []]; @@ -1845,7 +1823,7 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m // For the block which belongs to other-contact, link the location block to main-contact $otherBlockDAO = new $daoName(); - $otherBlockDAO->contact_id = $mainId; + $otherBlockDAO->contact_id = $mergeHandler->getToKeepID(); // Get the ID of this block on the 'other' contact, otherwise skip $otherBlockDAO->id = $otherBlockId; @@ -1884,9 +1862,8 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m $otherBlockDAO->is_billing = 0; } - $operation = CRM_Utils_Array::value('operation', $values, 2); // overwrite - need to delete block which belongs to main-contact. - if (!empty($mainBlockId) && ($operation == 2)) { + if (!empty($mainBlockId) && $values['is_replace']) { $deleteDAO = new $daoName(); $deleteDAO->id = $mainBlockId; $deleteDAO->find(TRUE); @@ -1906,7 +1883,7 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m } } - CRM_Utils_Hook::alterLocationMergeData($blocksDAO, $mainId, $otherId, $migrationInfo); + CRM_Utils_Hook::alterLocationMergeData($blocksDAO, $mergeHandler->getToKeepID(), $mergeHandler->getToRemoveID(), $migrationInfo); foreach ($blocksDAO as $blockDAOs) { if (!empty($blockDAOs['update'])) { foreach ($blockDAOs['update'] as $blockDAO) { @@ -1964,12 +1941,31 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m CRM_Core_BAO_PrevNextCache::markConflict($mainId, $otherId, $cacheKeyString, $conflicts, $mode); } else { - CRM_Core_BAO_PrevNextCache::deletePair($mainId, $otherId, $cacheKeyString); + self::deletePairFromPrevNextCache((int) $mainId, (int) $otherId); } self::releaseLocks($locks); return $resultStats; } + /** + * Delete merged pair from the previous next cache table as the are no longer a merge candidate. + * + * It's possible there may be more than one set of merge results cached, with different cache keys. + * Once we have merged a pair these should all go (even from a different merge search) as they + * can only be merged once. + * + * @param int $contactID1 + * @param int $contactID2 + */ + protected static function deletePairFromPrevNextCache(int $contactID1, int $contactID2) { + CRM_Core_DAO::executeQuery(" + DELETE FROM civicrm_prevnext_cache + WHERE entity_table = 'civicrm_contact' + AND (entity_id1 = %1 AND entity_id2 = %2) OR (entity_id1 = %2 AND entity_id2 = %1)", + [1 => [$contactID1, 'Integer'], 2 => [$contactID2, 'Integer']] + ); + } + /** * Replace the pseudo QFKey with zero if it is present. * diff --git a/civicrm/CRM/Event/BAO/Event.php b/civicrm/CRM/Event/BAO/Event.php index 794e5d14e5..b583fdd099 100644 --- a/civicrm/CRM/Event/BAO/Event.php +++ b/civicrm/CRM/Event/BAO/Event.php @@ -1158,6 +1158,8 @@ WHERE civicrm_event.is_active = 1 'conference_sessions' => $sessions, 'credit_card_number' => CRM_Utils_System::mungeCreditCard(CRM_Utils_Array::value('credit_card_number', $participantParams)), 'credit_card_exp_date' => CRM_Utils_Date::mysqlToIso(CRM_Utils_Date::format(CRM_Utils_Array::value('credit_card_exp_date', $participantParams))), + 'selfcancelxfer_time' => abs($values['event']['selfcancelxfer_time']), + 'selfservice_preposition' => $values['event']['selfcancelxfer_time'] < 0 ? 'after' : 'before', ]); // CRM-13890 : NOTE wait list condition need to be given so that diff --git a/civicrm/CRM/Event/BAO/Participant.php b/civicrm/CRM/Event/BAO/Participant.php index 1f19c32f74..f18198b209 100644 --- a/civicrm/CRM/Event/BAO/Participant.php +++ b/civicrm/CRM/Event/BAO/Participant.php @@ -1877,7 +1877,6 @@ WHERE civicrm_participant.contact_id = {$contactID} AND * Evaluate whether a participant record is eligible for self-service transfer/cancellation. If so, * return additional participant/event details. * - * TODO: This function fails when the "hours until self-service" is less than zero. * @param int $participantId * @param string $url * @param bool $isBackOffice @@ -1930,7 +1929,12 @@ WHERE civicrm_participant.contact_id = {$contactID} AND $cancelDeadline = (new Datetime($start_date))->sub($cancelInterval); if ($timenow > $cancelDeadline) { $details['eligible'] = FALSE; - $details['ineligible_message'] = ts("Registration for this event cannot be cancelled or transferred less than %1 hours prior to the event's start time. Contact the event organizer if you have questions.", [1 => $time_limit]); + // Change the language of the status message based on whether the waitlist time limit is positive or negative. + $afterOrPrior = $time_limit < 0 ? 'after' : 'prior'; + $moreOrLess = $time_limit < 0 ? 'more' : 'less'; + $details['ineligible_message'] = ts("Registration for this event cannot be cancelled or transferred %1 than %2 hours %3 to the event's start time. Contact the event organizer if you have questions.", + [1 => $moreOrLess, 2 => $cancelHours, 3 => $afterOrPrior]); + } } return $details; diff --git a/civicrm/CRM/Event/Cart/DAO/Cart.php b/civicrm/CRM/Event/Cart/DAO/Cart.php index 4683add13d..38da6b3227 100644 --- a/civicrm/CRM/Event/Cart/DAO/Cart.php +++ b/civicrm/CRM/Event/Cart/DAO/Cart.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Event/Cart/Cart.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:c0fdef43850965dce80a73e66caa3ceb) + * (GenCodeChecksum:b4aacbeb6deddb31e520ce700e774db5) */ /** * Database access object for the Cart entity. */ class CRM_Event_Cart_DAO_Cart extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = ''; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Event/Cart/DAO/EventInCart.php b/civicrm/CRM/Event/Cart/DAO/EventInCart.php index e10ee7974f..d1550cd54a 100644 --- a/civicrm/CRM/Event/Cart/DAO/EventInCart.php +++ b/civicrm/CRM/Event/Cart/DAO/EventInCart.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Event/Cart/EventInCart.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:a995a300028b34f7b866a41b2e2b96bf) + * (GenCodeChecksum:b1cb9524ae26740c93dda80d0cb4ff91) */ /** * Database access object for the EventInCart entity. */ class CRM_Event_Cart_DAO_EventInCart extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = ''; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Event/DAO/Event.php b/civicrm/CRM/Event/DAO/Event.php index adcf39c5d7..a7febe5bfe 100644 --- a/civicrm/CRM/Event/DAO/Event.php +++ b/civicrm/CRM/Event/DAO/Event.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Event/Event.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:331a210ceb36b5e9460705dbfbe71abf) + * (GenCodeChecksum:82ba48cbb804cf6f4b26fa50f07d44db) */ /** * Database access object for the Event entity. */ class CRM_Event_DAO_Event extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.7'; /** * Static instance to hold the table name. @@ -865,6 +867,7 @@ class CRM_Event_DAO_Event extends CRM_Core_DAO { 'entity' => 'Event', 'bao' => 'CRM_Event_BAO_Event', 'localizable' => 0, + 'serialize' => self::SERIALIZE_SEPARATOR_TRIMMED, 'html' => [ 'type' => 'Select', ], diff --git a/civicrm/CRM/Event/DAO/Participant.php b/civicrm/CRM/Event/DAO/Participant.php index 09f5bc0a93..94db537c3a 100644 --- a/civicrm/CRM/Event/DAO/Participant.php +++ b/civicrm/CRM/Event/DAO/Participant.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Event/Participant.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:5ae3aa69ac3d004f649038adbf993b8b) + * (GenCodeChecksum:bf8ed42264e81ccaef0ae236242990d0) */ /** * Database access object for the Participant entity. */ class CRM_Event_DAO_Participant extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.7'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Event/DAO/ParticipantPayment.php b/civicrm/CRM/Event/DAO/ParticipantPayment.php index b45665ccf3..ee0070dfc2 100644 --- a/civicrm/CRM/Event/DAO/ParticipantPayment.php +++ b/civicrm/CRM/Event/DAO/ParticipantPayment.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Event/ParticipantPayment.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:ad58ecbb570f97b4a2d459d750e8f9b3) + * (GenCodeChecksum:0c828890e84b791e0432445eb2d01086) */ /** * Database access object for the ParticipantPayment entity. */ class CRM_Event_DAO_ParticipantPayment extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.7'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Event/DAO/ParticipantStatusType.php b/civicrm/CRM/Event/DAO/ParticipantStatusType.php index a21a423e1a..2b6e4f8f03 100644 --- a/civicrm/CRM/Event/DAO/ParticipantStatusType.php +++ b/civicrm/CRM/Event/DAO/ParticipantStatusType.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Event/ParticipantStatusType.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:ede2497e706ee17bb6dce92b72a2c535) + * (GenCodeChecksum:4a3012f88c67826cb4264a3340e908ec) */ /** * Database access object for the ParticipantStatusType entity. */ class CRM_Event_DAO_ParticipantStatusType extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '3.0'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Event/Form/ManageEvent/Fee.php b/civicrm/CRM/Event/Form/ManageEvent/Fee.php index 1462a3697c..40b40fc912 100644 --- a/civicrm/CRM/Event/Form/ManageEvent/Fee.php +++ b/civicrm/CRM/Event/Form/ManageEvent/Fee.php @@ -289,7 +289,7 @@ class CRM_Event_Form_ManageEvent_Fee extends CRM_Event_Form_ManageEvent { // value $this->add('text', "value[$i]", ts('Value'), CRM_Core_DAO::getAttribute('CRM_Core_DAO_OptionValue', 'value')); - $this->addRule("value[$i]", ts('Please enter a valid money value for this field (e.g. %1).', [1 => CRM_Utils_Money::format('99.99', ' ')]), 'money'); + $this->addRule("value[$i]", ts('Please enter a valid money value for this field (e.g. %1).', [1 => CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency('99.99')]), 'money'); // default $default[] = $this->createElement('radio', NULL, NULL, NULL, $i); @@ -515,7 +515,7 @@ class CRM_Event_Form_ManageEvent_Fee extends CRM_Event_Form_ManageEvent { // value for ($j = 1; $j <= self::NUM_DISCOUNT; $j++) { $this->add('text', "discounted_value[$i][$j]", ts('Value'), ['size' => 10]); - $this->addRule("discounted_value[$i][$j]", ts('Please enter a valid money value for this field (e.g. %1).', [1 => CRM_Utils_Money::format('99.99', ' ')]), 'money'); + $this->addRule("discounted_value[$i][$j]", ts('Please enter a valid money value for this field (e.g. %1).', [1 => CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency('99.99')]), 'money'); } // default @@ -541,7 +541,7 @@ class CRM_Event_Form_ManageEvent_Fee extends CRM_Event_Form_ManageEvent { } if (!empty($params['payment_processor'])) { - $params['payment_processor'] = implode(CRM_Core_DAO::VALUE_SEPARATOR, array_keys($params['payment_processor'])); + $params['payment_processor'] = array_keys($params['payment_processor']); } else { $params['payment_processor'] = 'null'; @@ -810,7 +810,7 @@ class CRM_Event_Form_ManageEvent_Fee extends CRM_Event_Form_ManageEvent { $params['value'][$index] = CRM_Utils_Rule::cleanMoney(trim($value)); } } - foreach ($params['discounted_value'] as $index => $discountedValueSet) { + foreach ($params['discounted_value'] ?? [] as $index => $discountedValueSet) { foreach ($discountedValueSet as $innerIndex => $value) { if (CRM_Utils_System::isNull($value)) { unset($params['discounted_value'][$index][$innerIndex]); diff --git a/civicrm/CRM/Event/Form/Registration.php b/civicrm/CRM/Event/Form/Registration.php index 97ecf1f6f8..4b2692e67b 100644 --- a/civicrm/CRM/Event/Form/Registration.php +++ b/civicrm/CRM/Event/Form/Registration.php @@ -18,6 +18,7 @@ * This class generates form components for processing Event. */ class CRM_Event_Form_Registration extends CRM_Core_Form { + use CRM_Financial_Form_FrontEndPaymentFormTrait; /** @@ -142,7 +143,9 @@ class CRM_Event_Form_Registration extends CRM_Core_Form { public $_isEventFull; public $_lineItem; + public $_lineItemParticipantsCount; + public $_availableRegistrations; /** @@ -224,7 +227,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form { $this->_values = $this->_fields = []; //retrieve event information - $params = array('id' => $this->_eventId); + $params = ['id' => $this->_eventId]; CRM_Event_BAO_Event::retrieve($params, $this->_values['event']); // check for is_monetary status $isMonetary = $this->_values['event']['is_monetary'] ?? NULL; @@ -294,12 +297,12 @@ class CRM_Event_Form_Registration extends CRM_Core_Form { self::initEventFee($this, $this->_eventId); // get the profile ids - $ufJoinParams = array( + $ufJoinParams = [ 'entity_table' => 'civicrm_event', // CRM-4377: CiviEvent for the main participant, CiviEvent_Additional for additional participants 'module' => 'CiviEvent', 'entity_id' => $this->_eventId, - ); + ]; list($this->_values['custom_pre_id'], $this->_values['custom_post_id'] ) = CRM_Core_BAO_UFJoin::getUFGroupIds($ufJoinParams); @@ -336,7 +339,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form { if ($this->_values['event']['is_monetary']) { CRM_Core_Payment_Form::setPaymentFieldsByProcessor($this, $this->_paymentProcessor); } - $params = array('entity_id' => $this->_eventId, 'entity_table' => 'civicrm_event'); + $params = ['entity_id' => $this->_eventId, 'entity_table' => 'civicrm_event']; $this->_values['location'] = CRM_Core_BAO_Location::getValues($params, TRUE); $this->set('values', $this->_values); @@ -344,9 +347,9 @@ class CRM_Event_Form_Registration extends CRM_Core_Form { $this->_availableRegistrations = CRM_Event_BAO_Participant::eventFull( - $this->_values['event']['id'], TRUE, - CRM_Utils_Array::value('has_waitlist', $this->_values['event']) - ); + $this->_values['event']['id'], TRUE, + CRM_Utils_Array::value('has_waitlist', $this->_values['event']) + ); $this->set('availableRegistrations', $this->_availableRegistrations); } $this->assign_by_ref('paymentProcessor', $this->_paymentProcessor); @@ -429,14 +432,14 @@ class CRM_Event_Form_Registration extends CRM_Core_Form { $this->assign('billingName', $name); $this->set('name', $name); - $vars = array( + $vars = [ 'amount', 'currencyID', 'credit_card_type', 'trxn_id', 'amount_level', 'receive_date', - ); + ]; foreach ($vars as $v) { if (!empty($params[$v])) { @@ -501,10 +504,10 @@ class CRM_Event_Form_Registration extends CRM_Core_Form { // we don't allow conflicting fields to be // configured via profile - $fieldsToIgnore = array( + $fieldsToIgnore = [ 'participant_fee_amount' => 1, 'participant_fee_level' => 1, - ); + ]; if ($contactID) { //FIX CRM-9653 if (is_array($id)) { @@ -655,7 +658,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form { } if ($isPaidEvent && empty($form->_values['fee'])) { if (CRM_Utils_System::getClassName($form) != 'CRM_Event_Form_Participant') { - CRM_Core_Error::statusBounce(ts('No Fee Level(s) or Price Set is configured for this event.<br />Click <a href=\'%1\'>CiviEvent >> Manage Event >> Configure >> Event Fees</a> to configure the Fee Level(s) or Price Set for this event.', array(1 => CRM_Utils_System::url('civicrm/event/manage/fee', 'reset=1&action=update&id=' . $form->_eventId)))); + CRM_Core_Error::statusBounce(ts('No Fee Level(s) or Price Set is configured for this event.<br />Click <a href=\'%1\'>CiviEvent >> Manage Event >> Configure >> Event Fees</a> to configure the Fee Level(s) or Price Set for this event.', [1 => CRM_Utils_System::url('civicrm/event/manage/fee', 'reset=1&action=update&id=' . $form->_eventId)])); } } } @@ -788,7 +791,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form { } $participantFields = CRM_Event_DAO_Participant::fields(); - $participantParams = array( + $participantParams = [ 'id' => $params['participant_id'] ?? NULL, 'contact_id' => $contactID, 'event_id' => $form->_eventId ? $form->_eventId : $params['event_id'], @@ -808,7 +811,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form { 'discount_id' => $params['discount_id'] ?? NULL, 'fee_currency' => $params['currencyID'] ?? NULL, 'campaign_id' => $params['campaign_id'] ?? NULL, - ); + ]; if ($form->_action & CRM_Core_Action::PREVIEW || CRM_Utils_Array::value('mode', $params) == 'test') { $participantParams['is_test'] = 1; @@ -888,7 +891,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form { //first format the params. if ($singleFormParams) { $params = self::formatPriceSetParams($form, $params); - $params = array($params); + $params = [$params]; } foreach ($params as $key => $values) { @@ -976,10 +979,10 @@ class CRM_Event_Form_Registration extends CRM_Core_Form { $field = $priceSetDetails['fields'][$fieldId]; if ($field['html_type'] == 'Text') { $fieldOption = current($field['options']); - $value = array($fieldOption['id'] => $value); + $value = [$fieldOption['id'] => $value]; } else { - $value = array($value => TRUE); + $value = [$value => TRUE]; } } } @@ -1162,7 +1165,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form { foreach ($element->_options as $option) { if ($option['attr']['value'] === "crm_disabled_opt-{$value[0]}") { $placeholder = html_entity_decode($option['text'], ENT_QUOTES, "UTF-8"); - $element->updateAttributes(array('placeholder' => $placeholder)); + $element->updateAttributes(['placeholder' => $placeholder]); break; } } @@ -1192,11 +1195,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form { ) { return; } - foreach (array( - 'constantValues', - 'submitValues', - 'defaultValues', - ) as $val) { + foreach (['constantValues', 'submitValues', 'defaultValues'] as $val) { $values = $form->{"_$val"}; if (!is_array($values) || empty($values)) { continue; @@ -1331,7 +1330,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form { //validate for price field selection. if (empty($fieldSelected[$pNum])) { - $errors[$pNum]['_qf_default'] = ts('Select at least one option from Event Fee(s).'); + $errors[$pNum]['_qf_default'] = ts('SELECT at least one OPTION FROM EVENT Fee(s).'); } } @@ -1349,11 +1348,11 @@ class CRM_Event_Form_Registration extends CRM_Core_Form { } elseif (($optMax - $opDbCount) == 1) { $errors[$soldOutPnum[$optId]]["price_{$fieldId}"] - = ts('Sorry, currently only a single space is available for this option.', array(1 => ($optMax - $opDbCount))); + = ts('Sorry, currently only a single space is available for this option.', [1 => ($optMax - $opDbCount)]); } else { $errors[$soldOutPnum[$optId]]["price_{$fieldId}"] - = ts('Sorry, currently only %1 spaces are available for this option.', array(1 => ($optMax - $opDbCount))); + = ts('Sorry, currently only %1 spaces are available for this option.', [1 => ($optMax - $opDbCount)]); } } } @@ -1373,7 +1372,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form { $this->set('participantId', $this->_participantId); $ids = $participantValues = []; - $participantParams = array('id' => $this->_participantId); + $participantParams = ['id' => $this->_participantId]; CRM_Event_BAO_Participant::getValues($participantParams, $participantValues, $ids); $this->_values['participant'] = $participantValues[$this->_participantId]; $this->set('values', $this->_values); @@ -1391,11 +1390,12 @@ class CRM_Event_Form_Registration extends CRM_Core_Form { /** * Check if event is valid. * + * @param string $redirect + * * @todo - combine this with CRM_Event_BAO_Event::validRegistrationRequest * (probably extract relevant values here & call that with them & handle bounces & redirects here -as * those belong in the form layer) * - * @param string $redirect */ public function checkValidEvent($redirect = NULL) { // is the event active (enabled)? @@ -1423,7 +1423,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form { $startDate && $startDate >= $now ) { - CRM_Core_Error::statusBounce(ts('Registration for this event begins on %1', array(1 => CRM_Utils_Date::customFormat(CRM_Utils_Array::value('registration_start_date', $this->_values['event'])))), $redirect, ts('Sorry')); + CRM_Core_Error::statusBounce(ts('Registration for this event begins on %1', [1 => CRM_Utils_Date::customFormat(CRM_Utils_Array::value('registration_start_date', $this->_values['event']))]), $redirect, ts('Sorry')); } $regEndDate = CRM_Utils_Date::processDate(CRM_Utils_Array::value('registration_end_date', @@ -1435,7 +1435,7 @@ class CRM_Event_Form_Registration extends CRM_Core_Form { if (empty($regEndDate)) { $endDate = CRM_Utils_Date::customFormat(CRM_Utils_Array::value('event_end_date', $this->_values['event'])); } - CRM_Core_Error::statusBounce(ts('Registration for this event ended on %1', array(1 => $endDate)), $redirect, ts('Sorry')); + CRM_Core_Error::statusBounce(ts('Registration for this event ended on %1', [1 => $endDate]), $redirect, ts('Sorry')); } } diff --git a/civicrm/CRM/Event/Form/Registration/Register.php b/civicrm/CRM/Event/Form/Registration/Register.php index e44c213172..bd133450b0 100644 --- a/civicrm/CRM/Event/Form/Registration/Register.php +++ b/civicrm/CRM/Event/Form/Registration/Register.php @@ -391,8 +391,7 @@ class CRM_Event_Form_Registration_Register extends CRM_Event_Form_Registration { self::buildAmount($this); } - $pps = $this->getProcessors(); - if ($this->getContactID() === 0 && !$this->_values['event']['is_multiple_registrations']) { + if ($contactID === 0 && !$this->_values['event']['is_multiple_registrations']) { //@todo we are blocking for multiple registrations because we haven't tested $this->addCIDZeroOptions(); } @@ -405,9 +404,7 @@ class CRM_Event_Form_Registration_Register extends CRM_Event_Form_Registration { $this->assign('bypassPayment', $bypassPayment); - $userID = $this->getContactID(); - - if (!$userID) { + if (!$contactID) { $createCMSUser = FALSE; if ($this->_values['custom_pre_id']) { diff --git a/civicrm/CRM/Event/Form/Task/Batch.php b/civicrm/CRM/Event/Form/Task/Batch.php index 9a52015e42..bc00eccc68 100644 --- a/civicrm/CRM/Event/Form/Task/Batch.php +++ b/civicrm/CRM/Event/Form/Task/Batch.php @@ -171,24 +171,22 @@ class CRM_Event_Form_Task_Batch extends CRM_Event_Form_Task { $customValue['extends_entity_column_value'] ); } - $entityColumnValueRole = $entityColumnValue[$roleId] ?? NULL; - $entityColumnValueEventType = in_array($eventTypeId, $entityColumnValue) ? $eventTypeId : NULL; if (($this->_roleCustomDataTypeID == $customValue['extends_entity_column_id']) && - ($entityColumnValueRole) + in_array($roleId, $entityColumnValue) ) { CRM_Core_BAO_UFGroup::buildProfile($this, $field, NULL, $participantId); } elseif (($this->_eventNameCustomDataTypeID == $customValue['extends_entity_column_id']) && - ($eventId == $entityColumnValueRole) + in_array($eventId, $entityColumnValue) ) { CRM_Core_BAO_UFGroup::buildProfile($this, $field, NULL, $participantId); } elseif ($this->_eventTypeCustomDataTypeID == $customValue['extends_entity_column_id'] && - ($entityColumnValueEventType == $eventTypeId) + in_array($eventTypeId, $entityColumnValue) ) { CRM_Core_BAO_UFGroup::buildProfile($this, $field, NULL, $participantId); } - elseif (CRM_Utils_System::isNull($entityColumnValueRole)) { + elseif (CRM_Utils_System::isNull($entityColumnValue)) { CRM_Core_BAO_UFGroup::buildProfile($this, $field, NULL, $participantId); } } @@ -262,14 +260,10 @@ class CRM_Event_Form_Task_Batch extends CRM_Event_Form_Task { * @param int $participantId * @param int $statusId * - * @return mixed * @throws \CRM_Core_Exception * @throws \CiviCRM_API3_Exception */ public static function updatePendingOnlineContribution($participantId, $statusId) { - if (!$participantId || !$statusId) { - return NULL; - } $contributionId = CRM_Contribute_BAO_Contribution::checkOnlinePendingContribution($participantId, 'Event' @@ -294,7 +288,7 @@ class CRM_Event_Form_Task_Batch extends CRM_Event_Form_Task { $contributionStatusId = array_search('Cancelled', $contributionStatuses); } - if (!$contributionStatusId) { + if (!$contributionStatusId || !$participantId || !$contributionId) { return; } @@ -307,9 +301,7 @@ class CRM_Event_Form_Task_Batch extends CRM_Event_Form_Task { ]; //change related contribution status. - $updatedStatusId = self::updateContributionStatus($params); - - return $updatedStatusId; + self::updateContributionStatus($params); } /** @@ -317,7 +309,6 @@ class CRM_Event_Form_Task_Batch extends CRM_Event_Form_Task { * * @param array $params * - * @return NULL|int * @throws \CRM_Core_Exception * @throws \CiviCRM_API3_Exception * @throws \Exception @@ -334,10 +325,6 @@ class CRM_Event_Form_Task_Batch extends CRM_Event_Form_Task { $componentName = $params['componentName'] ?? NULL; $contributionId = $params['contribution_id'] ?? NULL; - if (!$contributionId || !$componentId || !$componentName || !$statusId) { - return NULL; - } - $input = $ids = $objects = []; //get the required ids. @@ -391,13 +378,13 @@ class CRM_Event_Form_Task_Batch extends CRM_Event_Form_Task { $transaction = new CRM_Core_Transaction(); $baseIPN->cancelled($objects, $transaction, $input); $transaction->commit(); - return $statusId; + return; } - elseif ($statusId == $contributionStatuses['Failed']) { + if ($statusId == $contributionStatuses['Failed']) { $transaction = new CRM_Core_Transaction(); $baseIPN->failed($objects, $transaction, $input); $transaction->commit(); - return $statusId; + return; } // status is not pending @@ -430,12 +417,14 @@ class CRM_Event_Form_Task_Batch extends CRM_Event_Form_Task { //complete the contribution. // @todo use the api - ie civicrm_api3('Contribution', 'completetransaction', $input); // as this method is not preferred / supported. - CRM_Contribute_BAO_Contribution::completeOrder($input, $ids, $objects); + CRM_Contribute_BAO_Contribution::completeOrder($input, [ + 'related_contact' => $ids['related_contact'] ?? NULL, + 'participant' => !empty($objects['participant']) ? $objects['participant']->id : NULL, + 'contributionRecur' => NULL, + ], $objects); // reset template values before processing next transactions $template->clearTemplateVars(); - - return $statusId; } /** @@ -515,7 +504,7 @@ class CRM_Event_Form_Task_Batch extends CRM_Event_Form_Task { if ($statusChange) { CRM_Event_BAO_Participant::transitionParticipants([$key], $value['status_id'], $fromStatusId); } - if ($relatedStatusChange) { + if ($relatedStatusChange && $key && $value['status_id']) { //update related contribution status, CRM-4395 self::updatePendingOnlineContribution($key, $value['status_id']); } diff --git a/civicrm/CRM/Event/Import/Form/MapField.php b/civicrm/CRM/Event/Import/Form/MapField.php index 8182aa80db..50c3e1f11f 100644 --- a/civicrm/CRM/Event/Import/Form/MapField.php +++ b/civicrm/CRM/Event/Import/Form/MapField.php @@ -387,7 +387,7 @@ class CRM_Event_Import_Form_MapField extends CRM_Import_Form_MapField { } $fileName = $this->controller->exportValue('DataSource', 'uploadFile'); - $seperator = $this->controller->exportValue('DataSource', 'fieldSeparator'); + $separator = $this->controller->exportValue('DataSource', 'fieldSeparator'); $skipColumnHeader = $this->controller->exportValue('DataSource', 'skipColumnHeader'); $mapperKeys = []; @@ -461,7 +461,7 @@ class CRM_Event_Import_Form_MapField extends CRM_Import_Form_MapField { } $parser = new CRM_Event_Import_Parser_Participant($mapperKeysMain); - $parser->run($fileName, $seperator, $mapper, $skipColumnHeader, + $parser->run($fileName, $separator, $mapper, $skipColumnHeader, CRM_Import_Parser::MODE_PREVIEW, $this->get('contactType') ); // add all the necessary variables to the form diff --git a/civicrm/CRM/Event/Import/Form/Preview.php b/civicrm/CRM/Event/Import/Form/Preview.php index 0a7e5ca96d..629ed8e5cf 100644 --- a/civicrm/CRM/Event/Import/Form/Preview.php +++ b/civicrm/CRM/Event/Import/Form/Preview.php @@ -95,7 +95,7 @@ class CRM_Event_Import_Form_Preview extends CRM_Import_Form_Preview { */ public function postProcess() { $fileName = $this->controller->exportValue('DataSource', 'uploadFile'); - $seperator = $this->controller->exportValue('DataSource', 'fieldSeparator'); + $separator = $this->controller->exportValue('DataSource', 'fieldSeparator'); $skipColumnHeader = $this->controller->exportValue('DataSource', 'skipColumnHeader'); $invalidRowCount = $this->get('invalidRowCount'); $conflictRowCount = $this->get('conflictRowCount'); @@ -119,7 +119,7 @@ class CRM_Event_Import_Form_Preview extends CRM_Import_Form_Preview { } $mapperFields[] = implode(' - ', $header); } - $parser->run($fileName, $seperator, + $parser->run($fileName, $separator, $mapperFields, $skipColumnHeader, CRM_Import_Parser::MODE_IMPORT, diff --git a/civicrm/CRM/Event/Import/Parser.php b/civicrm/CRM/Event/Import/Parser.php index 46e74961b9..097aa1313f 100644 --- a/civicrm/CRM/Event/Import/Parser.php +++ b/civicrm/CRM/Event/Import/Parser.php @@ -30,7 +30,7 @@ abstract class CRM_Event_Import_Parser extends CRM_Import_Parser { * * @var string */ - protected $_seperator; + protected $_separator; /** * Total number of lines in file. @@ -48,7 +48,7 @@ abstract class CRM_Event_Import_Parser extends CRM_Import_Parser { /** * @param string $fileName - * @param string $seperator + * @param string $separator * @param $mapper * @param bool $skipColumnHeader * @param int $mode @@ -60,7 +60,7 @@ abstract class CRM_Event_Import_Parser extends CRM_Import_Parser { */ public function run( $fileName, - $seperator = ',', + $separator = ',', &$mapper, $skipColumnHeader = FALSE, $mode = self::MODE_PREVIEW, @@ -89,7 +89,7 @@ abstract class CRM_Event_Import_Parser extends CRM_Import_Parser { $this->_haveColumnHeader = $skipColumnHeader; - $this->_seperator = $seperator; + $this->_separator = $separator; $fd = fopen($fileName, "r"); if (!$fd) { @@ -116,7 +116,7 @@ abstract class CRM_Event_Import_Parser extends CRM_Import_Parser { while (!feof($fd)) { $this->_lineCount++; - $values = fgetcsv($fd, 8192, $seperator); + $values = fgetcsv($fd, 8192, $separator); if (!$values) { continue; } @@ -343,7 +343,7 @@ abstract class CRM_Event_Import_Parser extends CRM_Import_Parser { public function set($store, $mode = self::MODE_SUMMARY) { $store->set('fileSize', $this->_fileSize); $store->set('lineCount', $this->_lineCount); - $store->set('seperator', $this->_seperator); + $store->set('separator', $this->_separator); $store->set('fields', $this->getSelectValues()); $store->set('fieldTypes', $this->getSelectTypes()); diff --git a/civicrm/CRM/Export/BAO/ExportProcessor.php b/civicrm/CRM/Export/BAO/ExportProcessor.php index 1dc733d9fd..c1231fecb7 100644 --- a/civicrm/CRM/Export/BAO/ExportProcessor.php +++ b/civicrm/CRM/Export/BAO/ExportProcessor.php @@ -639,6 +639,9 @@ class CRM_Export_BAO_ExportProcessor { $queryFields['world_region']['context'] = 'country'; $queryFields['state_province']['context'] = 'province'; $queryFields['contact_id'] = ['title' => ts('Contact ID'), 'type' => CRM_Utils_Type::T_INT]; + // Set the label to gender for gender_id as we it's ... magic (not in a good way). + // In other places the query object offers e.g contribution_status & contribution_status_id + $queryFields['gender_id']['title'] = ts('Gender'); $this->queryFields = $queryFields; } @@ -971,7 +974,6 @@ class CRM_Export_BAO_ExportProcessor { if ($this->isHouseholdToSkip($iterationDAO->contact_id)) { return FALSE; } - $phoneTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Phone', 'phone_type_id'); $imProviders = CRM_Core_PseudoConstant::get('CRM_Core_DAO_IM', 'provider_id'); $row = []; @@ -1002,10 +1004,7 @@ class CRM_Export_BAO_ExportProcessor { if (property_exists($iterationDAO, $field)) { $fieldValue = $iterationDAO->$field; // to get phone type from phone type id - if ($field == 'phone_type_id' && isset($phoneTypes[$fieldValue])) { - $fieldValue = $phoneTypes[$fieldValue]; - } - elseif ($field == 'provider_id' || $field == 'im_provider') { + if ($field == 'provider_id' || $field == 'im_provider') { $fieldValue = $imProviders[$fieldValue] ?? NULL; } elseif (strstr($field, 'master_id')) { @@ -1152,7 +1151,7 @@ class CRM_Export_BAO_ExportProcessor { if (!empty($fieldSpec['context'])) { return $i18n->crm_translate($fieldValue, $fieldSpec); } - if (!empty($fieldSpec['pseudoconstant']) && !empty($fieldSpec['hasLocationType'])) { + if (!empty($fieldSpec['pseudoconstant']) && !empty($fieldSpec['hasLocationType']) && $fieldSpec['name'] !== 'phone_type_id') { if (!empty($fieldSpec['bao'])) { $transformedValue = CRM_Core_PseudoConstant::getLabel($fieldSpec['bao'], $fieldSpec['name'], $fieldValue); if ($transformedValue) { @@ -2171,10 +2170,7 @@ WHERE id IN ( $deleteIDString ) foreach ($value as $relationField => $relationValue) { if (is_object($relDAO) && property_exists($relDAO, $relationField)) { $fieldValue = $relDAO->$relationField; - if ($relationField == 'phone_type_id') { - $fieldValue = $phoneTypes[$relationValue]; - } - elseif ($relationField == 'provider_id') { + if ($relationField == 'provider_id') { $fieldValue = $imProviders[$relationValue] ?? NULL; } // CRM-13995 diff --git a/civicrm/CRM/Financial/BAO/Payment.php b/civicrm/CRM/Financial/BAO/Payment.php index f841363f14..2b93c545e1 100644 --- a/civicrm/CRM/Financial/BAO/Payment.php +++ b/civicrm/CRM/Financial/BAO/Payment.php @@ -157,6 +157,7 @@ class CRM_Financial_BAO_Payment { 'is_post_payment_create' => TRUE, 'is_email_receipt' => $params['is_send_contribution_notification'], 'trxn_date' => $params['trxn_date'], + 'payment_instrument_id' => $paymentTrxnParams['payment_instrument_id'], ]); // Get the trxn $trxnId = CRM_Core_BAO_FinancialTrxn::getFinancialTrxnId($contribution['id'], 'DESC'); @@ -486,6 +487,9 @@ class CRM_Financial_BAO_Payment { if ($outstandingBalance !== 0.0) { $ratio = $params['total_amount'] / $outstandingBalance; } + elseif ($params['total_amount'] < 0) { + $ratio = $params['total_amount'] / (float) CRM_Core_BAO_FinancialTrxn::getTotalPayments($params['contribution_id'], TRUE); + } else { // Help we are making a payment but no money is owed. We won't allocate the overpayment to any line item. $ratio = 0; @@ -495,12 +499,16 @@ class CRM_Financial_BAO_Payment { $lineItems[$lineItemID]['id'] = $lineItemID; $lineItems[$lineItemID]['paid'] = self::getAmountOfLineItemPaid($lineItemID); $lineItems[$lineItemID]['balance'] = $lineItem['subTotal'] - $lineItems[$lineItemID]['paid']; - if (!empty($lineItemOverrides)) { $lineItems[$lineItemID]['allocation'] = $lineItemOverrides[$lineItemID] ?? NULL; } else { - $lineItems[$lineItemID]['allocation'] = $lineItems[$lineItemID]['balance'] * $ratio; + if (empty($lineItems[$lineItemID]['balance']) && !empty($ratio) && $params['total_amount'] < 0) { + $lineItems[$lineItemID]['allocation'] = $lineItem['subTotal'] * $ratio; + } + else { + $lineItems[$lineItemID]['allocation'] = $lineItems[$lineItemID]['balance'] * $ratio; + } } } return $lineItems; diff --git a/civicrm/CRM/Financial/DAO/Currency.php b/civicrm/CRM/Financial/DAO/Currency.php index 20dff31e7f..f96650badc 100644 --- a/civicrm/CRM/Financial/DAO/Currency.php +++ b/civicrm/CRM/Financial/DAO/Currency.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Financial/Currency.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:9141d433586789f5ea9003fdbe9fdf9b) + * (GenCodeChecksum:5501c59b453dedfb8bba1f2fab44d9ea) */ /** * Database access object for the Currency entity. */ class CRM_Financial_DAO_Currency extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.7'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Financial/DAO/EntityFinancialAccount.php b/civicrm/CRM/Financial/DAO/EntityFinancialAccount.php index a23822f2b4..973a99ca83 100644 --- a/civicrm/CRM/Financial/DAO/EntityFinancialAccount.php +++ b/civicrm/CRM/Financial/DAO/EntityFinancialAccount.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Financial/EntityFinancialAccount.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:5ed0eeb7d3f5f2caf668448cd441b004) + * (GenCodeChecksum:c1d51696dd326b61f65fd064a355e7fb) */ /** * Database access object for the EntityFinancialAccount entity. */ class CRM_Financial_DAO_EntityFinancialAccount extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '4.3'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Financial/DAO/EntityFinancialTrxn.php b/civicrm/CRM/Financial/DAO/EntityFinancialTrxn.php index b74d080894..1f959ad611 100644 --- a/civicrm/CRM/Financial/DAO/EntityFinancialTrxn.php +++ b/civicrm/CRM/Financial/DAO/EntityFinancialTrxn.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Financial/EntityFinancialTrxn.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:a4b054ba0fc5a16cd650bda8941690b1) + * (GenCodeChecksum:12eb23afdf6c1208bdc01aa7db52770a) */ /** * Database access object for the EntityFinancialTrxn entity. */ class CRM_Financial_DAO_EntityFinancialTrxn extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '3.2'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Financial/DAO/FinancialAccount.php b/civicrm/CRM/Financial/DAO/FinancialAccount.php index f6912ae03c..d8f161e943 100644 --- a/civicrm/CRM/Financial/DAO/FinancialAccount.php +++ b/civicrm/CRM/Financial/DAO/FinancialAccount.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Financial/FinancialAccount.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:e9c9d66596703d9dfbf411034ce909c4) + * (GenCodeChecksum:b9f200ff95d9186180eff484dcd12a57) */ /** * Database access object for the FinancialAccount entity. */ class CRM_Financial_DAO_FinancialAccount extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '3.2'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Financial/DAO/FinancialItem.php b/civicrm/CRM/Financial/DAO/FinancialItem.php index 055c8a5de1..a59dc38e66 100644 --- a/civicrm/CRM/Financial/DAO/FinancialItem.php +++ b/civicrm/CRM/Financial/DAO/FinancialItem.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Financial/FinancialItem.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:4f2e977c0c7c668be342598772b0eb61) + * (GenCodeChecksum:8bb63ebee681c2eb4acbf8650b224dc2) */ /** * Database access object for the FinancialItem entity. */ class CRM_Financial_DAO_FinancialItem extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '4.3'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Financial/DAO/FinancialTrxn.php b/civicrm/CRM/Financial/DAO/FinancialTrxn.php index 47f7ae2197..157984607f 100644 --- a/civicrm/CRM/Financial/DAO/FinancialTrxn.php +++ b/civicrm/CRM/Financial/DAO/FinancialTrxn.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Financial/FinancialTrxn.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:7e2cf7d374cc6e0af61672c0318248b6) + * (GenCodeChecksum:5a4324ffe222bf724ab9d4cde26eb4c2) */ /** * Database access object for the FinancialTrxn entity. */ class CRM_Financial_DAO_FinancialTrxn extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.3'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Financial/DAO/FinancialType.php b/civicrm/CRM/Financial/DAO/FinancialType.php index a047fd348e..cd99c56281 100644 --- a/civicrm/CRM/Financial/DAO/FinancialType.php +++ b/civicrm/CRM/Financial/DAO/FinancialType.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Financial/FinancialType.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:81cc1b7a95feede610081f2066eeb147) + * (GenCodeChecksum:024b000d94adcc65200c00d7cef5e624) */ /** * Database access object for the FinancialType entity. */ class CRM_Financial_DAO_FinancialType extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.3'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Financial/DAO/PaymentProcessor.php b/civicrm/CRM/Financial/DAO/PaymentProcessor.php index 6122a23076..7f1e8334a9 100644 --- a/civicrm/CRM/Financial/DAO/PaymentProcessor.php +++ b/civicrm/CRM/Financial/DAO/PaymentProcessor.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Financial/PaymentProcessor.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:625ab4ba983447ea3e236b48491eeed6) + * (GenCodeChecksum:7e296728147d44cb68a9231c4995e461) */ /** * Database access object for the PaymentProcessor entity. */ class CRM_Financial_DAO_PaymentProcessor extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.8'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Financial/DAO/PaymentProcessorType.php b/civicrm/CRM/Financial/DAO/PaymentProcessorType.php index c942177b36..597b8d27f0 100644 --- a/civicrm/CRM/Financial/DAO/PaymentProcessorType.php +++ b/civicrm/CRM/Financial/DAO/PaymentProcessorType.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Financial/PaymentProcessorType.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:b57486d319cf2c2022b7447a6c53c199) + * (GenCodeChecksum:624a9a001f451b6eb17930a9abcceb3e) */ /** * Database access object for the PaymentProcessorType entity. */ class CRM_Financial_DAO_PaymentProcessorType extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.8'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Financial/DAO/PaymentToken.php b/civicrm/CRM/Financial/DAO/PaymentToken.php index 363de403c2..cb2498f083 100644 --- a/civicrm/CRM/Financial/DAO/PaymentToken.php +++ b/civicrm/CRM/Financial/DAO/PaymentToken.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Financial/PaymentToken.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:c135e4bffc93086e83c082bbebc84372) + * (GenCodeChecksum:ce51f1e6eaf6b29f3adeb67828e85507) */ /** * Database access object for the PaymentToken entity. */ class CRM_Financial_DAO_PaymentToken extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '4.6'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Friend/DAO/Friend.php b/civicrm/CRM/Friend/DAO/Friend.php index 854c5f5de7..1513ab9dcc 100644 --- a/civicrm/CRM/Friend/DAO/Friend.php +++ b/civicrm/CRM/Friend/DAO/Friend.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Friend/Friend.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:ed3cb6458bdc4ee011d6b30baa9e24ff) + * (GenCodeChecksum:3f1c976d43e312175e85da0427f5210d) */ /** * Database access object for the Friend entity. */ class CRM_Friend_DAO_Friend extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '2.0'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Grant/BAO/Query.php b/civicrm/CRM/Grant/BAO/Query.php index d91699ca81..f34f894612 100644 --- a/civicrm/CRM/Grant/BAO/Query.php +++ b/civicrm/CRM/Grant/BAO/Query.php @@ -328,10 +328,10 @@ class CRM_Grant_BAO_Query extends CRM_Core_BAO_Query { $form->addElement('checkbox', 'grant_decision_date_notset', ts('Date is not set'), NULL); $form->add('text', 'grant_amount_low', ts('Minimum Amount'), ['size' => 8, 'maxlength' => 8]); - $form->addRule('grant_amount_low', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::format('9.99', ' ')]), 'money'); + $form->addRule('grant_amount_low', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency('9.99')]), 'money'); $form->add('text', 'grant_amount_high', ts('Maximum Amount'), ['size' => 8, 'maxlength' => 8]); - $form->addRule('grant_amount_high', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::format('99.99', ' ')]), 'money'); + $form->addRule('grant_amount_high', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency('99.99')]), 'money'); self::addCustomFormFields($form, ['Grant']); diff --git a/civicrm/CRM/Grant/DAO/Grant.php b/civicrm/CRM/Grant/DAO/Grant.php index fa8dd9d09f..91b816359a 100644 --- a/civicrm/CRM/Grant/DAO/Grant.php +++ b/civicrm/CRM/Grant/DAO/Grant.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Grant/Grant.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:b09a04b8edb982e838510dfb9d7dbc35) + * (GenCodeChecksum:a2e43b7f0fb8547daf5ed874bf6174c5) */ /** * Database access object for the Grant entity. */ class CRM_Grant_DAO_Grant extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.8'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Group/Form/Edit.php b/civicrm/CRM/Group/Form/Edit.php index d9be20cdfb..e384dc36cc 100644 --- a/civicrm/CRM/Group/Form/Edit.php +++ b/civicrm/CRM/Group/Form/Edit.php @@ -371,7 +371,8 @@ WHERE title = %1 ); $group = CRM_Contact_BAO_Group::create($params); - + // Set the entity id so it is available to postProcess hook consumers + $this->setEntityId($group->id); //Remove any parent groups requested to be removed if (!empty($this->_groupValues['parents'])) { $parentGroupIds = explode(',', $this->_groupValues['parents']); diff --git a/civicrm/CRM/Logging/Differ.php b/civicrm/CRM/Logging/Differ.php index a124a40069..863322bb2a 100644 --- a/civicrm/CRM/Logging/Differ.php +++ b/civicrm/CRM/Logging/Differ.php @@ -28,7 +28,8 @@ class CRM_Logging_Differ { * @param string $interval */ public function __construct($log_conn_id, $log_date, $interval = '10 SECOND') { - $dsn = defined('CIVICRM_LOGGING_DSN') ? DB::parseDSN(CIVICRM_LOGGING_DSN) : DB::parseDSN(CIVICRM_DSN); + $dsn = defined('CIVICRM_LOGGING_DSN') ? CRM_Utils_SQL::autoSwitchDSN(CIVICRM_LOGGING_DSN) : CRM_Utils_SQL::autoSwitchDSN(CIVICRM_DSN); + $dsn = DB::parseDSN($dsn); $this->db = $dsn['database']; $this->log_conn_id = $log_conn_id; $this->log_date = $log_date; diff --git a/civicrm/CRM/Logging/ReportDetail.php b/civicrm/CRM/Logging/ReportDetail.php index d5822b183d..fdbd92216a 100644 --- a/civicrm/CRM/Logging/ReportDetail.php +++ b/civicrm/CRM/Logging/ReportDetail.php @@ -257,7 +257,8 @@ class CRM_Logging_ReportDetail extends CRM_Report_Form { * Store the dsn for the logging database in $this->db. */ protected function storeDB() { - $dsn = defined('CIVICRM_LOGGING_DSN') ? DB::parseDSN(CIVICRM_LOGGING_DSN) : DB::parseDSN(CIVICRM_DSN); + $dsn = defined('CIVICRM_LOGGING_DSN') ? CRM_Utils_SQL::autoSwitchDSN(CIVICRM_LOGGING_DSN) : CRM_Utils_SQL::autoSwitchDSN(CIVICRM_DSN); + $dsn = DB::parseDSN($dsn); $this->db = $dsn['database']; } diff --git a/civicrm/CRM/Logging/Reverter.php b/civicrm/CRM/Logging/Reverter.php index 72d729d03a..eb1c9dedc1 100644 --- a/civicrm/CRM/Logging/Reverter.php +++ b/civicrm/CRM/Logging/Reverter.php @@ -33,7 +33,8 @@ class CRM_Logging_Reverter { * @param $log_date */ public function __construct($log_conn_id, $log_date) { - $dsn = defined('CIVICRM_LOGGING_DSN') ? DB::parseDSN(CIVICRM_LOGGING_DSN) : DB::parseDSN(CIVICRM_DSN); + $dsn = defined('CIVICRM_LOGGING_DSN') ? CRM_Utils_SQL::autoSwitchDSN(CIVICRM_LOGGING_DSN) : CRM_Utils_SQL::autoSwitchDSN(CIVICRM_DSN); + $dsn = DB::parseDSN($dsn); $this->db = $dsn['database']; $this->log_conn_id = $log_conn_id; $this->log_date = $log_date; diff --git a/civicrm/CRM/Logging/Schema.php b/civicrm/CRM/Logging/Schema.php index e9160bbdef..25b63413d2 100644 --- a/civicrm/CRM/Logging/Schema.php +++ b/civicrm/CRM/Logging/Schema.php @@ -158,11 +158,13 @@ AND TABLE_NAME LIKE 'civicrm_%' $nonStandardTableNameString = $this->getNonStandardTableNameFilterString(); if (defined('CIVICRM_LOGGING_DSN')) { - $dsn = DB::parseDSN(CIVICRM_LOGGING_DSN); + $dsn = CRM_Utils_SQL::autoSwitchDSN(CIVICRM_LOGGING_DSN); + $dsn = DB::parseDSN($dsn); $this->useDBPrefix = (CIVICRM_LOGGING_DSN != CIVICRM_DSN); } else { - $dsn = DB::parseDSN(CIVICRM_DSN); + $dsn = CRM_Utils_SQL::autoSwitchDSN(CIVICRM_DSN); + $dsn = DB::parseDSN($dsn); $this->useDBPrefix = FALSE; } $this->db = $dsn['database']; diff --git a/civicrm/CRM/Mailing/DAO/BouncePattern.php b/civicrm/CRM/Mailing/DAO/BouncePattern.php index 1e6c539e5d..43822bf7c5 100644 --- a/civicrm/CRM/Mailing/DAO/BouncePattern.php +++ b/civicrm/CRM/Mailing/DAO/BouncePattern.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Mailing/BouncePattern.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:e36e2c40e59bfe04998b2e4e216724b6) + * (GenCodeChecksum:90abbaf8e68b5749a084a74d77dcc3b7) */ /** * Database access object for the BouncePattern entity. */ class CRM_Mailing_DAO_BouncePattern extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = ''; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Mailing/DAO/BounceType.php b/civicrm/CRM/Mailing/DAO/BounceType.php index 9bb3db1807..a31f8324f4 100644 --- a/civicrm/CRM/Mailing/DAO/BounceType.php +++ b/civicrm/CRM/Mailing/DAO/BounceType.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Mailing/BounceType.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:b2b6ab45aa6fed676d81a68651f0bf93) + * (GenCodeChecksum:4e77659bd433033396e84b6de32c99af) */ /** * Database access object for the BounceType entity. */ class CRM_Mailing_DAO_BounceType extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = ''; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Mailing/DAO/Mailing.php b/civicrm/CRM/Mailing/DAO/Mailing.php index e89653ee93..084489593d 100644 --- a/civicrm/CRM/Mailing/DAO/Mailing.php +++ b/civicrm/CRM/Mailing/DAO/Mailing.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Mailing/Mailing.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:bfcc85d4eb2bab05f214dd946e5fdef6) + * (GenCodeChecksum:6a9dc5aaff7aa7f5dcfe3f892255e357) */ /** * Database access object for the Mailing entity. */ class CRM_Mailing_DAO_Mailing extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = ''; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Mailing/DAO/MailingAB.php b/civicrm/CRM/Mailing/DAO/MailingAB.php index 4875b11c50..0e2215e1fc 100644 --- a/civicrm/CRM/Mailing/DAO/MailingAB.php +++ b/civicrm/CRM/Mailing/DAO/MailingAB.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Mailing/MailingAB.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:fd7f00955d0a5eb4ec56089ccd71f68d) + * (GenCodeChecksum:af0f7d34ddde7f3971aaac5abccfcd8c) */ /** * Database access object for the MailingAB entity. */ class CRM_Mailing_DAO_MailingAB extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = ''; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Mailing/DAO/MailingComponent.php b/civicrm/CRM/Mailing/DAO/MailingComponent.php index cd35b7cce0..f1b81f1562 100644 --- a/civicrm/CRM/Mailing/DAO/MailingComponent.php +++ b/civicrm/CRM/Mailing/DAO/MailingComponent.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Mailing/MailingComponent.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:1d0efc3e5ba8dcd6d6bbc422877b86b4) + * (GenCodeChecksum:ca95f8566048836c03e1dc58eb51ac11) */ /** * Database access object for the MailingComponent entity. */ class CRM_Mailing_DAO_MailingComponent extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = ''; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Mailing/DAO/MailingGroup.php b/civicrm/CRM/Mailing/DAO/MailingGroup.php index 6c1822212c..220bfd13cb 100644 --- a/civicrm/CRM/Mailing/DAO/MailingGroup.php +++ b/civicrm/CRM/Mailing/DAO/MailingGroup.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Mailing/MailingGroup.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:1869c2f625f9a35820d1342217bd7feb) + * (GenCodeChecksum:a253e806fcb595ede70c812a10c0dbba) */ /** * Database access object for the MailingGroup entity. */ class CRM_Mailing_DAO_MailingGroup extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = ''; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Mailing/DAO/MailingJob.php b/civicrm/CRM/Mailing/DAO/MailingJob.php index f803973e1b..bde7e35f39 100644 --- a/civicrm/CRM/Mailing/DAO/MailingJob.php +++ b/civicrm/CRM/Mailing/DAO/MailingJob.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Mailing/MailingJob.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:b84e3c6e86ddabfe04066d742f5c0ca5) + * (GenCodeChecksum:0c6e76df20fe3579056c287aeed27cdb) */ /** * Database access object for the MailingJob entity. */ class CRM_Mailing_DAO_MailingJob extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = ''; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Mailing/DAO/Recipients.php b/civicrm/CRM/Mailing/DAO/Recipients.php index d3511d4ab7..1852c7ac02 100644 --- a/civicrm/CRM/Mailing/DAO/Recipients.php +++ b/civicrm/CRM/Mailing/DAO/Recipients.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Mailing/Recipients.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:589a6adc830c8f9197b5123e08b63ba3) + * (GenCodeChecksum:ebd2ec177861f8f82a4bc6bc8b33fd9b) */ /** * Database access object for the Recipients entity. */ class CRM_Mailing_DAO_Recipients extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = ''; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Mailing/DAO/Spool.php b/civicrm/CRM/Mailing/DAO/Spool.php index d33871b7b7..6027afd167 100644 --- a/civicrm/CRM/Mailing/DAO/Spool.php +++ b/civicrm/CRM/Mailing/DAO/Spool.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Mailing/Spool.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:8e8346020e4f3174bc4c4c2e87d8e136) + * (GenCodeChecksum:7bd4a9b64175915a43f602f4f9cfb721) */ /** * Database access object for the Spool entity. */ class CRM_Mailing_DAO_Spool extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = ''; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Mailing/DAO/TrackableURL.php b/civicrm/CRM/Mailing/DAO/TrackableURL.php index 771c0e469e..3d39634397 100644 --- a/civicrm/CRM/Mailing/DAO/TrackableURL.php +++ b/civicrm/CRM/Mailing/DAO/TrackableURL.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Mailing/TrackableURL.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:6436cc3cfd3b7a43b468c5372ec081be) + * (GenCodeChecksum:74f858b4e9e666e05416be884002408b) */ /** * Database access object for the TrackableURL entity. */ class CRM_Mailing_DAO_TrackableURL extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = ''; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Mailing/Event/DAO/Bounce.php b/civicrm/CRM/Mailing/Event/DAO/Bounce.php index b21631b738..1cd4024fb4 100644 --- a/civicrm/CRM/Mailing/Event/DAO/Bounce.php +++ b/civicrm/CRM/Mailing/Event/DAO/Bounce.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Mailing/Event/Bounce.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:ca3431a45bad00b42a6bf347338041ed) + * (GenCodeChecksum:8e0590dde97f57494203397255fd4604) */ /** * Database access object for the Bounce entity. */ class CRM_Mailing_Event_DAO_Bounce extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = ''; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Mailing/Event/DAO/Confirm.php b/civicrm/CRM/Mailing/Event/DAO/Confirm.php index bdde918922..343430c157 100644 --- a/civicrm/CRM/Mailing/Event/DAO/Confirm.php +++ b/civicrm/CRM/Mailing/Event/DAO/Confirm.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Mailing/Event/Confirm.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:01729079bec5be7346678447b6cf2844) + * (GenCodeChecksum:827b011dc50d032e8b74d6d164314d83) */ /** * Database access object for the Confirm entity. */ class CRM_Mailing_Event_DAO_Confirm extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = ''; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Mailing/Event/DAO/Delivered.php b/civicrm/CRM/Mailing/Event/DAO/Delivered.php index 1adada28bc..147d70fcf8 100644 --- a/civicrm/CRM/Mailing/Event/DAO/Delivered.php +++ b/civicrm/CRM/Mailing/Event/DAO/Delivered.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Mailing/Event/Delivered.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:ced38ed0f599efdc44cca8dad6049d8c) + * (GenCodeChecksum:c983e11b4de5a1c4e6d9765eb7d12755) */ /** * Database access object for the Delivered entity. */ class CRM_Mailing_Event_DAO_Delivered extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = ''; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Mailing/Event/DAO/Forward.php b/civicrm/CRM/Mailing/Event/DAO/Forward.php index d5bdfbf7ec..ac1c8d8967 100644 --- a/civicrm/CRM/Mailing/Event/DAO/Forward.php +++ b/civicrm/CRM/Mailing/Event/DAO/Forward.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Mailing/Event/Forward.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:ea0e4a784e682347d7320337cb5bc1ac) + * (GenCodeChecksum:359e0b700860c29a1e809fd4acbf7598) */ /** * Database access object for the Forward entity. */ class CRM_Mailing_Event_DAO_Forward extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = ''; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Mailing/Event/DAO/Opened.php b/civicrm/CRM/Mailing/Event/DAO/Opened.php index 15b5e0394e..cbd24372df 100644 --- a/civicrm/CRM/Mailing/Event/DAO/Opened.php +++ b/civicrm/CRM/Mailing/Event/DAO/Opened.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Mailing/Event/Opened.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:8c6c5978fae73802fbd9984ef51a2576) + * (GenCodeChecksum:dddc76ba8461f8b0c1f3c1cdccddd111) */ /** * Database access object for the Opened entity. */ class CRM_Mailing_Event_DAO_Opened extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = ''; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Mailing/Event/DAO/Queue.php b/civicrm/CRM/Mailing/Event/DAO/Queue.php index 40f075d0b3..073ef521c9 100644 --- a/civicrm/CRM/Mailing/Event/DAO/Queue.php +++ b/civicrm/CRM/Mailing/Event/DAO/Queue.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Mailing/Event/Queue.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:316f29677ad04a5347bfd30cd60f474e) + * (GenCodeChecksum:091cb300f1b0a67dfaf40f988806e6cf) */ /** * Database access object for the Queue entity. */ class CRM_Mailing_Event_DAO_Queue extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = ''; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Mailing/Event/DAO/Reply.php b/civicrm/CRM/Mailing/Event/DAO/Reply.php index 687cd8b30c..8fc31b3eb3 100644 --- a/civicrm/CRM/Mailing/Event/DAO/Reply.php +++ b/civicrm/CRM/Mailing/Event/DAO/Reply.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Mailing/Event/Reply.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:a8246c6739f95a8e25d17ddebe882db7) + * (GenCodeChecksum:b1d572f3d42f6480dc98a2e6f9710fa3) */ /** * Database access object for the Reply entity. */ class CRM_Mailing_Event_DAO_Reply extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = ''; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Mailing/Event/DAO/Subscribe.php b/civicrm/CRM/Mailing/Event/DAO/Subscribe.php index 97e420d72b..ce577b24d7 100644 --- a/civicrm/CRM/Mailing/Event/DAO/Subscribe.php +++ b/civicrm/CRM/Mailing/Event/DAO/Subscribe.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Mailing/Event/Subscribe.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:acb361992fd2db48c82ee7b13f8b7cf4) + * (GenCodeChecksum:9e1dec99f17dcccde7feeca30b880a85) */ /** * Database access object for the Subscribe entity. */ class CRM_Mailing_Event_DAO_Subscribe extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = ''; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Mailing/Event/DAO/TrackableURLOpen.php b/civicrm/CRM/Mailing/Event/DAO/TrackableURLOpen.php index 07af2524c7..88c84b0b92 100644 --- a/civicrm/CRM/Mailing/Event/DAO/TrackableURLOpen.php +++ b/civicrm/CRM/Mailing/Event/DAO/TrackableURLOpen.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Mailing/Event/TrackableURLOpen.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:7d55cf77c7be3a81666b51aaa55c6358) + * (GenCodeChecksum:b543a83312f2069a45872939517aa480) */ /** * Database access object for the TrackableURLOpen entity. */ class CRM_Mailing_Event_DAO_TrackableURLOpen extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = ''; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Mailing/Event/DAO/Unsubscribe.php b/civicrm/CRM/Mailing/Event/DAO/Unsubscribe.php index 9448593d04..e5a230b66d 100644 --- a/civicrm/CRM/Mailing/Event/DAO/Unsubscribe.php +++ b/civicrm/CRM/Mailing/Event/DAO/Unsubscribe.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Mailing/Event/Unsubscribe.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:d16e98421cdacbf5ad1b5542810fca6c) + * (GenCodeChecksum:2d080a63032c9dce0331a6ed4f6c3cd2) */ /** * Database access object for the Unsubscribe entity. */ class CRM_Mailing_Event_DAO_Unsubscribe extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = ''; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Mailing/Page/Browse.php b/civicrm/CRM/Mailing/Page/Browse.php index 9982bd1a79..a02b6adcbb 100644 --- a/civicrm/CRM/Mailing/Page/Browse.php +++ b/civicrm/CRM/Mailing/Page/Browse.php @@ -243,11 +243,6 @@ class CRM_Mailing_Page_Browse extends CRM_Core_Page { $controller->setEmbedded(TRUE); $controller->run(); - // hack to display results as per search - $rows = $controller->getRows($controller); - - $this->assign('rows', $rows); - $urlParams = 'reset=1'; $urlString = 'civicrm/mailing/browse'; if ($this->get('sms')) { diff --git a/civicrm/CRM/Member/BAO/Membership.php b/civicrm/CRM/Member/BAO/Membership.php index 6640b4b113..1030eaae1c 100644 --- a/civicrm/CRM/Member/BAO/Membership.php +++ b/civicrm/CRM/Member/BAO/Membership.php @@ -121,7 +121,7 @@ class CRM_Member_BAO_Membership extends CRM_Member_DAO_Membership { $allStatus = CRM_Member_BAO_Membership::buildOptions('status_id', 'get'); $activityParams = [ - 'status_id' => CRM_Utils_Array::value('membership_activity_status', $params, 'Completed'), + 'status_id' => $params['membership_activity_status'] ?? 'Completed', ]; if (in_array($allStatus[$membership->status_id], ['Pending', 'Grace'])) { $activityParams['status_id'] = 'Scheduled'; @@ -166,15 +166,11 @@ class CRM_Member_BAO_Membership extends CRM_Member_DAO_Membership { } foreach (['Membership Signup', 'Membership Renewal'] as $activityType) { - $activityParams['id'] = CRM_Utils_Array::value('id', - civicrm_api3('Activity', 'Get', - [ - 'source_record_id' => $membership->id, - 'activity_type_id' => $activityType, - 'status_id' => 'Scheduled', - ] - ) - ); + $activityParams['id'] = civicrm_api3('Activity', 'Get', [ + 'source_record_id' => $membership->id, + 'activity_type_id' => $activityType, + 'status_id' => 'Scheduled', + ])['id'] ?? NULL; // 1. Update Schedule Membership Signup/Renwal activity to completed on successful payment of pending membership // 2. OR Create renewal activity scheduled if its membership renewal will be paid later if (!empty($params['membership_activity_status']) && (!empty($activityParams['id']) || $activityType == 'Membership Renewal')) { @@ -261,7 +257,7 @@ class CRM_Member_BAO_Membership extends CRM_Member_DAO_Membership { $params['join_date'] = trim($params['join_date']) ? date('Ymd', strtotime(trim($params['join_date']))) : 'null'; //fix for CRM-3570, during import exclude the statuses those having is_admin = 1 - $excludeIsAdmin = CRM_Utils_Array::value('exclude_is_admin', $params, FALSE); + $excludeIsAdmin = $params['exclude_is_admin'] ?? FALSE; //CRM-3724 always skip is_admin if is_override != true. if (!$excludeIsAdmin && empty($params['is_override'])) { @@ -269,7 +265,7 @@ class CRM_Member_BAO_Membership extends CRM_Member_DAO_Membership { } $calcStatus = CRM_Member_BAO_MembershipStatus::getMembershipStatusByDate($params['start_date'], $params['end_date'], $params['join_date'], - 'today', $excludeIsAdmin, CRM_Utils_Array::value('membership_type_id', $params), $params + 'today', $excludeIsAdmin, $params['membership_type_id'] ?? NULL, $params ); if (empty($calcStatus)) { throw new CRM_Core_Exception(ts("The membership cannot be saved because the status cannot be calculated for start_date: {$params['start_date']} end_date {$params['end_date']} join_date {$params['join_date']} as at " . date('Y-m-d H:i:s'))); @@ -295,12 +291,7 @@ class CRM_Member_BAO_Membership extends CRM_Member_DAO_Membership { $transaction = new CRM_Core_Transaction(); - // @todo remove $ids from here $ids['userId'] is still used - $params['id'] = CRM_Utils_Array::value('id', $params, CRM_Utils_Array::value('membership', $ids)); - if (empty($params['modified_id']) && !empty($ids['userID'])) { - CRM_Core_Error::deprecatedFunctionWarning('$ids["userID"] no longer supported - use $params["modified_id"]'); - $params['modified_id'] = $ids['userID']; - } + $params['id'] = $params['id'] ?? $ids['membership'] ?? NULL; $membership = self::add($params); if (is_a($membership, 'CRM_Core_Error')) { @@ -364,7 +355,7 @@ class CRM_Member_BAO_Membership extends CRM_Member_DAO_Membership { if (!empty($params['contribution'])) { $params['line_item'][$priceSetId][$lineIndex]['contribution_id'] = $params['contribution']->id; } - if ($lineMembershipType && $lineMembershipType == CRM_Utils_Array::value('membership_type_id', $params)) { + if ($lineMembershipType && $lineMembershipType == ($params['membership_type_id'] ?? NULL)) { $params['line_item'][$priceSetId][$lineIndex]['entity_id'] = $membership->id; $params['line_item'][$priceSetId][$lineIndex]['entity_table'] = 'civicrm_membership'; } @@ -377,7 +368,7 @@ class CRM_Member_BAO_Membership extends CRM_Member_DAO_Membership { CRM_Price_BAO_LineItem::processPriceSet( $membership->id, $params['line_item'], - CRM_Utils_Array::value('contribution', $params) + $params['contribution'] ?? NULL ); } @@ -837,7 +828,7 @@ INNER JOIN civicrm_membership_type type ON ( type.id = membership.membership_ty 'limit' => 0, ], ]); - $memberTypesSameParentOrgList = implode(',', array_keys(CRM_Utils_Array::value('values', $memberTypesSameParentOrg, []))); + $memberTypesSameParentOrgList = implode(',', array_keys($memberTypesSameParentOrg['values'] ?? [])); $dao->whereAdd('membership_type_id IN (' . $memberTypesSameParentOrgList . ')'); } } @@ -1148,9 +1139,9 @@ AND civicrm_membership.is_test = %2"; } $status = CRM_Member_BAO_MembershipStatus::getMembershipStatusByDate( - CRM_Utils_Array::value('start_date', $currentMembership), - CRM_Utils_Array::value('end_date', $currentMembership), - CRM_Utils_Array::value('join_date', $currentMembership), + $currentMembership['start_date'] ?? NULL, + $currentMembership['end_date'] ?? NULL, + $currentMembership['join_date'] ?? NULL, $today, TRUE, $currentMembership['membership_type_id'], @@ -1199,7 +1190,7 @@ AND civicrm_membership.is_test = %2"; $format ), 'membership_type_id' => $currentMembership['membership_type_id'], - 'max_related' => CRM_Utils_Array::value('max_related', $currentMembership, 0), + 'max_related' => $currentMembership['max_related'] ?? 0, ]; $session = CRM_Core_Session::singleton(); @@ -1333,7 +1324,7 @@ WHERE civicrm_membership.contact_id = civicrm_contact.id * @throws \CRM_Core_Exception * @throws \CiviCRM_API3_Exception */ - public static function createRelatedMemberships(&$params, &$dao, $reset = FALSE) { + public static function createRelatedMemberships($params, $dao, $reset = FALSE) { // CRM-4213 check for loops, using static variable to record contacts already processed. if (!isset(\Civi::$statics[__CLASS__]['related_contacts'])) { \Civi::$statics[__CLASS__]['related_contacts'] = []; @@ -1376,7 +1367,7 @@ WHERE civicrm_membership.contact_id = civicrm_contact.id $relatedContacts = []; $allRelatedContacts = CRM_Member_BAO_Membership::checkMembershipRelationship($membership->membership_type_id, $membership->contact_id, - CRM_Utils_Array::value('action', $params) + $params['action'] ?? NULL ); // CRM-4213, CRM-19735 check for loops, using static variable to record contacts already processed. @@ -1448,7 +1439,7 @@ WHERE civicrm_membership.contact_id = civicrm_contact.id ) { $params['status_id'] = $deceasedStatusId; } - elseif ((CRM_Utils_Array::value('action', $params) & CRM_Core_Action::UPDATE) && + elseif ((($params['action'] ?? NULL) & CRM_Core_Action::UPDATE) && ($relationshipStatus == CRM_Contact_BAO_Relationship::PAST) ) { $params['status_id'] = $expiredStatusId; @@ -1882,7 +1873,7 @@ INNER JOIN civicrm_contact contact ON ( contact.id = membership.contact_id AND // Insert renewed dates for CURRENT membership $memParams = []; $memParams['join_date'] = CRM_Utils_Date::isoToMysql($membership->join_date); - $memParams['start_date'] = CRM_Utils_Array::value('start_date', $formDates, CRM_Utils_Date::isoToMysql($membership->start_date)); + $memParams['start_date'] = $formDates['start_date'] ?? CRM_Utils_Date::isoToMysql($membership->start_date); $memParams['end_date'] = $formDates['end_date'] ?? NULL; if (empty($memParams['end_date'])) { $memParams['end_date'] = $dates['end_date'] ?? NULL; @@ -2403,7 +2394,7 @@ WHERE {$whereClause}"; $contributionParams = []; $config = CRM_Core_Config::singleton(); $contributionParams['currency'] = $config->defaultCurrency; - $contributionParams['receipt_date'] = (CRM_Utils_Array::value('receipt_date', $params)) ? $params['receipt_date'] : 'null'; + $contributionParams['receipt_date'] = !empty($params['receipt_date']) ? $params['receipt_date'] : 'null'; $contributionParams['source'] = $params['contribution_source'] ?? NULL; $contributionParams['non_deductible_amount'] = 'null'; $contributionParams['skipCleanMoney'] = TRUE; diff --git a/civicrm/CRM/Member/DAO/Membership.php b/civicrm/CRM/Member/DAO/Membership.php index 289f9ba231..384c69ae6b 100644 --- a/civicrm/CRM/Member/DAO/Membership.php +++ b/civicrm/CRM/Member/DAO/Membership.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Member/Membership.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:9a307c1a63b4df70ae38f36ce4171cb6) + * (GenCodeChecksum:835c63ea0a55b78d6d115a7a6db5dde2) */ /** * Database access object for the Membership entity. */ class CRM_Member_DAO_Membership extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.5'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Member/DAO/MembershipBlock.php b/civicrm/CRM/Member/DAO/MembershipBlock.php index 36b91c2eae..91e65e6eeb 100644 --- a/civicrm/CRM/Member/DAO/MembershipBlock.php +++ b/civicrm/CRM/Member/DAO/MembershipBlock.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Member/MembershipBlock.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:317a1eb3c0a67ffe4662f939a0b1fb69) + * (GenCodeChecksum:8eb2f3a6c818d449da875421b54de619) */ /** * Database access object for the MembershipBlock entity. */ class CRM_Member_DAO_MembershipBlock extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.5'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Member/DAO/MembershipLog.php b/civicrm/CRM/Member/DAO/MembershipLog.php index 2f44712bc7..bd0b4f7924 100644 --- a/civicrm/CRM/Member/DAO/MembershipLog.php +++ b/civicrm/CRM/Member/DAO/MembershipLog.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Member/MembershipLog.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:ff966b7edcdd126ddef1ee3b3f3bff2f) + * (GenCodeChecksum:4d5744b433ca7bb5385b11945cc0fe10) */ /** * Database access object for the MembershipLog entity. */ class CRM_Member_DAO_MembershipLog extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.5'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Member/DAO/MembershipPayment.php b/civicrm/CRM/Member/DAO/MembershipPayment.php index 91c700e21a..ac386eee55 100644 --- a/civicrm/CRM/Member/DAO/MembershipPayment.php +++ b/civicrm/CRM/Member/DAO/MembershipPayment.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Member/MembershipPayment.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:b2fa8ca60001d75a9049ca179c4e68ce) + * (GenCodeChecksum:39168603c262c909ebeee2ce821f0f0d) */ /** * Database access object for the MembershipPayment entity. */ class CRM_Member_DAO_MembershipPayment extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.5'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Member/DAO/MembershipStatus.php b/civicrm/CRM/Member/DAO/MembershipStatus.php index c1b2afc1fe..5b780e4a5b 100644 --- a/civicrm/CRM/Member/DAO/MembershipStatus.php +++ b/civicrm/CRM/Member/DAO/MembershipStatus.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Member/MembershipStatus.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:e6fe414d2b0dc3ff72ee5adcad75fab8) + * (GenCodeChecksum:f0c470d5aca6e3696a0ad8345531f8b8) */ /** * Database access object for the MembershipStatus entity. */ class CRM_Member_DAO_MembershipStatus extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.5'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Member/DAO/MembershipType.php b/civicrm/CRM/Member/DAO/MembershipType.php index 63ef86aaaf..4b1d6ce509 100644 --- a/civicrm/CRM/Member/DAO/MembershipType.php +++ b/civicrm/CRM/Member/DAO/MembershipType.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Member/MembershipType.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:20b0eec540f3bb69e386f86b29419391) + * (GenCodeChecksum:713057d2c1a6dcb6cbd6449b8934d28c) */ /** * Database access object for the MembershipType entity. */ class CRM_Member_DAO_MembershipType extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.5'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Member/Form.php b/civicrm/CRM/Member/Form.php index 73f7cb46a9..3ae6973ef5 100644 --- a/civicrm/CRM/Member/Form.php +++ b/civicrm/CRM/Member/Form.php @@ -358,19 +358,19 @@ class CRM_Member_Form extends CRM_Contribute_Form_AbstractEditPayment { * * @param array $contributionRecurParams * - * @param int $membershipID + * @param int $membershipTypeID * * @return array * @throws \CiviCRM_API3_Exception */ - protected function processRecurringContribution($contributionRecurParams, $membershipID) { + protected function processRecurringContribution($contributionRecurParams, $membershipTypeID) { $mapping = [ 'frequency_interval' => 'duration_interval', 'frequency_unit' => 'duration_unit', ]; $membershipType = civicrm_api3('MembershipType', 'getsingle', [ - 'id' => $membershipID, + 'id' => $membershipTypeID, 'return' => $mapping, ]); diff --git a/civicrm/CRM/Member/Form/Membership.php b/civicrm/CRM/Member/Form/Membership.php index 3c98b8570b..14c2f28caa 100644 --- a/civicrm/CRM/Member/Form/Membership.php +++ b/civicrm/CRM/Member/Form/Membership.php @@ -570,7 +570,7 @@ class CRM_Member_Form_Membership extends CRM_Member_Form { CRM_Member_StatusOverrideTypes::getSelectOptions() ); - $this->add('datepicker', 'status_override_end_date', ts('Status Override End Date'), '', FALSE, ['minDate' => time(), 'time' => FALSE]); + $this->add('datepicker', 'status_override_end_date', ts('Status Override End Date'), '', FALSE, ['minDate' => date('Y-m-d'), 'time' => FALSE]); $this->addElement('checkbox', 'record_contribution', ts('Record Membership Payment?')); diff --git a/civicrm/CRM/Member/Form/MembershipRenewal.php b/civicrm/CRM/Member/Form/MembershipRenewal.php index 17b3dfc6f6..908e51adc9 100644 --- a/civicrm/CRM/Member/Form/MembershipRenewal.php +++ b/civicrm/CRM/Member/Form/MembershipRenewal.php @@ -553,7 +553,7 @@ class CRM_Member_Form_MembershipRenewal extends CRM_Member_Form { 'is_email_receipt' => !empty($this->_params['send_receipt']), 'payment_instrument_id' => $this->_params['payment_instrument_id'], 'invoice_id' => $this->_params['invoice_id'], - ], $membershipID = $paymentParams['membership_type_id'][1]); + ], $paymentParams['membership_type_id'][1]); $contributionRecurID = $contributionRecurParams['contributionRecurID']; $paymentParams = array_merge($paymentParams, $contributionRecurParams); @@ -748,14 +748,8 @@ class CRM_Member_Form_MembershipRenewal extends CRM_Member_Form { */ public function processMembership($contactID, $membershipTypeID, $is_test, $changeToday, $customFieldsFormatted, $numRenewTerms, $membershipID, $pending, $contributionRecurID, $isPayLater) { $allStatus = CRM_Member_PseudoConstant::membershipStatus(); - $membershipTypeDetails = CRM_Member_BAO_MembershipType::getMembershipTypeDetails($membershipTypeID); $ids = []; - - // CRM-7297 - allow membership type to be be changed during renewal so long as the parent org of new membershipType - // is the same as the parent org of an existing membership of the contact - $currentMembership = CRM_Member_BAO_Membership::getContactMembership($contactID, $membershipTypeID, - $is_test, $membershipID, TRUE - ); + $currentMembership = civicrm_api3('Membership', 'getsingle', ['id' => $membershipID]); // Do NOT do anything. //1. membership with status : PENDING/CANCELLED (CRM-2395) @@ -765,7 +759,6 @@ class CRM_Member_Form_MembershipRenewal extends CRM_Member_Form { // CRM-15475 array_search('Cancelled', CRM_Member_PseudoConstant::membershipStatus(NULL, " name = 'Cancelled' ", 'name', FALSE, TRUE)), ])) { - $memParams = [ 'id' => $currentMembership['id'], 'status_id' => $currentMembership['status_id'], @@ -773,7 +766,6 @@ class CRM_Member_Form_MembershipRenewal extends CRM_Member_Form { 'end_date' => $currentMembership['end_date'], 'join_date' => $currentMembership['join_date'], 'membership_type_id' => $membershipTypeID, - 'max_related' => !empty($membershipTypeDetails['max_related']) ? $membershipTypeDetails['max_related'] : NULL, 'membership_activity_status' => ($pending || $isPayLater) ? 'Scheduled' : 'Completed', ]; if ($contributionRecurID) { @@ -785,7 +777,7 @@ class CRM_Member_Form_MembershipRenewal extends CRM_Member_Form { // Check and fix the membership if it is STALE CRM_Member_BAO_Membership::fixMembershipStatusBeforeRenew($currentMembership, $changeToday); - $isMembershipCurrent = $currentMembership['is_current_member']; + $isMembershipCurrent = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipStatus', $currentMembership['status_id'], 'is_current_member'); // CRM-7297 Membership Upsell - calculate dates based on new membership type $dates = CRM_Member_BAO_MembershipType::getRenewalDatesForMembershipType($currentMembership['id'], diff --git a/civicrm/CRM/Member/Import/Form/MapField.php b/civicrm/CRM/Member/Import/Form/MapField.php index aa1ec10b21..4d341f9837 100644 --- a/civicrm/CRM/Member/Import/Form/MapField.php +++ b/civicrm/CRM/Member/Import/Form/MapField.php @@ -398,7 +398,7 @@ class CRM_Member_Import_Form_MapField extends CRM_Import_Form_MapField { } $fileName = $this->controller->exportValue('DataSource', 'uploadFile'); - $seperator = $this->controller->exportValue('DataSource', 'fieldSeparator'); + $separator = $this->controller->exportValue('DataSource', 'fieldSeparator'); $skipColumnHeader = $this->controller->exportValue('DataSource', 'skipColumnHeader'); $mapperKeys = []; @@ -487,7 +487,7 @@ class CRM_Member_Import_Form_MapField extends CRM_Import_Form_MapField { } $parser = new CRM_Member_Import_Parser_Membership($mapperKeysMain, $mapperLocType, $mapperPhoneType); - $parser->run($fileName, $seperator, $mapper, $skipColumnHeader, + $parser->run($fileName, $separator, $mapper, $skipColumnHeader, CRM_Import_Parser::MODE_PREVIEW, $this->get('contactType') ); // add all the necessary variables to the form diff --git a/civicrm/CRM/Member/Import/Form/Preview.php b/civicrm/CRM/Member/Import/Form/Preview.php index dbf1b1ec4e..0348433530 100644 --- a/civicrm/CRM/Member/Import/Form/Preview.php +++ b/civicrm/CRM/Member/Import/Form/Preview.php @@ -96,7 +96,7 @@ class CRM_Member_Import_Form_Preview extends CRM_Import_Form_Preview { */ public function postProcess() { $fileName = $this->controller->exportValue('DataSource', 'uploadFile'); - $seperator = $this->controller->exportValue('DataSource', 'fieldSeparator'); + $separator = $this->controller->exportValue('DataSource', 'fieldSeparator'); $skipColumnHeader = $this->controller->exportValue('DataSource', 'skipColumnHeader'); $invalidRowCount = $this->get('invalidRowCount'); $conflictRowCount = $this->get('conflictRowCount'); @@ -139,7 +139,7 @@ class CRM_Member_Import_Form_Preview extends CRM_Import_Form_Preview { } $mapperFields[] = implode(' - ', $header); } - $parser->run($fileName, $seperator, + $parser->run($fileName, $separator, $mapperFields, $skipColumnHeader, CRM_Import_Parser::MODE_IMPORT, diff --git a/civicrm/CRM/Member/Import/Parser.php b/civicrm/CRM/Member/Import/Parser.php index 045d336a84..53d08b75e0 100644 --- a/civicrm/CRM/Member/Import/Parser.php +++ b/civicrm/CRM/Member/Import/Parser.php @@ -27,10 +27,10 @@ abstract class CRM_Member_Import_Parser extends CRM_Import_Parser { protected $_fileSize; /** - * Seperator being used + * Separator being used * @var string */ - protected $_seperator; + protected $_separator; /** * Total number of lines in file @@ -47,7 +47,7 @@ abstract class CRM_Member_Import_Parser extends CRM_Import_Parser { /** * @param string $fileName - * @param string $seperator + * @param string $separator * @param $mapper * @param bool $skipColumnHeader * @param int $mode @@ -61,7 +61,7 @@ abstract class CRM_Member_Import_Parser extends CRM_Import_Parser { */ public function run( $fileName, - $seperator = ',', + $separator = ',', &$mapper, $skipColumnHeader = FALSE, $mode = self::MODE_PREVIEW, @@ -92,7 +92,7 @@ abstract class CRM_Member_Import_Parser extends CRM_Import_Parser { $this->_haveColumnHeader = $skipColumnHeader; - $this->_seperator = $seperator; + $this->_separator = $separator; $fd = fopen($fileName, "r"); if (!$fd) { @@ -123,7 +123,7 @@ abstract class CRM_Member_Import_Parser extends CRM_Import_Parser { while (!feof($fd)) { $this->_lineCount++; - $values = fgetcsv($fd, 8192, $seperator); + $values = fgetcsv($fd, 8192, $separator); if (!$values) { continue; } @@ -342,7 +342,7 @@ abstract class CRM_Member_Import_Parser extends CRM_Import_Parser { public function set($store, $mode = self::MODE_SUMMARY) { $store->set('fileSize', $this->_fileSize); $store->set('lineCount', $this->_lineCount); - $store->set('seperator', $this->_seperator); + $store->set('separator', $this->_separator); $store->set('fields', $this->getSelectValues()); $store->set('fieldTypes', $this->getSelectTypes()); diff --git a/civicrm/CRM/PCP/DAO/PCP.php b/civicrm/CRM/PCP/DAO/PCP.php index f5d8ed2363..eb56569675 100644 --- a/civicrm/CRM/PCP/DAO/PCP.php +++ b/civicrm/CRM/PCP/DAO/PCP.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/PCP/PCP.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:30c5a26e001449a2ace9e530714e833c) + * (GenCodeChecksum:286225e46c4e2f3c12b17cd5f83b210d) */ /** * Database access object for the PCP entity. */ class CRM_PCP_DAO_PCP extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '2.2'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/PCP/DAO/PCPBlock.php b/civicrm/CRM/PCP/DAO/PCPBlock.php index 897d9145f3..74f5204beb 100644 --- a/civicrm/CRM/PCP/DAO/PCPBlock.php +++ b/civicrm/CRM/PCP/DAO/PCPBlock.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/PCP/PCPBlock.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:039e37edb86cfd25e6c882ce4950e3c9) + * (GenCodeChecksum:ea1b4158570c5a79356b1dc0ad80db6a) */ /** * Database access object for the PCPBlock entity. */ class CRM_PCP_DAO_PCPBlock extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '2.2'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Pledge/BAO/Query.php b/civicrm/CRM/Pledge/BAO/Query.php index 4b780c5983..45f9b1c37c 100644 --- a/civicrm/CRM/Pledge/BAO/Query.php +++ b/civicrm/CRM/Pledge/BAO/Query.php @@ -539,10 +539,10 @@ class CRM_Pledge_BAO_Query extends CRM_Core_BAO_Query { $form->addYesNo('pledge_test', ts('Pledge is a Test?'), TRUE); $form->add('text', 'pledge_amount_low', ts('From'), ['size' => 8, 'maxlength' => 8]); - $form->addRule('pledge_amount_low', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::format('9.99', ' ')]), 'money'); + $form->addRule('pledge_amount_low', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency('9.99')]), 'money'); $form->add('text', 'pledge_amount_high', ts('To'), ['size' => 8, 'maxlength' => 8]); - $form->addRule('pledge_amount_high', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::format('99.99', ' ')]), 'money'); + $form->addRule('pledge_amount_high', ts('Please enter a valid money value (e.g. %1).', [1 => CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency('99.99')]), 'money'); $form->addYesNo('pledge_acknowledge_date_is_not_null', ts('Acknowledgement sent?'), TRUE); diff --git a/civicrm/CRM/Pledge/DAO/Pledge.php b/civicrm/CRM/Pledge/DAO/Pledge.php index a7a0fb7080..5f5a7632ed 100644 --- a/civicrm/CRM/Pledge/DAO/Pledge.php +++ b/civicrm/CRM/Pledge/DAO/Pledge.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Pledge/Pledge.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:8373762a2a35ef14b0d763eb27db0f34) + * (GenCodeChecksum:27003a5c2de79b60b4114bc92b65cc07) */ /** * Database access object for the Pledge entity. */ class CRM_Pledge_DAO_Pledge extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '2.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Pledge/DAO/PledgeBlock.php b/civicrm/CRM/Pledge/DAO/PledgeBlock.php index 1cb20cb50e..989b14383d 100644 --- a/civicrm/CRM/Pledge/DAO/PledgeBlock.php +++ b/civicrm/CRM/Pledge/DAO/PledgeBlock.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Pledge/PledgeBlock.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:fd4a1319ac7b6cdc29baa3e9e4df68de) + * (GenCodeChecksum:bf3640355f445e127c25402500d79668) */ /** * Database access object for the PledgeBlock entity. */ class CRM_Pledge_DAO_PledgeBlock extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '2.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Pledge/DAO/PledgePayment.php b/civicrm/CRM/Pledge/DAO/PledgePayment.php index b072dd7f06..234a5342b2 100644 --- a/civicrm/CRM/Pledge/DAO/PledgePayment.php +++ b/civicrm/CRM/Pledge/DAO/PledgePayment.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Pledge/PledgePayment.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:64c6aef9e06ba7320d917ec5515c5ffc) + * (GenCodeChecksum:c47a2cbc83c672a8209bc5e725b2f81a) */ /** * Database access object for the PledgePayment entity. */ class CRM_Pledge_DAO_PledgePayment extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '2.1'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Price/BAO/LineItem.php b/civicrm/CRM/Price/BAO/LineItem.php index b94b20755a..b25c02c37c 100644 --- a/civicrm/CRM/Price/BAO/LineItem.php +++ b/civicrm/CRM/Price/BAO/LineItem.php @@ -116,34 +116,6 @@ class CRM_Price_BAO_LineItem extends CRM_Price_DAO_LineItem { return NULL; } - /** - * Modifies $params array for filtering financial types. - * - * @param array $params - * (reference ) an assoc array of name/value pairs. - * - */ - public static function getAPILineItemParams(&$params) { - CRM_Financial_BAO_FinancialType::getAvailableFinancialTypes($types); - if ($types && empty($params['financial_type_id'])) { - $params['financial_type_id'] = ['IN' => array_keys($types)]; - } - elseif ($types) { - if (is_array($params['financial_type_id'])) { - $invalidFts = array_diff($params['financial_type_id'], array_keys($types)); - } - elseif (!in_array($params['financial_type_id'], array_keys($types))) { - $invalidFts = $params['financial_type_id']; - } - if ($invalidFts) { - $params['financial_type_id'] = ['NOT IN' => $invalidFts]; - } - } - else { - $params['financial_type_id'] = 0; - } - } - /** * @param int $contributionId * @@ -426,18 +398,9 @@ WHERE li.contribution_id = %1"; } if (!empty($contributionDetails->id)) { $line['contribution_id'] = $contributionDetails->id; - if ($line['entity_table'] == 'civicrm_contribution') { + if ($line['entity_table'] === 'civicrm_contribution') { $line['entity_id'] = $contributionDetails->id; } - // CRM-19094: entity_table is set to civicrm_membership then ensure - // the entityId is set to membership ID not contribution by default - elseif ($line['entity_table'] == 'civicrm_membership' && !empty($line['entity_id']) && $line['entity_id'] == $contributionDetails->id) { - $membershipId = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipPayment', $contributionDetails->id, 'membership_id', 'contribution_id'); - if ($membershipId && (int) $membershipId !== (int) $line['entity_id']) { - $line['entity_id'] = $membershipId; - Civi::log()->warning('Per https://lab.civicrm.org/dev/core/issues/15 this data fix should not be required. Please log a ticket at https://lab.civicrm.org/dev/core with steps to get this.', ['civi.tag' => 'deprecated']); - } - } } // if financial type is not set and if price field value is NOT NULL diff --git a/civicrm/CRM/Price/BAO/PriceField.php b/civicrm/CRM/Price/BAO/PriceField.php index 091d9810e4..61e570335a 100644 --- a/civicrm/CRM/Price/BAO/PriceField.php +++ b/civicrm/CRM/Price/BAO/PriceField.php @@ -296,7 +296,7 @@ class CRM_Price_BAO_PriceField extends CRM_Price_DAO_PriceField { //use value field. $valueFieldName = 'amount'; - $seperator = '|'; + $separator = '|'; $invoiceSettings = Civi::settings()->get('contribution_invoice_settings'); $taxTerm = Civi::settings()->get('tax_term'); $displayOpt = $invoiceSettings['tax_display_settings'] ?? NULL; @@ -312,7 +312,7 @@ class CRM_Price_BAO_PriceField extends CRM_Price_DAO_PriceField { $qf->assign('taxTerm', $taxTerm); $qf->assign('invoicing', $invoicing); } - $priceVal = implode($seperator, [ + $priceVal = implode($separator, [ $customOption[$optionKey][$valueFieldName] + $taxAmount, $count, $max_value, @@ -403,7 +403,7 @@ class CRM_Price_BAO_PriceField extends CRM_Price_DAO_PriceField { } $count = CRM_Utils_Array::value('count', $opt, ''); $max_value = CRM_Utils_Array::value('max_value', $opt, ''); - $priceVal = implode($seperator, [$opt[$valueFieldName] + $taxAmount, $count, $max_value]); + $priceVal = implode($separator, [$opt[$valueFieldName] + $taxAmount, $count, $max_value]); if (isset($opt['visibility_id'])) { $visibility_id = $opt['visibility_id']; } @@ -499,7 +499,7 @@ class CRM_Price_BAO_PriceField extends CRM_Price_DAO_PriceField { } } - $priceVal[$opt['id']] = implode($seperator, [$opt[$valueFieldName] + $taxAmount, $count, $max_value]); + $priceVal[$opt['id']] = implode($separator, [$opt[$valueFieldName] + $taxAmount, $count, $max_value]); if (!in_array($opt['id'], $freezeOptions)) { $allowedOptions[] = $opt['id']; @@ -562,7 +562,7 @@ class CRM_Price_BAO_PriceField extends CRM_Price_DAO_PriceField { } $opt['label'] = $preHelpText . $opt['label'] . $postHelpText; } - $priceVal = implode($seperator, [$opt[$valueFieldName] + $taxAmount, $count, $max_value]); + $priceVal = implode($separator, [$opt[$valueFieldName] + $taxAmount, $count, $max_value]); $check[$opId] = &$qf->createElement('checkbox', $opt['id'], NULL, $opt['label'], [ 'price' => json_encode([$opt['id'], $priceVal]), diff --git a/civicrm/CRM/Price/DAO/LineItem.php b/civicrm/CRM/Price/DAO/LineItem.php index 1b09a54dc9..2ad0da2b29 100644 --- a/civicrm/CRM/Price/DAO/LineItem.php +++ b/civicrm/CRM/Price/DAO/LineItem.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Price/LineItem.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:84c128f076238b53a8c0c6dd23a22587) + * (GenCodeChecksum:7403b3615b0225350d893750a547061a) */ /** * Database access object for the LineItem entity. */ class CRM_Price_DAO_LineItem extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.7'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Price/DAO/PriceField.php b/civicrm/CRM/Price/DAO/PriceField.php index babe806328..e14f60999b 100644 --- a/civicrm/CRM/Price/DAO/PriceField.php +++ b/civicrm/CRM/Price/DAO/PriceField.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Price/PriceField.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:7e0a818f8b8641ca3bae0abe1582c8ce) + * (GenCodeChecksum:1492c6421f1c3cb49dcab88bc411075c) */ /** * Database access object for the PriceField entity. */ class CRM_Price_DAO_PriceField extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.8'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Price/DAO/PriceFieldValue.php b/civicrm/CRM/Price/DAO/PriceFieldValue.php index 01aff17979..c7c8e0913e 100644 --- a/civicrm/CRM/Price/DAO/PriceFieldValue.php +++ b/civicrm/CRM/Price/DAO/PriceFieldValue.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Price/PriceFieldValue.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:11a02f3576be10e8c2a0ea47a19e2dac) + * (GenCodeChecksum:a1acc613daec86c6049e545af5fc7fd1) */ /** * Database access object for the PriceFieldValue entity. */ class CRM_Price_DAO_PriceFieldValue extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '3.3'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Price/DAO/PriceSet.php b/civicrm/CRM/Price/DAO/PriceSet.php index 8fc97986b9..1dd8435170 100644 --- a/civicrm/CRM/Price/DAO/PriceSet.php +++ b/civicrm/CRM/Price/DAO/PriceSet.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Price/PriceSet.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:53c1906856d1a16d2edba1caa6a4fafc) + * (GenCodeChecksum:52d1fb1b25eaa8f1c157012bfec0eaae) */ /** * Database access object for the PriceSet entity. */ class CRM_Price_DAO_PriceSet extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.8'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Price/DAO/PriceSetEntity.php b/civicrm/CRM/Price/DAO/PriceSetEntity.php index 36ec1ec20a..78e479a53a 100644 --- a/civicrm/CRM/Price/DAO/PriceSetEntity.php +++ b/civicrm/CRM/Price/DAO/PriceSetEntity.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Price/PriceSetEntity.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:a37bda1a3508a9195c561c2b2e4f03a7) + * (GenCodeChecksum:f2d6aeda95e4bde969d5ccebe9f26791) */ /** * Database access object for the PriceSetEntity entity. */ class CRM_Price_DAO_PriceSetEntity extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '1.8'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Queue/DAO/QueueItem.php b/civicrm/CRM/Queue/DAO/QueueItem.php index 64ea7e8950..a78b613e15 100644 --- a/civicrm/CRM/Queue/DAO/QueueItem.php +++ b/civicrm/CRM/Queue/DAO/QueueItem.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Queue/QueueItem.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:f71816c891a0730a45d4363883a5756c) + * (GenCodeChecksum:7e484400a7f8cf682b9c85e8b10c7bc7) */ /** * Database access object for the QueueItem entity. */ class CRM_Queue_DAO_QueueItem extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '4.2'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Report/DAO/ReportInstance.php b/civicrm/CRM/Report/DAO/ReportInstance.php index 591332e670..08a380c76f 100644 --- a/civicrm/CRM/Report/DAO/ReportInstance.php +++ b/civicrm/CRM/Report/DAO/ReportInstance.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/Report/ReportInstance.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:5e7790bacc5ffff1ecfcaada1abeafec) + * (GenCodeChecksum:d0c9e5593f161f18e7979012c4c13724) */ /** * Database access object for the ReportInstance entity. */ class CRM_Report_DAO_ReportInstance extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '2.2'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/Report/Form.php b/civicrm/CRM/Report/Form.php index 449e1a087c..aaf8bb15fd 100644 --- a/civicrm/CRM/Report/Form.php +++ b/civicrm/CRM/Report/Form.php @@ -1465,7 +1465,7 @@ class CRM_Report_Form extends CRM_Core_Form { if (!CRM_Core_Permission::check('view report sql')) { return; } - $ignored_output_modes = ['pdf', 'csv', 'print', 'excel2007']; + $ignored_output_modes = ['pdf', 'csv', 'print']; if (in_array($this->_outputMode, $ignored_output_modes)) { return; } @@ -2900,12 +2900,6 @@ WHERE cg.extends IN ('" . implode("','", $this->_customGroupExtends) . "') AND $this->addPaging = $this->outputHandler->isAddPaging(); $this->_absoluteUrl = $this->outputHandler->isAbsoluteUrl(); } - - } - elseif ($this->_outputMode == 'excel2007') { - $printOnly = TRUE; - $this->_absoluteUrl = TRUE; - $this->addPaging = FALSE; } elseif ($this->_outputMode == 'copy' && $this->_criteriaForm) { $this->_createNew = TRUE; @@ -5943,7 +5937,9 @@ LEFT JOIN civicrm_contact {$field['alias']} ON {$field['alias']}.id = {$this->_a $relative = $this->_params["{$fieldName}_relative"] ?? NULL; $from = $this->_params["{$fieldName}_from"] ?? NULL; $to = $this->_params["{$fieldName}_to"] ?? NULL; - return $this->dateClause($field['dbAlias'], $relative, $from, $to, $field['type']); + $fromTime = $this->_params["{$fieldName}_from_time"] ?? NULL; + $toTime = $this->_params["{$fieldName}_to_time"] ?? NULL; + return $this->dateClause($field['dbAlias'], $relative, $from, $to, $field['type'], $fromTime, $toTime); } } else { diff --git a/civicrm/CRM/Report/Form/Contribute/Bookkeeping.php b/civicrm/CRM/Report/Form/Contribute/Bookkeeping.php index f70c7d29d1..06f89ab8c9 100644 --- a/civicrm/CRM/Report/Form/Contribute/Bookkeeping.php +++ b/civicrm/CRM/Report/Form/Contribute/Bookkeeping.php @@ -300,8 +300,8 @@ class CRM_Report_Form_Contribute_Bookkeeping extends CRM_Report_Form { 'operatorType' => CRM_Report_Form::OP_INT, 'type' => CRM_Utils_Type::T_INT, ], - 'receive_date' => ['operatorType' => CRM_Report_Form::OP_DATE], - 'receipt_date' => ['operatorType' => CRM_Report_Form::OP_DATE], + 'receive_date' => ['operatorType' => CRM_Report_Form::OP_DATETIME], + 'receipt_date' => ['operatorType' => CRM_Report_Form::OP_DATETIME], 'contribution_source' => [ 'title' => ts('Source'), 'name' => 'source', @@ -317,6 +317,7 @@ class CRM_Report_Form_Contribute_Bookkeeping extends CRM_Report_Form { 'order_bys' => [ 'contribution_id' => ['title' => ts('Contribution #')], 'contribution_status_id' => ['title' => ts('Contribution Status')], + 'receive_date' => ['title' => ts('Date Received')], ], 'grouping' => 'contri-fields', ], @@ -364,7 +365,7 @@ class CRM_Report_Form_Contribute_Bookkeeping extends CRM_Report_Form { ], 'trxn_date' => [ 'title' => ts('Transaction Date'), - 'operatorType' => CRM_Report_Form::OP_DATE, + 'operatorType' => CRM_Report_Form::OP_DATETIME, 'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME, ], 'status_id' => [ @@ -383,6 +384,7 @@ class CRM_Report_Form_Contribute_Bookkeeping extends CRM_Report_Form { ], 'order_bys' => [ 'payment_instrument_id' => ['title' => ts('Payment Method')], + 'trxn_date' => ['title' => ts('Transaction Date')], ], ], 'civicrm_entity_financial_trxn' => [ @@ -516,52 +518,31 @@ class CRM_Report_Form_Contribute_Bookkeeping extends CRM_Report_Form { } } - public function where() { - foreach ($this->_columns as $tableName => $table) { - if (array_key_exists('filters', $table)) { - foreach ($table['filters'] as $fieldName => $field) { - $clause = NULL; - if (in_array($fieldName, [ - 'credit_accounting_code', - 'credit_name', - 'credit_contact_id', - ])) { - $field['dbAlias'] = "CASE + /** + * overriding to modify dbAlias for few fields. + * + * @param array $field Field specifications + * @param string $op Query operator (not an exact match to sql) + * @param mixed $value + * @param float $min + * @param float $max + * + * @return null|string + */ + public function whereClause(&$field, $op, $value, $min, $max) { + if ($field['alias'] == 'financial_account_civireport_credit' && + in_array($field['name'], ['accounting_code', 'id', 'contact_id']) + ) { + $field['dbAlias'] = "CASE WHEN financial_trxn_civireport.from_financial_account_id IS NOT NULL THEN financial_account_civireport_credit_1.{$field['name']} ELSE financial_account_civireport_credit_2.{$field['name']} END"; - } - if (CRM_Utils_Array::value('type', $field) & CRM_Utils_Type::T_DATE) { - $relative = $this->_params["{$fieldName}_relative"] ?? NULL; - $from = $this->_params["{$fieldName}_from"] ?? NULL; - $to = $this->_params["{$fieldName}_to"] ?? NULL; - - $clause = $this->dateClause($field['name'], $relative, $from, $to, $field['type']); - } - else { - $op = $this->_params["{$fieldName}_op"] ?? NULL; - if ($op) { - $clause = $this->whereClause($field, - $op, - CRM_Utils_Array::value("{$fieldName}_value", $this->_params), - CRM_Utils_Array::value("{$fieldName}_min", $this->_params), - CRM_Utils_Array::value("{$fieldName}_max", $this->_params) - ); - } - } - if (!empty($clause)) { - $clauses[] = $clause; - } - } - } - } - if (empty($clauses)) { - $this->_where = 'WHERE ( 1 )'; - } - else { - $this->_where = 'WHERE ' . implode(' AND ', $clauses); } + + $clause = parent::whereClause($field, $op, $value, $min, $max); + + return $clause; } public function postProcess() { diff --git a/civicrm/CRM/Report/Form/Contribute/History.php b/civicrm/CRM/Report/Form/Contribute/History.php index c7526b3918..bc1a49fe02 100644 --- a/civicrm/CRM/Report/Form/Contribute/History.php +++ b/civicrm/CRM/Report/Form/Contribute/History.php @@ -815,7 +815,7 @@ class CRM_Report_Form_Contribute_History extends CRM_Report_Form { } if ($last_primary && ($rowNum == "{$last_primary}_total")) { - $value = CRM_Utils_Money::format($value, ' '); + $value = CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency($value); } $row[$key] = '<strong>' . $value . '</strong>'; } diff --git a/civicrm/CRM/Report/Form/Event/ParticipantListing.php b/civicrm/CRM/Report/Form/Event/ParticipantListing.php index c465fb6f85..faefeac943 100644 --- a/civicrm/CRM/Report/Form/Event/ParticipantListing.php +++ b/civicrm/CRM/Report/Form/Event/ParticipantListing.php @@ -693,7 +693,7 @@ ORDER BY cv.label } } - // Handle value seperator in Fee Level + // Handle value separator in Fee Level if (array_key_exists('civicrm_participant_participant_fee_level', $row)) { $feeLevel = $row['civicrm_participant_participant_fee_level']; if ($feeLevel) { diff --git a/civicrm/CRM/Report/Form/Mailing/Summary.php b/civicrm/CRM/Report/Form/Mailing/Summary.php index 4289f9020b..1bee34bd33 100644 --- a/civicrm/CRM/Report/Form/Mailing/Summary.php +++ b/civicrm/CRM/Report/Form/Mailing/Summary.php @@ -54,6 +54,12 @@ class CRM_Report_Form_Mailing_Summary extends CRM_Report_Form { 'subject' => [ 'title' => ts('Subject'), ], + 'from_name' => [ + 'title' => ts('Sender Name'), + ], + 'from_email' => [ + 'title' => ts('Sender Email'), + ], ], 'filters' => [ 'is_completed' => [ diff --git a/civicrm/CRM/SMS/DAO/Provider.php b/civicrm/CRM/SMS/DAO/Provider.php index 8f87fa5a14..3aa49ab0a6 100644 --- a/civicrm/CRM/SMS/DAO/Provider.php +++ b/civicrm/CRM/SMS/DAO/Provider.php @@ -6,13 +6,15 @@ * * Generated from xml/schema/CRM/SMS/Provider.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:b512d0ed25ec37b9c890952e48c667e6) + * (GenCodeChecksum:6ecda65bd52b36e04764cec8ee81e1b8) */ /** * Database access object for the Provider entity. */ class CRM_SMS_DAO_Provider extends CRM_Core_DAO { + const EXT = 'civicrm'; + const TABLE_ADDED = '4.2'; /** * Static instance to hold the table name. diff --git a/civicrm/CRM/SMS/Form/Group.php b/civicrm/CRM/SMS/Form/Group.php index 4a53b78852..9755626958 100644 --- a/civicrm/CRM/SMS/Form/Group.php +++ b/civicrm/CRM/SMS/Form/Group.php @@ -102,7 +102,7 @@ class CRM_SMS_Form_Group extends CRM_Contact_Form_Task { ); // Get the mailing groups. - $groups = CRM_Core_PseudoConstant::nestedGroup('Mailing'); + $groups = CRM_Core_PseudoConstant::nestedGroup(TRUE, 'Mailing'); // Get the sms mailing list. $mailings = CRM_Mailing_PseudoConstant::completed('sms'); diff --git a/civicrm/CRM/SMS/Form/Schedule.php b/civicrm/CRM/SMS/Form/Schedule.php index 3de19681be..04d68bda9f 100644 --- a/civicrm/CRM/SMS/Form/Schedule.php +++ b/civicrm/CRM/SMS/Form/Schedule.php @@ -60,7 +60,7 @@ class CRM_SMS_Form_Schedule extends CRM_Core_Form { 'send_later' => ['id' => 'send_later'], ]); - $this->add('datepicker', 'start_date', '', NULL, FALSE, ['minDate' => time()]); + $this->add('datepicker', 'start_date', '', NULL, FALSE, ['minDate' => date('Y-m-d')]); $this->addFormRule(['CRM_SMS_Form_Schedule', 'formRule'], $this); diff --git a/civicrm/CRM/UF/Form/AdvanceSetting.php b/civicrm/CRM/UF/Form/AdvanceSetting.php index 0a7be0b724..b78c0c5b86 100644 --- a/civicrm/CRM/UF/Form/AdvanceSetting.php +++ b/civicrm/CRM/UF/Form/AdvanceSetting.php @@ -47,7 +47,7 @@ class CRM_UF_Form_AdvanceSetting extends CRM_UF_Form_Group { $form->addElement('text', 'notify', ts('Notify when profile form is submitted?')); //group where new contacts are directed. - $form->addElement('select', 'add_contact_to_group', ts('Add new contacts to a Group?'), $group); + $form->addElement('select', 'add_contact_to_group', ts('Add contacts to a group?'), $group); // add CAPTCHA To this group ? $form->addElement('checkbox', 'add_captcha', ts('Include reCAPTCHA?')); diff --git a/civicrm/CRM/Upgrade/Form.php b/civicrm/CRM/Upgrade/Form.php index 6eb65bf70d..2ab3c1c7e8 100644 --- a/civicrm/CRM/Upgrade/Form.php +++ b/civicrm/CRM/Upgrade/Form.php @@ -286,19 +286,6 @@ SET version = '$version' return FALSE; } - /** - * @param $version - * - * @return bool - */ - public function checkVersion($version) { - $domainID = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_Domain', - $version, 'id', - 'version' - ); - return (bool) $domainID; - } - /** * @return array * @throws Exception diff --git a/civicrm/CRM/Upgrade/Incremental/MessageTemplates.php b/civicrm/CRM/Upgrade/Incremental/MessageTemplates.php index 0b86bc6a6d..0fc233bb2b 100644 --- a/civicrm/CRM/Upgrade/Incremental/MessageTemplates.php +++ b/civicrm/CRM/Upgrade/Incremental/MessageTemplates.php @@ -215,7 +215,23 @@ class CRM_Upgrade_Incremental_MessageTemplates { ['name' => 'contribution_invoice_receipt', 'type' => 'html'], ], ], - + [ + 'version' => '5.30.alpha1', + 'upgrade_descriptor' => ts('Support negative hours for cancellation/transfer'), + 'templates' => [ + ['name' => 'participant_confirm', 'type' => 'html'], + ['name' => 'participant_confirm', 'type' => 'text'], + ['name' => 'event_online_receipt', 'type' => 'html'], + ['name' => 'event_online_receipt', 'type' => 'text'], + ], + ], + [ + 'version' => '5.30.beta1', + 'upgrade_descriptor' => ts('Ensure that amount paid is shown even when fully paid'), + 'templates' => [ + ['name' => 'contribution_invoice_receipt', 'type' => 'html'], + ], + ], ]; } diff --git a/civicrm/CRM/Upgrade/Incremental/php/FiveThirty.php b/civicrm/CRM/Upgrade/Incremental/php/FiveThirty.php new file mode 100644 index 0000000000..8ccaf073f3 --- /dev/null +++ b/civicrm/CRM/Upgrade/Incremental/php/FiveThirty.php @@ -0,0 +1,114 @@ +<?php +/* + +--------------------------------------------------------------------+ + | Copyright CiviCRM LLC. All rights reserved. | + | | + | This work is published under the GNU AGPLv3 license with some | + | permitted exceptions and without any warranty. For full license | + | and copyright information, see https://civicrm.org/licensing | + +--------------------------------------------------------------------+ + */ + +/** + * Upgrade logic for FiveThirty */ +class CRM_Upgrade_Incremental_php_FiveThirty extends CRM_Upgrade_Incremental_Base { + + /** + * Compute any messages which should be displayed beforeupgrade. + * + * Note: This function is called iteratively for each upcoming + * revision to the database. + * + * @param string $preUpgradeMessage + * @param string $rev + * a version number, e.g. '4.4.alpha1', '4.4.beta3', '4.4.0'. + * @param null $currentVer + */ + public function setPreUpgradeMessage(&$preUpgradeMessage, $rev, $currentVer = NULL) { + // Example: Generate a pre-upgrade message. + // if ($rev == '5.12.34') { + // $preUpgradeMessage .= '<p>' . ts('A new permission, "%1", has been added. This permission is now used to control access to the Manage Tags screen.', array(1 => ts('manage tags'))) . '</p>'; + // } + } + + /** + * Compute any messages which should be displayed after upgrade. + * + * @param string $postUpgradeMessage + * alterable. + * @param string $rev + * an intermediate version; note that setPostUpgradeMessage is called repeatedly with different $revs. + */ + public function setPostUpgradeMessage(&$postUpgradeMessage, $rev) { + // Example: Generate a post-upgrade message. + // if ($rev == '5.12.34') { + // $postUpgradeMessage .= '<br /><br />' . ts("By default, CiviCRM now disables the ability to import directly from SQL. To use this feature, you must explicitly grant permission 'import SQL datasource'."); + // } + } + + /* + * Important! All upgrade functions MUST add a 'runSql' task. + * Uncomment and use the following template for a new upgrade version + * (change the x in the function name): + */ + + // /** + // * Upgrade function. + // * + // * @param string $rev + // */ + // public function upgrade_5_0_x($rev) { + // $this->addTask(ts('Upgrade DB to %1: SQL', [1 => $rev]), 'runSql', $rev); + // $this->addTask('Do the foo change', 'taskFoo', ...); + // // Additional tasks here... + // // Note: do not use ts() in the addTask description because it adds unnecessary strings to transifex. + // // The above is an exception because 'Upgrade DB to %1: SQL' is generic & reusable. + // } + + /** + * Upgrade function. + * + * @param string $rev + */ + public function upgrade_5_30_alpha1($rev) { + $this->addTask(ts('Upgrade DB to %1: SQL', [1 => $rev]), 'runSql', $rev); + $this->addTask('Add core (required) extension Financial ACLs', 'installFinancialAcls'); + } + + // public static function taskFoo(CRM_Queue_TaskContext $ctx, ...) { + // return TRUE; + // } + + /** + * Install financialacls extension. + * + * This feature is restructured as a core extension - which is primarily a code cleanup step. + * + * @param \CRM_Queue_TaskContext $ctx + * + * @return bool + * + * @throws \CiviCRM_API3_Exception + * @throws \CRM_Core_Exception + */ + public static function installFinancialAcls(CRM_Queue_TaskContext $ctx) { + // Install via direct SQL manipulation. Note that: + // (1) This extension has no activation logic. + // (2) On new installs, the extension is activated purely via default SQL INSERT. + // (3) Caches are flushed at the end of the upgrade. + // ($) Over long term, upgrade steps are more reliable in SQL. API/BAO sometimes don't work mid-upgrade. + $insert = CRM_Utils_SQL_Insert::into('civicrm_extension')->row([ + 'type' => 'module', + 'full_name' => 'financialacls', + 'name' => 'financialacls', + 'label' => 'Financial ACLs', + 'file' => 'financialacls', + 'schema_version' => NULL, + 'is_active' => 1, + ]); + CRM_Core_DAO::executeQuery($insert->usingReplace()->toSQL()); + + return TRUE; + } + +} diff --git a/civicrm/CRM/Upgrade/Incremental/php/FourFive.php b/civicrm/CRM/Upgrade/Incremental/php/FourFive.php index d905ea536b..0f27a0be48 100644 --- a/civicrm/CRM/Upgrade/Incremental/php/FourFive.php +++ b/civicrm/CRM/Upgrade/Incremental/php/FourFive.php @@ -52,7 +52,8 @@ class CRM_Upgrade_Incremental_php_FourFive extends CRM_Upgrade_Incremental_Base // if DB is been into upgrade for 3.4.2 version, it would have pdf_format_id name for FK // else FK_civicrm_msg_template_pdf_format_id $config = CRM_Core_Config::singleton(); - $dbUf = DB::parseDSN($config->dsn); + $dsn = CRM_Utils_SQL::autoSwitchDSN($config->dsn); + $dbUf = DB::parseDSN($dsn); $query = " SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = 'civicrm_msg_template' diff --git a/civicrm/CRM/Upgrade/Incremental/sql/5.29.0.mysql.tpl b/civicrm/CRM/Upgrade/Incremental/sql/5.29.0.mysql.tpl deleted file mode 100644 index a25ed8c29a..0000000000 --- a/civicrm/CRM/Upgrade/Incremental/sql/5.29.0.mysql.tpl +++ /dev/null @@ -1 +0,0 @@ -{* file to handle db changes in 5.29.0 during upgrade *} diff --git a/civicrm/CRM/Upgrade/Incremental/sql/5.30.0.mysql.tpl b/civicrm/CRM/Upgrade/Incremental/sql/5.30.0.mysql.tpl new file mode 100644 index 0000000000..110936b376 --- /dev/null +++ b/civicrm/CRM/Upgrade/Incremental/sql/5.30.0.mysql.tpl @@ -0,0 +1 @@ +{* file to handle db changes in 5.30.0 during upgrade *} diff --git a/civicrm/CRM/Upgrade/Incremental/sql/5.30.alpha1.mysql.tpl b/civicrm/CRM/Upgrade/Incremental/sql/5.30.alpha1.mysql.tpl new file mode 100644 index 0000000000..3b0804b3c3 --- /dev/null +++ b/civicrm/CRM/Upgrade/Incremental/sql/5.30.alpha1.mysql.tpl @@ -0,0 +1,4 @@ +{* file to handle db changes in 5.30.alpha1 during upgrade *} +-- Allow self-service/transfer to have a negative time. +ALTER TABLE civicrm_event MODIFY COLUMN selfcancelxfer_time INT; + diff --git a/civicrm/CRM/Upgrade/Incremental/sql/5.30.beta1.mysql.tpl b/civicrm/CRM/Upgrade/Incremental/sql/5.30.beta1.mysql.tpl new file mode 100644 index 0000000000..9c22ccacf1 --- /dev/null +++ b/civicrm/CRM/Upgrade/Incremental/sql/5.30.beta1.mysql.tpl @@ -0,0 +1 @@ +{* file to handle db changes in 5.30.beta1 during upgrade *} diff --git a/civicrm/CRM/Upgrade/Page/Upgrade.php b/civicrm/CRM/Upgrade/Page/Upgrade.php index 77b7ad1103..06a377924c 100644 --- a/civicrm/CRM/Upgrade/Page/Upgrade.php +++ b/civicrm/CRM/Upgrade/Page/Upgrade.php @@ -78,35 +78,49 @@ class CRM_Upgrade_Page_Upgrade extends CRM_Core_Page { $template = CRM_Core_Smarty::singleton(); list($currentVer, $latestVer) = $upgrade->getUpgradeVersions(); - if ($error = $upgrade->checkUpgradeableVersion($currentVer, $latestVer)) { + // Show success msg if db already upgraded + if (version_compare($currentVer, $latestVer) == 0) { + $template->assign('upgraded', TRUE); + $template->assign('newVersion', $latestVer); + CRM_Utils_System::setTitle(ts('Your database has already been upgraded to CiviCRM %1', + [1 => $latestVer] + )); + $template->assign('pageTitle', ts('Your database has already been upgraded to CiviCRM %1', + [1 => $latestVer] + )); + } + + // Throw error if db in unexpected condition + elseif ($error = $upgrade->checkUpgradeableVersion($currentVer, $latestVer)) { throw new CRM_Core_Exception($error); } - $config = CRM_Core_Config::singleton(); + else { + $config = CRM_Core_Config::singleton(); - // All cached content needs to be cleared because the civi codebase was just replaced - CRM_Core_Resources::singleton()->flushStrings()->resetCacheCode(); + // All cached content needs to be cleared because the civi codebase was just replaced + CRM_Core_Resources::singleton()->flushStrings()->resetCacheCode(); - // cleanup only the templates_c directory - $config->cleanup(1, FALSE); + // cleanup only the templates_c directory + $config->cleanup(1, FALSE); - $preUpgradeMessage = NULL; - $upgrade->setPreUpgradeMessage($preUpgradeMessage, $currentVer, $latestVer); + $preUpgradeMessage = NULL; + $upgrade->setPreUpgradeMessage($preUpgradeMessage, $currentVer, $latestVer); - $template->assign('currentVersion', $currentVer); - $template->assign('newVersion', $latestVer); - $template->assign('upgradeTitle', ts('Upgrade CiviCRM from v %1 To v %2', - [1 => $currentVer, 2 => $latestVer] - )); - $template->assign('upgraded', FALSE); + $template->assign('preUpgradeMessage', $preUpgradeMessage); + $template->assign('currentVersion', $currentVer); + $template->assign('newVersion', $latestVer); + $template->assign('upgradeTitle', ts('Upgrade CiviCRM from v %1 To v %2', + [1 => $currentVer, 2 => $latestVer] + )); + $template->assign('upgraded', FALSE); + } // Render page header if (!defined('CIVICRM_UF_HEAD') && $region = CRM_Core_Region::instance('html-header', FALSE)) { CRM_Utils_System::addHTMLHead($region->render('')); } - $template->assign('preUpgradeMessage', $preUpgradeMessage); - $content = $template->fetch('CRM/common/success.tpl'); echo CRM_Utils_System::theme($content, $this->_print, TRUE); } diff --git a/civicrm/CRM/Utils/Array.php b/civicrm/CRM/Utils/Array.php index 14563ccabe..5ecdb554d5 100644 --- a/civicrm/CRM/Utils/Array.php +++ b/civicrm/CRM/Utils/Array.php @@ -119,26 +119,26 @@ class CRM_Utils_Array { * The array to be serialized. * @param int $depth * (optional) Indentation depth counter. - * @param string $seperator + * @param string $separator * (optional) String to be appended after open/close tags. * * @return string * XML fragment representing $list. */ - public static function &xml(&$list, $depth = 1, $seperator = "\n") { + public static function &xml(&$list, $depth = 1, $separator = "\n") { $xml = ''; foreach ($list as $name => $value) { $xml .= str_repeat(' ', $depth * 4); if (is_array($value)) { - $xml .= "<{$name}>{$seperator}"; - $xml .= self::xml($value, $depth + 1, $seperator); + $xml .= "<{$name}>{$separator}"; + $xml .= self::xml($value, $depth + 1, $separator); $xml .= str_repeat(' ', $depth * 4); - $xml .= "</{$name}>{$seperator}"; + $xml .= "</{$name}>{$separator}"; } else { // make sure we escape value $value = self::escapeXML($value); - $xml .= "<{$name}>$value</{$name}>{$seperator}"; + $xml .= "<{$name}>$value</{$name}>{$separator}"; } } return $xml; @@ -218,14 +218,14 @@ class CRM_Utils_Array { * Destination array. * @param string $prefix * (optional) String to prepend to keys. - * @param string $seperator + * @param string $separator * (optional) String that separates the concatenated keys. */ - public static function flatten(&$list, &$flat, $prefix = '', $seperator = ".") { + public static function flatten(&$list, &$flat, $prefix = '', $separator = ".") { foreach ($list as $name => $value) { - $newPrefix = ($prefix) ? $prefix . $seperator . $name : $name; + $newPrefix = ($prefix) ? $prefix . $separator . $name : $name; if (is_array($value)) { - self::flatten($value, $flat, $newPrefix, $seperator); + self::flatten($value, $flat, $newPrefix, $separator); } else { $flat[$newPrefix] = $value; diff --git a/civicrm/CRM/Utils/Check/Component.php b/civicrm/CRM/Utils/Check/Component.php index 37a7171cab..2f57004ade 100644 --- a/civicrm/CRM/Utils/Check/Component.php +++ b/civicrm/CRM/Utils/Check/Component.php @@ -28,15 +28,9 @@ abstract class CRM_Utils_Check_Component { */ public function getChecksConfig() { if (!isset(Civi::$statics[__FUNCTION__])) { - // TODO: Remove this check when MINIMUM_UPGRADABLE_VERSION goes to 4.7. - if (CRM_Utils_System::version() !== CRM_Core_BAO_Domain::version() && !CRM_Core_DAO::checkTableExists('civicrm_status_pref')) { - Civi::$statics[__FUNCTION__] = []; - } - else { - Civi::$statics[__FUNCTION__] = (array) StatusPreference::get(FALSE) - ->addWhere('domain_id', '=', 'current_domain') - ->execute()->indexBy('name'); - } + Civi::$statics[__FUNCTION__] = (array) StatusPreference::get(FALSE) + ->addWhere('domain_id', '=', 'current_domain') + ->execute()->indexBy('name'); } return Civi::$statics[__FUNCTION__]; } diff --git a/civicrm/CRM/Utils/Check/Component/Env.php b/civicrm/CRM/Utils/Check/Component/Env.php index 26e77d8cae..7c57df65fd 100644 --- a/civicrm/CRM/Utils/Check/Component/Env.php +++ b/civicrm/CRM/Utils/Check/Component/Env.php @@ -471,13 +471,15 @@ class CRM_Utils_Check_Component_Env extends CRM_Utils_Check_Component { /** * Checks if new versions are available + * @param bool $force * @return CRM_Utils_Check_Message[] + * @throws CRM_Core_Exception */ - public function checkVersion() { + public function checkVersion($force = FALSE) { $messages = []; try { $vc = new CRM_Utils_VersionCheck(); - $vc->initialize(); + $vc->initialize($force); } catch (Exception $e) { $messages[] = new CRM_Utils_Check_Message( @@ -492,7 +494,7 @@ class CRM_Utils_Check_Component_Env extends CRM_Utils_Check_Component { } // Show a notice if the version_check job is disabled - if (empty($vc->cronJob['is_active'])) { + if (!$force && empty($vc->cronJob['is_active'])) { $args = empty($vc->cronJob['id']) ? ['reset' => 1] : ['reset' => 1, 'action' => 'update', 'id' => $vc->cronJob['id']]; $messages[] = new CRM_Utils_Check_Message( 'checkVersionDisabled', @@ -504,15 +506,9 @@ class CRM_Utils_Check_Component_Env extends CRM_Utils_Check_Component { } if ($vc->isInfoAvailable) { - $severities = [ - 'info' => CRM_Utils_Check::severityMap(\Psr\Log\LogLevel::INFO), - 'notice' => CRM_Utils_Check::severityMap(\Psr\Log\LogLevel::NOTICE) , - 'warning' => CRM_Utils_Check::severityMap(\Psr\Log\LogLevel::WARNING) , - 'critical' => CRM_Utils_Check::severityMap(\Psr\Log\LogLevel::CRITICAL), - ]; foreach ($vc->getVersionMessages() ?? [] as $msg) { $messages[] = new CRM_Utils_Check_Message(__FUNCTION__ . '_' . $msg['name'], - $msg['message'], $msg['title'], $severities[$msg['severity']], 'fa-cloud-upload'); + $msg['message'], $msg['title'], $msg['severity'], 'fa-cloud-upload'); } } diff --git a/civicrm/CRM/Utils/Check/Message.php b/civicrm/CRM/Utils/Check/Message.php index 94f51db49d..e0a7d51d7b 100644 --- a/civicrm/CRM/Utils/Check/Message.php +++ b/civicrm/CRM/Utils/Check/Message.php @@ -246,32 +246,25 @@ class CRM_Utils_Check_Message { * @throws \CiviCRM_API3_Exception */ private function checkStatusPreference() { - // TODO: Remove this check when MINIMUM_UPGRADABLE_VERSION goes to 4.7. - if (CRM_Utils_System::version() !== CRM_Core_BAO_Domain::version() && !CRM_Core_DAO::checkTableExists('civicrm_status_pref')) { - return FALSE; - } - $this->hiddenUntil = FALSE; // Debug & info can't be hidden if ($this->level < 2) { return FALSE; } - $statusPreferenceParams = [ - 'name' => $this->getName(), - 'domain_id' => CRM_Core_Config::domainID(), - 'sequential' => 1, + $where = [ + ['name', '=', $this->getName()], + ['domain_id', '=', CRM_Core_Config::domainID()], ]; // Check if there's a StatusPreference matching this name/domain. - $statusPreference = civicrm_api3('StatusPreference', 'get', $statusPreferenceParams); - $prefs = CRM_Utils_Array::value('values', $statusPreference, []); - if ($prefs) { + $pref = civicrm_api4('StatusPreference', 'get', ['checkPermissions' => FALSE, 'where' => $where])->first(); + if ($pref) { // If so, compare severity to StatusPreference->severity. - if ($this->level <= $prefs[0]['ignore_severity']) { - if (isset($prefs[0]['hush_until'])) { + if ($this->level <= $pref['ignore_severity']) { + if (isset($pref['hush_until'])) { // Time-based hush. - $this->hiddenUntil = $prefs[0]['hush_until']; + $this->hiddenUntil = $pref['hush_until']; $today = new DateTime(); - $snoozeDate = new DateTime($prefs[0]['hush_until']); + $snoozeDate = new DateTime($pref['hush_until']); return !($today > $snoozeDate); } else { diff --git a/civicrm/CRM/Utils/Date.php b/civicrm/CRM/Utils/Date.php index 831fca5bc8..11008a8443 100644 --- a/civicrm/CRM/Utils/Date.php +++ b/civicrm/CRM/Utils/Date.php @@ -26,7 +26,7 @@ class CRM_Utils_Date { * @param array $date * ('Y', 'M', 'd'). * @param string $separator - * The seperator to use when formatting the date. + * The separator to use when formatting the date. * @param int|string $invalidDate what to return if the date is invalid * * @return string diff --git a/civicrm/CRM/Utils/File.php b/civicrm/CRM/Utils/File.php index 67da4b10a6..c196b3dd71 100644 --- a/civicrm/CRM/Utils/File.php +++ b/civicrm/CRM/Utils/File.php @@ -325,6 +325,7 @@ class CRM_Utils_File { } else { require_once 'DB.php'; + $dsn = CRM_Utils_SQL::autoSwitchDSN($dsn); $db = DB::connect($dsn); } diff --git a/civicrm/CRM/Utils/Mail/EmailProcessor.php b/civicrm/CRM/Utils/Mail/EmailProcessor.php index 9d68310b9d..a8be02ce0a 100644 --- a/civicrm/CRM/Utils/Mail/EmailProcessor.php +++ b/civicrm/CRM/Utils/Mail/EmailProcessor.php @@ -134,10 +134,10 @@ class CRM_Utils_Mail_EmailProcessor { } $config = CRM_Core_Config::singleton(); - $verpSeperator = preg_quote($config->verpSeparator); - $twoDigitStringMin = $verpSeperator . '(\d+)' . $verpSeperator . '(\d+)'; - $twoDigitString = $twoDigitStringMin . $verpSeperator; - $threeDigitString = $twoDigitString . '(\d+)' . $verpSeperator; + $verpSeparator = preg_quote($config->verpSeparator); + $twoDigitStringMin = $verpSeparator . '(\d+)' . $verpSeparator . '(\d+)'; + $twoDigitString = $twoDigitStringMin . $verpSeparator; + $threeDigitString = $twoDigitString . '(\d+)' . $verpSeparator; // FIXME: legacy regexen to handle CiviCRM 2.1 address patterns, with domain id and possible VERP part $commonRegex = '/^' . preg_quote($dao->localpart) . '(b|bounce|c|confirm|o|optOut|r|reply|re|e|resubscribe|u|unsubscribe)' . $threeDigitString . '([0-9a-f]{16})(-.*)?@' . preg_quote($dao->domain) . '$/'; diff --git a/civicrm/CRM/Utils/SQL.php b/civicrm/CRM/Utils/SQL.php index 70e1c5a26e..3b9167e626 100644 --- a/civicrm/CRM/Utils/SQL.php +++ b/civicrm/CRM/Utils/SQL.php @@ -168,4 +168,39 @@ class CRM_Utils_SQL { return CRM_Core_DAO::singleValueQuery('SELECT VERSION()'); } + /** + * Does the DSN indicate the connection should use ssl. + * + * @param string $dsn + * + * @return bool + */ + public static function isSSLDSN(string $dsn):bool { + // Note that ssl= below is not an official PEAR::DB option. It doesn't know + // what to do with it. We made it up because it's not required + // to have client-side certificates to use ssl, so here you can specify + // you want that by putting ssl=1 in the DSN string. + // + // Cast to bool in case of error which we interpret as no ssl. + return (bool) preg_match('/[\?&](key|cert|ca|capath|cipher|ssl)=/', $dsn); + } + + /** + * If DB_DSN_MODE is auto then we should replace mysql with mysqli if mysqli is available or the other way around as appropriate + * @param string $dsn + * + * @return string + */ + public static function autoSwitchDSN($dsn) { + if (defined('DB_DSN_MODE') && DB_DSN_MODE === 'auto') { + if (extension_loaded('mysqli')) { + $dsn = preg_replace('/^mysql:/', 'mysqli:', $dsn); + } + else { + $dsn = preg_replace('/^mysqli:/', 'mysql:', $dsn); + } + } + return $dsn; + } + } diff --git a/civicrm/CRM/Utils/SQL/TempTable.php b/civicrm/CRM/Utils/SQL/TempTable.php index 8993807f3c..89ed932432 100644 --- a/civicrm/CRM/Utils/SQL/TempTable.php +++ b/civicrm/CRM/Utils/SQL/TempTable.php @@ -50,7 +50,14 @@ */ class CRM_Utils_SQL_TempTable { + /** + * @deprecated + * The system will attempt to use the same as your other tables, and + * if you really need something else then use createWithColumns and + * specify it per-column there. + */ const UTF8 = 'DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci'; + const CATEGORY_LENGTH = 12; const CATEGORY_REGEXP = ';^[a-zA-Z0-9]+$;'; // MAX{64} - CATEGORY_LENGTH{12} - CONST_LENGHTH{15} = 37 @@ -135,8 +142,10 @@ class CRM_Utils_SQL_TempTable { /** * Get the utf8 string for the table. * - * Our tables are either utf8_unicode_ci OR utf8mb8_unicode_ci - check the contact table - * to see which & use the matching one. + * Our tables are either utf8_unicode_ci OR utf8mb4_unicode_ci - check the contact table + * to see which & use the matching one. Or early adopters may have switched + * switched to other collations e.g. utf8mb4_0900_ai_ci (the default in mysql + * 8). * * @return string */ @@ -241,9 +250,11 @@ class CRM_Utils_SQL_TempTable { } /** + * @deprecated * @return bool */ public function isUtf8() { + CRM_Core_Error::deprecatedFunctionWarning('your own charset/collation per column with createWithColumns if you really need latin1'); return $this->utf8; } @@ -322,6 +333,7 @@ class CRM_Utils_SQL_TempTable { * @return $this */ public function setUtf8($value = TRUE) { + CRM_Core_Error::deprecatedFunctionWarning('your own charset/collation per column with createWithColumns if you really need latin1'); $this->utf8 = $value; return $this; } diff --git a/civicrm/CRM/Utils/SameSite.php b/civicrm/CRM/Utils/SameSite.php new file mode 100644 index 0000000000..0431761aae --- /dev/null +++ b/civicrm/CRM/Utils/SameSite.php @@ -0,0 +1,224 @@ +<?php +/* + +--------------------------------------------------------------------+ + | Copyright CiviCRM LLC. All rights reserved. | + | | + | This work is published under the GNU AGPLv3 license with some | + | permitted exceptions and without any warranty. For full license | + | and copyright information, see https://civicrm.org/licensing | + +--------------------------------------------------------------------+ + */ + +/** + * SameSite Utility Class. + * + * Determines if the current User Agent can handle the `SameSite=None` parameter + * by mapping against known incompatible clients. + * + * Sample code: + * + * // Get User Agent string. + * $rawUserAgent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : ''; + * $userAgent = mb_convert_encoding($rawUserAgent, 'UTF-8'); + * + * // Get boolean representing User Agent compatibility. + * $shouldUseSameSite = CRM_Utils_SameSite::shouldSendSameSiteNone($userAgent); + * + * Based on code provided by "The Chromium Projects". + * + * @see https://www.chromium.org/updates/same-site/incompatible-clients + */ +class CRM_Utils_SameSite { + + /** + * Determine if the current User Agent can handle the `SameSite=None` parameter. + * + * @param str $userAgent The User Agent. + * @return bool True if the User Agent is compatible, FALSE otherwise. + */ + public static function shouldSendSameSiteNone($userAgent) { + return !self::isSameSiteNoneIncompatible($userAgent); + } + + /** + * Detect classes of browsers known to be incompatible. + * + * @param str $userAgent The User Agent. + * @return bool True if the User Agent is determined to be incompatible, FALSE otherwise. + */ + private static function isSameSiteNoneIncompatible($userAgent) { + return self::hasWebKitSameSiteBug($userAgent) || + self::dropsUnrecognizedSameSiteCookies($userAgent); + } + + /** + * Detect versions of Safari and embedded browsers on MacOS 10.14 and all + * browsers on iOS 12. + * + * These versions will erroneously treat cookies marked with `SameSite=None` + * as if they were marked `SameSite=Strict`. + * + * @param str $userAgent The User Agent. + * @return bool + */ + private static function hasWebKitSameSiteBug($userAgent) { + return self::isIosVersion(12, $userAgent) || (self::isMacosxVersion(10, 14, $userAgent) && + (self::isSafari($userAgent) || self::isMacEmbeddedBrowser($userAgent))); + } + + /** + * Detect versions of UC Browser on Android prior to version 12.13.2. + * + * Older versions will reject a cookie with `SameSite=None`. This behavior was + * correct according to the version of the cookie specification at that time, + * but with the addition of the new "None" value to the specification, this + * behavior has been updated in newer versions of UC Browser. + * + * @param str $userAgent The User Agent. + * @return bool + */ + private static function dropsUnrecognizedSameSiteCookies($userAgent) { + if (self::isUcBrowser($userAgent)) { + return !self::isUcBrowserVersionAtLeast(12, 13, 2, $userAgent); + } + + return self::isChromiumBased($userAgent) && + self::isChromiumVersionAtLeast(51, $userAgent, '>=') && + self::isChromiumVersionAtLeast(67, $userAgent, '<='); + } + + /** + * Detect iOS version. + * + * @param int $major The major version to test. + * @param str $userAgent The User Agent. + * @return bool + */ + private static function isIosVersion($major, $userAgent) { + $regex = "/\(iP.+; CPU .*OS (\d+)[_\d]*.*\) AppleWebKit\//"; + $matched = []; + + if (preg_match($regex, $userAgent, $matched)) { + // Extract digits from first capturing group. + $version = (int) $matched[1]; + return version_compare($version, $major, '<='); + } + + return FALSE; + } + + /** + * Detect MacOS version. + * + * @param int $major The major version to test. + * @param int $minor The minor version to test. + * @param str $userAgent The User Agent. + * @return bool + */ + private static function isMacosxVersion($major, $minor, $userAgent) { + $regex = "/\(Macintosh;.*Mac OS X (\d+)_(\d+)[_\d]*.*\) AppleWebKit\//"; + $matched = []; + + if (preg_match($regex, $userAgent, $matched)) { + // Extract digits from first and second capturing groups. + return version_compare((int) $matched[1], $major, '=') && + version_compare((int) $matched[2], $minor, '<='); + } + + return FALSE; + } + + /** + * Detect MacOS Safari. + * + * @param str $userAgent The User Agent. + * @return bool + */ + private static function isSafari($userAgent) { + $regex = "/Version\/.* Safari\//"; + return preg_match($regex, $userAgent) && !self::isChromiumBased($userAgent); + } + + /** + * Detect MacOS embedded browser. + * + * @param str $userAgent The User Agent. + * @return FALSE|int + */ + private static function isMacEmbeddedBrowser($userAgent) { + $regex = "/^Mozilla\/[\.\d]+ \(Macintosh;.*Mac OS X [_\d]+\) AppleWebKit\/[\.\d]+ \(KHTML, like Gecko\)$/"; + return preg_match($regex, $userAgent); + } + + /** + * Detect if browser is Chromium based. + * + * @param str $userAgent The User Agent. + * @return FALSE|int + */ + private static function isChromiumBased($userAgent) { + $regex = "/Chrom(e|ium)/"; + return preg_match($regex, $userAgent); + } + + /** + * Detect if Chromium version meets requirements. + * + * @param int $major The major version to test. + * @param str $userAgent The User Agent. + * @param str $operator + * @return bool|int + */ + private static function isChromiumVersionAtLeast($major, $userAgent, $operator) { + $regex = "/Chrom[^ \/]+\/(\d+)[\.\d]* /"; + $matched = []; + + if (preg_match($regex, $userAgent, $matched)) { + // Extract digits from first capturing group. + $version = (int) $matched[1]; + return version_compare($version, $major, $operator); + } + return FALSE; + } + + /** + * Detect UCBrowser. + * + * @param str $userAgent The User Agent. + * @return FALSE|int + */ + private static function isUcBrowser($userAgent) { + $regex = "/UCBrowser\//"; + return preg_match($regex, $userAgent); + } + + /** + * Detect if UCBrowser version meets requirements. + * + * @param int $major The major version to test. + * @param int $minor The minor version to test. + * @param int $build The build version to test. + * @param str $userAgent The User Agent. + * @return bool|int + */ + private static function isUcBrowserVersionAtLeast($major, $minor, $build, $userAgent) { + $regex = "/UCBrowser\/(\d+)\.(\d+)\.(\d+)[\.\d]* /"; + $matched = []; + + if (preg_match($regex, $userAgent, $matched)) { + // Extract digits from three capturing groups. + $majorVersion = (int) $matched[1]; + $minorVersion = (int) $matched[2]; + $buildVersion = (int) $matched[3]; + + if (version_compare($majorVersion, $major, '>=')) { + if (version_compare($minorVersion, $minor, '>=')) { + return version_compare($buildVersion, $build, '>='); + } + } + } + + return FALSE; + } + +} diff --git a/civicrm/CRM/Utils/System.php b/civicrm/CRM/Utils/System.php index 74ddd37d93..51ba3572d4 100644 --- a/civicrm/CRM/Utils/System.php +++ b/civicrm/CRM/Utils/System.php @@ -511,7 +511,7 @@ class CRM_Utils_System { } self::setHttpHeader('Location', $url); - self::civiExit(); + self::civiExit(0, ['url' => $url, 'context' => 'redirect']); } /** @@ -717,15 +717,12 @@ class CRM_Utils_System { * process typically done in CLI and cron scripts. See: CRM-12648 * * Q: Can we move this to the userSystem class so that it can be tuned - * per-CMS? For example, when dealing with UnitTests UF, there's no - * userFrameworkDSN. + * per-CMS? For example, when dealing with UnitTests UF, does it need to + * do this session write since the original issue was for Drupal. */ $session = CRM_Core_Session::singleton(); $session->set('civicrmInitSession', TRUE); - if ($config->userFrameworkDSN) { - $dbDrupal = DB::connect($config->userFrameworkDSN); - } return $config->userSystem->authenticate($name, $password, $loadCMSBootstrap, $realPath); } @@ -1918,4 +1915,11 @@ class CRM_Utils_System { $config = CRM_Core_Config::singleton()->userSystem->sendResponse($response); } + /** + * Perform any necessary actions prior to redirecting via POST. + */ + public static function prePostRedirect() { + CRM_Core_Config::singleton()->userSystem->prePostRedirect(); + } + } diff --git a/civicrm/CRM/Utils/System/Backdrop.php b/civicrm/CRM/Utils/System/Backdrop.php index d46e56146c..83b66078cd 100644 --- a/civicrm/CRM/Utils/System/Backdrop.php +++ b/civicrm/CRM/Utils/System/Backdrop.php @@ -291,9 +291,10 @@ class CRM_Utils_System_Backdrop extends CRM_Utils_System_DrupalBase { public function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realPath = NULL) { $config = CRM_Core_Config::singleton(); - $dbBackdrop = DB::connect($config->userFrameworkDSN); + $ufDSN = CRM_Utils_SQL::autoSwitchDSN($config->userFrameworkDSN); + $dbBackdrop = DB::connect($ufDSN); if (DB::isError($dbBackdrop)) { - throw new CRM_Core_Exception("Cannot connect to Backdrop database via $config->userFrameworkDSN, " . $dbBackdrop->getMessage()); + throw new CRM_Core_Exception("Cannot connect to Backdrop database via $ufDSN, " . $dbBackdrop->getMessage()); } $account = $userUid = $userMail = NULL; diff --git a/civicrm/CRM/Utils/System/Base.php b/civicrm/CRM/Utils/System/Base.php index 5c4f1d58bb..aabb9aa48b 100644 --- a/civicrm/CRM/Utils/System/Base.php +++ b/civicrm/CRM/Utils/System/Base.php @@ -998,4 +998,10 @@ abstract class CRM_Utils_System_Base { return FALSE; } + /** + * Perform any necessary actions prior to redirecting via POST. + */ + public function prePostRedirect() { + } + } diff --git a/civicrm/CRM/Utils/System/Drupal.php b/civicrm/CRM/Utils/System/Drupal.php index bf4f8693cf..710e178355 100644 --- a/civicrm/CRM/Utils/System/Drupal.php +++ b/civicrm/CRM/Utils/System/Drupal.php @@ -317,9 +317,10 @@ class CRM_Utils_System_Drupal extends CRM_Utils_System_DrupalBase { $config = CRM_Core_Config::singleton(); - $dbDrupal = DB::connect($config->userFrameworkDSN); + $ufDSN = CRM_Utils_SQL::autoSwitchDSN($config->userFrameworkDSN); + $dbDrupal = DB::connect($ufDSN); if (DB::isError($dbDrupal)) { - throw new CRM_Core_Exception("Cannot connect to drupal db via $config->userFrameworkDSN, " . $dbDrupal->getMessage()); + throw new CRM_Core_Exception("Cannot connect to drupal db via $ufDSN, " . $dbDrupal->getMessage()); } $account = $userUid = $userMail = NULL; diff --git a/civicrm/CRM/Utils/System/Drupal6.php b/civicrm/CRM/Utils/System/Drupal6.php index 0ac291164e..343ec7fe74 100644 --- a/civicrm/CRM/Utils/System/Drupal6.php +++ b/civicrm/CRM/Utils/System/Drupal6.php @@ -300,9 +300,10 @@ class CRM_Utils_System_Drupal6 extends CRM_Utils_System_DrupalBase { $config = CRM_Core_Config::singleton(); - $dbDrupal = DB::connect($config->userFrameworkDSN); + $ufDSN = CRM_Utils_SQL::autoSwitchDSN($config->userFrameworkDSN); + $dbDrupal = DB::connect($ufDSN); if (DB::isError($dbDrupal)) { - throw new CRM_Core_Exception("Cannot connect to drupal db via $config->userFrameworkDSN, " . $dbDrupal->getMessage()); + throw new CRM_Core_Exception("Cannot connect to drupal db via $ufDSN, " . $dbDrupal->getMessage()); } $strtolower = function_exists('mb_strtolower') ? 'mb_strtolower' : 'strtolower'; diff --git a/civicrm/CRM/Utils/System/WordPress.php b/civicrm/CRM/Utils/System/WordPress.php index 5983774cd7..f13ad38830 100644 --- a/civicrm/CRM/Utils/System/WordPress.php +++ b/civicrm/CRM/Utils/System/WordPress.php @@ -326,9 +326,9 @@ class CRM_Utils_System_WordPress extends CRM_Utils_System_Base { if ($config->userFrameworkFrontend) { global $post; if (get_option('permalink_structure') != '') { - $script = get_permalink($post->ID); + $script = $post ? get_permalink($post->ID) : ""; } - if ($config->wpBasePage == $post->post_name) { + if ($post && $config->wpBasePage == $post->post_name) { $basepage = TRUE; } // when shortcode is included in page @@ -1087,4 +1087,161 @@ class CRM_Utils_System_WordPress extends CRM_Utils_System_Base { } } + /** + * Perform any necessary actions prior to redirecting via POST. + * + * Redirecting via POST means that cookies need to be sent with SameSite=None. + */ + public function prePostRedirect() { + // Get User Agent string. + $rawUserAgent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : ''; + $userAgent = mb_convert_encoding($rawUserAgent, 'UTF-8'); + + // Bail early if User Agent does not support `SameSite=None`. + $shouldUseSameSite = CRM_Utils_SameSite::shouldSendSameSiteNone($userAgent); + if (!$shouldUseSameSite) { + return; + } + + // Make sure session cookie is present in header. + $cookie_params = session_name() . '=' . session_id() . '; SameSite=None; Secure'; + CRM_Utils_System::setHttpHeader('Set-Cookie', $cookie_params); + + // Add WordPress auth cookies when user is logged in. + $user = wp_get_current_user(); + if ($user->exists()) { + self::setAuthCookies($user->ID, TRUE, TRUE); + } + } + + /** + * Explicitly set WordPress authentication cookies. + * + * Chrome 84 introduced a cookie policy change which prevents cookies for the + * session and for WordPress user authentication from being indentified when + * a purchaser returns to the site from PayPal using the "Back to Merchant" + * button. + * + * In order to comply with this policy, cookies need to be sent with their + * "SameSite" attribute set to "None" and with the "Secure" flag set, but this + * isn't possible to do via `wp_set_auth_cookie()` as it stands. + * + * This method is a modified clone of `wp_set_auth_cookie()` which satisfies + * the Chrome policy. + * + * @see wp_set_auth_cookie() + * + * The $remember parameter increases the time that the cookie will be kept. The + * default the cookie is kept without remembering is two days. When $remember is + * set, the cookies will be kept for 14 days or two weeks. + * + * @param int $user_id The WordPress User ID. + * @param bool $remember Whether to remember the user. + * @param bool|string $secure Whether the auth cookie should only be sent over + * HTTPS. Default is an empty string which means the + * value of `is_ssl()` will be used. + * @param string $token Optional. User's session token to use for this cookie. + */ + private function setAuthCookies($user_id, $remember = FALSE, $secure = '', $token = '') { + if ($remember) { + /** This filter is documented in wp-includes/pluggable.php */ + $expiration = time() + apply_filters('auth_cookie_expiration', 14 * DAY_IN_SECONDS, $user_id, $remember); + + /* + * Ensure the browser will continue to send the cookie after the expiration time is reached. + * Needed for the login grace period in wp_validate_auth_cookie(). + */ + $expire = $expiration + (12 * HOUR_IN_SECONDS); + } + else { + /** This filter is documented in wp-includes/pluggable.php */ + $expiration = time() + apply_filters('auth_cookie_expiration', 2 * DAY_IN_SECONDS, $user_id, $remember); + $expire = 0; + } + + if ('' === $secure) { + $secure = is_ssl(); + } + + // Front-end cookie is secure when the auth cookie is secure and the site's home URL is forced HTTPS. + $secure_logged_in_cookie = $secure && 'https' === parse_url(get_option('home'), PHP_URL_SCHEME); + + /** This filter is documented in wp-includes/pluggable.php */ + $secure = apply_filters('secure_auth_cookie', $secure, $user_id); + + /** This filter is documented in wp-includes/pluggable.php */ + $secure_logged_in_cookie = apply_filters('secure_logged_in_cookie', $secure_logged_in_cookie, $user_id, $secure); + + if ($secure) { + $auth_cookie_name = SECURE_AUTH_COOKIE; + $scheme = 'secure_auth'; + } + else { + $auth_cookie_name = AUTH_COOKIE; + $scheme = 'auth'; + } + + if ('' === $token) { + $manager = WP_Session_Tokens::get_instance($user_id); + $token = $manager->create($expiration); + } + + $auth_cookie = wp_generate_auth_cookie($user_id, $expiration, $scheme, $token); + $logged_in_cookie = wp_generate_auth_cookie($user_id, $expiration, 'logged_in', $token); + + /** This filter is documented in wp-includes/pluggable.php */ + do_action('set_auth_cookie', $auth_cookie, $expire, $expiration, $user_id, $scheme, $token); + + /** This filter is documented in wp-includes/pluggable.php */ + do_action('set_logged_in_cookie', $logged_in_cookie, $expire, $expiration, $user_id, 'logged_in', $token); + + /** This filter is documented in wp-includes/pluggable.php */ + if (!apply_filters('send_auth_cookies', TRUE)) { + return; + } + + $base_options = [ + 'expires' => $expire, + 'domain' => COOKIE_DOMAIN, + 'httponly' => TRUE, + 'samesite' => 'None', + ]; + + self::setAuthCookie($auth_cookie_name, $auth_cookie, $base_options + ['secure' => $secure, 'path' => PLUGINS_COOKIE_PATH]); + self::setAuthCookie($auth_cookie_name, $auth_cookie, $base_options + ['secure' => $secure, 'path' => ADMIN_COOKIE_PATH]); + self::setAuthCookie(LOGGED_IN_COOKIE, $logged_in_cookie, $base_options + ['secure' => $secure_logged_in_cookie, 'path' => COOKIEPATH]); + if (COOKIEPATH != SITECOOKIEPATH) { + self::setAuthCookie(LOGGED_IN_COOKIE, $logged_in_cookie, $base_options + ['secure' => $secure_logged_in_cookie, 'path' => SITECOOKIEPATH]); + } + } + + /** + * Set cookie with "SameSite" flag. + * + * The method here is compatible with all versions of PHP. Needed because it + * is only as of PHP 7.3.0 that the setcookie() method supports the "SameSite" + * attribute in its options and will accept "None" as a valid value. + * + * @param $name The name of the cookie. + * @param $value The value of the cookie. + * @param array $options The header options for the cookie. + */ + private function setAuthCookie($name, $value, $options) { + $header = 'Set-Cookie: '; + $header .= rawurlencode($name) . '=' . rawurlencode($value) . '; '; + $header .= 'expires=' . gmdate('D, d-M-Y H:i:s T', $options['expires']) . '; '; + $header .= 'Max-Age=' . max(0, (int) ($options['expires'] - time())) . '; '; + $header .= 'path=' . rawurlencode($options['path']) . '; '; + $header .= 'domain=' . rawurlencode($options['domain']) . '; '; + + if (!empty($options['secure'])) { + $header .= 'secure; '; + } + $header .= 'httponly; '; + $header .= 'SameSite=' . rawurlencode($options['samesite']); + + header($header, FALSE); + $_COOKIE[$name] = $value; + } + } diff --git a/civicrm/CRM/Utils/Token.php b/civicrm/CRM/Utils/Token.php index d3774f5d68..850a2516ac 100644 --- a/civicrm/CRM/Utils/Token.php +++ b/civicrm/CRM/Utils/Token.php @@ -1806,8 +1806,10 @@ class CRM_Utils_Token { break; case 'receive_date': + case 'receipt_date': $value = CRM_Utils_Array::retrieveValueRecursive($contribution, $token); - $value = CRM_Utils_Date::customFormat($value, NULL, ['j', 'm', 'Y']); + $config = CRM_Core_Config::singleton(); + $value = CRM_Utils_Date::customFormat($value, $config->dateformatDatetime); break; default: diff --git a/civicrm/CRM/Utils/VersionCheck.php b/civicrm/CRM/Utils/VersionCheck.php index 0b3b9fa460..fbb05a1929 100644 --- a/civicrm/CRM/Utils/VersionCheck.php +++ b/civicrm/CRM/Utils/VersionCheck.php @@ -74,9 +74,10 @@ class CRM_Utils_VersionCheck { /** * Self-populates version info * - * @throws \Exception + * @param bool $force + * @throws Exception */ - public function initialize() { + public function initialize($force = FALSE) { $this->getJob(); // Populate remote $versionInfo from cache file @@ -84,9 +85,9 @@ class CRM_Utils_VersionCheck { // Fallback if scheduled job is enabled but has failed to run. $expiryTime = time() - self::CACHEFILE_EXPIRE; - if (!empty($this->cronJob['is_active']) && + if ($force || (!empty($this->cronJob['is_active']) && (!$this->isInfoAvailable || filemtime($this->cacheFile) < $expiryTime) - ) { + )) { // First try updating the files modification time, for 2 reasons: // - if the file is not writeable, this saves the trouble of pinging back // - if the remote server is down, this will prevent an immediate retry diff --git a/civicrm/Civi/Api4/Generic/AbstractQueryAction.php b/civicrm/Civi/Api4/Generic/AbstractQueryAction.php index ed7ac9bf37..85ebcfb5ed 100644 --- a/civicrm/Civi/Api4/Generic/AbstractQueryAction.php +++ b/civicrm/Civi/Api4/Generic/AbstractQueryAction.php @@ -19,6 +19,8 @@ namespace Civi\Api4\Generic; +use Civi\Api4\Utils\CoreUtil; + /** * Base class for all actions that need to fetch records (`Get`, `Update`, `Delete`, etc.). * @@ -86,7 +88,7 @@ abstract class AbstractQueryAction extends AbstractAction { * @throws \API_Exception */ public function addWhere(string $fieldName, string $op, $value = NULL) { - if (!in_array($op, \CRM_Core_DAO::acceptedSQLOperators())) { + if (!in_array($op, CoreUtil::getOperators())) { throw new \API_Exception('Unsupported operator'); } $this->where[] = [$fieldName, $op, $value]; @@ -145,7 +147,7 @@ abstract class AbstractQueryAction extends AbstractAction { } return $output . '(' . $this->whereClauseToString($whereClause, $op) . ')'; } - elseif (isset($whereClause[1]) && in_array($whereClause[1], \CRM_Core_DAO::acceptedSQLOperators())) { + elseif (isset($whereClause[1]) && in_array($whereClause[1], CoreUtil::getOperators())) { $output = $whereClause[0] . ' ' . $whereClause[1] . ' '; if (isset($whereClause[2])) { $output .= is_array($whereClause[2]) ? '[' . implode(', ', $whereClause[2]) . ']' : $whereClause[2]; diff --git a/civicrm/Civi/Api4/Generic/BasicGetFieldsAction.php b/civicrm/Civi/Api4/Generic/BasicGetFieldsAction.php index ec58714fe4..5f5d25ef3d 100644 --- a/civicrm/Civi/Api4/Generic/BasicGetFieldsAction.php +++ b/civicrm/Civi/Api4/Generic/BasicGetFieldsAction.php @@ -129,6 +129,7 @@ class BasicGetFieldsAction extends BasicGetAction { 'data_type' => \CRM_Utils_Array::value('type', $field, 'String'), ], array_flip($fields)); $field += $defaults; + $field['label'] = $field['label'] ?? $field['title']; if (isset($defaults['options'])) { $field['options'] = $this->formatOptionList($field['options']); } @@ -217,14 +218,22 @@ class BasicGetFieldsAction extends BasicGetAction { [ 'name' => 'name', 'data_type' => 'String', + 'description' => ts('Unique field identifier'), ], [ 'name' => 'title', 'data_type' => 'String', + 'description' => ts('Technical name of field, shown in API and exports'), + ], + [ + 'name' => 'label', + 'data_type' => 'String', + 'description' => ts('User-facing label, shown on most forms and displays'), ], [ 'name' => 'description', 'data_type' => 'String', + 'description' => ts('Explanation of the purpose of the field'), ], [ 'name' => 'default_value', diff --git a/civicrm/Civi/Api4/Generic/DAOGetAction.php b/civicrm/Civi/Api4/Generic/DAOGetAction.php index 9ad1d54adc..852a333ccf 100644 --- a/civicrm/Civi/Api4/Generic/DAOGetAction.php +++ b/civicrm/Civi/Api4/Generic/DAOGetAction.php @@ -20,6 +20,7 @@ namespace Civi\Api4\Generic; use Civi\Api4\Query\Api4SelectQuery; +use Civi\Api4\Utils\CoreUtil; /** * Retrieve $ENTITIES based on criteria specified in the `where` parameter. @@ -85,6 +86,13 @@ class DAOGetAction extends AbstractGetAction { protected $having = []; public function _run(Result $result) { + // Early return if table doesn't exist yet due to pending upgrade + $baoName = $this->getBaoName(); + if (!$baoName::tableHasBeenAdded()) { + \Civi::log()->warning("Could not read from {$this->getEntityName()} before table has been added. Upgrade required.", ['civi.tag' => 'upgrade_needed']); + return; + } + $this->setDefaultWhereClause(); $this->expandSelectClauseWildcards(); $this->getObjects($result); @@ -147,7 +155,7 @@ class DAOGetAction extends AbstractGetAction { * @throws \API_Exception */ public function addHaving(string $expr, string $op, $value = NULL) { - if (!in_array($op, \CRM_Core_DAO::acceptedSQLOperators())) { + if (!in_array($op, CoreUtil::getOperators())) { throw new \API_Exception('Unsupported operator'); } $this->having[] = [$expr, $op, $value]; diff --git a/civicrm/Civi/Api4/Generic/Traits/ArrayQueryActionTrait.php b/civicrm/Civi/Api4/Generic/Traits/ArrayQueryActionTrait.php index 4ae6096c50..fd515b9a4b 100644 --- a/civicrm/Civi/Api4/Generic/Traits/ArrayQueryActionTrait.php +++ b/civicrm/Civi/Api4/Generic/Traits/ArrayQueryActionTrait.php @@ -158,6 +158,15 @@ trait ArrayQueryActionTrait { case 'NOT IN': return !in_array($value, $expected); + case 'CONTAINS': + if (is_array($value)) { + return in_array($expected, $value); + } + elseif (is_string($value) || is_numeric($value)) { + return strpos((string) $value, (string) $expected) !== FALSE; + } + return $value == $expected; + default: throw new NotImplementedException("Unsupported operator: '$operator' cannot be used with array data"); } diff --git a/civicrm/Civi/Api4/Generic/Traits/CustomValueActionTrait.php b/civicrm/Civi/Api4/Generic/Traits/CustomValueActionTrait.php index 78065d55f3..2fed2b7f46 100644 --- a/civicrm/Civi/Api4/Generic/Traits/CustomValueActionTrait.php +++ b/civicrm/Civi/Api4/Generic/Traits/CustomValueActionTrait.php @@ -52,9 +52,8 @@ trait CustomValueActionTrait { * @inheritDoc */ protected function writeObjects($items) { - $result = []; $fields = $this->entityFields(); - foreach ($items as $item) { + foreach ($items as $idx => $item) { FormattingUtil::formatWriteParams($item, $fields); // Convert field names to custom_xx format @@ -65,9 +64,16 @@ trait CustomValueActionTrait { } } - $result[] = \CRM_Core_BAO_CustomValueTable::setValues($item); + \CRM_Core_BAO_CustomValueTable::setValues($item); + + // Darn setValues function doesn't return an id. + if (empty($item['id'])) { + $tableName = CoreUtil::getTableName($this->getEntityName()); + $items[$idx]['id'] = (int) \CRM_Core_DAO::singleValueQuery('SELECT MAX(id) FROM ' . $tableName); + } } - return $result; + FormattingUtil::formatOutputValues($items, $this->entityFields(), $this->getEntityName(), 'create'); + return $items; } /** @@ -106,4 +112,11 @@ trait CustomValueActionTrait { return $this->customGroup; } + /** + * @return \CRM_Core_DAO|string + */ + protected function getBaoName() { + return \CRM_Core_BAO_CustomValue::class; + } + } diff --git a/civicrm/Civi/Api4/Query/Api4SelectQuery.php b/civicrm/Civi/Api4/Query/Api4SelectQuery.php index 5721e91968..e4f8ceb187 100644 --- a/civicrm/Civi/Api4/Query/Api4SelectQuery.php +++ b/civicrm/Civi/Api4/Query/Api4SelectQuery.php @@ -26,8 +26,8 @@ use Civi\Api4\Utils\SelectUtil; * Leaf operators are one of: * * * '=', '<=', '>=', '>', '<', 'LIKE', "<>", "!=", - * * "NOT LIKE", 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN', - * * 'IS NOT NULL', or 'IS NULL'. + * * 'NOT LIKE', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN', + * * 'IS NOT NULL', or 'IS NULL', 'CONTAINS'. */ class Api4SelectQuery { @@ -362,7 +362,7 @@ class Api4SelectQuery { protected function composeClause(array $clause, string $type) { // Pad array for unary operators list($expr, $operator, $value) = array_pad($clause, 3, NULL); - if (!in_array($operator, \CRM_Core_DAO::acceptedSQLOperators(), TRUE)) { + if (!in_array($operator, CoreUtil::getOperators(), TRUE)) { throw new \API_Exception('Illegal operator'); } @@ -379,7 +379,8 @@ class Api4SelectQuery { $fieldAlias = $expr; // Attempt to format if this is a real field if (isset($this->apiFieldSpec[$expr])) { - FormattingUtil::formatInputValue($value, $expr, $this->apiFieldSpec[$expr]); + $field = $this->getField($expr); + FormattingUtil::formatInputValue($value, $expr, $field); } } // Expr references a non-field expression like a function; convert to alias @@ -392,7 +393,8 @@ class Api4SelectQuery { foreach ($this->selectAliases as $selectAlias => $selectExpr) { list($selectField) = explode(':', $selectAlias); if ($selectAlias === $selectExpr && $fieldName === $selectField && isset($this->apiFieldSpec[$fieldName])) { - FormattingUtil::formatInputValue($value, $expr, $this->apiFieldSpec[$fieldName]); + $field = $this->getField($fieldName); + FormattingUtil::formatInputValue($value, $expr, $field); $fieldAlias = $selectAlias; break; } @@ -415,7 +417,29 @@ class Api4SelectQuery { return sprintf('%s %s %s', $fieldAlias, $operator, $valExpr->render($this->apiFieldSpec)); } elseif ($fieldName) { - FormattingUtil::formatInputValue($value, $fieldName, $this->apiFieldSpec[$fieldName]); + $field = $this->getField($fieldName); + FormattingUtil::formatInputValue($value, $fieldName, $field); + } + } + + if ($operator === 'CONTAINS') { + switch ($field['serialize'] ?? NULL) { + case \CRM_Core_DAO::SERIALIZE_JSON: + $operator = 'LIKE'; + $value = '%"' . $value . '"%'; + // FIXME: Use this instead of the above hack once MIN_INSTALL_MYSQL_VER is bumped to 5.7. + // return sprintf('JSON_SEARCH(%s, "one", "%s") IS NOT NULL', $fieldAlias, \CRM_Core_DAO::escapeString($value)); + break; + + case \CRM_Core_DAO::SERIALIZE_SEPARATOR_BOOKEND: + $operator = 'LIKE'; + $value = '%' . \CRM_Core_DAO::VALUE_SEPARATOR . $value . \CRM_Core_DAO::VALUE_SEPARATOR . '%'; + break; + + default: + $operator = 'LIKE'; + $value = '%' . $value . '%'; + break; } } diff --git a/civicrm/Civi/Api4/Service/Spec/FieldSpec.php b/civicrm/Civi/Api4/Service/Spec/FieldSpec.php index 8b9ba5ca24..eb35cf1d30 100644 --- a/civicrm/Civi/Api4/Service/Spec/FieldSpec.php +++ b/civicrm/Civi/Api4/Service/Spec/FieldSpec.php @@ -32,6 +32,11 @@ class FieldSpec { */ protected $name; + /** + * @var string + */ + protected $label; + /** * @var string */ @@ -165,6 +170,24 @@ class FieldSpec { return $this; } + /** + * @return string + */ + public function getLabel() { + return $this->label; + } + + /** + * @param string $label + * + * @return $this + */ + public function setLabel($label) { + $this->label = $label; + + return $this; + } + /** * @return string */ diff --git a/civicrm/Civi/Api4/Service/Spec/SpecFormatter.php b/civicrm/Civi/Api4/Service/Spec/SpecFormatter.php index 1da26d1520..23209a4e24 100644 --- a/civicrm/Civi/Api4/Service/Spec/SpecFormatter.php +++ b/civicrm/Civi/Api4/Service/Spec/SpecFormatter.php @@ -73,6 +73,7 @@ class SpecFormatter { $field = new FieldSpec($name, $entity, $dataTypeName); $field->setRequired(!empty($data['required'])); $field->setTitle($data['title'] ?? NULL); + $field->setLabel($data['html']['label'] ?? NULL); $field->setOptions(!empty($data['pseudoconstant'])); } $field->setSerialize($data['serialize'] ?? NULL); diff --git a/civicrm/Civi/Api4/Utils/CoreUtil.php b/civicrm/Civi/Api4/Utils/CoreUtil.php index 8d06ce9678..b9090c97de 100644 --- a/civicrm/Civi/Api4/Utils/CoreUtil.php +++ b/civicrm/Civi/Api4/Utils/CoreUtil.php @@ -71,4 +71,13 @@ class CoreUtil { return $entityName; } + /** + * @return string[] + */ + public static function getOperators() { + $operators = \CRM_Core_DAO::acceptedSQLOperators(); + $operators[] = 'CONTAINS'; + return $operators; + } + } diff --git a/civicrm/Civi/Install/Requirements.php b/civicrm/Civi/Install/Requirements.php index da7b2cc765..7d30855076 100644 --- a/civicrm/Civi/Install/Requirements.php +++ b/civicrm/Civi/Install/Requirements.php @@ -132,7 +132,24 @@ class Requirements { elseif (!empty($db_config['server'])) { $host = $db_config['server']; } - $conn = @mysqli_connect($host, $db_config['username'], $db_config['password'], $db_config['database'], !empty($db_config['port']) ? $db_config['port'] : NULL); + if (empty($db_config['ssl_params'])) { + $conn = @mysqli_connect($host, $db_config['username'], $db_config['password'], $db_config['database'], !empty($db_config['port']) ? $db_config['port'] : NULL); + } + else { + $conn = NULL; + $init = mysqli_init(); + mysqli_ssl_set( + $init, + $db_config['ssl_params']['key'] ?? NULL, + $db_config['ssl_params']['cert'] ?? NULL, + $db_config['ssl_params']['ca'] ?? NULL, + $db_config['ssl_params']['capath'] ?? NULL, + $db_config['ssl_params']['cipher'] ?? NULL + ); + if (@mysqli_real_connect($init, $host, $db_config['username'], $db_config['password'], $db_config['database'], (!empty($db_config['port']) ? $db_config['port'] : NULL), NULL, MYSQLI_CLIENT_SSL)) { + $conn = $init; + } + } return $conn; } diff --git a/civicrm/Civi/Test.php b/civicrm/Civi/Test.php index 1d9db12079..cf9c841ab7 100644 --- a/civicrm/Civi/Test.php +++ b/civicrm/Civi/Test.php @@ -59,7 +59,8 @@ class Test { public static function dsn($part = NULL) { if (!isset(self::$singletons['dsn'])) { require_once "DB.php"; - self::$singletons['dsn'] = \DB::parseDSN(CIVICRM_DSN); + $dsn = \CRM_Utils_SQL::autoSwitchDSN(CIVICRM_DSN); + self::$singletons['dsn'] = \DB::parseDSN($dsn); } if ($part === NULL) { diff --git a/civicrm/ang/api4Explorer/Explorer.js b/civicrm/ang/api4Explorer/Explorer.js index 0dca04e3bc..41750691c6 100644 --- a/civicrm/ang/api4Explorer/Explorer.js +++ b/civicrm/ang/api4Explorer/Explorer.js @@ -72,7 +72,9 @@ {name: 'ang2', label: ts('Batch Calls'), code: ''} ], cli: [ - {name: 'cv', label: ts('CV'), code: ''} + {name: 'short', label: ts('CV (short)'), code: ''}, + {name: 'long', label: ts('CV (long)'), code: ''}, + {name: 'pipe', label: ts('CV (pipe)'), code: ''} ] }; @@ -324,7 +326,11 @@ } // Then lookup implicit links _.each(path, function(node) { - entity = _.find(links[entity], {alias: node}).entity; + var link = _.find(links[entity], {alias: node}); + if (!link) { + return false; + } + entity = link.entity; }); return entity; } @@ -654,8 +660,44 @@ break; case 'cli': - // Write cli code - code.cv = 'cv api4 ' + entity + '.' + action + " '" + stringify(params) + "'"; + // Cli code using json input + code.long = 'cv api4 ' + entity + '.' + action + ' ' + cliFormat(JSON.stringify(params)); + code.pipe = 'echo ' + cliFormat(JSON.stringify(params)) + ' | cv api4 ' + entity + '.' + action + ' --in=json'; + + // Cli code using short syntax + code.short = 'cv api4 ' + entity + '.' + action; + var limitSet = false; + _.each(params, function(param, key) { + switch (true) { + case (key === 'select' && !_.includes(param.join(), ' ')): + code.short += ' +s ' + cliFormat(param.join(',')); + break; + case (key === 'where' && !_.intersection(_.map(param, 0), ['AND', 'OR', 'NOT']).length): + _.each(param, function(clause) { + code.short += ' +w ' + cliFormat(clause[0] + ' ' + clause[1] + (clause.length > 2 ? (' ' + JSON.stringify(clause[2])) : '')); + }); + break; + case (key === 'orderBy'): + _.each(param, function(dir, field) { + code.short += ' +o ' + cliFormat(field + ' ' + dir); + }); + break; + case (key === 'values'): + _.each(param, function(val, field) { + code.short += ' +v ' + cliFormat(field + '=' + val); + }); + break; + case (key === 'limit' || key === 'offset'): + // These 2 get combined + if (!limitSet) { + limitSet = true; + code.short += ' +l ' + (params.limit || '0') + (params.offset ? ('@' + params.offset) : ''); + } + break; + default: + code.short += ' ' + key + '=' + (typeof param === 'string' ? cliFormat(param) : cliFormat(JSON.stringify(param))); + } + }); } } _.each($scope.code, function(vals) { @@ -801,6 +843,20 @@ return JSON.stringify(val).replace(/\$/g, '\\$'); } + // Format string to be cli-input-safe + function cliFormat(str) { + if (!_.includes(str, ' ') && !_.includes(str, '"') && !_.includes(str, "'")) { + return str; + } + if (!_.includes(str, "'")) { + return "'" + str + "'"; + } + if (!_.includes(str, '"')) { + return '"' + str + '"'; + } + return "'" + str.replace(/'/g, "\\'") + "'"; + } + function fetchMeta() { crmApi4(getMetaParams) .then(function(data) { diff --git a/civicrm/ang/crmMailingAB/services.js b/civicrm/ang/crmMailingAB/services.js index 2e9fa9260b..a1d64b7da6 100644 --- a/civicrm/ang/crmMailingAB/services.js +++ b/civicrm/ang/crmMailingAB/services.js @@ -69,7 +69,6 @@ mailing_id_a: null, mailing_id_b: null, mailing_id_c: null, - domain_id: null, testing_criteria: 'subject', winner_criteria: null, specific_url: '', diff --git a/civicrm/api/class.api.php b/civicrm/api/class.api.php index 4ba61dd30b..df6ef1283f 100644 --- a/civicrm/api/class.api.php +++ b/civicrm/api/class.api.php @@ -14,15 +14,15 @@ * ``` * require_once('/your/civi/folder/api/class.api.php'); * // the path to civicrm.settings.php - * $api = new civicrm_api3 (array('conf_path'=> '/your/path/to/your/civicrm/or/joomla/site)); + * $api = new civicrm_api3 (['conf_path'=> '/your/path/to/your/civicrm/or/joomla/site']); * ``` * * or to query a remote server via the rest api * * ``` - * $api = new civicrm_api3 (array ('server' => 'http://example.org', - * 'api_key'=>'theusersecretkey', - * 'key'=>'thesitesecretkey')); + * $api = new civicrm_api3 (['server' => 'http://example.org', + * 'api_key'=>'theusersecretkey', + * 'key'=>'thesitesecretkey']); * ``` * * No matter how initialised and if civicrm is local or remote, you use the class the same way. @@ -34,7 +34,7 @@ * So, to get the individual contacts: * * ``` - * if ($api->Contact->Get(array('contact_type'=>'Individual','return'=>'sort_name,current_employer')) { + * if ($api->Contact->Get(['contact_type'=>'Individual','return'=>'sort_name,current_employer']) { * // each key of the result array is an attribute of the api * echo "\n contacts found " . $api->count; * foreach ($api->values as $c) { @@ -49,7 +49,7 @@ * Or, to create an event: * * ``` - * if ($api->Event->Create(array('title'=>'Test','event_type_id' => 1,'is_public' => 1,'start_date' => 19430429))) { + * if ($api->Event->Create(['title'=>'Test','event_type_id' => 1,'is_public' => 1,'start_date' => 19430429])) { * echo "created event id:". $api->id; * } else { * echo $api->errorMsg(); @@ -62,7 +62,7 @@ * * ``` * $api->Activity->Get (42); - * $api->Activity->Get (array('id'=>42)); + * $api->Activity->Get (['id'=>42]); * ``` * * @@ -87,23 +87,23 @@ class civicrm_api3 { $this->local = TRUE; $this->input = []; $this->lastResult = []; - if (isset($config) && isset($config['server'])) { + if (!empty($config) && !empty($config['server'])) { // we are calling a remote server via REST $this->local = FALSE; $this->uri = $config['server']; - if (isset($config['path'])) { + if (!empty($config['path'])) { $this->uri .= "/" . $config['path']; } else { $this->uri .= '/sites/all/modules/civicrm/extern/rest.php'; } - if (isset($config['key'])) { + if (!empty($config['key'])) { $this->key = $config['key']; } else { die("\nFATAL:param['key] missing\n"); } - if (isset($config['api_key'])) { + if (!empty($config['api_key'])) { $this->api_key = $config['api_key']; } else { @@ -111,7 +111,7 @@ class civicrm_api3 { } return; } - if (isset($config) && isset($config['conf_path'])) { + if (!empty($config) && !empty($config['conf_path'])) { if (!defined('CIVICRM_SETTINGS_PATH')) { define('CIVICRM_SETTINGS_PATH', $config['conf_path'] . '/civicrm.settings.php'); } diff --git a/civicrm/api/v3/Contribution.php b/civicrm/api/v3/Contribution.php index 298980b4a7..1e70afa7ec 100644 --- a/civicrm/api/v3/Contribution.php +++ b/civicrm/api/v3/Contribution.php @@ -32,7 +32,7 @@ function civicrm_api3_contribution_create($params) { // The BAO should not clean money - it should be done in the form layer & api wrapper // (although arguably the api should expect pre-cleaned it seems to do some cleaning.) if (empty($params['skipCleanMoney'])) { - foreach (['total_amount', 'net_amount', 'fee_amount'] as $field) { + foreach (['total_amount', 'net_amount', 'fee_amount', 'non_deductible_amount'] as $field) { if (isset($params[$field])) { $params[$field] = CRM_Utils_Rule::cleanMoney($params[$field]); } @@ -602,7 +602,6 @@ function civicrm_api3_contribution_repeattransaction($params) { ); } - $original_contribution = clone $contribution; $input['payment_processor_id'] = civicrm_api3('contributionRecur', 'getvalue', [ 'return' => 'payment_processor_id', 'id' => $contribution->contribution_recur_id, @@ -626,7 +625,7 @@ function civicrm_api3_contribution_repeattransaction($params) { ]; $input = array_intersect_key($params, array_fill_keys($passThroughParams, NULL)); - return _ipn_process_transaction($params, $contribution, $input, $ids, $original_contribution); + return _ipn_process_transaction($params, $contribution, $input, $ids); } catch (Exception $e) { throw new API_Exception('failed to load related objects' . $e->getMessage() . "\n" . $e->getTraceAsString()); @@ -645,19 +644,13 @@ function civicrm_api3_contribution_repeattransaction($params) { * * @param array $ids * - * @param CRM_Contribute_BAO_Contribution $firstContribution - * * @return mixed * @throws \CRM_Core_Exception * @throws \CiviCRM_API3_Exception */ -function _ipn_process_transaction(&$params, $contribution, $input, $ids, $firstContribution = NULL) { +function _ipn_process_transaction($params, $contribution, $input, $ids) { $objects = $contribution->_relatedObjects; $objects['contribution'] = &$contribution; - - if ($firstContribution) { - $objects['first_contribution'] = $firstContribution; - } $input['component'] = $contribution->_component; $input['is_test'] = $contribution->is_test; $input['amount'] = empty($input['total_amount']) ? $contribution->total_amount : $input['total_amount']; @@ -682,7 +675,14 @@ function _ipn_process_transaction(&$params, $contribution, $input, $ids, $firstC } $input['card_type_id'] = $params['card_type_id'] ?? NULL; $input['pan_truncation'] = $params['pan_truncation'] ?? NULL; - return CRM_Contribute_BAO_Contribution::completeOrder($input, $ids, $objects, + if (!empty($params['payment_instrument_id'])) { + $input['payment_instrument_id'] = $params['payment_instrument_id']; + } + return CRM_Contribute_BAO_Contribution::completeOrder($input, [ + 'related_contact' => $ids['related_contact'] ?? NULL, + 'participant' => !empty($objects['participant']) ? $objects['participant']->id : NULL, + 'contributionRecur' => !empty($objects['contributionRecur']) ? $objects['contributionRecur']->id : NULL, + ], $objects, $params['is_post_payment_create'] ?? NULL); } diff --git a/civicrm/api/v3/LineItem.php b/civicrm/api/v3/LineItem.php index a5e5f34ff3..6e3690388f 100644 --- a/civicrm/api/v3/LineItem.php +++ b/civicrm/api/v3/LineItem.php @@ -29,9 +29,13 @@ */ function civicrm_api3_line_item_create($params) { // @todo the following line is not really appropriate for the api. The BAO should - // do the work, and it should be in a tighter function. The below function is not really - // readable because it is handling contribution and line item together. - $params = CRM_Contribute_BAO_Contribution::checkTaxAmount($params, TRUE); + // do the work. + $taxRates = CRM_Core_PseudoConstant::getTaxRates(); + if (isset($params['financial_type_id']) && array_key_exists($params['financial_type_id'], $taxRates)) { + $taxRate = $taxRates[$params['financial_type_id']]; + $taxAmount = CRM_Contribute_BAO_Contribution_Utils::calculateTaxAmount($params['line_total'], $taxRate); + $params['tax_amount'] = round($taxAmount['tax_amount'], 2); + } return _civicrm_api3_basic_create(_civicrm_api3_get_BAO(__FUNCTION__), $params, 'LineItem'); } @@ -62,9 +66,6 @@ function _civicrm_api3_line_item_create_spec(&$params) { * Array of matching line_items */ function civicrm_api3_line_item_get($params) { - if (CRM_Financial_BAO_FinancialType::isACLFinancialTypeStatus() && !empty($params['check_permissions'])) { - CRM_Price_BAO_LineItem::getAPILineItemParams($params); - } return _civicrm_api3_basic_get(_civicrm_api3_get_BAO(__FUNCTION__), $params); } @@ -75,18 +76,11 @@ function civicrm_api3_line_item_get($params) { * * @param array $params * Array containing id of the group to be deleted. + * * @return array API result array * @throws API_Exception + * @throws \CiviCRM_API3_Exception */ function civicrm_api3_line_item_delete($params) { - if (CRM_Financial_BAO_FinancialType::isACLFinancialTypeStatus() && !empty($params['check_permissions'])) { - CRM_Financial_BAO_FinancialType::getAvailableFinancialTypes($types, CRM_Core_Action::DELETE); - if (empty($params['financial_type_id'])) { - $params['financial_type_id'] = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_LineItem', $params['id'], 'financial_type_id'); - } - if (!in_array($params['financial_type_id'], array_keys($types))) { - throw new API_Exception('You do not have permission to delete this line item'); - } - } return _civicrm_api3_basic_delete(_civicrm_api3_get_BAO(__FUNCTION__), $params); } diff --git a/civicrm/api/v3/MailingEventSubscribe.php b/civicrm/api/v3/MailingEventSubscribe.php index 92a8795bdb..7046fe166e 100644 --- a/civicrm/api/v3/MailingEventSubscribe.php +++ b/civicrm/api/v3/MailingEventSubscribe.php @@ -66,12 +66,12 @@ function civicrm_api3_mailing_event_subscribe_create($params) { function _civicrm_api3_mailing_event_subscribe_create_spec(&$params) { $params['email'] = [ 'api.required' => 1, - 'title' => 'Unsubscribe Email', + 'title' => 'Subscribe Email', 'type' => CRM_Utils_Type::T_STRING, ]; $params['group_id'] = [ 'api.required' => 1, - 'title' => 'Unsubscribe From Group', + 'title' => 'Subscribe To Group', 'type' => CRM_Utils_Type::T_INT, ]; } diff --git a/civicrm/api/v3/MessageTemplate.php b/civicrm/api/v3/MessageTemplate.php index 8f33ce88c5..b75e43df5b 100644 --- a/civicrm/api/v3/MessageTemplate.php +++ b/civicrm/api/v3/MessageTemplate.php @@ -181,4 +181,9 @@ function _civicrm_api3_message_template_send_spec(&$params) { $params['pdf_filename']['title'] = 'PDF Filename'; $params['pdf_filename']['api.aliases'] = ['PDFFilename']; $params['pdf_filename']['type'] = CRM_Utils_Type::T_STRING; + + $params['disable_smarty']['description'] = 'Disable Smarty. Normal CiviMail tokens are still supported. By default Smarty is enabled.'; + $params['disable_smarty']['title'] = 'Disable Smarty'; + $params['disable_smarty']['api.aliases'] = ['disableSmarty']; + $params['disable_smarty']['type'] = CRM_Utils_Type::T_BOOLEAN; } diff --git a/civicrm/api/v3/Order.php b/civicrm/api/v3/Order.php index 32d59a1939..5e9c07b81b 100644 --- a/civicrm/api/v3/Order.php +++ b/civicrm/api/v3/Order.php @@ -83,7 +83,7 @@ function civicrm_api3_order_create($params) { $priceSetID = NULL; CRM_Contribute_BAO_Contribution::checkLineItems($params); foreach ($params['line_items'] as $lineItems) { - $entityParams = CRM_Utils_Array::value('params', $lineItems, []); + $entityParams = $lineItems['params'] ?? []; if (!empty($entityParams) && !empty($lineItems['line_item'])) { $item = reset($lineItems['line_item']); $entity = str_replace('civicrm_', '', $item['entity_table']); @@ -105,7 +105,7 @@ function civicrm_api3_order_create($params) { } if (empty($priceSetID)) { $item = reset($lineItems['line_item']); - $priceSetID = civicrm_api3('PriceField', 'getvalue', [ + $priceSetID = (int) civicrm_api3('PriceField', 'getvalue', [ 'return' => 'price_set_id', 'id' => $item['price_field_id'], ]); @@ -142,10 +142,10 @@ function civicrm_api3_order_create($params) { elseif ($entity == 'membership') { $paymentParams['isSkipLineItem'] = TRUE; } - $payments = civicrm_api3($entity . '_payment', 'create', $paymentParams); + civicrm_api3($entity . '_payment', 'create', $paymentParams); } } - return civicrm_api3_create_success(CRM_Utils_Array::value('values', $contribution), $params, 'Order', 'create'); + return civicrm_api3_create_success($contribution['values'] ?? [], $params, 'Order', 'create'); } /** diff --git a/civicrm/api/v3/System.php b/civicrm/api/v3/System.php index b597c48793..c207ac9e13 100644 --- a/civicrm/api/v3/System.php +++ b/civicrm/api/v3/System.php @@ -397,7 +397,14 @@ function civicrm_api3_system_updatelogtables($params) { * @throws \API_Exception */ function civicrm_api3_system_utf8conversion($params) { - if (CRM_Core_BAO_SchemaHandler::migrateUtf8mb4($params['is_revert'])) { + $params['patterns'] = explode(',', $params['patterns']); + $params['databases'] = empty($params['databases']) ? NULL : explode(',', $params['databases']); + if (CRM_Core_BAO_SchemaHandler::migrateUtf8mb4( + $params['is_revert'], + $params['patterns'], + $params['databases'] + ) + ) { return civicrm_api3_create_success(1); } throw new API_Exception('Conversion failed'); @@ -414,6 +421,15 @@ function _civicrm_api3_system_utf8conversion_spec(&$params) { 'type' => CRM_Utils_Type::T_BOOLEAN, 'api.default' => FALSE, ]; + $params['patterns'] = [ + 'title' => ts('CSV list of table patterns (defaults to "civicrm\_%")'), + 'type' => CRM_Utils_Type::T_STRING, + 'api.default' => 'civicrm\_%', + ]; + $params['databases'] = [ + 'title' => ts('CSV list of database names (defaults to CiviCRM database)'), + 'type' => CRM_Utils_Type::T_STRING, + ]; } /** diff --git a/civicrm/api/v3/utils.php b/civicrm/api/v3/utils.php index 50268a400c..799dc78031 100644 --- a/civicrm/api/v3/utils.php +++ b/civicrm/api/v3/utils.php @@ -1221,7 +1221,7 @@ function formatCheckBoxField(&$checkboxFieldValue, $customFieldLabel, $entity) { /** * Function to do a 'standard' api get - when the api is only doing a $bao->find then use this. * - * @param string $bao_name + * @param string|CRM_Core_DAO $bao_name * Name of BAO. * @param array $params * Params from api. @@ -1240,20 +1240,27 @@ function _civicrm_api3_basic_get($bao_name, $params, $returnAsSuccess = TRUE, $e $entity = $entity ?: CRM_Core_DAO_AllCoreTables::getBriefName($bao_name); $options = _civicrm_api3_get_options_from_params($params); - $query = new \Civi\API\Api3SelectQuery($entity, CRM_Utils_Array::value('check_permissions', $params, FALSE)); - $query->where = $params; - if ($options['is_count']) { - $query->select = ['count_rows']; + // Skip query if table doesn't exist yet due to pending upgrade + if (!$bao_name::tableHasBeenAdded()) { + \Civi::log()->warning("Could not read from {$entity} before table has been added. Upgrade required.", ['civi.tag' => 'upgrade_needed']); + $result = []; } else { - $query->select = array_keys(array_filter($options['return'])); - $query->orderBy = $options['sort']; - $query->isFillUniqueFields = $uniqueFields; - } - $query->limit = $options['limit']; - $query->offset = $options['offset']; - $query->merge($sql); - $result = $query->run(); + $query = new \Civi\API\Api3SelectQuery($entity, $params['check_permissions'] ?? FALSE); + $query->where = $params; + if ($options['is_count']) { + $query->select = ['count_rows']; + } + else { + $query->select = array_keys(array_filter($options['return'])); + $query->orderBy = $options['sort']; + $query->isFillUniqueFields = $uniqueFields; + } + $query->limit = $options['limit']; + $query->offset = $options['offset']; + $query->merge($sql); + $result = $query->run(); + } if ($returnAsSuccess) { return civicrm_api3_create_success($result, $params, $entity, 'get'); diff --git a/civicrm/bin/regen.sh b/civicrm/bin/regen.sh index f7451a23fc..6cf3eb54c6 100755 --- a/civicrm/bin/regen.sh +++ b/civicrm/bin/regen.sh @@ -47,7 +47,7 @@ php GenerateData.php ## Prune local data $MYSQLCMD -e "DROP TABLE IF EXISTS civicrm_install_canary; DELETE FROM civicrm_cache; DELETE FROM civicrm_setting;" -$MYSQLCMD -e "DELETE FROM civicrm_extension WHERE full_name NOT IN ('sequentialcreditnotes', 'eventcart', 'search', 'flexmailer');" +$MYSQLCMD -e "DELETE FROM civicrm_extension WHERE full_name NOT IN ('sequentialcreditnotes', 'eventcart', 'search', 'flexmailer', 'financialacls');" TABLENAMES=$( echo "show tables like 'civicrm_%'" | $MYSQLCMD | grep ^civicrm_ | xargs ) cd $CIVISOURCEDIR/sql diff --git a/civicrm/civicrm-version.php b/civicrm/civicrm-version.php index 2fd54269c2..0474649f10 100644 --- a/civicrm/civicrm-version.php +++ b/civicrm/civicrm-version.php @@ -1,7 +1,7 @@ <?php /** @deprecated */ function civicrmVersion( ) { - return array( 'version' => '5.29.1', + return array( 'version' => '5.30.0', 'cms' => 'Wordpress', 'revision' => '' ); } diff --git a/civicrm/composer.json b/civicrm/composer.json index 0a737c6dd9..9dfefaa375 100644 --- a/civicrm/composer.json +++ b/civicrm/composer.json @@ -78,7 +78,8 @@ "typo3/phar-stream-wrapper": "^2 || ^3.0", "brick/money": "~0.4", "ext-intl": "*", - "pear/mail_mime": "~1.10" + "pear/mail_mime": "~1.10", + "pear/db": "1.10" }, "scripts": { "post-install-cmd": [ @@ -251,6 +252,9 @@ "electrolinux/phpquery": { "PHP7.4 Fix for array access using {} instead of []": "https://raw.githubusercontent.com/civicrm/civicrm-core/fe45bdfc4f3e3d3deb27e3d853cdbc7f616620a9/tools/scripts/composer/patches/php74_array_access_fix_phpquery.patch" }, + "pear/db": { + "Apply CiviCRM Customisations for the pear:db package": "https://raw.githubusercontent.com/civicrm/civicrm-core/a48a43c2b5f6d694fff1cfb99d522c5d9e2459a0/tools/scripts/composer/pear_db_civicrm_changes.patch" + }, "pear/mail": { "Apply CiviCRM Customisations for CRM-1367 and CRM-5946": "https://raw.githubusercontent.com/civicrm/civicrm-core/36319938a5bf26c1e7e2110a26a65db6a5979268/tools/scripts/composer/patches/pear-mail.patch" }, diff --git a/civicrm/composer.lock b/civicrm/composer.lock index e39dd5c9d2..44f3903468 100644 --- a/civicrm/composer.lock +++ b/civicrm/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "1ff9c045fb03756148c0c66562aa61fd", + "content-hash": "46e891da51f0683373d9a6e62fb6f868", "packages": [ { "name": "adrienrn/php-mimetyper", @@ -998,6 +998,66 @@ "description": "More info available on: http://pear.php.net/package/Console_Getopt", "time": "2015-07-20T20:28:12+00:00" }, + { + "name": "pear/db", + "version": "v1.10.0", + "source": { + "type": "git", + "url": "https://github.com/pear/DB.git", + "reference": "e158c3a48246b67cd8c95856ffbb93de4ef380fe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pear/DB/zipball/e158c3a48246b67cd8c95856ffbb93de4ef380fe", + "reference": "e158c3a48246b67cd8c95856ffbb93de4ef380fe", + "shasum": "" + }, + "require": { + "pear/pear-core-minimal": "*" + }, + "type": "library", + "extra": { + "patches_applied": { + "Apply CiviCRM Customisations for the pear:db package": "https://raw.githubusercontent.com/civicrm/civicrm-core/a48a43c2b5f6d694fff1cfb99d522c5d9e2459a0/tools/scripts/composer/pear_db_civicrm_changes.patch" + } + }, + "autoload": { + "psr-0": { + "DB": "./" + } + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "./" + ], + "license": [ + "PHP License v3.01" + ], + "authors": [ + { + "name": "Daniel Convissor", + "email": "danielc@php.net", + "role": "Lead" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net", + "role": "Lead" + }, + { + "name": "Stig Bakken", + "email": "stig@php.net", + "role": "Developer" + }, + { + "name": "Tomas V.V.Cox", + "email": "cox@idecnet.com", + "role": "Developer" + } + ], + "description": "More info available on: http://pear.php.net/package/DB", + "time": "2020-04-19T19:45:59+00:00" + }, { "name": "pear/log", "version": "1.13.2", diff --git a/civicrm/ext/eventcart/CRM/Event/Cart/StateMachine/Checkout.php b/civicrm/ext/eventcart/CRM/Event/Cart/StateMachine/Checkout.php index 3ca2285281..bff2b8adb0 100644 --- a/civicrm/ext/eventcart/CRM/Event/Cart/StateMachine/Checkout.php +++ b/civicrm/ext/eventcart/CRM/Event/Cart/StateMachine/Checkout.php @@ -19,14 +19,7 @@ class CRM_Event_Cart_StateMachine_Checkout extends CRM_Core_StateMachine { } $pages = []; - $is_monetary = FALSE; - $is_conference = FALSE; - foreach ($cart->events_in_carts as $event_in_cart) { - if ($event_in_cart->event->is_monetary) { - $is_monetary = TRUE; - } - } - $pages["CRM_Event_Cart_Form_Checkout_ParticipantsAndPrices"] = NULL; + $pages['CRM_Event_Cart_Form_Checkout_ParticipantsAndPrices'] = NULL; foreach ($cart->events_in_carts as $event_in_cart) { if ($event_in_cart->is_parent_event()) { foreach ($event_in_cart->participants as $participant) { diff --git a/civicrm/ext/financialacls/LICENSE.txt b/civicrm/ext/financialacls/LICENSE.txt new file mode 100644 index 0000000000..96bc3698ee --- /dev/null +++ b/civicrm/ext/financialacls/LICENSE.txt @@ -0,0 +1,667 @@ +Package: financialacls +Copyright (C) 2020, eileen <emcnaughton@wikimedia.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/civicrm/ext/financialacls/README.md b/civicrm/ext/financialacls/README.md new file mode 100644 index 0000000000..531f9bea76 --- /dev/null +++ b/civicrm/ext/financialacls/README.md @@ -0,0 +1,44 @@ +# financialacls + + + +(*FIXME: In one or two paragraphs, describe what the extension does and why one would download it. *) + +The extension is licensed under [AGPL-3.0](LICENSE.txt). + +## Requirements + +* PHP v7.0+ +* CiviCRM (*FIXME: Version number*) + +## Installation (Web UI) + +This extension has not yet been published for installation via the web UI. + +## Installation (CLI, Zip) + +Sysadmins and developers may download the `.zip` file for this extension and +install it with the command-line tool [cv](https://github.com/civicrm/cv). + +```bash +cd <extension-dir> +cv dl financialacls@https://github.com/FIXME/financialacls/archive/master.zip +``` + +## Installation (CLI, Git) + +Sysadmins and developers may clone the [Git](https://en.wikipedia.org/wiki/Git) repo for this extension and +install it with the command-line tool [cv](https://github.com/civicrm/cv). + +```bash +git clone https://github.com/FIXME/financialacls.git +cv en financialacls +``` + +## Usage + +(* FIXME: Where would a new user navigate to get started? What changes would they see? *) + +## Known Issues + +(* FIXME *) diff --git a/civicrm/ext/financialacls/financialacls.civix.php b/civicrm/ext/financialacls/financialacls.civix.php new file mode 100644 index 0000000000..2f5da65961 --- /dev/null +++ b/civicrm/ext/financialacls/financialacls.civix.php @@ -0,0 +1,477 @@ +<?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_Financialacls_ExtensionUtil { + const SHORT_NAME = "financialacls"; + const LONG_NAME = "financialacls"; + const CLASS_PREFIX = "CRM_Financialacls"; + + /** + * 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 = []) { + if (!array_key_exists('domain', $params)) { + $params['domain'] = [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_Financialacls_ExtensionUtil as E; + +/** + * (Delegated) Implements hook_civicrm_config(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_config + */ +function _financialacls_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 = [$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 https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_xmlMenu + */ +function _financialacls_civix_civicrm_xmlMenu(&$files) { + foreach (_financialacls_civix_glob(__DIR__ . '/xml/Menu/*.xml') as $file) { + $files[] = $file; + } +} + +/** + * Implements hook_civicrm_install(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_install + */ +function _financialacls_civix_civicrm_install() { + _financialacls_civix_civicrm_config(); + if ($upgrader = _financialacls_civix_upgrader()) { + $upgrader->onInstall(); + } +} + +/** + * Implements hook_civicrm_postInstall(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_postInstall + */ +function _financialacls_civix_civicrm_postInstall() { + _financialacls_civix_civicrm_config(); + if ($upgrader = _financialacls_civix_upgrader()) { + if (is_callable([$upgrader, 'onPostInstall'])) { + $upgrader->onPostInstall(); + } + } +} + +/** + * Implements hook_civicrm_uninstall(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_uninstall + */ +function _financialacls_civix_civicrm_uninstall() { + _financialacls_civix_civicrm_config(); + if ($upgrader = _financialacls_civix_upgrader()) { + $upgrader->onUninstall(); + } +} + +/** + * (Delegated) Implements hook_civicrm_enable(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_enable + */ +function _financialacls_civix_civicrm_enable() { + _financialacls_civix_civicrm_config(); + if ($upgrader = _financialacls_civix_upgrader()) { + if (is_callable([$upgrader, 'onEnable'])) { + $upgrader->onEnable(); + } + } +} + +/** + * (Delegated) Implements hook_civicrm_disable(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_disable + * @return mixed + */ +function _financialacls_civix_civicrm_disable() { + _financialacls_civix_civicrm_config(); + if ($upgrader = _financialacls_civix_upgrader()) { + if (is_callable([$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 https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_upgrade + */ +function _financialacls_civix_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) { + if ($upgrader = _financialacls_civix_upgrader()) { + return $upgrader->onUpgrade($op, $queue); + } +} + +/** + * @return CRM_Financialacls_Upgrader + */ +function _financialacls_civix_upgrader() { + if (!file_exists(__DIR__ . '/CRM/Financialacls/Upgrader.php')) { + return NULL; + } + else { + return CRM_Financialacls_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 string $dir base dir + * @param string $pattern , glob pattern, eg "*.txt" + * + * @return array + */ +function _financialacls_civix_find_files($dir, $pattern) { + if (is_callable(['CRM_Utils_File', 'findFiles'])) { + return CRM_Utils_File::findFiles($dir, $pattern); + } + + $todos = [$dir]; + $result = []; + while (!empty($todos)) { + $subdir = array_shift($todos); + foreach (_financialacls_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 https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_managed + */ +function _financialacls_civix_civicrm_managed(&$entities) { + $mgdFiles = _financialacls_civix_find_files(__DIR__, '*.mgd.php'); + sort($mgdFiles); + foreach ($mgdFiles as $file) { + $es = include $file; + foreach ($es as $e) { + if (empty($e['module'])) { + $e['module'] = E::LONG_NAME; + } + if (empty($e['params']['version'])) { + $e['params']['version'] = '3'; + } + $entities[] = $e; + } + } +} + +/** + * (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 https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_caseTypes + */ +function _financialacls_civix_civicrm_caseTypes(&$caseTypes) { + if (!is_dir(__DIR__ . '/xml/case')) { + return; + } + + foreach (_financialacls_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)); + throw new CRM_Core_Exception($errorMessage); + } + $caseTypes[$name] = [ + '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 https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_angularModules + */ +function _financialacls_civix_civicrm_angularModules(&$angularModules) { + if (!is_dir(__DIR__ . '/ang')) { + return; + } + + $files = _financialacls_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; + } +} + +/** + * (Delegated) Implements hook_civicrm_themes(). + * + * Find any and return any files matching "*.theme.php" + */ +function _financialacls_civix_civicrm_themes(&$themes) { + $files = _financialacls_civix_glob(__DIR__ . '/*.theme.php'); + foreach ($files as $file) { + $themeMeta = include $file; + if (empty($themeMeta['name'])) { + $themeMeta['name'] = preg_replace(':\.theme\.php$:', '', basename($file)); + } + if (empty($themeMeta['ext'])) { + $themeMeta['ext'] = E::LONG_NAME; + } + $themes[$themeMeta['name']] = $themeMeta; + } +} + +/** + * 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 + */ +function _financialacls_civix_glob($pattern) { + $result = glob($pattern); + return is_array($result) ? $result : []; +} + +/** + * 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) + * + * @return bool + */ +function _financialacls_civix_insert_navigation_menu(&$menu, $path, $item) { + // If we are done going down the path, insert menu + if (empty($path)) { + $menu[] = [ + 'attributes' => array_merge([ + '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'] = []; + } + $found = _financialacls_civix_insert_navigation_menu($entry['child'], implode('/', $path), $item); + } + } + return $found; + } +} + +/** + * (Delegated) Implements hook_civicrm_navigationMenu(). + */ +function _financialacls_civix_navigationMenu(&$nodes) { + if (!is_callable(['CRM_Core_BAO_Navigation', 'fixNavigationMenu'])) { + _financialacls_civix_fixNavigationMenu($nodes); + } +} + +/** + * Given a navigation menu, generate navIDs for any items which are + * missing them. + */ +function _financialacls_civix_fixNavigationMenu(&$nodes) { + $maxNavID = 1; + array_walk_recursive($nodes, function($item, $key) use (&$maxNavID) { + if ($key === 'navID') { + $maxNavID = max($maxNavID, $item); + } + }); + _financialacls_civix_fixNavigationMenuItems($nodes, $maxNavID, NULL); +} + +function _financialacls_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'])) { + _financialacls_civix_fixNavigationMenuItems($nodes[$origKey]['child'], $maxNavID, $nodes[$origKey]['attributes']['navID']); + } + } +} + +/** + * (Delegated) Implements hook_civicrm_alterSettingsFolders(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_alterSettingsFolders + */ +function _financialacls_civix_civicrm_alterSettingsFolders(&$metaDataFolders = NULL) { + $settingsDir = __DIR__ . DIRECTORY_SEPARATOR . 'settings'; + if (!in_array($settingsDir, $metaDataFolders) && is_dir($settingsDir)) { + $metaDataFolders[] = $settingsDir; + } +} + +/** + * (Delegated) Implements hook_civicrm_entityTypes(). + * + * Find any *.entityType.php files, merge their content, and return. + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_entityTypes + */ +function _financialacls_civix_civicrm_entityTypes(&$entityTypes) { + $entityTypes = array_merge($entityTypes, []); +} diff --git a/civicrm/ext/financialacls/financialacls.php b/civicrm/ext/financialacls/financialacls.php new file mode 100644 index 0000000000..b079114d9f --- /dev/null +++ b/civicrm/ext/financialacls/financialacls.php @@ -0,0 +1,218 @@ +<?php + +require_once 'financialacls.civix.php'; +// phpcs:disable +use CRM_Financialacls_ExtensionUtil as E; +// phpcs:enable + +/** + * Implements hook_civicrm_config(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_config/ + */ +function financialacls_civicrm_config(&$config) { + _financialacls_civix_civicrm_config($config); +} + +/** + * Implements hook_civicrm_xmlMenu(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_xmlMenu + */ +function financialacls_civicrm_xmlMenu(&$files) { + _financialacls_civix_civicrm_xmlMenu($files); +} + +/** + * Implements hook_civicrm_install(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_install + */ +function financialacls_civicrm_install() { + _financialacls_civix_civicrm_install(); +} + +/** + * Implements hook_civicrm_postInstall(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_postInstall + */ +function financialacls_civicrm_postInstall() { + _financialacls_civix_civicrm_postInstall(); +} + +/** + * Implements hook_civicrm_uninstall(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_uninstall + */ +function financialacls_civicrm_uninstall() { + _financialacls_civix_civicrm_uninstall(); +} + +/** + * Implements hook_civicrm_enable(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_enable + */ +function financialacls_civicrm_enable() { + _financialacls_civix_civicrm_enable(); +} + +/** + * Implements hook_civicrm_disable(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_disable + */ +function financialacls_civicrm_disable() { + _financialacls_civix_civicrm_disable(); +} + +/** + * Implements hook_civicrm_upgrade(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_upgrade + */ +function financialacls_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) { + return _financialacls_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 https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_managed + */ +function financialacls_civicrm_managed(&$entities) { + _financialacls_civix_civicrm_managed($entities); +} + +/** + * Implements hook_civicrm_caseTypes(). + * + * Generate a list of case-types. + * + * Note: This hook only runs in CiviCRM 4.4+. + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_caseTypes + */ +function financialacls_civicrm_caseTypes(&$caseTypes) { + _financialacls_civix_civicrm_caseTypes($caseTypes); +} + +/** + * 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 https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_angularModules + */ +function financialacls_civicrm_angularModules(&$angularModules) { + _financialacls_civix_civicrm_angularModules($angularModules); +} + +/** + * Implements hook_civicrm_alterSettingsFolders(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_alterSettingsFolders + */ +function financialacls_civicrm_alterSettingsFolders(&$metaDataFolders = NULL) { + _financialacls_civix_civicrm_alterSettingsFolders($metaDataFolders); +} + +/** + * Implements hook_civicrm_entityTypes(). + * + * Declare entity types provided by this module. + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_entityTypes + */ +function financialacls_civicrm_entityTypes(&$entityTypes) { + _financialacls_civix_civicrm_entityTypes($entityTypes); +} + +/** + * Implements hook_civicrm_thems(). + */ +function financialacls_civicrm_themes(&$themes) { + _financialacls_civix_civicrm_themes($themes); +} + +/** + * Intervene to prevent deletion, where permissions block it. + * + * @param \CRM_Core_DAO $op + * @param string $objectName + * @param int|null $id + * @param array $params + * + * @throws \API_Exception + * @throws \CRM_Core_Exception + */ +function financialacls_civicrm_pre($op, $objectName, $id, &$params) { + if ($objectName === 'LineItem' && $op === 'delete' && !empty($params['check_permissions'])) { + if (CRM_Financial_BAO_FinancialType::isACLFinancialTypeStatus()) { + CRM_Financial_BAO_FinancialType::getAvailableFinancialTypes($types, CRM_Core_Action::DELETE); + if (empty($params['financial_type_id'])) { + $params['financial_type_id'] = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_LineItem', $params['id'], 'financial_type_id'); + } + if (!in_array($params['financial_type_id'], array_keys($types))) { + throw new API_Exception('You do not have permission to delete this line item'); + } + } + } +} + +/** + * Implements hook_civicrm_selectWhereClause(). + * + * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_selectWhereClause + */ +function financialacls_civicrm_selectWhereClause($entity, &$clauses) { + if ($entity === 'LineItem') { + if (CRM_Financial_BAO_FinancialType::isACLFinancialTypeStatus()) { + $types = []; + CRM_Financial_BAO_FinancialType::getAvailableFinancialTypes($types); + if ($types) { + $clauses['financial_type_id'] = 'IN (' . implode(',', array_keys($types)) . ')'; + } + else { + $clauses['financial_type_id'] = '= 0'; + } + } + } + +} + +// --- Functions below this ship commented out. Uncomment as required. --- + +/** + * Implements hook_civicrm_preProcess(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_preProcess + */ +//function financialacls_civicrm_preProcess($formName, &$form) { +// +//} + +/** + * Implements hook_civicrm_navigationMenu(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_navigationMenu + */ +//function financialacls_civicrm_navigationMenu(&$menu) { +// _financialacls_civix_insert_navigation_menu($menu, 'Mailings', array( +// 'label' => E::ts('New subliminal message'), +// 'name' => 'mailing_subliminal_message', +// 'url' => 'civicrm/mailing/subliminal', +// 'permission' => 'access CiviMail', +// 'operator' => 'OR', +// 'separator' => 0, +// )); +// _financialacls_civix_navigationMenu($menu); +//} diff --git a/civicrm/ext/financialacls/images/screenshot.png b/civicrm/ext/financialacls/images/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..6765b696fa03249ac2cd605d5f0e4aa000ad6dad GIT binary patch literal 11775 zcmcI~2{hF0+y56OQnV|Q9{obnC}fhoMT;zDD-6k&WsqSQyC_cyNfKkwBV?D!GRBt5 zGGmLzHfHR`GDD0#%X^QW=l8znfBxq^@Be?!drqgOx$n8}>%Q*KwcOX|dS;+^ZTC)* zoe%`=zH$A^9SGtfLJ-$;K3?!m^9hqK2s%@D<BFD%PcMt6`w%yQ(YUX5JVNvMo`$~+ zq_-9EZQB_F+ZhsaNjOCF_{*!u_wae{<rCwx-OG2N{PY3tkV}8;X^_~%x2@>9eVB`m ze0z+oc|*}j>nVYr)!O=^lmDV7CVZoA&aAMLlfE~UxJ+GV44YZJ@XT$!mYBA*L~lj) zU!JMn4BQT+S+^Z82wHdcnxR)ZA(43^qM|u-7s2=QMf=d#u3gKaP$-jW(dyZW&GzS2 zRLlc@e%^724}yY|<<-?KpA#;#gaVfb!zq1z7R9BdrefmarerdiQtaF%dl;;OIQ^~n z0Mf8R_l@p3?{Q;C9~+xABR_F9Un5%Cqc0NN5Y%@5P7>nYy;EH9tJDK-QwY-K0zZb4 zAA`ZjSzlkDTwyat{bwjSre|cqa`6)1x)0bHCh5i~ed{%BWhPTbMr;=bK8H>YM{DGl zl$Pd9OgIYch9J%Q^zp<Y=jVl|>N2FULrgL~a2%=L*wApjxUkSb?AS3Aa5l=&&;v}9 z5Lh>u{aV8LdETk=jO9cL<~_RGI6~u5+V_V6L(R?2bLHaTz>r%`WsL}CMOPsAGGpPJ z1;HcT%*-r1S<TPnCdqq-p7%67d_5J+EBl#-zl>YvM&0#saLDuzQI_>zU-i<s0EX00 zFkEN)2<)tkhEo|;Q}tq#Bf>bIuS+xi1(p>)x77kx=Zkmz$pt}&cj1nCt9b71zcO}| zR)N7&M0}A*){o9Mu*kgkK{(!BVLu%IC_X-(oZF}8pZ_jmV@&(^Wz9vZ1+JAP;&8~_ z>)?J<H$O3I?D3dCl-<XRs(SowSkKh^eKPl{ft7?)R$!>`83VZNrK-V&g@sN_mh7KS zazJ}C%00x1MHV%_o#^-hd+4OVOG%S^FUti?50T_{fpKqaLV~02XybA!IhmyJBTwM` zxpUtW3tn$0*RE^_mpXpz#EBD>_Vznoo}NY=ZU3zx;*hiG<l;B-CA-f>?)PeBM=(tm zVEO<{Q6$m{g~A_@V)s|jkygVmw*%P+a-TGWhK43fUlCjwl5o%>78?ly!03A{q5+J& zzRuT0imJ_e2`#MFrl@Yi;j04w0J@`FCho|L@Bn}1c$A-BGGh<C3tX|b9qg!7MkKL$ zZkRI258VJn+H&gZ>PV`6FP<Qkuxe_D&>#8(FT|^r0obN?$^T$7UHD5I4RUgFDC6S~ zTZ61FudiJuqFH7uS{#u3`T21$-%5`Iio7sW@u{L>Evw>(KG7zecQE{Jf0obM@}m{4 zMKTUJfnazXuOX30On;88Gj!CXME5h=QrGX}(+Tv@<h+*3iXCF$w6c@WlqMd1s;;(@ z?49?EPJ5@dZq3+%l{BgBLja`@7-wW;WV2W-`O2}uFW~_BBQ^drmjk(cy<HDLP|XdF z%4=&cdHML<6%!MSi)VMI=7kng?R|X8Ub785Tjj2j)=n*%i-Pk})17_+tMRZ^p!7{l zOoUhpSudRwiPSD85bkAHu3T-L>IC{9uNzJ{)jg^@UFo-UwDkbGwQBqQM~^-l`Qmrq zHz)<?hG-`uy}P=)G$=k@<$Of9GgN5u3$5qa+3Vp%E-08BcS=6t2{xf@;X0*>RIF4x zO=v6$b`knOMLSS6t4mqH8^Y>=cKJ*A%c8|K^6ecQBJ~fVRpfA3JTmoILlSEnM-HDZ z8P^iYu6s5}q;BUPwA{54Az3-Oc;cBJlb5XR*_Gh%)M*J|JXwhMwXWW@kj9e7_V|0# zHMT^p60y7Bzyo&e;tzo}Qp6uVcu)&Or8&E}<hsDGkLxa1dQH8lTmYbc)H@PCIB4yB zg1H#wqS8<@=5pC*cIax)bgr`q1ntVuPP>hCb$uf<-Pvh^p@BuzW>y!b(qoLXD|<3t z@|to)T{V{}0a!oNVZm_>XU?24HqE#As8s73v{}UXNw7jgkOA{jH|un4Y%JwVxDZ+C zDY#LO3$$_|fSb6D|J=wkn-{gP1+ImoH(LkiSA%_z;gtYb*X5sF4K=fl@4D^<H>Ml; z87}B9ba8&<edDd}hrDail1vdb8KwuXa=p1wT+0eQiGAJ(hz&C>IqBf%QF6+G=uvWp zs3(lZ=qNW;5sBv0heu!f2@=hPfy?T4o>NTp(1kIFVEcfFS#u@d(r;`?^)0o_$hj+O zEzIBnpkV+bH4(=(H|qv?h9O{=FHKKQI_0pbOlEG28wCA)dgo)F)cd1iV#W~>5t8ru zh@PRo8C`U`rI)|IO9t2`>GRjGS2*7K&bQ`-zFXHm!fIg%neb86Lv;%MTJ*8FFY_E7 zZkR|Xrlb@ks|T26q}c8x0q36=S*RnouJ6xi!Fjx;Z+->WQ9e66+dh*4+X?(*$6ihA zc+2?93q8p(n;%i#^hIDVyjry`tG%kg86I8IsTOTUJVC4)2=<hUSG1A^={!)ITYI(S zXC*Fp08-K8Ao|F0Df4WG*AzFTDM#)cak<?RRR?@xkyj76>0Q!ainyS0y9u4x4aEt= z*r^`Hl$)C(04kSsF2XZMeX}bk#*<oFfcU|#uhkV7^uEn!-bfLddR$0$o{6$EnTZgV zyQ#BCEp;QR@Ip1|Ieu;LH*c-BjcbJ#lFb|c*jPjfIZ~$kfS!fV7*oTA#NWucW)lnz z4Li46$FpM&vAnJk?eSS@z)^_B;hs!L#iGiTqp|7=6L1^k9`wZBVsBU1{7>!$4sq$l z#9}UJK=r-bt<`#4hr#J-*NM|Mh&^b6K~H<qn`w*$$%Ns8F=1_o29VihURoyuY}z%n zwZKOCJ{~tCmK5=%L$RL%^<z&v^FTHCqDbjJLm!TUyXsdxp4Z=@dfetk`#w4VUt6Fo zOoe;D>y+dN8ylNWUh8=JSyfdFv(K3){ms5)z6&n-2|=M`ADuA7s$San4@z*jPBDD5 znd4pVpMC$?2)#CHb~ZAzJ(yH}oY`*LE~9>s?#t(6)|kodf&|vIR6a8>P~#<D3k$We zx6kkjmviu!l9bF9fP%#Wz%Mi$;O&(Jbz)3@ON%Q2Zj_OWA%T*!-C9`hw)ZHxf?j^w zYHZm|6bhbxjW1;xUMNDff}Ltl|6q4UMMsnGKVo-6Q2Ax_fSr^jdc@Ab5<PC0nOv;Z z{RE!7C^@bU(6g<7+rTz!pb7VnMZ++anIaxJmD!>b`scW<g;hUuL-Q_!?|NKhRKc=W z-Ef4U&8j>F2x5%)7CTes<~*jZ_w@B8@&3g}bdS+fy4q0V0U-7h?QzB!E<o$<Zj(J= zEetcKO{?>er_f7BY+b>*Ezfk_a+F_KTVXo|1yz@$7*R67R6NrbwZ_cN&3O}epri-B zzU94iI;`tf&ndXFvLm?Ocfz(WS3pXWHE&<D0s=u61bqGdmj%%ty04wnR#&B??}>&X z+!_0TDUbAKPNQBq|79V1PdE%Qeqy4xFHrI%0*AZ71+CuBf8?>D)TK&TY&`!mS(1-< z7Lw02E!Bcj`$rNbyOaoZjm1%p2f_EL^4DDL?7kgUhEg3knc=92WS2Z4qp?^w|55Jl zTA0Dc^`@2<lV*GTTFhMpA`j#!dAp&uVvoF6&TlM;BfqDARN^DnWPxa$U0hsTv@R8N zp=h*6To6h%cWo;buP2?47`_X%E82)2bK&=0op)Rq`WE4Y!SuX24h`J-5vhdIedWAA zQVHA|>e2e@f(bqxhZAc)1_cY8Q&2F>SDW1XoFMrT2G$^m9y^vlO|PUn4Of5q^gdDp za?jV+)+U{=`~LklCvxVidF+0G)a-sD5W~g7VzGH#tSuvie4Hb-)4Y%$i;ImdOF2uW z(*qE9?>?jjG?Ot+y%6L;JCAmEH^@K96ydQJE^nWo_sp8>WH{YfFNT5W?4|M2LScLd zcP(uGMPH0={nw<a<`sJ1N6NXq5HHm+_E@`JPj7IcfZ8c<CvR_W9VtfNXUbJDM(*yu zn-Gq54gn!;Y{cF&znIHfc=eL~{FtEY^oTlU?tNd6F>8D9#_O{xAVIH1Zu#Z&7cV+- zj`4YAI9{c4W0^~Hlh#M4Qc);?fy1}>2`p;jzQ;&sZm9kZ#95D4DB&GE_4FV?0a#wD zU-}<*+S?tk^Vh=Mc^9M~_Z<nGACI}c4QjIkS%_I0FB}e!6xn7i?9<^igH!P);fDIo zwMd^B=7$D6Uc7i=Bp@I_0l}1$|5>&G*ARvn5F3e_4c#F`5*z8ek(``d0)qzJ&K_qz zCX%3<9&gj;)>c#f6!l`_&M-s`<_~BbIXpZ}SzYzbajR>_!t(?GNbIsywAB3;b1D2D zK0e*OP|XqB5i9LYJBixa*+<3Y<xUdZd_>LC8;mgk0EG=`;Jo~lI`HvJv%~fFw9rC< z`Nv78NxAeUW-}%YIAiRu9l<iwn*#Wr#H2GM!nul)5|a{`x#%8r$g%Y_x{K2K?H%`1 z-MBLwjS_W7CEXMC3H-ZiVQt~m;(dY0F7DKRyITgdrgOugiYTCraT`Lc8XVr`^z~y= z(O-LaKmjLCtQ-;+CjVLWVv_t;JyKMwdpqxoy#mmDgb`BVxtwvI5lS@jsTa6W1J-(B zk`+lgBguh8$VhT4Qi443MlK2-a%oHQkLA*q<zKaO@Zmm*n@w^i;bvc)BVnYbxHaL8 z4=Z1ElUv@e<&nnKrho!!5@y~ok_=l^M*9q>-!P88F6J62LjRG@_!=vvPvXq(Kyia3 zdcQ*H1Amg<i)9<MXmPpUme)O=#s<MzF~q#oH9-;rORbQUKN$HKPN-eDAkBF1S+;GU z;$)NLkCN7B`s2k)KpszvmTRL)Mry>fj7;E{XC#{U;?<O?uR9daQQzPLdU_!Mp;N+= z6Lbr!x9@ZC0{bEQ7_U=ynWO3`ugq~KH==k{7Uio@u(Z5f=AukKYURvRlM_{EYippr zLs^6e+-Db=kPV)p`+S!c7qfeM?loV5q!rb+g&|VqvMc$^`78LV_-jO7K)aG-hnYa# zk$4_3reUjtQoAT&j>C`MD;7?z*1dJ>wdB*Rp|M-j^Yfp)xHwbNF~*5^(-Vvn&C&_R zi5B-np;k7Kw@tVmiC>S}Y-)Pp#Z?%(L$iDPDnr(^-2K?mqp$H66D<iaTqkcTky3f& zm_}eOoCh4E;%2!=hKd771Uj>Hq%@ZZ;HgktRMb9Mt^tQ<J_qQ99AaR&te+X}*lFCv zkmH6#&1g0w%OPiOb+sHb2~_zL)?TUL;A_ZgKGNUVcw>s&U8~(fMmHl_Bgm39GZQ~% z=y=Hv39L1hN7!OwZsM^(DjqIUT3V`euq#p<2w4Ux3)`O-({zY>InsUo*WOpE_MGVh zkG-5@UmGPGhVc5}j&_tW8M#JCadrQ@Q4#MGRnDIub=w6F;0Z$WupD^io4%;#<h@zy zJQaG$a<E~HOueT3;u(hf{&Li<uibF4(fEU&_AOhvAhkC3VD7J+N}#kn>m`s6Q|jup zF;RUqTApiP*`V7Fu=4yXqcg7kRfv)Uj0YYV3_+2w4;`W<K*Xpwor^xY)G!^dDn54j z&+3Cf2W`WXH?>>RM*S@^Pagq`?zV>4D|_Ug;DW0lJo?f^S499SA%+Wh)OcyLdhiaM zSLU^r5B$J6JfW4_1k@r+WEgkrySv|3bAk2FO1B4if|n&F;M4bq086b;X+Q%(n}I7n z%2hEoN9|*HYGF0d)K`le*1}~U3b8gvl#^pby_NH0Y($f(z%YaL6Oxl*#&iosb)TQ9 zMnO*ysnlB6D65gf+OF+l01(4Rlrsh~NB5x5aMw7O^4O2r+Ut+`bG#z`p-L*S5DDg3 z+0}qGiA}ZEVPOETn&8GmpE>!iwwwb^LYm3VIEbFA^UWH_cty>s^WB<3x<zWrXyS`* z%5G{R#T)xQK@1?ixVq+!Ubw0zkF>Y1L!IMjOZmuD%)PjUyU|UBO|m+`9d{LCMITQ5 zmE=mosM6w4xwR-cFwgznJ`aU2^6%h-OPzV<CwB=DYp`K7kU@8%Zba#h%&NxwByZn? z=7Je#pX=#d8)=O77)(Tl!-hie%OkP+q}e|^(9a+aNFtN)`_Uozo9CBw%PTp!w-BMe z4bf}q*$!X<R<ITZ|AIeZ2mn0r2Yj{l@%LyL;`iwP>%?L{pcJqfxOzS!IM3Ec*wF9Y zwiW|h{oZwJx8J*N?FK&nv)k`)|Mw?u?eqJ<|GD)O&e8uF`TcQALBFN`FS`0i>i?p) z|LH_bdjI;gH^sY(jhoD|T6bFT4qEaat>%159uEB^2r2}2Vu34UtK?tx8)U1EA#dF+ znZTQxBo{av;q>s~ht)NPhE>3@X#WSg-%RCPe`~-rYIHVbbB)1+KNPgVP9ZNP)%Ycg z!<~KU;`_Ihxd)ATCZw@e7}i(4`Kx;K*66H$(5hk9oaFqNo%IsSYsf}o>*{J@a<f*O z3sxupOfUEAt=?n=Z8)qfE%gU&rVpOAEIofgPd$*;TRc~_GLu6O0ts#f?OrJt1G#_5 zYH3#QSGoRE4rCPqB3Jw|MBpH;P5+Ue3QCw$`@VXpFMSHqZ}`=}x!$k;gc9~GWo<-? z5??i+K36q6Di=gpjow_2?xe0wRc|=Ql;-qj2>!_0x5?JnL`}^uX9lfiwi-pVF@f`G z;>K3XwZdj@OK}DM>DzuTmgZ=f9&7EoUCrvMS}dMis@`1BRHq9CEq~DusPGrkSf9|> z*pIGWXStd7Z*DB<P*e&s%h%;L=j2YVC0kSnEr$m^45+9GxP%mo3i?Iue>3T`aV7;W z$2eE*R}0%hbork%sYVhVFJ!El;+wPLIyM<y$a{KxO;oA-lFao<(`p$9fA(F=7OFoR z+b{cBflJP9DbFn+N0E5bODQ^NHo_`5a1oypcO17Tss67@Uu@029kZJ&DVwLnRo-2Q zE{4A(F-Y}f#?7*gnaz!v$$E{ANBkdpl5sDwr8%MA+-~Y_Rg3-zOs?RhS9kL-$**Ix z{`6JWTxC}ZJVh#cl(p{@``rE(cGbO(T%=6Z%Hf68u~AcF+}__79*<(~ORDL%bkzNM zt2_6Lv)`zKHEeLkcKZcug^+x~RMsCwi<L701*#j^{<Sd$S}NOTqcZ=`l4`moPWPMA z!5&|-`#M1{Fps|xQ;|PC#=3Jv{m3HQ=drh6x2}(Ce|b_Xb_sk4Qk(Mhh(L?@;`&yR zqWCReaGYuRDy-IJqWRG9VqR5NMkm$V@OHCJmQ(VfM19`2SZd&YK|_8u7F+6Tr?uk2 z_KHo%wCH^jR34PNX^BuZlXd42vfzM}CL>_#`HZ6j8gI8UQe7OBd@(8WroUOSs+;MK zpnrpnc;)f^l9f(sY|zh8@z?d6tp1I>ij8oMwZXU<t8iL}$oWb=I_&dLFE>-uNH)Ra z(BcRqyHu#l8m?8c?vLaK7)bQQrHi6|Eg8WVeS1{t{%S0%*uv9)yEkEJsyC`?!EQ5| z<WjP$9`l680r$y2t8V#qH)H7Wm56|n<y`Ah!CB=^>~j~HMz5cFp&vLu-|~t9sjkcO z*&o&UGnKz2#_w0RDv4D7Ovs0-WtozS)h?o_lG|NY*VJ3cCFdB(jpcfcKqvD2z0*WY zbu2z?iWjjU{T?4i^UsqKHNo}OB^4h;Kl$y-m-c<~-l>j!<AV5{+gezat!4a}*~n`w zrC~`^oU~QQGJ4K_G_};4&|5ytZe&p~*flEF1kGMZPxPD1od&*Ssm>bSo-5<<QXg%a z-@3CjT5;dM*M8hOCfU#__UHbFB4cH9>{PZJH{#O$b@d)@iyhy?I=2lO1?@MPy1!CX zXdZoxgz52*st73I&OB#KuMXfv(hOv*6;m<;<_-L=E&$cgA0vV)d{t0>9|B_wvC+4q z;Uq@3hUKsEcdJPDx};0y>>Fj`me~at`e}v0hdp|+aLVqaJI~aR5vc5VezyhcSU^Wx z_Qq0JeFyhLG3osm_V1ip(!cAfVQwCT;Qzo%k&?@N&})fnW;o6G_gTiZbQy$`EA;JI zB|#Pb(fl90y2kwrFex47vMGydCkMuy-8OwD7X6EECbRmz!l!(j_Wo`59$I%IZygSx z9HGa?^~R#?`~PLjK;cRPRzJ0(ItwObix$c%&DQfL&$I=G{||!dzvJNlHKKrE^S=~& zKq&t2xBfl46%YSC@xPDN)?IAL-?6?H2Esr12*Ur?$Nw`E_&o}~{r{Sne--*CO#c&% zHM_m`3T^H7dspz0HW4bM{y}|p{DkmqO~S~^1MmQ4OM&4wT)t(c8Lc{~+Q?CTWn};F zVTllLz)_S3u9q_m!Ff|vnwpx7!26t1FCJu}1`<h&+}^rf6^%+`eX{13mM}RDaO33x zzt9VV;o`ngTgg}_hpzhc$p}>Qjl;vkDKw(DM)B_3-P`G7O|DT|BOgIUAiL7p*_i^K zP7IEfrsDr8;+L(sS$3uF9KRBp{r&rQCoiw!i|XpIL2op=B+R<?8_-w{;rchFI4U4m z?lkINT_VMuE<eX3+#ab+O<;_TWmi^K?(;V_G0ArB%Etuq0u9miTId<px3bHWWBrEf zA{D9&KyLeB-k?OWpr9cBOiT1JHJO>26%n9BpFAUj_QR69A9!#jK0W7nl${}dt$X`L zWe0exF`LaEC2)8>3ZNC}lZ2gM{x}NM57YPF%08(q8ZXf1tt=|V<i_nHvLvOXbflJj zg-PJa#_$3&58LwN9hdKTtLdJ2s~Dyn9esw$EqoA7{UsnPEBjh-)+;E)Giu47b9p=x zk%&pm8^`5Wb#e@~);rzXVHcUn%%j!C#S6VHvp@x!;@3m7Xf)cLZ@R--im8)77~o+- z-bF0-j4E(=oSC-Pm@vSZ7M(3O-(F(}YH=3hIPZfX?I=IbECC$YslQ)qdi0+>KtmU8 zot&~kRn8STsKXN<v9<;tFi7jtH~GyIbP&E=TP7y`_rkeAMjTxFT3eRYd~a^f?!3cZ zbcpM2B1?C5ZbhL0Ua!&~qmJ@lSK~~BeOE`Hp!B&OCO-1_w}2hvvXpXgl>4Q88aLh% zquz>e?2Ff1n2`_M%~85ek4Jo-chBQ^c3ID>_&iJ}?*Q$e2QowX)z+LRE+5UUtmG&! zT`I|kRHwMx5*Sn$q~3U5ikq<?FyhoNaZfp14J1sAm1IP)-gDA-hX!c%X5B%G+CKR@ zX*M~JlYb|De4e8st?3O4YA_VJ!{Rh;Mxj*|)ttARGicBna0`D5CC=r0E$zEByUCD6 z>+O{(Fy|!cyq0y_>?{mmuZqsYY{GH{G-nzu*qj<ea|OHUqW*SJ*Qte_H4wf6Xav}w zP+!9~P^+NFN8o&O%wdmRym;KNaJ-oB9oko0&Xq79KHxt4wihep>f-WHoew<n+(<sp zEt*FDs`J?iIVHqafoHhLX;87s>;*1F)0_P5Cd@2Z6?3Q3XZ9~MV0^;oJ9&d+<(**c z4s5eMe^o+G^2%f<z<fx4Ohl~3`}fbW5pOIiMkMNbA@@U|h)$SX=#O#1EP8W9(;=P+ zn(EB+xc!PVx9o&a4Tibrrb3QyxB;i607ShA6BZ*ZH(b3A(@rYpPTfBr2!VRor4fk} z!lFk+Uq?h+gxwW}F6x>Ffypfhn54(;S!NI3BPJ#y(!vuESg<dRnBR3supi!8QG^0G zD8k!@t5-oS67r0QiHUKtwtjc<;>B!1AtCZsL9!P1vmN<>@UY?AWtno=QD_|9(b17k z!is(pI;JX3v6=$jQWNmDw_$=FF)tMegolMNXnyEB)^BM#4pph_i(<5h?LNS%Sm|yo zx3;#HYO9P4)jZq*tzJ!(nVz10qt}us^SDr8S1`!R<%QruYg@cNpsSn606_C;z;hJW zjjCbhzLu#1xdUHq6(~9MH1I|FCZ?u2py&#!JvHlRT$He*Pu*PCR?Yc<&3y631J84_ zhDN2H_Qo4O;yUcfPgsCuhV_Tt4-y9w`Hm#rDUt4Cyynm|P)(+t3d(gG<EeXzvI7z2 zdSI)72YWSkhb1Q6;ZI8V7jyxuSV-DE)F-WHlhb#hg!7h+Z*b9qF7!LOX&)M`d*pf? zYXV^B9s;Vt*-xH4nf&>)uwhG&H7kv~M|=eXTxGGBg@ACMHOhs=)#tAXL$C=+_V@2G z*e0+Vu2;`%lynf#1o8KQ6Y%nXh(dMD{sE2+j}s($rM>Vp>6GSy%CEVq$^lleHCgR6 z{5|NoFyAdCv?Xr5@uR7|d<l`x+qfam>b{rso*u`@H5~^B&OQC?+SKR^1WtUL#TOP= zDm>j8<!a<$Khu$^p99L6qC7xryOP6tuyD6GppLo!pE@Qq9$h_42w}OYf?~aacfd@4 zb$|JkiiXBaz8utDxt)*LhPAi1Ct;F3-I|)z85rokdlHk(sb0$M0E0yNP&u54LaZEq z#nWjURFsYh2&g>&=nwN`V?R)BPi}jtNMcs{@>+3VSkvg_7NszHw}Z`S&EbG(SdUZp z9hb@K&hYR9rEwWT*0?=y3LvK}9XvdWj-p7Bb4UFWIi+AwPah@qP@8__Y+kVIO!ne} z=!;_<5<_{zs9&}9qqH+9(^zzuq&0!WHO<qw<(3*X3bbvg9|DxY=-mTARtyLsb|#i0 zH*NicpYDS+Ge_?l(SFL;rz=)~(rnAsS{M5vO6Z`%X*mUU?rzBayIz!h(%ZL%oV~G) zOrnc^i_&P*SF%fmi&cpdRAWb;&GK|>@wyphoSuW9I<N;l7}tIu)aFZ#9(DN0?EV0% zoo#t4Z63KYY+n@X)v!szurr|ILiBM@8eb4W^NPozI@)b0HGbr+ag;e|8i+?YDnCsx z<=oYWtgI|2KR+U6^sK@(XQ3$yeH(2XeVf&~_8zpgrmYF$*IIdnOQ0Ck7G0DMQsVu- zcuLQ|_<=qzXZ2<{TW6PLjp^y@1N!#<@ev=Bzss1MG(?e51PGP39r{c_5avHg?&b>C zdNYRg;^OdjNgaUyx+H|`xd);Ef`96a3gF@f+(7e+j$sSP1w1~TZob&@siPOnidW7% za@l^AL!X%fpObzMR5wArc+?Bfnga2%Atu-WDA+qb<9AWhKG|_3VP_$SI}^wq5=j+= z+LRr~?nOUrI%0J~WH0(%=vFba-9<(>v)x}t_XD^DO-4cW;?fc#=nv*&l?c#`lpf$f ztE+p8^Wr1^EOF2V#a!GGK)lLNBouLKsR{BNCmvBKP!OAqr0qUSv7kX*P`RPn?zA44 zk__t?QBh`q-}o@9cYmy6^yu}L(6?+}sLdQ`Q1>$aoAMysJXyCvv)k@IqB^iLX{jN0 zsHH{EYq)MVAm!SIufA|`)>HT{EN5v1nwadUcyJiI`^iv>DrSg(52R@g>dL<6Ep8)6 zuT>VGWT?0xfowsJ@{<iBwXlG$PXnCdTA$>|*wOF|KxBtb*($VWWt{@*(wS-aIlG0h zq^`Coz)Yc!C?tG;#j=rPzXhA5pB_D`-C{GMhiGvaNdkNTr`sblQzs7I_403T4VdI~ zGA1I4*nl7;eQChFNFo=4#(;_muThXkkqGqF^7d&>z{^_zqP3)?gd|o5=5m<UV0Q1h zGC=sRR&}5Ivrwm=f}8iqmx${#^2Fr<THjFqucGPgvESo-KObj$<V#9Ql2)D>Jwi#s z88QHwknl-Eb@}r4*Fs?k`n~D5fToFeB@trZu=h_CaY))SKqm1M`78OW`9Jf2;gG<l zBI4q1Y8X}D`48n7c7_z_uqrRQ5Q}UEO*82iVp%Oci!R78guv%4>LtL!f)+Dl>Lt)_ za>Vg|oVJPAXUX09xe<lrVDV+Et;R6#uO1G!4hloTmidcx0xI(bjKp{1+H4qt_tv!M zXp||9q-GwU2Si=B1#X36>*eQX9&2nJ9~h@%#=2bI;eV3~`Yu)x+IBEm*-v#2V|K#| zpcz1O`u?b%$@Ig~7L#fBd!Rpo_xq0@MpL~$z;hRW1PurMl{>u)K=X)Oz6Ie9r<a4{ zwTZ|6?X^K)1n8DX)p(!M3{XUPs;7bVM3-=y8U{W^p$LYcRu2s@+lM9bz6$uIiH=MP zi(0x+wiiv<HIeG_1~eKa6}%X#z_9HpE=uI$#ope;iIcT3;oIMuW&4`GHoNroVMw_a zAf`YJH&4$`W(dTYE;@fT9*;k{%N(?2Y{qgyn*9fHCMG5aaNv5peqAY%rmBM`C>kos z9k^<*q_bVjowsXY4yuP!Bn=lnFR!d%b_{btsRB8b1e4bDel%&NuDMR_?$NM>gai{Z z`-M>Z+))Vfz5I)>&7wn;ONJ>}0kRAErq%KB4;NKc6`Md)iSOO~QQ<>}NW}s9v#7ij z=RnX1A^76&{umVv@bm!T0BW=9S_AGPf8`17a~V07#QOYMYYNsHciI$KJ~aQ)g55<_ zj@`~2JAnSB-9iW1+o#2ue$W;OItyx`i#?eRzk78m)04l%wYW@VS5#EwDu8YtgSRyj z%*CXSA3uVuL!mUWIU~Uw{6y#E>3=EMTDr+!DZ0WT{jE!#1GO9+9CXT><;Fp)$`&OG znz0mgo?d@*0c0SHi;Fkw3@+?P%Y6g=iloI67qtdCo02nBU(ni=bnV=kBPTwap408F zJnMM27#I+Tvx?(20wx7ra06LjUNe7X&m{Bh`$E6zN%UZAiber&W;#T7V+(Nc694jh z|IC;MEp4p-5*gS@oRJJo0bGrFXsVQy6bG&DjzxGyn}`=m2hsiA|Jj-N=5s4X8Bp~A zS?0ThgyO|AckI}4veZ_}3H^R2F;H3kgfVqh@J2QPq;nim#-E90^*l?I#dDZxVKghi zjTO*}>Dz@aSpv->;}k?T$fMKSvAFD>^u(Qog({lu6c>OEPosQd;z|kt*SK&`mf4ax zNw8CELPPPT_0lJdaYEcw&^91~l^w$gUDz3GSA;K4ks|Lh>efxwE!3^iZPOh%j|IJL zhZTFiC*tyY2FpKwyp68dNmRT04Y{dhzXzqUah%hRmvOudxNaG~mgeRK-QrTWFRp&i z)O9mgE3W{*@H79K8c4aPJ}>~h3a5Ua2OUj8hebtCt31FfC=J3@?EGez9jw9-NRTx^ zEvD!?dvg?;RP{S?pU$eQQWHZP7U%7d%uG~i-~sjkT^#+VD~t2ojueH5(e(NdWqwPp zj&V44nU0PQ1we3(8}uD|-bJ@9-aIyyP)LpN2Br+HQW|k3fbXxJ#!l}Inj5*HKMh!1 zXxw|^-Rr6w6DdYZ1|*x>=~L}P7xVI`rmY-9Xc4q(@I&4kaNYWe#6s*@KkyXT=epmc zW3s%9aS_O~>#wBYO79s<<t{9{Qkj9p5qS=x3qZ-BM9Kl{VP@t)ox3Urgn@7|fjXQf z!4C=Qw<hX;fP!JayNGUjML<mdSuiXtw;n%v$zfIAtGo^K&KjQ1{FI>%SpK-8j`Cii zV-V=58R2sV!>fE#Pr_&%YeXXBWL(V*8D1K;0Qa9e8^^wi2@n?w2R|k2orE8xxjxhY zmZ<5SRo^nrV&SOWJt!gK{{8#Yzj<<A`Pj~mKp(Jk($!Vv^a8`|O3n*^JsEa}DftAv z2S6COG3a;>2O|d>fLRsDmI@R@Z(`~7UTwETh5l1a;M?QBeDxZt!m!5(tOC&O7yLFU z$%S>CPN%DP2oAvoi7XtR(}!a{r03ZJbP$Z5-~kP~ZIip>;;lMGC!4I{;h|ekf#!FE zM~|6&19%^hw<YOkS1kNUF$(IJW{#b8FOhc0@et&9m@0Y###2Q(y}7qGF^kN5%Kg|Z z%>+SDr|#x={YN*N{}`xvl&C-IUigHAZS(ZIU^r_rP=-17(hb4eGT0w$cOM*O9DQm) zuvq+Oq^a0Xuwt;~+1KB-9FR27RR*c;MFVzWbA3N&p48?JJ%4|t45-&1{1>`$Rqsmv IrTdTn0|#irRsaA1 literal 0 HcmV?d00001 diff --git a/civicrm/ext/financialacls/info.xml b/civicrm/ext/financialacls/info.xml new file mode 100644 index 0000000000..8539355b32 --- /dev/null +++ b/civicrm/ext/financialacls/info.xml @@ -0,0 +1,33 @@ +<?xml version="1.0"?> +<extension key="financialacls" type="module"> + <file>financialacls</file> + <name>Financial ACLs</name> + <description>Implements financial ACLS - may not be disabled without risk of site breakage while in progress</description> + <license>AGPL-3.0</license> + <maintainer> + <author>CiviCRM</author> + <email>info@civicrm.org</email> + </maintainer> + <urls> + <url desc="Main Extension Page">http://FIXME</url> + <url desc="Documentation">http://FIXME</url> + <url desc="Support">http://FIXME</url> + <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url> + </urls> + <releaseDate>2020-08-27</releaseDate> + <version>1.0</version> + <develStage>stable</develStage> + <compatibility> + <ver>5.30</ver> + </compatibility> + <tags> + <tag>mgmt:hidden</tag> + </tags> + <comments>Financial acls - working towards moving this code to the extension from core</comments> + <classloader> + <psr4 prefix="Civi\" path="Civi"/> + </classloader> + <civix> + <namespace>CRM/Financialacls</namespace> + </civix> +</extension> diff --git a/civicrm/ext/financialacls/phpunit.xml.dist b/civicrm/ext/financialacls/phpunit.xml.dist new file mode 100644 index 0000000000..fc8f870b72 --- /dev/null +++ b/civicrm/ext/financialacls/phpunit.xml.dist @@ -0,0 +1,18 @@ +<?xml version="1.0"?> +<phpunit backupGlobals="false" backupStaticAttributes="false" colors="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="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/> + </listener> + </listeners> +</phpunit> diff --git a/civicrm/ext/financialacls/tests/phpunit/LineItemTest.php b/civicrm/ext/financialacls/tests/phpunit/LineItemTest.php new file mode 100644 index 0000000000..71eb6380c4 --- /dev/null +++ b/civicrm/ext/financialacls/tests/phpunit/LineItemTest.php @@ -0,0 +1,114 @@ +<?php + +use CRM_Financialacls_ExtensionUtil as E; +use Civi\Test\HeadlessInterface; +use Civi\Test\HookInterface; +use Civi\Test\TransactionalInterface; +use Civi\Api4\PriceField; + +/** + * FIXME - Add test description. + * + * Tips: + * - With HookInterface, you may implement CiviCRM hooks directly in the test class. + * Simply create corresponding functions (e.g. "hook_civicrm_post(...)" or similar). + * - With TransactionalInterface, any data changes made by setUp() or test****() functions will + * rollback automatically -- as long as you don't manipulate schema or truncate tables. + * If this test needs to manipulate schema or truncate tables, then either: + * a. Do all that using setupHeadless() and Civi\Test. + * b. Disable TransactionalInterface, and handle all setup/teardown yourself. + * + * @group headless + */ +class LineItemTest extends \PHPUnit\Framework\TestCase implements HeadlessInterface, HookInterface, TransactionalInterface { + + use Civi\Test\ContactTestTrait; + use Civi\Test\Api3TestTrait; + + /** + * @return \Civi\Test\CiviEnvBuilder + * @throws \CRM_Extension_Exception_ParseException + */ + public function setUpHeadless() { + // Civi\Test has many helpers, like install(), uninstall(), sql(), and sqlFile(). + // See: https://docs.civicrm.org/dev/en/latest/testing/phpunit/#civitest + return \Civi\Test::headless() + ->installMe(__DIR__) + ->apply(); + } + + /** + * Test api applies permissions on line item actions (delete & get). + */ + public function testLineItemApiPermissions() { + $contact1 = $this->individualCreate(); + $defaultPriceFieldID = $this->getDefaultPriceFieldID(); + $this->callAPISuccess('Order', 'create', [ + 'financial_type_id' => 'Donation', + 'contact_id' => $contact1, + 'line_items' => [ + [ + 'line_item' => [ + [ + 'financial_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'financial_type_id', 'Donation'), + 'line_total' => 40, + 'price_field_id' => $defaultPriceFieldID, + 'qty' => 1, + ], + [ + 'financial_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'financial_type_id', 'Member Dues'), + 'line_total' => 50, + 'price_field_id' => $defaultPriceFieldID, + 'qty' => 1, + ], + ], + ], + ], + ]); + + $this->setPermissions([ + 'access CiviCRM', + 'access CiviContribute', + 'edit contributions', + 'delete in CiviContribute', + 'view contributions of type Donation', + 'delete contributions of type Donation', + ]); + Civi::settings()->set('acl_financial_type', TRUE); + $this->createLoggedInUser(); + + $lineItems = $this->callAPISuccess('LineItem', 'get', ['sequential' => TRUE])['values']; + $this->assertCount(2, $lineItems); + $this->callAPISuccessGetCount('LineItem', ['check_permissions' => TRUE], 1); + + $this->callAPISuccess('LineItem', 'Delete', ['check_permissions' => TRUE, 'id' => $lineItems[0]['id']]); + $this->callAPIFailure('LineItem', 'Delete', ['check_permissions' => TRUE, 'id' => $lineItems[1]['id']]); + } + + /** + * Set ACL permissions, overwriting any existing ones. + * + * @param array $permissions + * Array of permissions e.g ['access CiviCRM','access CiviContribute'], + */ + protected function setPermissions($permissions) { + CRM_Core_Config::singleton()->userPermissionClass->permissions = $permissions; + if (isset(\Civi::$statics['CRM_Financial_BAO_FinancialType'])) { + unset(\Civi::$statics['CRM_Financial_BAO_FinancialType']); + } + } + + /** + * @return mixed + * @throws \API_Exception + * @throws \Civi\API\Exception\UnauthorizedException + */ + protected function getDefaultPriceFieldID(): int { + return PriceField::get() + ->addWhere('price_set_id:name', '=', 'default_contribution_amount') + ->addWhere('name', '=', 'contribution_amount') + ->addWhere('html_type', '=', 'Text') + ->addSelect('id')->execute()->first()['id']; + } + +} diff --git a/civicrm/ext/financialacls/tests/phpunit/bootstrap.php b/civicrm/ext/financialacls/tests/phpunit/bootstrap.php new file mode 100644 index 0000000000..a5b49253c8 --- /dev/null +++ b/civicrm/ext/financialacls/tests/phpunit/bootstrap.php @@ -0,0 +1,63 @@ +<?php + +ini_set('memory_limit', '2G'); +ini_set('safe_mode', 0); +// phpcs:disable +eval(cv('php:boot --level=classloader', 'phpcode')); +// phpcs:enable +// Allow autoloading of PHPUnit helper classes in this extension. +$loader = new \Composer\Autoload\ClassLoader(); +$loader->add('CRM_', __DIR__); +$loader->add('Civi\\', __DIR__); +$loader->add('api_', __DIR__); +$loader->add('api\\', __DIR__); +$loader->register(); + +/** + * 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 = array(0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => STDERR); + $oldOutput = getenv('CV_OUTPUT'); + putenv("CV_OUTPUT=json"); + + // Execute `cv` in the original folder. This is a work-around for + // phpunit/codeception, which seem to manipulate PWD. + $cmd = sprintf('cd %s; %s', escapeshellarg(getenv('PWD')), $cmd); + + $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/civicrm/ext/search/CRM/Search/Page/Ang.php b/civicrm/ext/search/CRM/Search/Page/Ang.php new file mode 100644 index 0000000000..b6a03e2bbf --- /dev/null +++ b/civicrm/ext/search/CRM/Search/Page/Ang.php @@ -0,0 +1,197 @@ +<?php + +class CRM_Search_Page_Ang extends CRM_Core_Page { + /** + * @var string[] + */ + private $loadOptions = ['id', 'name', 'label', 'description', 'color', 'icon']; + + /** + * @var array + */ + private $schema = []; + + /** + * @var string[] + */ + private $allowedEntities = []; + + public function run() { + $breadCrumb = [ + 'title' => ts('Search'), + 'url' => CRM_Utils_System::url('civicrm/search'), + ]; + CRM_Utils_System::appendBreadCrumb([$breadCrumb]); + + $this->getSchema(); + + // If user does not have permission to search any entity, bye bye. + if (!$this->allowedEntities) { + CRM_Utils_System::permissionDenied(); + } + + // Add client-side vars for the search UI + $vars = [ + 'operators' => CRM_Utils_Array::makeNonAssociative($this->getOperators()), + 'schema' => $this->schema, + 'links' => $this->getLinks(), + 'loadOptions' => $this->loadOptions, + 'actions' => $this->getActions(), + 'functions' => CRM_Api4_Page_Api4Explorer::getSqlFunctions(), + ]; + + Civi::resources() + ->addPermissions(['edit groups', 'administer reserved groups']) + ->addVars('search', $vars); + + // Load angular module + $loader = new Civi\Angular\AngularLoader(); + $loader->setModules(['search']); + $loader->setPageName('civicrm/search'); + $loader->useApp([ + 'defaultRoute' => '/Contact', + ]); + $loader->load(); + parent::run(); + } + + /** + * @return string[] + */ + private function getOperators() { + return [ + '=' => '=', + '!=' => '≠', + '>' => '>', + '<' => '<', + '>=' => '≥', + '<=' => '≤', + 'CONTAINS' => ts('Contains'), + 'IN' => ts('Is In'), + 'NOT IN' => ts('Not In'), + 'LIKE' => ts('Is Like'), + 'NOT LIKE' => ts('Not Like'), + 'BETWEEN' => ts('Is Between'), + 'NOT BETWEEN' => ts('Not Between'), + 'IS NULL' => ts('Is Null'), + 'IS NOT NULL' => ts('Not Null'), + ]; + } + + /** + * Populates $this->schema & $this->allowedEntities + */ + private function getSchema() { + $schema = \Civi\Api4\Entity::get() + ->addSelect('name', 'title', 'description', 'icon') + ->addWhere('name', '!=', 'Entity') + ->addOrderBy('title') + ->setChain([ + 'get' => ['$name', 'getActions', ['where' => [['name', '=', 'get']]], ['params']], + ])->execute(); + $getFields = ['name', 'label', 'description', 'options', 'input_type', 'input_attrs', 'data_type', 'serialize']; + foreach ($schema as $entity) { + // Skip if entity doesn't have a 'get' action or the user doesn't have permission to use get + if ($entity['get']) { + // Get fields and pre-load options for certain prominent entities + $loadOptions = in_array($entity['name'], ['Contact', 'Group']) ? $this->loadOptions : FALSE; + if ($loadOptions) { + $entity['optionsLoaded'] = TRUE; + } + $entity['fields'] = civicrm_api4($entity['name'], 'getFields', [ + 'select' => $getFields, + 'where' => [['permission', 'IS NULL']], + 'orderBy' => ['label'], + 'loadOptions' => $loadOptions, + ]); + // Get the names of params this entity supports (minus some obvious ones) + $params = $entity['get'][0]; + CRM_Utils_Array::remove($params, 'checkPermissions', 'debug', 'chain', 'language'); + unset($entity['get']); + $this->schema[] = ['params' => array_keys($params)] + array_filter($entity); + $this->allowedEntities[] = $entity['name']; + } + } + } + + /** + * @return array + */ + private function getLinks() { + $results = []; + $keys = array_flip(['alias', 'entity', 'joinType']); + foreach (civicrm_api4('Entity', 'getLinks', ['where' => [['entity', 'IN', $this->allowedEntities]]], ['entity' => 'links']) as $entity => $links) { + $entityLinks = []; + foreach ($links as $link) { + if (!empty($link['entity']) && in_array($link['entity'], $this->allowedEntities)) { + // Use entity.alias as array key to avoid duplicates + $entityLinks[$link['entity'] . $link['alias']] = array_intersect_key($link, $keys); + } + } + $results[$entity] = array_values($entityLinks); + } + return array_filter($results); + } + + /** + * @return array[] + */ + private function getActions() { + // Note: the placeholder %1 will be replaced with entity name on the clientside + $actions = [ + 'export' => [ + 'title' => ts('Export %1'), + 'icon' => 'fa-file-excel-o', + 'entities' => array_keys(CRM_Export_BAO_Export::getComponents()), + 'crmPopup' => [ + 'path' => "'civicrm/export/standalone'", + 'query' => "{entity: entity, id: ids.join(',')}", + ], + ], + 'update' => [ + 'title' => ts('Update %1'), + 'icon' => 'fa-save', + 'entities' => [], + 'uiDialog' => ['templateUrl' => '~/search/crmSearchActions/crmSearchActionUpdate.html'], + ], + 'delete' => [ + 'title' => ts('Delete %1'), + 'icon' => 'fa-trash', + 'entities' => [], + 'uiDialog' => ['templateUrl' => '~/search/crmSearchActions/crmSearchActionDelete.html'], + ], + ]; + + // Check permissions for update & delete actions + foreach ($this->allowedEntities as $entity) { + $result = civicrm_api4($entity, 'getActions', [ + 'where' => [['name', 'IN', ['update', 'delete']]], + ], ['name']); + foreach ($result as $action) { + // Contacts have their own delete action + if (!($entity === 'Contact' && $action === 'delete')) { + $actions[$action]['entities'][] = $entity; + } + } + } + + // Add contact tasks which support standalone mode (with a 'url' property) + $contactTasks = CRM_Contact_Task::permissionedTaskTitles(CRM_Core_Permission::getPermission()); + foreach (CRM_Contact_Task::tasks() as $id => $task) { + if (isset($contactTasks[$id]) && !empty($task['url'])) { + $actions['contact.' . $id] = [ + 'title' => $task['title'], + 'entities' => ['Contact'], + 'icon' => $task['icon'] ?? 'fa-gear', + 'crmPopup' => [ + 'path' => "'{$task['url']}'", + 'query' => "{cids: ids.join(',')}", + ], + ]; + } + } + + return $actions; + } + +} diff --git a/civicrm/ext/search/CRM/Search/Upgrader.php b/civicrm/ext/search/CRM/Search/Upgrader.php new file mode 100644 index 0000000000..7260e39813 --- /dev/null +++ b/civicrm/ext/search/CRM/Search/Upgrader.php @@ -0,0 +1,34 @@ +<?php +use CRM_Search_ExtensionUtil as E; + +/** + * Collection of upgrade steps. + */ +class CRM_Search_Upgrader extends CRM_Search_Upgrader_Base { + + /** + * Add menu item when enabled. + */ + public function enable() { + \Civi\Api4\Navigation::create(FALSE) + ->addValue('parent_id:name', 'Search') + ->addValue('label', E::ts('Create Search...')) + ->addValue('name', 'create_search') + ->addValue('url', 'civicrm/search') + ->addValue('icon', 'crm-i fa-search-plus') + ->addValue('has_separator', 2) + ->addValue('weight', 99) + ->execute(); + } + + /** + * Delete menu item when disabled. + */ + public function disable() { + \Civi\Api4\Navigation::delete(FALSE) + ->addWhere('name', '=', 'create_search') + ->addWhere('domain_id', '=', 'current_domain') + ->execute(); + } + +} diff --git a/civicrm/ext/search/CRM/Search/Upgrader/Base.php b/civicrm/ext/search/CRM/Search/Upgrader/Base.php new file mode 100644 index 0000000000..3015d7908f --- /dev/null +++ b/civicrm/ext/search/CRM/Search/Upgrader/Base.php @@ -0,0 +1,391 @@ +<?php + +// AUTO-GENERATED FILE -- Civix may overwrite any changes made to this file +use CRM_Search_ExtensionUtil as E; + +/** + * Base class which provides helpers to execute upgrade logic + */ +class CRM_Search_Upgrader_Base { + + /** + * @var CRM_Search_Upgrader_Base + */ + public 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 + * sorted numerically + */ + private $revisions; + + /** + * @var bool + * Flag to clean up extension revision data in civicrm_setting + */ + private $revisionStorageIsDeprecated = FALSE; + + /** + * Obtain a reference to the active upgrade handler. + */ + public static function instance() { + if (!self::$instance) { + // FIXME auto-generate + self::$instance = new CRM_Search_Upgrader( + 'org.civicrm.search', + 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_Search_Upgrader_Base::_queueAdapter($ctx, 'methodName', 'arg1', 'arg2'); + * @endcode + */ + public static 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); + } + + /** + * CRM_Search_Upgrader_Base constructor. + * + * @param $extensionName + * @param $extensionDir + */ + 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 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; + } + + /** + * Run the sql commands in the specified file. + * + * @param string $tplFile + * The SQL file path (relative to this extension's dir). + * Ex: "sql/mydata.mysql.tpl". + * + * @return bool + * @throws \CRM_Core_Exception + */ + 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 syntactic sugar for queueing several tasks that + * run different queries + * + * @return bool + */ + public function executeSql($query, $params = []) { + // FIXME verify that we raise an exception on error + CRM_Core_DAO::executeQuery($query, $params); + return TRUE; + } + + /** + * Syntactic 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 = E::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 = \Civi::settings()->get($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://docs.civicrm.org/dev/en/latest/hooks/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://docs.civicrm.org/dev/en/latest/hooks/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://docs.civicrm.org/dev/en/latest/hooks/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://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_enable + */ + public function onEnable() { + // stub for possible future use + if (is_callable([$this, 'enable'])) { + $this->enable(); + } + } + + /** + * @see https://docs.civicrm.org/dev/en/latest/hooks/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/civicrm/ext/search/README.md b/civicrm/ext/search/README.md new file mode 100644 index 0000000000..456e59f4f8 --- /dev/null +++ b/civicrm/ext/search/README.md @@ -0,0 +1,7 @@ +# org.civicrm.search + +A core extension to create advanced searches. + +## Usage + +Once enabled, navigate to **Search > Create Search...** in the menu. diff --git a/civicrm/ext/search/ang/search.ang.php b/civicrm/ext/search/ang/search.ang.php new file mode 100644 index 0000000000..67275088f1 --- /dev/null +++ b/civicrm/ext/search/ang/search.ang.php @@ -0,0 +1,17 @@ +<?php +// Autoloader data for search builder. +return [ + 'js' => [ + 'ang/*.js', + 'ang/search/*.js', + 'ang/search/*/*.js', + ], + 'css' => [ + 'css/*.css', + ], + 'partials' => [ + 'ang/search', + ], + 'basePages' => [], + 'requires' => ['crmUi', 'crmUtil', 'ngRoute', 'crmRouteBinder', 'ui.sortable', 'ui.bootstrap', 'dialogService', 'api4'], +]; diff --git a/civicrm/ext/search/ang/search.module.js b/civicrm/ext/search/ang/search.module.js new file mode 100644 index 0000000000..3b2eeb34ff --- /dev/null +++ b/civicrm/ext/search/ang/search.module.js @@ -0,0 +1,95 @@ +(function(angular, $, _) { + "use strict"; + + // Shared between router and searchMeta service + var searchEntity; + + // Declare module and route/controller/services + angular.module('search', CRM.angRequires('search')) + + .config(function($routeProvider) { + $routeProvider.when('/:entity', { + controller: 'searchRoute', + template: '<div id="bootstrap-theme" class="crm-search"><crm-search entity="entity"></crm-search></div>', + reloadOnSearch: false + }); + }) + + // Controller binds entity to route + .controller('searchRoute', function($scope, $routeParams, $location) { + searchEntity = $scope.entity = $routeParams.entity; + + // Changing entity will refresh the angular page + $scope.$watch('entity', function(newEntity, oldEntity) { + if (newEntity && oldEntity && newEntity !== oldEntity) { + $location.url('/' + newEntity); + } + }); + }) + + .factory('searchMeta', function() { + function getEntity(entityName) { + if (entityName) { + entityName = entityName === true ? searchEntity : entityName; + return _.find(CRM.vars.search.schema, {name: entityName}); + } + } + function getField(name) { + var dotSplit = name.split('.'), + joinEntity = dotSplit.length > 1 ? dotSplit[0] : null, + fieldName = _.last(dotSplit).split(':')[0], + entityName = searchEntity; + // Custom fields contain a dot in their fieldname + // If 3 segments, the first is the joinEntity and the last 2 are the custom field + if (dotSplit.length === 3) { + fieldName = dotSplit[1] + '.' + fieldName; + } + // If 2 segments, it's ambiguous whether this is a custom field or joined field. Search the main entity first. + if (dotSplit.length === 2) { + var field = _.find(getEntity(true).fields, {name: dotSplit[0] + '.' + fieldName}); + if (field) { + return field; + } + } + if (joinEntity) { + entityName = _.find(CRM.vars.search.links[entityName], {alias: joinEntity}).entity; + } + return _.find(getEntity(entityName).fields, {name: fieldName}); + } + return { + getEntity: getEntity, + getField: getField, + parseExpr: function(expr) { + var result = {}, + fieldName = expr, + bracketPos = expr.indexOf('('); + if (bracketPos >= 0) { + fieldName = expr.match(/[A-Z( _]*([\w.:]+)/)[1]; + result.fn = _.find(CRM.vars.search.functions, {name: expr.substring(0, bracketPos)}); + } + result.field = getField(fieldName); + var split = fieldName.split(':'), + prefixPos = split[0].lastIndexOf(result.field.name); + result.path = split[0]; + result.prefix = prefixPos > 0 ? result.path.substring(0, prefixPos) : ''; + result.suffix = !split[1] ? '' : ':' + split[1]; + return result; + } + }; + }) + + // Reformat an array of objects for compatibility with select2 + // Todo this probably belongs in core + .factory('formatForSelect2', function() { + return function(input, key, label, extra) { + return _.transform(input, function(result, item) { + var formatted = {id: item[key], text: item[label]}; + if (extra) { + _.merge(formatted, _.pick(item, extra)); + } + result.push(formatted); + }, []); + }; + }); + +})(angular, CRM.$, CRM._); diff --git a/civicrm/ext/search/ang/search/SaveSmartGroup.ctrl.js b/civicrm/ext/search/ang/search/SaveSmartGroup.ctrl.js new file mode 100644 index 0000000000..74401b9847 --- /dev/null +++ b/civicrm/ext/search/ang/search/SaveSmartGroup.ctrl.js @@ -0,0 +1,54 @@ +(function(angular, $, _) { + "use strict"; + + angular.module('search').controller('SaveSmartGroup', function ($scope, crmApi4, dialogService) { + var ts = $scope.ts = CRM.ts(), + model = $scope.model; + $scope.groupEntityRefParams = { + entity: 'Group', + api: { + params: {is_hidden: 0, is_active: 1, 'saved_search_id.api_entity': model.entity}, + extra: ['saved_search_id', 'description', 'visibility', 'group_type'] + }, + select: { + allowClear: true, + minimumInputLength: 0, + placeholder: ts('Select existing group') + } + }; + if (!CRM.checkPerm('administer reserved groups')) { + $scope.groupEntityRefParams.api.params.is_reserved = 0; + } + $scope.perm = { + administerReservedGroups: CRM.checkPerm('administer reserved groups') + }; + $scope.groupFields = _.indexBy(_.find(CRM.vars.search.schema, {name: 'Group'}).fields, 'name'); + $scope.$watch('model.id', function (id) { + if (id) { + _.assign(model, $('#api-save-search-select-group').select2('data').extra); + } + }); + $scope.cancel = function () { + dialogService.cancel('saveSearchDialog'); + }; + $scope.save = function () { + $('.ui-dialog:visible').block(); + var group = model.id ? {id: model.id} : {title: model.title}; + group.description = model.description; + group.visibility = model.visibility; + group.group_type = model.group_type; + group.saved_search_id = '$id'; + var savedSearch = { + api_entity: model.entity, + api_params: model.params + }; + if (group.id) { + savedSearch.id = model.saved_search_id; + } + crmApi4('SavedSearch', 'save', {records: [savedSearch], chain: {group: ['Group', 'save', {'records': [group]}]}}) + .then(function (result) { + dialogService.close('saveSearchDialog', result[0]); + }); + }; + }); +})(angular, CRM.$, CRM._); diff --git a/civicrm/ext/search/ang/search/crmSearch.component.js b/civicrm/ext/search/ang/search/crmSearch.component.js new file mode 100644 index 0000000000..5eaaee5aba --- /dev/null +++ b/civicrm/ext/search/ang/search/crmSearch.component.js @@ -0,0 +1,580 @@ +(function(angular, $, _) { + "use strict"; + + angular.module('search').component('crmSearch', { + bindings: { + entity: '=' + }, + templateUrl: '~/search/crmSearch.html', + controller: function($scope, $element, $timeout, crmApi4, dialogService, searchMeta, formatForSelect2) { + var ts = $scope.ts = CRM.ts(), + ctrl = this; + + this.DEFAULT_AGGREGATE_FN = 'GROUP_CONCAT'; + + this.selectedRows = []; + this.limit = CRM.cache.get('searchPageSize', 30); + this.page = 1; + this.params = {}; + // After a search this.results is an object of result arrays keyed by page, + // Prior to searching it's an empty string because 1: falsey and 2: doesn't throw an error if you try to access undefined properties + this.results = ''; + this.rowCount = false; + // Have the filters (WHERE, HAVING, GROUP BY, JOIN) changed? + this.stale = true; + this.allRowsSelected = false; + + $scope.controls = {}; + $scope.joinTypes = [{k: false, v: ts('Optional')}, {k: true, v: ts('Required')}]; + $scope.entities = formatForSelect2(CRM.vars.search.schema, 'name', 'title', ['description', 'icon']); + this.perm = { + editGroups: CRM.checkPerm('edit groups') + }; + + this.getEntity = searchMeta.getEntity; + + this.paramExists = function(param) { + return _.includes(searchMeta.getEntity(ctrl.entity).params, param); + }; + + $scope.getJoinEntities = function() { + var joinEntities = _.transform(CRM.vars.search.links[ctrl.entity], function(joinEntities, link) { + var entity = searchMeta.getEntity(link.entity); + if (entity) { + joinEntities.push({ + id: link.entity + ' AS ' + link.alias, + text: entity.title, + description: '(' + link.alias + ')', + icon: entity.icon + }); + } + }, []); + return {results: joinEntities}; + }; + + $scope.addJoin = function() { + // Debounce the onchange event using timeout + $timeout(function() { + if ($scope.controls.join) { + ctrl.params.join = ctrl.params.join || []; + ctrl.params.join.push([$scope.controls.join, false]); + loadFieldOptions(); + } + $scope.controls.join = ''; + }); + }; + + $scope.changeJoin = function(idx) { + if (ctrl.params.join[idx][0]) { + ctrl.params.join[idx].length = 2; + loadFieldOptions(); + } else { + ctrl.clearParam('join', idx); + } + }; + + $scope.changeGroupBy = function(idx) { + if (!ctrl.params.groupBy[idx]) { + ctrl.clearParam('groupBy', idx); + } + // Remove aggregate functions when no grouping + if (!ctrl.params.groupBy.length) { + _.each(ctrl.params.select, function(col, pos) { + if (_.contains(col, '(')) { + var info = searchMeta.parseExpr(col); + if (info.fn.category === 'aggregate') { + ctrl.params.select[pos] = info.path + info.suffix; + } + } + }); + } + }; + + /** + * Called when clicking on a column header + * @param col + * @param $event + */ + $scope.setOrderBy = function(col, $event) { + var dir = $scope.getOrderBy(col) === 'fa-sort-asc' ? 'DESC' : 'ASC'; + if (!$event.shiftKey) { + ctrl.params.orderBy = {}; + } + ctrl.params.orderBy[col] = dir; + if (ctrl.results) { + ctrl.refreshPage(); + } + }; + + /** + * Returns crm-i icon class for a sortable column + * @param col + * @returns {string} + */ + $scope.getOrderBy = function(col) { + var dir = ctrl.params.orderBy && ctrl.params.orderBy[col]; + if (dir) { + return 'fa-sort-' + dir.toLowerCase(); + } + return 'fa-sort disabled'; + }; + + $scope.addParam = function(name) { + if ($scope.controls[name] && !_.contains(ctrl.params[name], $scope.controls[name])) { + ctrl.params[name].push($scope.controls[name]); + if (name === 'groupBy') { + // Expand the aggregate block + $timeout(function() { + $('#crm-search-build-group-aggregate.collapsed .collapsible-title').click(); + }, 10); + } + } + $scope.controls[name] = ''; + }; + + // Deletes an item from an array param + this.clearParam = function(name, idx) { + ctrl.params[name].splice(idx, 1); + }; + + // Prevent visual jumps in results table height during loading + function lockTableHeight() { + var $table = $('.crm-search-results', $element); + $table.css('height', $table.height()); + } + + function unlockTableHeight() { + $('.crm-search-results', $element).css('height', ''); + } + + // Ensure all non-grouped columns are aggregated if using GROUP BY + function aggregateGroupByColumns() { + if (ctrl.params.groupBy.length) { + _.each(ctrl.params.select, function(col, pos) { + if (!_.contains(col, '(') && ctrl.canAggregate(col)) { + ctrl.params.select[pos] = ctrl.DEFAULT_AGGREGATE_FN + '(' + col + ')'; + } + }); + } + } + + // Debounced callback for loadResults + function _loadResultsCallback() { + // Multiply limit to read 2 pages at once & save ajax requests + var params = angular.merge({debug: true, limit: ctrl.limit * 2}, ctrl.params); + lockTableHeight(); + $scope.error = false; + if (ctrl.stale) { + ctrl.page = 1; + ctrl.rowCount = false; + } + if (ctrl.rowCount === false) { + params.select.push('row_count'); + } + params.offset = ctrl.limit * (ctrl.page - 1); + crmApi4(ctrl.entity, 'get', params).then(function(success) { + if (ctrl.stale) { + ctrl.results = {}; + } + if (ctrl.rowCount === false) { + ctrl.rowCount = success.count; + } + ctrl.debug = success.debug; + // populate this page & the next + ctrl.results[ctrl.page] = success.slice(0, ctrl.limit); + if (success.length > ctrl.limit) { + ctrl.results[ctrl.page + 1] = success.slice(ctrl.limit); + } + $scope.loading = false; + ctrl.stale = false; + unlockTableHeight(); + }, function(error) { + $scope.loading = false; + ctrl.results = {}; + ctrl.stale = true; + ctrl.debug = error.debug; + $scope.error = errorMsg(error); + }) + .finally(function() { + if (ctrl.debug) { + ctrl.debug.params = JSON.stringify(ctrl.params, null, 2); + } + }); + } + + var _loadResults = _.debounce(_loadResultsCallback, 250); + + function loadResults() { + $scope.loading = true; + aggregateGroupByColumns(); + _loadResults(); + } + + // What to tell the user when search returns an error from the server + // Todo: parse error codes and give helpful feedback. + function errorMsg(error) { + return ts('Ensure all search critera are set correctly and try again.'); + } + + this.changePage = function() { + if (ctrl.stale || !ctrl.results[ctrl.page]) { + lockTableHeight(); + loadResults(); + } + }; + + this.refreshAll = function() { + ctrl.stale = true; + ctrl.selectedRows.length = 0; + loadResults(); + }; + + // Refresh results while staying on current page. + this.refreshPage = function() { + lockTableHeight(); + ctrl.results = {}; + loadResults(); + }; + + $scope.onClickSearch = function() { + if (ctrl.autoSearch) { + ctrl.autoSearch = false; + } else { + ctrl.refreshAll(); + } + }; + + $scope.onClickAuto = function() { + ctrl.autoSearch = !ctrl.autoSearch; + if (ctrl.autoSearch && ctrl.stale) { + ctrl.refreshAll(); + } + $('.crm-search-auto-toggle').blur(); + }; + + $scope.onChangeLimit = function() { + // Refresh only if search has already been run + if (ctrl.autoSearch || ctrl.results) { + // Save page size in localStorage + CRM.cache.set('searchPageSize', ctrl.limit); + ctrl.refreshAll(); + } + }; + + function onChangeSelect(newSelect, oldSelect) { + // When removing a column from SELECT, also remove from ORDER BY + _.each(_.difference(_.keys(ctrl.params.orderBy), newSelect), function(col) { + delete ctrl.params.orderBy[col]; + }); + // Re-arranging or removing columns doesn't merit a refresh, only adding columns does + if (!oldSelect || _.difference(newSelect, oldSelect).length) { + if (ctrl.autoSearch) { + ctrl.refreshPage(); + } else { + ctrl.stale = true; + } + } + } + + function onChangeFilters() { + ctrl.stale = true; + ctrl.selectedRows.length = 0; + if (ctrl.autoSearch) { + ctrl.refreshAll(); + } + } + + $scope.selectAllRows = function() { + // Deselect all + if (ctrl.allRowsSelected) { + ctrl.allRowsSelected = false; + ctrl.selectedRows.length = 0; + return; + } + // Select all + ctrl.allRowsSelected = true; + if (ctrl.page === 1 && ctrl.results[1].length < ctrl.limit) { + ctrl.selectedRows = _.pluck(ctrl.results[1], 'id'); + return; + } + // If more than one page of results, use ajax to fetch all ids + $scope.loadingAllRows = true; + var params = _.cloneDeep(ctrl.params); + params.select = ['id']; + crmApi4(ctrl.entity, 'get', params, ['id']).then(function(ids) { + $scope.loadingAllRows = false; + ctrl.selectedRows = _.toArray(ids); + }); + }; + + $scope.selectRow = function(row) { + var index = ctrl.selectedRows.indexOf(row.id); + if (index < 0) { + ctrl.selectedRows.push(row.id); + ctrl.allRowsSelected = (ctrl.rowCount === ctrl.selectedRows.length); + } else { + ctrl.allRowsSelected = false; + ctrl.selectedRows.splice(index, 1); + } + }; + + $scope.isRowSelected = function(row) { + return ctrl.allRowsSelected || _.includes(ctrl.selectedRows, row.id); + }; + + this.getFieldLabel = function(col) { + var info = searchMeta.parseExpr(col), + label = info.field.label; + if (info.fn) { + label = '(' + info.fn.title + ') ' + label; + } + return label; + }; + + // Is a column eligible to use an aggregate function? + this.canAggregate = function(col) { + // If the column is used for a groupBy, no + if (ctrl.params.groupBy.indexOf(col) > -1) { + return false; + } + // If the entity this column belongs to is being grouped by id, then also no + var info = searchMeta.parseExpr(col); + return ctrl.params.groupBy.indexOf(info.prefix + 'id') < 0; + }; + + $scope.formatResult = function formatResult(row, col) { + var info = searchMeta.parseExpr(col), + key = info.fn ? (info.fn.name + ':' + info.path + info.suffix) : col, + value = row[key]; + // Handle grouped results + if (info.fn && info.fn.name === 'GROUP_CONCAT' && value) { + return formatGroupConcatValues(info, value); + } + else if (info.fn && info.fn.name === 'COUNT') { + return value; + } + return formatFieldValue(info.field, value); + }; + + function formatFieldValue(field, value) { + var type = field.data_type; + if (value && (type === 'Date' || type === 'Timestamp') && /^\d{4}-\d{2}-\d{2}/.test(value)) { + return CRM.utils.formatDate(value, null, type === 'Timestamp'); + } + else if (type === 'Boolean' && typeof value === 'boolean') { + return value ? ts('Yes') : ts('No'); + } + else if (type === 'Money') { + return CRM.formatMoney(value); + } + if (_.isArray(value)) { + return value.join(', '); + } + return value; + } + + function formatGroupConcatValues(info, values) { + return _.transform(values.split(','), function(result, val) { + if (info.field.options && !info.suffix) { + result.push(_.result(getOption(info.field, val), 'label')); + } else { + result.push(formatFieldValue(info.field, val)); + } + }).join(', '); + } + + function getOption(field, value) { + return _.find(field.options, function(option) { + // Type coersion is intentional + return option.id == value; + }); + } + + $scope.fieldsForGroupBy = function() { + return {results: getAllFields('', function(key) { + return _.contains(ctrl.params.groupBy, key); + }) + }; + }; + + $scope.fieldsForSelect = function() { + return {results: getAllFields(':label', function(key) { + return _.contains(ctrl.params.select, key); + }) + }; + }; + + $scope.fieldsForWhere = function() { + return {results: getAllFields(':name', _.noop)}; + }; + + $scope.fieldsForHaving = function() { + return {results: _.transform(ctrl.params.select, function(fields, name) { + fields.push({id: name, text: ctrl.getFieldLabel(name)}); + })}; + }; + + function getDefaultSelect() { + return _.filter(['id', 'display_name', 'label', 'title', 'location_type_id:label'], searchMeta.getField); + } + + function getAllFields(suffix, disabledIf) { + function formatFields(entityName, prefix) { + return _.transform(searchMeta.getEntity(entityName).fields, function(result, field) { + var item = { + id: prefix + field.name + (field.options ? suffix : ''), + text: field.label, + description: field.description + }; + if (disabledIf(item.id)) { + item.disabled = true; + } + result.push(item); + }, []); + } + + var mainEntity = searchMeta.getEntity(ctrl.entity), + result = [{ + text: mainEntity.title, + icon: mainEntity.icon, + children: formatFields(ctrl.entity, '') + }]; + _.each(ctrl.params.join, function(join) { + var joinName = join[0].split(' AS '), + joinEntity = searchMeta.getEntity(joinName[0]); + result.push({ + text: joinEntity.title + ' (' + joinName[1] + ')', + icon: joinEntity.icon, + children: formatFields(joinEntity.name, joinName[1] + '.') + }); + }); + return result; + } + + /** + * Fetch pseudoconstants for main entity + joined entities + * + * Sets an optionsLoaded property on each entity to avoid duplicate requests + */ + function loadFieldOptions() { + var mainEntity = searchMeta.getEntity(ctrl.entity), + entities = {}; + + function enqueue(entity) { + entity.optionsLoaded = false; + entities[entity.name] = [entity.name, 'getFields', { + loadOptions: CRM.vars.search.loadOptions, + where: [['options', '!=', false]], + select: ['options'] + }, {name: 'options'}]; + } + + if (typeof mainEntity.optionsLoaded === 'undefined') { + enqueue(mainEntity); + } + _.each(ctrl.params.join, function(join) { + var joinName = join[0].split(' AS '), + joinEntity = searchMeta.getEntity(joinName[0]); + if (typeof joinEntity.optionsLoaded === 'undefined') { + enqueue(joinEntity); + } + }); + if (!_.isEmpty(entities)) { + crmApi4(entities).then(function(results) { + _.each(results, function(fields, entityName) { + var entity = searchMeta.getEntity(entityName); + _.each(fields, function(options, fieldName) { + _.find(entity.fields, {name: fieldName}).options = options; + }); + entity.optionsLoaded = true; + }); + }); + } + } + + this.$onInit = function() { + $scope.$bindToRoute({ + expr: '$ctrl.params.select', + param: 'select', + format: 'json', + default: getDefaultSelect() + }); + $scope.$watchCollection('$ctrl.params.select', onChangeSelect); + + $scope.$bindToRoute({ + expr: '$ctrl.params.orderBy', + param: 'orderBy', + format: 'json', + default: {} + }); + + $scope.$bindToRoute({ + expr: '$ctrl.params.where', + param: 'where', + format: 'json', + default: [], + deep: true + }); + $scope.$watch('$ctrl.params.where', onChangeFilters, true); + + if (this.paramExists('groupBy')) { + $scope.$bindToRoute({ + expr: '$ctrl.params.groupBy', + param: 'groupBy', + format: 'json', + default: [] + }); + } + $scope.$watchCollection('$ctrl.params.groupBy', onChangeFilters); + + if (this.paramExists('join')) { + $scope.$bindToRoute({ + expr: '$ctrl.params.join', + param: 'join', + format: 'json', + default: [], + deep: true + }); + } + $scope.$watch('$ctrl.params.join', onChangeFilters, true); + + if (this.paramExists('having')) { + $scope.$bindToRoute({ + expr: '$ctrl.params.having', + param: 'having', + format: 'json', + default: [], + deep: true + }); + } + $scope.$watch('$ctrl.params.having', onChangeFilters, true); + + loadFieldOptions(); + }; + + $scope.saveGroup = function() { + var selectField = ctrl.entity === 'Contact' ? 'id' : 'contact_id'; + if (ctrl.entity !== 'Contact' && !searchMeta.getField('contact_id')) { + CRM.alert(ts('Cannot create smart group from %1.', {1: searchMeta.getEntity(true).title}), ts('Missing contact_id'), 'error', {expires: 5000}); + return; + } + var model = { + title: '', + description: '', + visibility: 'User and User Admin Only', + group_type: [], + id: null, + entity: ctrl.entity, + params: angular.extend({}, ctrl.params, {version: 4, select: [selectField]}) + }; + delete model.params.orderBy; + var options = CRM.utils.adjustDialogDefaults({ + autoOpen: false, + title: ts('Save smart group') + }); + dialogService.open('saveSearchDialog', '~/search/saveSmartGroup.html', model, options); + }; + } + }); + +})(angular, CRM.$, CRM._); diff --git a/civicrm/ext/search/ang/search/crmSearch.html b/civicrm/ext/search/ang/search/crmSearch.html new file mode 100644 index 0000000000..bd0feabcba --- /dev/null +++ b/civicrm/ext/search/ang/search/crmSearch.html @@ -0,0 +1,20 @@ +<div id="bootstrap-theme" class="crm-search"> + <h1 crm-page-title>{{:: ts('Create Search for %1', {1: $ctrl.getEntity($ctrl.entity).title}) }}</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" aria-hidden="true"></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> + + <form> + <div ng-include="'~/search/crmSearch/criteria.html'"></div> + <div ng-include="'~/search/crmSearch/controls.html'"></div> + <div ng-include="'~/search/crmSearch/debug.html'" ng-if="$ctrl.debug"></div> + <div ng-include="'~/search/crmSearch/results.html'" class="crm-search-results"></div> + <div ng-include="'~/search/crmSearch/pager.html'"></div> + </form> +</div> diff --git a/civicrm/ext/search/ang/search/crmSearch/controls.html b/civicrm/ext/search/ang/search/crmSearch/controls.html new file mode 100644 index 0000000000..c17724ba31 --- /dev/null +++ b/civicrm/ext/search/ang/search/crmSearch/controls.html @@ -0,0 +1,25 @@ +<hr> +<div class="form-inline"> + <div class="btn-group" role="group"> + <button class="btn btn-primary{{ $ctrl.autoSearch ? '-outline' : '' }}" ng-click="onClickSearch()" ng-disabled="loading || (!$ctrl.autoSearch && !$ctrl.stale)"> + <i class="crm-i {{ loading ? 'fa-spin fa-spinner' : 'fa-search' }}"></i> + {{:: ts('Search') }} + </button> + <button class="btn crm-search-auto-toggle btn-primary{{ $ctrl.autoSearch ? '' : '-outline' }}" ng-click="onClickAuto()"> + <i class="crm-i fa-toggle-{{ $ctrl.autoSearch ? 'on' : 'off' }}"></i> + {{:: ts('Auto') }} + </button> + </div> + <crm-search-actions entity="$ctrl.entity" ids="$ctrl.selectedRows"></crm-search-actions> + <div class="btn-group pull-right"> + <button type="button" class="btn form-control dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> + <i class="crm-i fa-save"></i> {{:: ts('Create')}} + <span class="caret"></span> + </button> + <ul class="dropdown-menu"> + <li ng-if=":: $ctrl.perm.editGroups"> + <a href ng-click="saveGroup()">{{:: ts('Smart Group') }}</a> + </li> + </ul> + </div> +</div> diff --git a/civicrm/ext/search/ang/search/crmSearch/criteria.html b/civicrm/ext/search/ang/search/crmSearch/criteria.html new file mode 100644 index 0000000000..6bb500a741 --- /dev/null +++ b/civicrm/ext/search/ang/search/crmSearch/criteria.html @@ -0,0 +1,48 @@ +<div class="crm-flex-box"> + <div> + <div class="form-inline"> + <label for="crm-search-main-entity">{{:: ts('Search for') }}</label> + <input id="crm-search-main-entity" class="form-control" ng-model="$ctrl.entity" crm-ui-select="::{allowClear: false, data: entities}" ng-change="changeEntity()" /> + </div> + <div ng-if=":: $ctrl.paramExists('join')"> + <fieldset ng-repeat="join in $ctrl.params.join"> + <div class="form-inline"> + <label for="crm-search-join-{{ $index }}">{{:: ts('With') }}</label> + <input id="crm-search-join-{{ $index }}" class="form-control" ng-model="join[0]" crm-ui-select="{placeholder: ' ', data: getJoinEntities}" ng-change="changeJoin($index)" /> + <select class="form-control" ng-model="join[1]" ng-options="o.k as o.v for o in ::joinTypes" ></select> + </div> + <fieldset class="api4-clause-fieldset" crm-search-clause="{format: 'json', clauses: join, skip: 2, op: 'AND', label: ts('If'), fields: fieldsForWhere}"> + </fieldset> + </fieldset> + <fieldset> + <div class="form-inline"> + <input id="crm-search-add-join" class="form-control crm-action-menu fa-plus" ng-model="controls.join" crm-ui-select="{placeholder: ts('With'), data: getJoinEntities}" ng-change="addJoin()"/> + </div> + </fieldset> + </div> + <fieldset ng-if=":: $ctrl.paramExists('groupBy')"> + <div class="form-inline" ng-repeat="groupBy in $ctrl.params.groupBy"> + <label for="crm-search-groupBy-{{ $index }}">{{:: ts('Group By') }}</label> + <input id="crm-search-groupBy-{{ $index }}" class="form-control" ng-model="$ctrl.params.groupBy[$index]" crm-ui-select="{placeholder: ' ', data: fieldsForGroupBy}" ng-change="changeGroupBy($index)" /> + <hr> + </div> + <div class="form-inline"> + <input id="crm-search-add-groupBy" class="form-control crm-action-menu fa-plus" ng-model="controls.groupBy" crm-ui-select="{placeholder: ts('Group By'), data: fieldsForGroupBy}" ng-change="addParam('groupBy')"/> + </div> + <fieldset id="crm-search-build-group-aggregate" ng-if="$ctrl.params.groupBy.length" class="crm-collapsible collapsed"> + <legend class="collapsible-title">{{:: ts('Aggregate fields') }}</legend> + <div> + <fieldset ng-repeat="col in $ctrl.params.select" ng-if="$ctrl.canAggregate(col)"> + <crm-search-function expr="$ctrl.params.select[$index]" cat="'aggregate'"></crm-search-function> + </fieldset> + </div> + </fieldset> + </fieldset> + </div> + <div> + <fieldset class="api4-clause-fieldset" crm-search-clause="{format: 'string', clauses: $ctrl.params.where, op: 'AND', label: ts('Where'), fields: fieldsForWhere}"> + </fieldset> + <fieldset ng-if="$ctrl.paramExists('having') && $ctrl.params.groupBy.length" class="api4-clause-fieldset" crm-search-clause="{format: 'string', clauses: $ctrl.params.having, op: 'AND', label: ts('Filter'), fields: fieldsForHaving}"> + </fieldset> + </div> +</div> diff --git a/civicrm/ext/search/ang/search/crmSearch/debug.html b/civicrm/ext/search/ang/search/crmSearch/debug.html new file mode 100644 index 0000000000..4bb483d1af --- /dev/null +++ b/civicrm/ext/search/ang/search/crmSearch/debug.html @@ -0,0 +1,10 @@ +<fieldset class="crm-collapsible collapsed"> + <legend class="collapsible-title">{{:: ts('Query Info') }}</legend> + <div> + <pre ng-if="$ctrl.debug.timeIndex">{{ ts('Request took %1 seconds.', {1: $ctrl.debug.timeIndex}) }}</pre> + <div><strong>API:</strong></div> + <pre>{{ $ctrl.debug.params }}</pre> + <div><strong>SQL:</strong></div> + <pre ng-repeat="query in $ctrl.debug.sql">{{ query }}</pre> + </div> +</fieldset> diff --git a/civicrm/ext/search/ang/search/crmSearch/pager.html b/civicrm/ext/search/ang/search/crmSearch/pager.html new file mode 100644 index 0000000000..2c4a2b4ba1 --- /dev/null +++ b/civicrm/ext/search/ang/search/crmSearch/pager.html @@ -0,0 +1,35 @@ +<div class="crm-flex-box"> + <div> + <div class="form-inline"> + <label ng-if="loading && $ctrl.rowCount === false"><i class="crm-i fa-spin fa-spinner"></i></label> + <label ng-if="$ctrl.rowCount === 1"> + {{ $ctrl.selectedRows.length ? ts('%1 selected of 1 result', {1: $ctrl.selectedRows.length}) : ts('1 result') }} + </label> + <label ng-if="$ctrl.rowCount === 0 || $ctrl.rowCount > 1"> + {{ $ctrl.selectedRows.length ? ts('%1 selected of %2 results', {1: $ctrl.selectedRows.length, 2: $ctrl.rowCount}) : ts('%1 results', {1: $ctrl.rowCount}) }} + </label> + </div> + </div> + <div class="text-center crm-flex-2"> + <ul uib-pagination ng-if="$ctrl.rowCount && !$ctrl.stale" + class="pagination" + boundary-links="true" + total-items="$ctrl.rowCount" + ng-model="$ctrl.page" + ng-change="$ctrl.changePage()" + items-per-page="$ctrl.limit" + max-size="6" + force-ellipses="true" + previous-text="‹" + next-text="›" + first-text="«" + last-text="»" + ></ul> + </div> + <div class="form-inline text-right"> + <label for="crm-search-results-page-size" > + {{:: ts('Page size:') }} + </label> + <input class="form-control" id="crm-search-results-page-size" type="number" ng-model="$ctrl.limit" min="10" step="10" ng-change="onChangeLimit()"> + </div> +</div> diff --git a/civicrm/ext/search/ang/search/crmSearch/results.html b/civicrm/ext/search/ang/search/crmSearch/results.html new file mode 100644 index 0000000000..01b200207c --- /dev/null +++ b/civicrm/ext/search/ang/search/crmSearch/results.html @@ -0,0 +1,32 @@ +<table> + <thead> + <tr ng-model="$ctrl.params.select" ui-sortable="{axis: 'x'}"> + <th class="crm-search-result-select"> + <input type="checkbox" ng-checked="$ctrl.allRowsSelected" ng-click="selectAllRows()" ng-disabled="!(loading === false && !loadingAllRows && $ctrl.results[$ctrl.page] && $ctrl.results[$ctrl.page][0].id)"> + </th> + <th ng-repeat="col in $ctrl.params.select" ng-click="setOrderBy(col, $event)" title="{{:: ts('Drag to reorder columns, click to sort results (shift-click to sort by multiple).')}}"> + <i class="crm-i {{ getOrderBy(col) }}"></i> + <span>{{ $ctrl.getFieldLabel(col) }}</span> + <a href class="crm-hover-button" title="{{:: ts('Clear') }}" ng-click="$ctrl.clearParam('select', $index)"><i class="crm-i fa-times" aria-hidden="true"></i></a> + </th> + <th class="form-inline"> + <input class="form-control crm-action-menu fa-plus" ng-model="controls.select" crm-ui-select="::{data: fieldsForSelect, placeholder: ts('Add')}" ng-change="addParam('select')"> + </th> + </tr> + </thead> + <tbody> + <tr ng-repeat="row in $ctrl.results[$ctrl.page]"> + <td> + <input type="checkbox" ng-checked="isRowSelected(row)" ng-click="selectRow(row)" ng-disabled="!(loading === false && !loadingAllRows && row.id)"> + </td> + <td ng-repeat="col in $ctrl.params.select"> + {{ formatResult(row, col) }} + </td> + <td></td> + </tr> + </tbody> +</table> +<div class="messages warning no-popup" ng-if="error"> + <h4>{{:: ts('An error occurred') }}</h4> + <p>{{ error }}</p> +</div> diff --git a/civicrm/ext/search/ang/search/crmSearchActions.component.js b/civicrm/ext/search/ang/search/crmSearchActions.component.js new file mode 100644 index 0000000000..138cb089a9 --- /dev/null +++ b/civicrm/ext/search/ang/search/crmSearchActions.component.js @@ -0,0 +1,62 @@ +(function(angular, $, _) { + "use strict"; + + angular.module('search').component('crmSearchActions', { + bindings: { + entity: '<', + ids: '<' + }, + require: { + search: '^crmSearch' + }, + templateUrl: '~/search/crmSearchActions.html', + controller: function($scope, crmApi4, dialogService, searchMeta) { + var ts = $scope.ts = CRM.ts(), + entityTitle = searchMeta.getEntity(this.entity).title, + ctrl = this; + + this.init = function() { + if (!ctrl.actions) { + var actions = _.transform(_.cloneDeep(CRM.vars.search.actions), function (actions, action) { + if (_.includes(action.entities, ctrl.entity)) { + action.title = action.title.replace('%1', entityTitle); + actions.push(action); + } + }, []); + ctrl.actions = _.sortBy(actions, 'title'); + } + }; + + this.isActionAllowed = function(action) { + return !action.number || $scope.eval('' + $ctrl.ids.length + action.number); + }; + + this.doAction = function(action) { + if (!ctrl.isActionAllowed(action) || !ctrl.ids.length) { + return; + } + var data = { + ids: ctrl.ids, + entity: ctrl.entity + }; + // If action uses a crmPopup form + if (action.crmPopup) { + var path = $scope.$eval(action.crmPopup.path, data), + query = action.crmPopup.query && $scope.$eval(action.crmPopup.query, data); + CRM.loadForm(CRM.url(path, query)) + .on('crmFormSuccess', ctrl.search.refreshPage); + } + // If action uses dialogService + else if (action.uiDialog) { + var options = CRM.utils.adjustDialogDefaults({ + autoOpen: false, + title: action.title + }); + dialogService.open('crmSearchAction', action.uiDialog.templateUrl, data, options) + .then(ctrl.search.refreshPage); + } + }; + } + }); + +})(angular, CRM.$, CRM._); diff --git a/civicrm/ext/search/ang/search/crmSearchActions.html b/civicrm/ext/search/ang/search/crmSearchActions.html new file mode 100644 index 0000000000..dbeae2c84c --- /dev/null +++ b/civicrm/ext/search/ang/search/crmSearchActions.html @@ -0,0 +1,10 @@ +<div class="btn-group" title="{{:: ts('Perform action on selected items.') }}"> + <button type="button" ng-disabled="!$ctrl.ids.length" ng-click="$ctrl.init()" class="btn form-control dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> + {{:: ts('Action') }} <span class="caret"></span> + </button> + <ul class="dropdown-menu" ng-if=":: $ctrl.actions"> + <li ng-disabled="!$ctrl.isActionAllowed(action)" ng-repeat="action in $ctrl.actions"> + <a href ng-click="$ctrl.doAction(action)"><i class="fa {{action.icon}}"></i> {{ action.title }}</a> + </li> + </ul> +</div> diff --git a/civicrm/ext/search/ang/search/crmSearchActions/crmSearchActionDelete.ctrl.js b/civicrm/ext/search/ang/search/crmSearchActions/crmSearchActionDelete.ctrl.js new file mode 100644 index 0000000000..28a401e652 --- /dev/null +++ b/civicrm/ext/search/ang/search/crmSearchActions/crmSearchActionDelete.ctrl.js @@ -0,0 +1,24 @@ +(function(angular, $, _) { + "use strict"; + + angular.module('search').controller('crmSearchActionDelete', function($scope, crmApi4, dialogService, searchMeta) { + var ts = $scope.ts = CRM.ts(), + model = $scope.model, + ctrl = $scope.$ctrl = this; + + this.entity = searchMeta.getEntity(model.entity); + + this.cancel = function() { + dialogService.cancel('crmSearchAction'); + }; + + this.delete = function() { + crmApi4(model.entity, 'Delete', { + where: [['id', 'IN', model.ids]], + }).then(function() { + dialogService.close('crmSearchAction'); + }); + }; + + }); +})(angular, CRM.$, CRM._); diff --git a/civicrm/ext/search/ang/search/crmSearchActions/crmSearchActionDelete.html b/civicrm/ext/search/ang/search/crmSearchActions/crmSearchActionDelete.html new file mode 100644 index 0000000000..a8c95de8a2 --- /dev/null +++ b/civicrm/ext/search/ang/search/crmSearchActions/crmSearchActionDelete.html @@ -0,0 +1,10 @@ +<div id="bootstrap-theme"> + <div ng-controller="crmSearchActionDelete"> + <p>{{:: ts('Are you sure you want to delete %1 %2?', {1: model.ids.length, 2: $ctrl.entity.title}) }}</p> + <hr /> + <div class="buttons pull-right"> + <button type="button" ng-click="$ctrl.cancel()" class="btn btn-danger">{{:: ts('Cancel') }}</button> + <button ng-click="$ctrl.delete()" class="btn btn-primary">{{:: ts('Delete %1 %2', {1: model.ids.length, 2: $ctrl.entity.title}) }}</button> + </div> + </div> +</div> diff --git a/civicrm/ext/search/ang/search/crmSearchActions/crmSearchActionUpdate.ctrl.js b/civicrm/ext/search/ang/search/crmSearchActions/crmSearchActionUpdate.ctrl.js new file mode 100644 index 0000000000..63394a2427 --- /dev/null +++ b/civicrm/ext/search/ang/search/crmSearchActions/crmSearchActionUpdate.ctrl.js @@ -0,0 +1,63 @@ +(function(angular, $, _) { + "use strict"; + + angular.module('search').controller('crmSearchActionUpdate', function ($scope, $timeout, crmApi4, dialogService, searchMeta) { + var ts = $scope.ts = CRM.ts(), + model = $scope.model, + ctrl = $scope.$ctrl = this; + + this.entity = searchMeta.getEntity(model.entity); + this.values = []; + this.add = null; + + function fieldInUse(fieldName) { + return _.includes(_.collect(ctrl.values, 0), fieldName); + } + + this.updateField = function(index) { + // Debounce the onchange event using timeout + $timeout(function() { + if (!ctrl.values[index][0]) { + ctrl.values.splice(index, 1); + } + }); + }; + + this.addField = function() { + // Debounce the onchange event using timeout + $timeout(function() { + if (ctrl.add) { + ctrl.values.push([ctrl.add, '']); + } + ctrl.add = null; + }); + }; + + this.availableFields = function() { + var results = _.transform(ctrl.entity.fields, function(result, item) { + var formatted = {id: item.name, text: item.title, description: item.description}; + if (fieldInUse(item.name)) { + formatted.disabled = true; + } + if (item.name !== 'id') { + result.push(formatted); + } + }, []); + return {results: results}; + }; + + this.cancel = function() { + dialogService.cancel('crmSearchAction'); + }; + + this.save = function() { + crmApi4(model.entity, 'Update', { + where: [['id', 'IN', model.ids]], + values: _.zipObject(ctrl.values) + }).then(function() { + dialogService.close('crmSearchAction'); + }); + }; + + }); +})(angular, CRM.$, CRM._); diff --git a/civicrm/ext/search/ang/search/crmSearchActions/crmSearchActionUpdate.html b/civicrm/ext/search/ang/search/crmSearchActions/crmSearchActionUpdate.html new file mode 100644 index 0000000000..63178a0bae --- /dev/null +++ b/civicrm/ext/search/ang/search/crmSearchActions/crmSearchActionUpdate.html @@ -0,0 +1,16 @@ +<div id="bootstrap-theme"> + <div ng-controller="crmSearchActionUpdate"> + <div class="form-inline" ng-repeat="clause in $ctrl.values" > + <input class="form-control" ng-change="$ctrl.updateField($index)" ng-model="clause[0]" crm-ui-select="{data: $ctrl.availableFields, allowClear: true, placeholder: 'Field'}" /> + <input class="form-control" ng-model="clause[1]" crm-search-value="{field: clause[0]}" /> + </div> + <div class="form-inline"> + <input class="form-control twenty" style="width: 15em;" ng-model="$ctrl.add" ng-change="$ctrl.addField()" crm-ui-select="{data: $ctrl.availableFields, placeholder: ts('Add Value')}"/> + </div> + <hr /> + <div class="buttons pull-right"> + <button type="button" ng-click="$ctrl.cancel()" class="btn btn-danger">{{:: ts('Cancel') }}</button> + <button ng-click="$ctrl.save()" class="btn btn-primary" ng-disabled="!$ctrl.values.length">{{:: ts('Update %1 %2', {1: model.ids.length, 2: $ctrl.entity.title}) }}</button> + </div> + </div> +</div> diff --git a/civicrm/ext/search/ang/search/crmSearchClause.directive.js b/civicrm/ext/search/ang/search/crmSearchClause.directive.js new file mode 100644 index 0000000000..ab59aff122 --- /dev/null +++ b/civicrm/ext/search/ang/search/crmSearchClause.directive.js @@ -0,0 +1,75 @@ +(function(angular, $, _) { + "use strict"; + + angular.module('search').directive('crmSearchClause', function() { + return { + scope: { + data: '<crmSearchClause' + }, + templateUrl: '~/search/crmSearchClause.html', + controller: function ($scope, $element, $timeout) { + var ts = $scope.ts = CRM.ts(); + var ctrl = $scope.$ctrl = this; + this.conjunctions = {AND: ts('And'), OR: ts('Or'), NOT: ts('Not')}; + this.operators = CRM.vars.search.operators; + this.sortOptions = { + axis: 'y', + connectWith: '.api4-clause-group-sortable', + containment: $element.closest('.api4-clause-fieldset'), + over: onSortOver, + start: onSort, + stop: onSort + }; + + this.addGroup = function(op) { + $scope.data.clauses.push([op, []]); + }; + + this.removeGroup = function() { + $scope.data.groupParent.splice($scope.data.groupIndex, 1); + }; + + function onSort(event, ui) { + $($element).closest('.api4-clause-fieldset').toggleClass('api4-sorting', event.type === 'sortstart'); + $('.api4-input.form-inline').css('margin-left', ''); + } + + // Indent clause while dragging between nested groups + function onSortOver(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'); + } + + this.addClause = function() { + $timeout(function() { + if (ctrl.newClause) { + $scope.data.clauses.push([ctrl.newClause, '=', '']); + ctrl.newClause = null; + } + }); + }; + $scope.$watch('data.clauses', function(values) { + // Iterate in reverse order so index doesn't get out-of-sync during splice + _.forEachRight(values, function(clause, index) { + // Remove empty values + if (index >= ($scope.data.skip || 0)) { + if (typeof clause !== 'undefined' && !clause[0]) { + values.splice(index, 1); + } + // Add/remove value if operator allows for one + else if (typeof clause[1] === 'string' && _.contains(clause[1], 'NULL')) { + clause.length = 2; + } else if (typeof clause[1] === 'string' && clause.length === 2) { + clause.push(''); + } + } + }); + }, true); + } + }; + }); + +})(angular, CRM.$, CRM._); diff --git a/civicrm/ext/search/ang/search/crmSearchClause.html b/civicrm/ext/search/ang/search/crmSearchClause.html new file mode 100644 index 0000000000..97ef49621f --- /dev/null +++ b/civicrm/ext/search/ang/search/crmSearchClause.html @@ -0,0 +1,41 @@ +<legend>{{ data.label || ts('%1 group', {1: $ctrl.conjunctions[data.op]}) }}</legend> +<div class="btn-group btn-group-xs" ng-if="data.groupParent"> + <button class="btn btn-danger-outline" ng-click="$ctrl.removeGroup()" title="{{:: ts('Remove group') }}"> + <i class="crm-i fa-trash" aria-hidden="true"></i> + </button> +</div> +<div class="api4-clause-group-sortable" ng-model="data.clauses" ui-sortable="$ctrl.sortOptions"> + <div class="api4-input form-inline clearfix" ng-repeat="(index, clause) in data.clauses" ng-class="{hiddenElement: index < (data.skip || 0)}"> + <div ng-if="index >= (data.skip || 0)"> + <div class="api4-clause-badge" title="{{:: ts('Drag to reposition') }}"> + <span class="badge badge-info"> + <span ng-if="index === (data.skip || 0) && !data.groupParent">{{ data.label }}</span> + <span ng-if="index > (data.skip || 0) || data.groupParent">{{ $ctrl.conjunctions[data.op] }}</span> + <i class="crm-i fa-arrows" aria-hidden="true"></i> + </span> + </div> + <div ng-if="!$ctrl.conjunctions[clause[0]]" class="api4-input-group"> + <input class="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.key as o.value for o in $ctrl.operators" ></select> + <input class="form-control" ng-model="clause[2]" crm-search-value="{field: clause[0], op: clause[1], format: data.format}" /> + </div> + <fieldset class="clearfix" ng-if="$ctrl.conjunctions[clause[0]]" crm-search-clause="{format: data.format, clauses: clause[1], op: clause[0], fields: data.fields, groupParent: data.clauses, groupIndex: index}"> + </fieldset> + </div> + </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"> + {{ $ctrl.conjunctions[data.op] }} <span class="caret"></span> + </button> + <ul class="dropdown-menu api4-add-where-group-menu"> + <li ng-repeat="(con, label) in $ctrl.conjunctions" ng-show="data.op !== con"> + <a href ng-click="$ctrl.addGroup(con)">{{ label }}</a> + </li> + </ul> + </div> + </div> + <input class="form-control" ng-model="$ctrl.newClause" ng-change="$ctrl.addClause()" crm-ui-select="{data: data.fields, placeholder: ts('Select field')}" /> +</div> diff --git a/civicrm/ext/search/ang/search/crmSearchFunction.component.js b/civicrm/ext/search/ang/search/crmSearchFunction.component.js new file mode 100644 index 0000000000..e75bac9571 --- /dev/null +++ b/civicrm/ext/search/ang/search/crmSearchFunction.component.js @@ -0,0 +1,28 @@ +(function(angular, $, _) { + "use strict"; + + angular.module('search').component('crmSearchFunction', { + bindings: { + expr: '=', + cat: '<' + }, + templateUrl: '~/search/crmSearchFunction.html', + controller: function($scope, formatForSelect2, searchMeta) { + var ts = $scope.ts = CRM.ts(), + ctrl = this; + this.functions = formatForSelect2(_.where(CRM.vars.search.functions, {category: this.cat}), 'name', 'title'); + + this.$onInit = function() { + var fieldInfo = searchMeta.parseExpr(ctrl.expr); + ctrl.path = fieldInfo.path; + ctrl.field = fieldInfo.field; + ctrl.fn = !fieldInfo.fn ? '' : fieldInfo.fn.name; + }; + + this.selectFunction = function() { + ctrl.expr = ctrl.fn ? (ctrl.fn + '(' + ctrl.path + ')') : ctrl.path; + }; + } + }); + +})(angular, CRM.$, CRM._); diff --git a/civicrm/ext/search/ang/search/crmSearchFunction.html b/civicrm/ext/search/ang/search/crmSearchFunction.html new file mode 100644 index 0000000000..e0692e33d3 --- /dev/null +++ b/civicrm/ext/search/ang/search/crmSearchFunction.html @@ -0,0 +1,4 @@ +<div class="form-inline"> + <label>{{ $ctrl.field.label }}:</label> + <input class="form-control" style="width: 15em;" ng-model="$ctrl.fn" crm-ui-select="{data: $ctrl.functions, placeholder: ts('Select')}" ng-change="$ctrl.selectFunction()"> +</div> diff --git a/civicrm/ext/search/ang/search/crmSearchValue.directive.js b/civicrm/ext/search/ang/search/crmSearchValue.directive.js new file mode 100644 index 0000000000..9e39cf1585 --- /dev/null +++ b/civicrm/ext/search/ang/search/crmSearchValue.directive.js @@ -0,0 +1,115 @@ +(function(angular, $, _) { + "use strict"; + + angular.module('search').directive('crmSearchValue', function($interval, searchMeta, formatForSelect2) { + return { + scope: { + data: '=crmSearchValue' + }, + require: 'ngModel', + link: function (scope, element, attrs, ngModel) { + var ts = scope.ts = CRM.ts(), + multi = _.includes(['IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN'], scope.data.op), + format = scope.data.format; + + 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, optionKey) { + var $el = $(element), + inputType = field.input_type, + dataType = field.data_type; + if (!op) { + op = field.serialize || dataType === 'Array' ? 'IN' : '='; + } + multi = _.includes(['IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN'], op); + if (op === 'IS NULL' || op === 'IS NOT NULL') { + $el.hide(); + return; + } + if (inputType === 'Date') { + if (_.includes(['=', '!=', '>', '>=', '<', '<='], op)) { + $el.crmDatepicker({time: (field.input_attrs && field.input_attrs.time) || false}); + } + } else if (_.includes(['=', '!=', 'IN', 'NOT IN', 'CONTAINS'], op) && (field.fk_entity || field.options || dataType === 'Boolean')) { + if (field.options) { + if (field.options === true) { + $el.addClass('loading'); + var waitForOptions = $interval(function() { + if (field.options !== true) { + $interval.cancel(waitForOptions); + $el.removeClass('loading').crmSelect2({data: getFieldOptions, multiple: multi}); + } + }, 200); + } + $el.attr('placeholder', ts('select')).crmSelect2({data: getFieldOptions, multiple: multi}); + } else if (field.fk_entity) { + $el.crmEntityRef({entity: field.fk_entity, select:{multiple: multi}}); + } else if (dataType === 'Boolean') { + $el.attr('placeholder', ts('- select -')).crmSelect2({allowClear: false, multiple: multi, placeholder: ts('- select -'), data: [ + // FIXME: it would be more correct to use real true/false booleans instead of numbers, but select2 doesn't seem to like them + {id: 1, text: ts('Yes')}, + {id: 0, text: ts('No')} + ]}); + } + } else if (dataType === 'Integer' && !multi) { + $el.attr('type', 'number'); + } + + function getFieldOptions() { + return {results: formatForSelect2(field.options, optionKey, 'label', ['description', 'color', 'icon'])}; + } + } + + // Copied from ng-list but applied conditionally if field is multi-valued + var parseList = function(viewValue) { + // If the viewValue is invalid (say required but empty) it will be `undefined` + if (_.isUndefined(viewValue)) return; + + if (!multi) { + return format === 'json' ? JSON.stringify(viewValue) : viewValue; + } + + var list = []; + + if (viewValue) { + _.each(viewValue.split(','), function(value) { + if (value) list.push(_.trim(value)); + }); + } + + return list; + }; + + // Copied from ng-list + ngModel.$parsers.push(parseList); + ngModel.$formatters.push(function(value) { + return _.isArray(value) ? value.join(', ') : (format === 'json' && value !== '' ? JSON.parse(value) : value); + }); + + // Copied from ng-list + ngModel.$isEmpty = function(value) { + return !value || !value.length; + }; + + scope.$watchCollection('data', function(data) { + destroyWidget(); + var field = searchMeta.parseExpr(data.field).field; + if (field) { + var optionKey = data.field.split(':')[1] || 'id'; + makeWidget(field, data.op, optionKey); + } + }); + } + }; + }); + +})(angular, CRM.$, CRM._); diff --git a/civicrm/ext/search/ang/search/saveSmartGroup.html b/civicrm/ext/search/ang/search/saveSmartGroup.html new file mode 100644 index 0000000000..a0caf4af2c --- /dev/null +++ b/civicrm/ext/search/ang/search/saveSmartGroup.html @@ -0,0 +1,26 @@ +<form id="bootstrap-theme"> + <div ng-controller="SaveSmartGroup"> + <input class="form-control" id="api-save-search-select-group" ng-model="model.id" crm-entityref="groupEntityRefParams" > + <label ng-show="!model.id">{{:: ts('Or') }}</label> + <input class="form-control" placeholder="Create new group" ng-model="model.title" ng-show="!model.id"> + <hr /> + <label>{{:: ts('Description:') }}</label> + <textarea class="form-control" ng-model="model.description"></textarea> + <label>{{:: ts('Group Type:') }}</label> + <div class="form-inline"> + <div class="checkbox" ng-repeat="option in groupFields.group_type.options track by option.id"> + <label> + <input type="checkbox" checklist-model="model.group_type" checklist-value="option.id"> + {{ option.label }} + </label> + </div> + </div> + <label>{{:: ts('Visibility:') }}</label> + <select class="form-control" ng-model="model.visibility" ng-options="item.id as item.label for item in groupFields.visibility.options track by item.id"></select> + <hr /> + <div class="buttons pull-right"> + <button type="button" ng-click="cancel()" class="btn btn-danger">{{:: ts('Cancel') }}</button> + <button ng-click="save()" class="btn btn-primary" ng-disabled="!model.title && !model.id">{{:: ts('Save') }}</button> + </div> + </div> +</form> diff --git a/civicrm/ext/search/css/search.css b/civicrm/ext/search/css/search.css new file mode 100644 index 0000000000..554f1f724b --- /dev/null +++ b/civicrm/ext/search/css/search.css @@ -0,0 +1,153 @@ +.crm-flex-box { + display: flex; +} +.crm-flex-box > * { + flex: 1; +} +.crm-flex-box > .crm-flex-2 { + flex: 2; +} +.crm-flex-box > .crm-flex-3 { + flex: 3; +} +.crm-flex-box > .crm-flex-4 { + flex: 4; +} +#bootstrap-theme #crm-search-results-page-size { + width: 5em; +} +#bootstrap-theme .crm-search-results { + min-height: 200px; +} +.crm-search-results thead th[ng-repeat] { + cursor: pointer; +} +.crm-search-results thead th[ng-repeat] > span { + cursor: move; +} + +#bootstrap-theme.crm-search fieldset { + padding: 6px; + border-top: 1px solid lightgrey; + margin-top: 10px; + margin-bottom: 10px; + position: relative; +} + +#bootstrap-theme.crm-search fieldset fieldset { + padding-top: 0; + border-left: 1px solid lightgrey; + border-right: 1px solid lightgrey; + border-bottom: 1px solid lightgrey; +} + +#bootstrap-theme.crm-search fieldset legend { + background-color: white; + font-size: 13px; + margin: 0; + width: auto; + border: 0 none; + padding: 2px 5px; + text-transform: capitalize; +} +#bootstrap-theme.crm-search fieldset > .btn-group { + position: absolute; + right: 0; + top: 11px; +} +#bootstrap-theme.crm-search fieldset > .btn-group .btn { + border: 0 none; +} + +#bootstrap-theme.crm-search fieldset div.api4-input { + margin-bottom: 10px; +} + +#bootstrap-theme.crm-search fieldset div.api4-input.ui-sortable-helper { + background-color: rgba(255, 255, 255, .9); +} + +#bootstrap-theme.crm-search fieldset div.api4-input.ui-sortable-helper { + background-color: rgba(255, 255, 255, .9); +} + +#bootstrap-theme.crm-search div.api4-input.form-inline label.form-control { + margin-right: 6px; +} +#bootstrap-theme.crm-search div.api4-input.form-inline label.form-control input[type=checkbox] { + margin: 0 2px 0 0; +} + +#bootstrap-theme.crm-search div.api4-input.form-inline label.form-control:not(.api4-option-selected) { + transition: none; + box-shadow: none; + -webkit-box-shadow: none; + -moz-box-shadow: none; + font-weight: normal; +} + +#bootstrap-theme.crm-search div.api4-input.form-inline .form-control label { + font-weight: normal; + position: relative; + top: -2px; +} +#bootstrap-theme.crm-search .api4-clause-fieldset fieldset { + float: right; + width: calc(100% - 58px); + margin-top: -8px; +} + +#bootstrap-theme.crm-search .api4-clause-fieldset.api4-sorting fieldset .api4-clause-group-sortable { + min-height: 3.5em; +} + +#bootstrap-theme.crm-search .api4-input-group { + display: inline-block; +} + +#bootstrap-theme.crm-search i.fa-arrows { + cursor: move; +} + +#bootstrap-theme.crm-search .api4-clause-badge { + width: 55px; + display: inline-block; + cursor: move; +} +#bootstrap-theme.crm-search .api4-clause-badge .badge { + opacity: .5; + position: relative; +} +#bootstrap-theme.crm-search .api4-clause-badge .caret { + margin: 0; +} +#bootstrap-theme.crm-search .api4-clause-badge .crm-i { + display: none; + padding: 0 6px; +} +#bootstrap-theme.crm-search .ui-sortable-helper .api4-clause-badge .badge span { + display: none; +} +#bootstrap-theme.crm-search .ui-sortable-helper .api4-clause-badge .crm-i { + display: inline-block; +} + +#bootstrap-theme.crm-search .api4-operator { + width: 110px; +} + +#bootstrap-theme.crm-search .api4-add-where-group-menu { + min-width: 80px; + background-color: rgba(186, 225, 251, 0.94); +} +#bootstrap-theme.crm-search .api4-add-where-group-menu a { + padding: 5px 10px; +} + +#bootstrap-theme.crm-search .btn.form-control { + height: 36px; +} + +#bootstrap-theme.crm-search th.crm-search-result-select { + padding-right: 10px; +} diff --git a/civicrm/ext/search/info.xml b/civicrm/ext/search/info.xml new file mode 100644 index 0000000000..3908cea976 --- /dev/null +++ b/civicrm/ext/search/info.xml @@ -0,0 +1,30 @@ +<?xml version="1.0"?> +<extension key="org.civicrm.search" type="module"> + <file>search</file> + <name>Search</name> + <description>Build searches for a wide variety of CiviCRM entities</description> + <license>AGPL-3.0</license> + <maintainer> + <author>Coleman Watts</author> + <email>coleman@civicrm.org</email> + </maintainer> + <urls> + <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url> + </urls> + <releaseDate>2020-07-08</releaseDate> + <version>1.0</version> + <tags> + <tag>mgmt:hidden</tag> + </tags> + <develStage>stable</develStage> + <compatibility> + <ver>5.28</ver> + </compatibility> + <comments>Distributed with CiviCRM core</comments> + <classloader> + <psr4 prefix="Civi\" path="Civi"/> + </classloader> + <civix> + <namespace>CRM/Search</namespace> + </civix> +</extension> diff --git a/civicrm/ext/search/search.civix.php b/civicrm/ext/search/search.civix.php new file mode 100644 index 0000000000..2a696e2cff --- /dev/null +++ b/civicrm/ext/search/search.civix.php @@ -0,0 +1,477 @@ +<?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_Search_ExtensionUtil { + const SHORT_NAME = "search"; + const LONG_NAME = "org.civicrm.search"; + const CLASS_PREFIX = "CRM_Search"; + + /** + * 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 = []) { + if (!array_key_exists('domain', $params)) { + $params['domain'] = [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_Search_ExtensionUtil as E; + +/** + * (Delegated) Implements hook_civicrm_config(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_config + */ +function _search_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 = [$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 https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_xmlMenu + */ +function _search_civix_civicrm_xmlMenu(&$files) { + foreach (_search_civix_glob(__DIR__ . '/xml/Menu/*.xml') as $file) { + $files[] = $file; + } +} + +/** + * Implements hook_civicrm_install(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_install + */ +function _search_civix_civicrm_install() { + _search_civix_civicrm_config(); + if ($upgrader = _search_civix_upgrader()) { + $upgrader->onInstall(); + } +} + +/** + * Implements hook_civicrm_postInstall(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_postInstall + */ +function _search_civix_civicrm_postInstall() { + _search_civix_civicrm_config(); + if ($upgrader = _search_civix_upgrader()) { + if (is_callable([$upgrader, 'onPostInstall'])) { + $upgrader->onPostInstall(); + } + } +} + +/** + * Implements hook_civicrm_uninstall(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_uninstall + */ +function _search_civix_civicrm_uninstall() { + _search_civix_civicrm_config(); + if ($upgrader = _search_civix_upgrader()) { + $upgrader->onUninstall(); + } +} + +/** + * (Delegated) Implements hook_civicrm_enable(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_enable + */ +function _search_civix_civicrm_enable() { + _search_civix_civicrm_config(); + if ($upgrader = _search_civix_upgrader()) { + if (is_callable([$upgrader, 'onEnable'])) { + $upgrader->onEnable(); + } + } +} + +/** + * (Delegated) Implements hook_civicrm_disable(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_disable + * @return mixed + */ +function _search_civix_civicrm_disable() { + _search_civix_civicrm_config(); + if ($upgrader = _search_civix_upgrader()) { + if (is_callable([$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 https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_upgrade + */ +function _search_civix_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) { + if ($upgrader = _search_civix_upgrader()) { + return $upgrader->onUpgrade($op, $queue); + } +} + +/** + * @return CRM_Search_Upgrader + */ +function _search_civix_upgrader() { + if (!file_exists(__DIR__ . '/CRM/Search/Upgrader.php')) { + return NULL; + } + else { + return CRM_Search_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 string $dir base dir + * @param string $pattern , glob pattern, eg "*.txt" + * + * @return array + */ +function _search_civix_find_files($dir, $pattern) { + if (is_callable(['CRM_Utils_File', 'findFiles'])) { + return CRM_Utils_File::findFiles($dir, $pattern); + } + + $todos = [$dir]; + $result = []; + while (!empty($todos)) { + $subdir = array_shift($todos); + foreach (_search_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 https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_managed + */ +function _search_civix_civicrm_managed(&$entities) { + $mgdFiles = _search_civix_find_files(__DIR__, '*.mgd.php'); + sort($mgdFiles); + foreach ($mgdFiles as $file) { + $es = include $file; + foreach ($es as $e) { + if (empty($e['module'])) { + $e['module'] = E::LONG_NAME; + } + if (empty($e['params']['version'])) { + $e['params']['version'] = '3'; + } + $entities[] = $e; + } + } +} + +/** + * (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 https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_caseTypes + */ +function _search_civix_civicrm_caseTypes(&$caseTypes) { + if (!is_dir(__DIR__ . '/xml/case')) { + return; + } + + foreach (_search_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)); + throw new CRM_Core_Exception($errorMessage); + } + $caseTypes[$name] = [ + '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 https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_angularModules + */ +function _search_civix_civicrm_angularModules(&$angularModules) { + if (!is_dir(__DIR__ . '/ang')) { + return; + } + + $files = _search_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; + } +} + +/** + * (Delegated) Implements hook_civicrm_themes(). + * + * Find any and return any files matching "*.theme.php" + */ +function _search_civix_civicrm_themes(&$themes) { + $files = _search_civix_glob(__DIR__ . '/*.theme.php'); + foreach ($files as $file) { + $themeMeta = include $file; + if (empty($themeMeta['name'])) { + $themeMeta['name'] = preg_replace(':\.theme\.php$:', '', basename($file)); + } + if (empty($themeMeta['ext'])) { + $themeMeta['ext'] = E::LONG_NAME; + } + $themes[$themeMeta['name']] = $themeMeta; + } +} + +/** + * 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 + */ +function _search_civix_glob($pattern) { + $result = glob($pattern); + return is_array($result) ? $result : []; +} + +/** + * 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) + * + * @return bool + */ +function _search_civix_insert_navigation_menu(&$menu, $path, $item) { + // If we are done going down the path, insert menu + if (empty($path)) { + $menu[] = [ + 'attributes' => array_merge([ + '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'] = []; + } + $found = _search_civix_insert_navigation_menu($entry['child'], implode('/', $path), $item); + } + } + return $found; + } +} + +/** + * (Delegated) Implements hook_civicrm_navigationMenu(). + */ +function _search_civix_navigationMenu(&$nodes) { + if (!is_callable(['CRM_Core_BAO_Navigation', 'fixNavigationMenu'])) { + _search_civix_fixNavigationMenu($nodes); + } +} + +/** + * Given a navigation menu, generate navIDs for any items which are + * missing them. + */ +function _search_civix_fixNavigationMenu(&$nodes) { + $maxNavID = 1; + array_walk_recursive($nodes, function($item, $key) use (&$maxNavID) { + if ($key === 'navID') { + $maxNavID = max($maxNavID, $item); + } + }); + _search_civix_fixNavigationMenuItems($nodes, $maxNavID, NULL); +} + +function _search_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'])) { + _search_civix_fixNavigationMenuItems($nodes[$origKey]['child'], $maxNavID, $nodes[$origKey]['attributes']['navID']); + } + } +} + +/** + * (Delegated) Implements hook_civicrm_alterSettingsFolders(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_alterSettingsFolders + */ +function _search_civix_civicrm_alterSettingsFolders(&$metaDataFolders = NULL) { + $settingsDir = __DIR__ . DIRECTORY_SEPARATOR . 'settings'; + if (!in_array($settingsDir, $metaDataFolders) && is_dir($settingsDir)) { + $metaDataFolders[] = $settingsDir; + } +} + +/** + * (Delegated) Implements hook_civicrm_entityTypes(). + * + * Find any *.entityType.php files, merge their content, and return. + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_entityTypes + */ +function _search_civix_civicrm_entityTypes(&$entityTypes) { + $entityTypes = array_merge($entityTypes, []); +} diff --git a/civicrm/ext/search/search.php b/civicrm/ext/search/search.php new file mode 100644 index 0000000000..7c7f9e1a83 --- /dev/null +++ b/civicrm/ext/search/search.php @@ -0,0 +1,128 @@ +<?php + +require_once 'search.civix.php'; + +/** + * Implements hook_civicrm_config(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_config/ + */ +function search_civicrm_config(&$config) { + _search_civix_civicrm_config($config); +} + +/** + * Implements hook_civicrm_xmlMenu(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_xmlMenu + */ +function search_civicrm_xmlMenu(&$files) { + _search_civix_civicrm_xmlMenu($files); +} + +/** + * Implements hook_civicrm_install(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_install + */ +function search_civicrm_install() { + _search_civix_civicrm_install(); +} + +/** + * Implements hook_civicrm_postInstall(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_postInstall + */ +function search_civicrm_postInstall() { + _search_civix_civicrm_postInstall(); +} + +/** + * Implements hook_civicrm_uninstall(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_uninstall + */ +function search_civicrm_uninstall() { + _search_civix_civicrm_uninstall(); +} + +/** + * Implements hook_civicrm_enable(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_enable + */ +function search_civicrm_enable() { + _search_civix_civicrm_enable(); +} + +/** + * Implements hook_civicrm_disable(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_disable + */ +function search_civicrm_disable() { + _search_civix_civicrm_disable(); +} + +/** + * Implements hook_civicrm_upgrade(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_upgrade + */ +function search_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) { + return _search_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 https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_managed + */ +function search_civicrm_managed(&$entities) { + _search_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 https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_angularModules + */ +function search_civicrm_angularModules(&$angularModules) { + _search_civix_civicrm_angularModules($angularModules); +} + +/** + * Implements hook_civicrm_alterSettingsFolders(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_alterSettingsFolders + */ +function search_civicrm_alterSettingsFolders(&$metaDataFolders = NULL) { + _search_civix_civicrm_alterSettingsFolders($metaDataFolders); +} + +/** + * Implements hook_civicrm_entityTypes(). + * + * Declare entity types provided by this module. + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_entityTypes + */ +function search_civicrm_entityTypes(&$entityTypes) { + _search_civix_civicrm_entityTypes($entityTypes); +} + +/** + * Implements hook_civicrm_thems(). + */ +function search_civicrm_themes(&$themes) { + _search_civix_civicrm_themes($themes); +} diff --git a/civicrm/ext/search/templates/CRM/Search/Page/Ang.tpl b/civicrm/ext/search/templates/CRM/Search/Page/Ang.tpl new file mode 100644 index 0000000000..e69de29bb2 diff --git a/civicrm/ext/search/xml/Menu/search.xml b/civicrm/ext/search/xml/Menu/search.xml new file mode 100644 index 0000000000..d57a0c52ce --- /dev/null +++ b/civicrm/ext/search/xml/Menu/search.xml @@ -0,0 +1,8 @@ +<?xml version="1.0"?> +<menu> + <item> + <path>civicrm/search</path> + <page_callback>CRM_Search_Page_Ang</page_callback> + <access_arguments>access CiviCRM</access_arguments> + </item> +</menu> diff --git a/civicrm/install/civicrm.php b/civicrm/install/civicrm.php index e688613403..5b6dcb263a 100644 --- a/civicrm/install/civicrm.php +++ b/civicrm/install/civicrm.php @@ -131,8 +131,6 @@ function civicrm_main(&$config) { function civicrm_source($dsn, $fileName, $lineMode = FALSE) { global $crmPath; - require_once "$crmPath/packages/DB.php"; - // CRM-19699 See also CRM_Core_DAO for PHP7 mysqli compatiblity. // Duplicated here because this is not using CRM_Core_DAO directly // and this function may be called directly from Drush. diff --git a/civicrm/js/model/crm.uf.js b/civicrm/js/model/crm.uf.js index 4f198c12a9..636304bad7 100644 --- a/civicrm/js/model/crm.uf.js +++ b/civicrm/js/model/crm.uf.js @@ -510,8 +510,8 @@ options: YESNO }, 'add_to_group_id': { - title: ts('Add new contacts to a Group?'), - help: ts('Select a group if you are using this profile for adding new contacts, AND you want the new contacts to be automatically assigned to a group.'), + title: ts('Add contacts to a group?'), + help: ts('Select a group if you want contacts to be automatically added to that group when the profile is submitted.'), type: 'Number' }, 'cancel_URL': { diff --git a/civicrm/packages/DB/DataObject.php b/civicrm/packages/DB/DataObject.php index 1df37c89bb..2e996dd402 100644 --- a/civicrm/packages/DB/DataObject.php +++ b/civicrm/packages/DB/DataObject.php @@ -2758,7 +2758,7 @@ class DB_DataObject extends DB_DataObject_Overload $action = strtolower(substr(trim($queryString),0,6)); // CRM-20445 ends - if (!empty($_DB_DATAOBJECT['CONFIG']['debug']) || defined('CIVICRM_DEBUG_LOG_QUERY')) { + if (!empty($_DB_DATAOBJECT['CONFIG']['debug']) || (defined('CIVICRM_DEBUG_LOG_QUERY') && CIVICRM_DEBUG_LOG_QUERY)) { $timeTaken = sprintf("%0.6f", microtime(TRUE) - $time); $alertLevel = $this->getAlertLevel($timeTaken); $message = "$alertLevel QUERY DONE IN $timeTaken seconds."; @@ -2774,7 +2774,7 @@ class DB_DataObject extends DB_DataObject_Overload else { echo $message .= " not quite sure why this query does not have more info"; } - if (defined('CIVICRM_DEBUG_LOG_QUERY')) { + if ((defined('CIVICRM_DEBUG_LOG_QUERY') && CIVICRM_DEBUG_LOG_QUERY)) { CRM_Core_Error::debug_log_message($message, FALSE, 'sql_log'); } else { diff --git a/civicrm/packages/VERSIONS.php b/civicrm/packages/VERSIONS.php index 13510eb9e3..f1d52e0518 100644 --- a/civicrm/packages/VERSIONS.php +++ b/civicrm/packages/VERSIONS.php @@ -107,7 +107,6 @@ * Contact_Vcard_Build 1.1.2 PHP 3 local changes * Contact_Vcard_Parse 1.32.0 PHP 3.0 * Date 1.4.7 BSD 3-cl. - * DB 1.9.3 PHP 3.0 * DB_DataObject 1.11.3 PHP 3 local changes * HTML_Common 1.2.5 PHP 3 * HTML_QuickForm 3.2.16 PHP 3 local changes, hierselect.php from a very old version (PHP 2) diff --git a/civicrm/release-notes.md b/civicrm/release-notes.md index b12dfb5a91..970335ce2d 100644 --- a/civicrm/release-notes.md +++ b/civicrm/release-notes.md @@ -15,6 +15,17 @@ Other resources for identifying changes are: * https://github.com/civicrm/civicrm-joomla * https://github.com/civicrm/civicrm-wordpress +## CiviCRM 5.30.0 + +Released October 7, 2020 + +- **[Synopsis](release-notes/5.30.0.md#synopsis)** +- **[Features](release-notes/5.30.0.md#features)** +- **[Bugs resolved](release-notes/5.30.0.md#bugs)** +- **[Miscellany](release-notes/5.30.0.md#misc)** +- **[Credits](release-notes/5.30.0.md#credits)** +- **[Feedback](release-notes/5.30.0.md#feedback)** + ## CiviCRM 5.29.1 Released September 15, 2020 diff --git a/civicrm/release-notes/5.30.0.md b/civicrm/release-notes/5.30.0.md new file mode 100644 index 0000000000..a1086516ce --- /dev/null +++ b/civicrm/release-notes/5.30.0.md @@ -0,0 +1,579 @@ +# CiviCRM 5.30.0 + +Released October 7, 2020 + +- **[Synopsis](#synopsis)** +- **[Features](#features)** +- **[Bugs resolved](#bugs)** +- **[Miscellany](#misc)** +- **[Credits](#credits)** +- **[Feedback](#feedback)** + +## <a name="synopsis"></a>Synopsis + +| *Does this version...?* | | +|:--------------------------------------------------------------- |:-------:| +| Fix security vulnerabilities? | no | +| **Change the database schema?** | **yes** | +| **Alter the API?** | **yes** | +| Require attention to configuration options? | no | +| **Fix problems installing or upgrading to a previous version?** | **yes** | +| **Introduce features?** | **yes** | +| **Fix bugs?** | **yes** | + +## <a name="features"></a>Features + +### Core CiviCRM + +- **APIv4 Explorer: Generate short cv code + ([18089](https://github.com/civicrm/civicrm-core/pull/18089))** + + Improves the APIv4 Explorer user interface by including cv code to run the API + from CLI. + +- **Optimise proximity custom search, by reducing addGeocodingData fn call + ([18248](https://github.com/civicrm/civicrm-core/pull/18248))** + + Improves performance of the proximity custom search. + +- **Support more table names for utf8mb4 conversions and database name + ([18249](https://github.com/civicrm/civicrm-core/pull/18249))** + + Improves the System.utf8conversion API so that one can specify table patterns + and databases. + +- **distmaker - Autogenerate civicrm-*-patchset.tar.gz + ([18229](https://github.com/civicrm/civicrm-core/pull/18229))** + + Adds a script to autogenerate `civicrm-*-patchset.tar.gz` instead of having to + manually generate it every release. + +- **Finish allowing use of SSL to connect to database + (Work Towards [dev/core#1926](https://lab.civicrm.org/dev/core/-/issues/1926): + [18095](https://github.com/civicrm/civicrm-core/pull/18095))** + + Code cleanup towards the goal of allowing SSL database connections. + +- **Profile settings - Add new contacts to a Group? is misleading + ([dev/core#1950](https://lab.civicrm.org/dev/core/-/issues/1950): + [18153](https://github.com/civicrm/civicrm-core/pull/18153))** + + Improves user experience by editing the help text and description for the "Add + new contacts to a Group?" setting. + +- **API4: Inconsistent behavior on Role ID + ([dev/event#37](https://lab.civicrm.org/dev/event/-/issues/37): + [18285](https://github.com/civicrm/civicrm-core/pull/18285))** + + Allows searching within arrays or serialized strings via APIv4 (and the new + Search extension). + +- **APIv4 - Fix output of CustomValue create/save/update + ([18195](https://github.com/civicrm/civicrm-core/pull/18195))** + + Improves APIv4 output when saving custom pseudo-entities so that it outputs + the values saved instead of `[is_error => 0, result => true]`. + +- **Add ability to enable query logging per process + ([18335](https://github.com/civicrm/civicrm-core/pull/18335))** + + Makes it possible to enable logging by process as opposed to all processes or + no processes. + +- **Expose field label to APIv4 and Search creator + ([18255](https://github.com/civicrm/civicrm-core/pull/18255) and + [18256](https://github.com/civicrm/civicrm-core/pull/18256))** + + Progress towards codifying the difference between a field's title and + label in the schema. + + These changes: + - expose labels to APIv4 + - switch to using labels in the new search extension. + - separates titles from labels in exportable fields. + - attempts to clarify the difference between titles and labels in the + getfields description as: + - title: "Technical name of field, shown in API and exports" + - label: "User-facing label, shown on most forms and displays" + +- **Metadata fix - phone_type_id, location_type_id, gender_id + ([18114](https://github.com/civicrm/civicrm-core/pull/18114))** + + Improves metadata for various fields to align with new title and label + standards. + +- **CRM_Utils_VersionCheck - respect force param + ([18183](https://github.com/civicrm/civicrm-core/pull/18183))** + + Makes the versionCheck job more consistent with other checks by respecting + APIv4's "force" setting to run even if disabled. + +- **Add Serialize key to payment_processor field for Event & Contribution Page + ([18110](https://github.com/civicrm/civicrm-core/pull/18110))** + + Use metatdata rather than code handling to save array of processors. This + makes it so that the DAO can handle arrays if it knows the format. + +### CiviContribute + +- **Shell Financial ACLs extension + ([18269](https://github.com/civicrm/civicrm-core/pull/18269))** + + Begins work to move Financial ACL code into an extension. + +- **Add Line Item v4 API (Work Towards + [dev/core#1980](https://lab.civicrm.org/dev/core/-/issues/1980): + [18275](https://github.com/civicrm/civicrm-core/pull/18275) and + [18293](https://github.com/civicrm/civicrm-core/pull/18293))** + + Extends permission handling for LineItem delete and LineItem ACLs from APIv3 to + to APIv4 by moving it to the `financialacls` extension. + +- **Book keeping Report : Add Time field for Transaction Date and Receive Date + field ([dev/report#47](https://lab.civicrm.org/dev/report/-/issues/47): + [18268](https://github.com/civicrm/civicrm-core/pull/18268))** + + Adds a time field to the "Transaction Date" and "Receive Date" filters on the + Bookkeeping report. + +### CiviEvent + +- **Wrap "not you" message in crmRegion + ([18236](https://github.com/civicrm/civicrm-core/pull/18236))** + + Wraps the "Not you, do you want to register for someone else" message in a + crmRegion so that it can be removed/replaced by the buildForm hook. + +### CiviMail + +- **Add new columns to mailing summary report + ([dev/core#1944](https://lab.civicrm.org/dev/core/-/issues/1944): + [18132](https://github.com/civicrm/civicrm-core/pull/18132))** + + Adds two new columns to the mailing summary report: "Sender Name" and "Sender + Email". + +- **Add disable_smarty option to MessageTemplate.send API + ([18118](https://github.com/civicrm/civicrm-core/pull/18118))** + + Improves the `MessageTemplate.send` API by adding a new optional parameter + "disable_smarty", when "disable_smarty" is set to true smarty is not applied + to the contents of the message. + +### WordPress Integration + +- **Switch WP over to new installer + ([dev/wordpress#37](https://lab.civicrm.org/dev/wordpress/-/issues/37): + [215](https://github.com/civicrm/civicrm-wordpress/pull/215))** + + Switch to new CiviCRM Installer. + +### Drupal Integration + +- **Allow for the setting of an additional constant CIVICRM_DRUSH_DSN + ([613](https://github.com/civicrm/civicrm-drupal/pull/613))** + + Allows sites to configure CIVICRM_DRUSH_DSN for drush to use to set up + database connections. + +## <a name="bugs"></a>Bugs resolved + +### Core CiviCRM + +- **Increase size of mysql field in export table for column with + CRM_Utils_Type::T_INT type + ([dev/core#1661](https://lab.civicrm.org/dev/core/-/issues/1661): + [17956](https://github.com/civicrm/civicrm-core/pull/17956))** + + Fixes some data too long for column errors when exporting. + +- **Improve robustness of api/class.api.php + ([18283](https://github.com/civicrm/civicrm-core/pull/18283))** + + Ensures that the API mode is set to "local" when an empty string is passed + as the server. + +- **add serialize data to actionschedule + ([18289](https://github.com/civicrm/civicrm-core/pull/18289))** + + Ensures one can create a scheduled reminder via the API that triggers on two + different events. + +- **API - Add upgrade-safe checks to ensure table exists before reading + ([18135](https://github.com/civicrm/civicrm-core/pull/18135))** + + Adds efficient checks into the API (v3 and v4) to ensure a DAO table + exists before trying to read from it. This prevents hard-crashes in the UI + when there are pending updates (or during the upgrade process itself), + +- **DB_DataObject - Read CIVICRM_DEBUG_LOG_QUERY correctly + ([305](https://github.com/civicrm/civicrm-packages/pull/305))** + + Makes The value of `CIVICRM_DEBUG_LOG_QUERY` TRUE-ish or FALSE-ish instead of + considering any value true. + +- **APIv4 Explorer - Fix possible undefined index + ([18093](https://github.com/civicrm/civicrm-core/pull/18093))** + +- **Option value cache key missing domain ID can result in wrong value retrieved + for domain ([dev/core#1971](https://lab.civicrm.org/dev/core/-/issues/1971): + [18252](https://github.com/civicrm/civicrm-core/pull/18252))** + +- **Upgrade screen - show success instead of error if already upgraded + ([18181](https://github.com/civicrm/civicrm-core/pull/18181))** + +- **trim white space when checking dedupe + ([18234](https://github.com/civicrm/civicrm-core/pull/18234))** + +- **Fix search extension excluded from tarball + ([18402](https://github.com/civicrm/civicrm-core/pull/18402))** + +- **Permissions problem on add or edit template + ([dev/core#2008](https://lab.civicrm.org/dev/core/-/issues/2008): + [18437](https://github.com/civicrm/civicrm-core/pull/18437))** + +- **Search ext: Aggregate field fixes + ([18520](https://github.com/civicrm/civicrm-core/pull/18520))** + +- **Some datepicker fields only allow choosing dates far in the future + ([dev/core#2052](https://lab.civicrm.org/dev/core/-/issues/2052): + [18524](https://github.com/civicrm/civicrm-core/pull/18524))** + +### CiviCase + +- **Fix js for case type status change on load + ([18254](https://github.com/civicrm/civicrm-core/pull/18254))** + +### CiviContribute + +- **Clean money for non-deductible amount + ([18300](https://github.com/civicrm/civicrm-core/pull/18300))** + + Ensures that the Contribution API cleans data entered into the "non-deductible + amount" field the same way it cleans the other money fields. + +- **Processors - remove gross_amount param from processors + ([18177](https://github.com/civicrm/civicrm-core/pull/18177))** + + The gross_amount param is returned by a bunch of processors but is not used so + this removes it. + +- **{contribution.receipt_date} token does not use any CiviCRM date formatter, + output in YYYY-MM-DD HH:MM:SS format and {contribution.receive_date} also uses + a non-standard format + ([18176](https://github.com/civicrm/civicrm-core/pull/18176))** + +- **Fix PHP notice on contribution page + ([18116](https://github.com/civicrm/civicrm-core/pull/18116))** + +- **E_NOTICE on contribution page widget tab + ([18189](https://github.com/civicrm/civicrm-core/pull/18189))** + +- **change payment instrument when pending payment paid through credit card + ([dev/core#912](https://lab.civicrm.org/dev/core/-/issues/912): + [18266](https://github.com/civicrm/civicrm-core/pull/18266))** + +- **PayPal Standard redirect fails on WordPress + ([dev/core#1997](https://lab.civicrm.org/dev/core/-/issues/1997): + [18525](https://github.com/civicrm/civicrm-core/pull/18525) and + [18534](https://github.com/civicrm/civicrm-core/pull/18534))** + +- **Invoice no longer displays amount paid/due + ([dev/core#2035](https://lab.civicrm.org/dev/core/-/issues/2035): + [18602](https://github.com/civicrm/civicrm-core/pull/18602) and + [18628](https://github.com/civicrm/civicrm-core/pull/18628))** + +### CiviEvent + +- **Allow the "Cancellation or transfer time limit (hours)" to be negative + ([dev/event#34](https://lab.civicrm.org/dev/event/-/issues/34): + [18067](https://github.com/civicrm/civicrm-core/pull/18067))** + + Ensures that entering a negative value into "Cancellation or transfer time + limit (hours)" saves correctly and works as expected. + +- **Batch Update via Profile does not supply data for editing when custom fields + created after participant added to event + ([18235](https://github.com/civicrm/civicrm-core/pull/18235))** + +- **E_WARNING when saving event fees admin page if there's no discounts set + ([18169](https://github.com/civicrm/civicrm-core/pull/18169))** + +### CiviMail + +- **Unexpected behavior from api.MailingEventSubscribe.create + (Work Towards [dev/mail#24](https://lab.civicrm.org/dev/mail/-/issues/24): + [18325](https://github.com/civicrm/civicrm-core/pull/18325))** + + Fixes labels for APIv3 `MailingEventSubscribe.create` so that they accurately + represent the behavior (unsubscribe v subscribe). + +- **Mass SMS: Limit group selection to mailing groups + ([18154](https://github.com/civicrm/civicrm-core/pull/18154))** + + Ensures that the fields "Include Groups" and "Exclude Groups" on the "New SMS" form + only list groups of the type "Mailing List". + +- **Help text bubble is empty and smarty errors when .hlp file is missing for + settings forms + ([dev/core#1920](https://lab.civicrm.org/dev/core/-/issues/1920): + [18648](https://github.com/civicrm/civicrm-core/pull/18648))** + +- **Fix error when creating new AB test mailing because domain_id is a required + field ([18685](https://github.com/civicrm/civicrm-core/pull/18685))** + +### CiviMember + +- **Fix regression on back-office membership renewal by credit card + ([18615](https://github.com/civicrm/civicrm-core/pull/18615))** + +### Drupal Integration + +- **Fix : Enable Drupal Watchdog Logging for Drupal 8 + ([dev/core#1943](https://lab.civicrm.org/dev/core/-/issues/1943): + [18115](https://github.com/civicrm/civicrm-core/pull/18115))** + + Ensure the setting `Enable Drupal Watchdog Logging` works on Drupal 8 sites. + +### WordPress Integration + +- **Check if $post defined before trying to use its field values. + ([18168](https://github.com/civicrm/civicrm-core/pull/18168))** + + This fixes php notices like `PHP Notice: Trying to get property 'post_name' + of non-object in <webroot + path>\wp-content\plugins\civicrm\civicrm\CRM\Utils\System\WordPress.php on + line 288` + +## <a name="misc"></a>Miscellany + +- **Fix regen and update civicrm_generated + ([18273](https://github.com/civicrm/civicrm-core/pull/18273))** + +- **Do not pass by reference for createRelatedMemberships + ([18243](https://github.com/civicrm/civicrm-core/pull/18243))** + +- **Typo in groups dropdown on scheduled reminders admin form + ([dev/core#1956](https://lab.civicrm.org/dev/core/-/issues/1956): + [18323](https://github.com/civicrm/civicrm-core/pull/18323))** + +- **Replace CRM_Utils_Array::value in contribution/confirm and bao/membership + ([18157](https://github.com/civicrm/civicrm-core/pull/18157))** + +- **Cleanup contactID variables in event/contribution register buildQuickForm + ([18208](https://github.com/civicrm/civicrm-core/pull/18208))** + +- **Use already determined value for contributionRecurID + ([18265](https://github.com/civicrm/civicrm-core/pull/18265))** + +- **Minor code cleanup to Order API + ([18217](https://github.com/civicrm/civicrm-core/pull/18217))** + +- **CodeGen - Add use statement to extensions DAO files + ([18094](https://github.com/civicrm/civicrm-core/pull/18094))** + +- **Use more appropriate money format function + ([18151](https://github.com/civicrm/civicrm-core/pull/18151))** + +- **Metadata fix in activity search + ([18216](https://github.com/civicrm/civicrm-core/pull/18216))** + +- **Replace deprecated Drupal 8 constants + ([18240](https://github.com/civicrm/civicrm-core/pull/18240))** + +- **Spelling mistake "separator" not "seperator" + ([18238](https://github.com/civicrm/civicrm-core/pull/18238))** + +- **Remove unused variables from repeatTransaction + ([18209](https://github.com/civicrm/civicrm-core/pull/18209))** + +- **Remove first_contribution key from repeattransaction + ([18197](https://github.com/civicrm/civicrm-core/pull/18197))** + +- **Remove unnecessary transformation of upgrade msg severity + ([18182](https://github.com/civicrm/civicrm-core/pull/18182))** + +- **Remove old dropped items from the schema xml + ([18244](https://github.com/civicrm/civicrm-core/pull/18244))** + +- **Remove 1 of 2 remaining places where relatedObjects is called in + completeOrder ([18257](https://github.com/civicrm/civicrm-core/pull/18257))** + +- **Remove last pass-by-reference in completeOrder signature + ([18258](https://github.com/civicrm/civicrm-core/pull/18258))** + +- **Remove legacy handling for 'fixing' line_item.entity_id + ([18155](https://github.com/civicrm/civicrm-core/pull/18155))** + +- **Remove deprecated code ids['userId'] + ([18156](https://github.com/civicrm/civicrm-core/pull/18156))** + +- **Remove 'hack' that overwrites result of searchColumns hook in mailings list + ([18237](https://github.com/civicrm/civicrm-core/pull/18237))** + +- **Remove unused variable + ([18108](https://github.com/civicrm/civicrm-core/pull/18108))** + +- **Remove handling for 4.2 DBs + ([612](https://github.com/civicrm/civicrm-drupal/pull/612))** + +- **Remove ref to logging time + ([18221](https://github.com/civicrm/civicrm-core/pull/18221))** + +- **Remove 2019 deprecated function + ([18242](https://github.com/civicrm/civicrm-core/pull/18242))** + +- **remove dropped tables for activity target and assignment + ([18280](https://github.com/civicrm/civicrm-core/pull/18280))** + +- **Deprecate 'trapException' in DAO::executeQuery + ([18138](https://github.com/civicrm/civicrm-core/pull/18138))** + +- **Deprecate/remove unused checkVersion() functions + ([18134](https://github.com/civicrm/civicrm-core/pull/18134))** + +- **[REF] Ensure that the form param _id is set when adding a new group w… + ([18250](https://github.com/civicrm/civicrm-core/pull/18250))** + +- **[REF] Update recent replacement for CRM_Utils_Array::value + ([18172](https://github.com/civicrm/civicrm-core/pull/18172) and + [18453](https://github.com/civicrm/civicrm-core/pull/18453))** + +- **[REF] Remove usages of CRM_Utils_Money::format that pass in a blank s… + ([18142](https://github.com/civicrm/civicrm-core/pull/18142))** + +- **[REF] Move isSSLDSN() function to avoid potential problems + ([18131](https://github.com/civicrm/civicrm-core/pull/18131))** + +- **(REF) CRM_Core_Region - Remove unused bits + ([18139](https://github.com/civicrm/civicrm-core/pull/18139))** + +- **[REF] Move Auto DSN Switching into a core function + ([18270](https://github.com/civicrm/civicrm-core/pull/18270))** + +- **[REF] Remove DB Package as it is now deployed using composer + ([302](https://github.com/civicrm/civicrm-packages/pull/302))** + +- **[REF] Deploy Pear DB package using composer + ([18027](https://github.com/civicrm/civicrm-core/pull/18027) and + [18473](https://github.com/civicrm/civicrm-core/pull/18473))** + +- **[REF] Update subtypeInfo function to leverage getAllContactTypes + ([17934](https://github.com/civicrm/civicrm-core/pull/17934))** + +- **[REF] Fix metadata label for contribution_page_id + ([18047](https://github.com/civicrm/civicrm-core/pull/18047))** + +- **REF Remove unused passbyreference and var from ipn_process_transaction + ([18311](https://github.com/civicrm/civicrm-core/pull/18311))** + +- **[REF] Clean up return variables on updateContributionStatus, + updatePendingOnlineContribution + ([18303](https://github.com/civicrm/civicrm-core/pull/18303))** + +- **[REF] Move function to delete merged contacts to the Merger class and + include all instances of the pair + ([17980](https://github.com/civicrm/civicrm-core/pull/17980))** + +- **[REF] Remove most interaction with $objects in completeOrder + ([18315](https://github.com/civicrm/civicrm-core/pull/18315))** + +- **[REF] Fix financial item allocation of negative payments against completed + payments ([17810](https://github.com/civicrm/civicrm-core/pull/17810))** + +- **[REF] Separate and move line-item specific portion of checkTaxAmount to + LineItem api ([18294](https://github.com/civicrm/civicrm-core/pull/18294))** + +- **[REF] [test] Slight increase in test cover & make functions more re-usable + ([18291](https://github.com/civicrm/civicrm-core/pull/18291))** + +- **[REF] simplify retrieval of existing membership on membership renewal form + ([18296](https://github.com/civicrm/civicrm-core/pull/18296))** + +- **[REF] Extract function to get locations to merge, rename 'operation' … + ([17991](https://github.com/civicrm/civicrm-core/pull/17991))** + +- **[REF] Deprecate unused SQL temptable functions + ([18171](https://github.com/civicrm/civicrm-core/pull/18171))** + +- **Minor cleanup in test class + ([18170](https://github.com/civicrm/civicrm-core/pull/18170))** + +- **Add line item acl tests + ([18274](https://github.com/civicrm/civicrm-core/pull/18274))** + +- **Fix test set up to call Order.create to create the correct line items + ([18279](https://github.com/civicrm/civicrm-core/pull/18279))** + +- **Test & remove handling for max_related in renewal form + ([18295](https://github.com/civicrm/civicrm-core/pull/18295))** + +- **Add test to check, remove unnecessary lines + ([18292](https://github.com/civicrm/civicrm-core/pull/18292))** + +- **[Test framework] - Remove test for deprecated way of calling hook invoke + ([18136](https://github.com/civicrm/civicrm-core/pull/18136))** + +- **(NFC) Update various DAO checksums + ([18184](https://github.com/civicrm/civicrm-core/pull/18184))** + +- **[NFC] Add code comments about hidden but still used buttons + ([18119](https://github.com/civicrm/civicrm-core/pull/18119))** + +- **[NFC] [Test] Initial testing on event payment forms. + ([18112](https://github.com/civicrm/civicrm-core/pull/18112))** + +- **[NFC] Update locale over-ride documentation to mention that it is mos… + ([17919](https://github.com/civicrm/civicrm-core/pull/17919))** + +- **(NFC) Add some tests to the `resources` group + ([18211](https://github.com/civicrm/civicrm-core/pull/18211))** + +- **[NFC] Some DAO checksums not updated + ([18239](https://github.com/civicrm/civicrm-core/pull/18239))** + +- **[NFC] Array formatting + ([18109](https://github.com/civicrm/civicrm-core/pull/18109))** + +- **[NFC] IDE cleanup - arrays, single quotes + ([614](https://github.com/civicrm/civicrm-drupal/pull/614))** + +- **[NFC] Update Event DAO Checksum + ([18361](https://github.com/civicrm/civicrm-core/pull/18361))** + +- **[NFC] Fix dates in RelationshipTest + ([18530](https://github.com/civicrm/civicrm-core/pull/18530))** + +- **api_v3_MultilingualTest - Skip Cxn entities + ([18547](https://github.com/civicrm/civicrm-core/pull/18547))** + +- **[NFC] Update Checksum on CustomField DAO + ([18689](https://github.com/civicrm/civicrm-core/pull/18689))** + +## <a name="credits"></a>Credits + +This release was developed by the following code authors: + +AGH Strategies - Alice Frumin, Andrew Hunt; Agileware - Justin Freeman, Pengyi +Zhang; Circle Interactive - Pradeep Nayak; CiviCRM - Coleman Watts, Tim Otten; +CiviDesk - Sunil Pawar, Yashodha Chaku; Dave D; Flinders University of South +Australia - Tom Anderson; JMA Consulting - Monish Deb, Seamus Lee; John +Kingsnorth; Lemniscus - Noah Miller; Lighthouse Consulting and Design - Brian +Shaughnessy; Megaphone Technology Consulting - Jon Goldberg; MillerTech - Chamil +Wijesooriya; MJW Consulting - Matthew Wire; Squiffle Consulting - Aidan +Saunders; Wikimedia Foundation - Eileen McNaughton, Maggie Epps; Wildsight - +Lars Sanders-Green + +Most authors also reviewed code for this release; in addition, the following +reviewers contributed their comments: + +Artful Robot - Rich Lott; Christian Wach; Circle Interactive - Martin Castle; +Dave D; Fuzion - Jitendra Purohit; Greenpeace Central and Eastern Europe - +Patrick Figel; MJCO - Mikey O'Toole; Semper IT - Karin Gerritsen; Squiffle +Consulting - Aidan Saunders; Tadpole Collective - Kevin Cristiano + +## <a name="feedback"></a>Feedback + +These release notes are edited by Alice Frumin and Andrew Hunt. If you'd like +to provide feedback on them, please log in to https://chat.civicrm.org/civicrm +and contact `@agh1`. diff --git a/civicrm/settings/Mailing.setting.php b/civicrm/settings/Mailing.setting.php index cb0b5ec0b6..bfcc358074 100644 --- a/civicrm/settings/Mailing.setting.php +++ b/civicrm/settings/Mailing.setting.php @@ -386,6 +386,6 @@ return [ 'is_domain' => 1, 'is_contact' => 0, 'description' => ts('The frequency that CiviMail updates its sent mail database.'), - 'help_text' => 'CiviMail records email sent at the frequency you specify. If you set it to 1, it will update the database every time it sends an email. This ensures that emails are not resent if the batch job fails, but this may cause a performance hit, particularly for large jobs.', + 'help_text' => NULL, ], ]; diff --git a/civicrm/setup/plugins/checkRequirements/CheckDbWellFormed.civi-setup.php b/civicrm/setup/plugins/checkRequirements/CheckDbWellFormed.civi-setup.php index d48925c3de..4f09d7c832 100644 --- a/civicrm/setup/plugins/checkRequirements/CheckDbWellFormed.civi-setup.php +++ b/civicrm/setup/plugins/checkRequirements/CheckDbWellFormed.civi-setup.php @@ -23,19 +23,24 @@ if (!defined('CIVI_SETUP')) { $expectedKeys = array('server', 'username', 'password', 'database'); sort($expectedKeys); if ($keys !== $expectedKeys) { - $e->addError('database', $dbField, sprintf("The database credentials for \"%s\" should be specified as (%s) not (%s)", - $dbField, - implode(',', $expectedKeys), - implode(',', $keys) - )); - $errors++; + // if it failed it might be because of the optional ssl parameters + $expectedKeys[] = 'ssl_params'; + sort($expectedKeys); + if ($keys !== $expectedKeys) { + $e->addError('database', $dbField, sprintf("The database credentials for \"%s\" should be specified as (%s) not (%s)", + $dbField, + implode(',', $expectedKeys), + implode(',', $keys) + )); + $errors++; + } } foreach ($db as $k => $v) { if ($k === 'password' && empty($v)) { $e->addWarning('database', "$dbField.$k", "The property \"$dbField.$k\" is blank. This may be correct in some controlled environments; it could also be a mistake or a symptom of an insecure configuration."); } - elseif (!is_scalar($v)) { + elseif ($k !== 'ssl_params' && !is_scalar($v)) { $e->addError('database', "$dbField.$k", "The property \"$dbField.$k\" is not well-formed."); $errors++; } diff --git a/civicrm/setup/plugins/checkRequirements/CoreRequirementsAdapter.civi-setup.php b/civicrm/setup/plugins/checkRequirements/CoreRequirementsAdapter.civi-setup.php index f0d15507c3..4e4e051f31 100644 --- a/civicrm/setup/plugins/checkRequirements/CoreRequirementsAdapter.civi-setup.php +++ b/civicrm/setup/plugins/checkRequirements/CoreRequirementsAdapter.civi-setup.php @@ -29,6 +29,7 @@ if (!defined('CIVI_SETUP')) { 'username' => $model->db['username'], 'password' => $model->db['password'], 'database' => $model->db['database'], + 'ssl_params' => $model->db['ssl_params'] ?? NULL, )); _corereqadapter_addMessages($e, 'database', $dbMsgs); }); diff --git a/civicrm/setup/plugins/installDatabase/FlushDrupal8.civi-setup.php b/civicrm/setup/plugins/installDatabase/FlushDrupal8.civi-setup.php index c1d57e2f49..0b25fd3120 100644 --- a/civicrm/setup/plugins/installDatabase/FlushDrupal8.civi-setup.php +++ b/civicrm/setup/plugins/installDatabase/FlushDrupal8.civi-setup.php @@ -51,6 +51,6 @@ function civicrm_install_set_drupal8_perms() { ]); } $perms = array_intersect($perms, $allPerms); - user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, $perms); - user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, $perms); + user_role_grant_permissions(\Drupal\user\RoleInterface::AUTHENTICATED_ID, $perms); + user_role_grant_permissions(\Drupal\user\RoleInterface::ANONYMOUS_ID, $perms); } diff --git a/civicrm/setup/plugins/installFiles/InstallSettingsFile.civi-setup.php b/civicrm/setup/plugins/installFiles/InstallSettingsFile.civi-setup.php index 3581eb3bed..29aedb2b07 100644 --- a/civicrm/setup/plugins/installFiles/InstallSettingsFile.civi-setup.php +++ b/civicrm/setup/plugins/installFiles/InstallSettingsFile.civi-setup.php @@ -59,6 +59,13 @@ if (!defined('CIVI_SETUP')) { $params['dbPass'] = addslashes($m->db['password']); $params['dbHost'] = addslashes($m->db['server']); $params['dbName'] = addslashes($m->db['database']); + // The '&' prefix is awkward, but we don't know what's already in the file. + // At the time of writing, it has ?new_link=true. If that is removed, + // then need to update this. + // The PHP_QUERY_RFC3986 is important because PEAR::DB will interpret plus + // signs as a reference to its old DSN format and mangle the DSN, so we + // need to use %20 for spaces. + $params['dbSSL'] = empty($m->db['ssl_params']) ? '' : addslashes('&' . http_build_query($m->db['ssl_params'], '', '&', PHP_QUERY_RFC3986)); $params['cms'] = addslashes($m->cms); $params['CMSdbUser'] = addslashes($m->cmsDb['username']); $params['CMSdbPass'] = addslashes($m->cmsDb['password']); diff --git a/civicrm/setup/src/Setup/DbUtil.php b/civicrm/setup/src/Setup/DbUtil.php index 054fe8d731..87849057c6 100644 --- a/civicrm/setup/src/Setup/DbUtil.php +++ b/civicrm/setup/src/Setup/DbUtil.php @@ -12,14 +12,16 @@ class DbUtil { public static function parseDsn($dsn) { $parsed = parse_url($dsn); return array( - 'server' => self::encodeHostPort($parsed['host'], $parsed['port']), + 'server' => self::encodeHostPort($parsed['host'], $parsed['port'] ?? NULL), 'username' => $parsed['user'] ?: NULL, 'password' => $parsed['pass'] ?: NULL, 'database' => $parsed['path'] ? ltrim($parsed['path'], '/') : NULL, + 'ssl_params' => self::parseSSL($parsed['query'] ?? NULL), ); } /** + * @todo Is this used anywhere? It doesn't support SSL as-is. * Convert an datasource from array notation to URL notation. * * @param array $db @@ -40,7 +42,25 @@ class DbUtil { */ public static function softConnect($db) { list($host, $port) = self::decodeHostPort($db['server']); - $conn = @mysqli_connect($host, $db['username'], $db['password'], $db['database'], $port); + if (empty($db['ssl_params'])) { + $conn = @mysqli_connect($host, $db['username'], $db['password'], $db['database'], $port); + } + else { + $conn = NULL; + $init = mysqli_init(); + mysqli_ssl_set( + $init, + $db['ssl_params']['key'] ?? NULL, + $db['ssl_params']['cert'] ?? NULL, + $db['ssl_params']['ca'] ?? NULL, + $db['ssl_params']['capath'] ?? NULL, + $db['ssl_params']['cipher'] ?? NULL + ); + // @todo socket parameter, but if you're using sockets do you need SSL? + if (@mysqli_real_connect($init, $host, $db['username'], $db['password'], $db['database'], $port, NULL, MYSQLI_CLIENT_SSL)) { + $conn = $init; + } + } return $conn; } @@ -94,6 +114,33 @@ class DbUtil { return $host . ($port ? (':' . $port) : ''); } + /** + * For SSL you can have client certificates, which has some required and + * optional parameters, or you can have anonymous SSL, which just requires + * some indication that you want that. + * + * @param string $query_string + * @return array + */ + public static function parseSSL($query_string) { + if (empty($query_string)) { + return []; + } + parse_str($query_string, $parsed_query); + $sensible_parameters = [ + // ssl=1 alone means no client certificate - it's not a real mysqli option + 'ssl' => NULL, + 'key' => NULL, + 'cert' => NULL, + 'ca' => NULL, + 'capath' => NULL, + 'cipher' => NULL, + ]; + // Only want to include a param if it's in our list of sensibles, e.g. + // we don't want new_link=true. + return array_intersect_key($parsed_query, $sensible_parameters); + } + /** * @param array $db * @param string $SQLcontent diff --git a/civicrm/sql/civicrm.mysql b/civicrm/sql/civicrm.mysql index cbcb27da70..e4aebd6091 100644 --- a/civicrm/sql/civicrm.mysql +++ b/civicrm/sql/civicrm.mysql @@ -4808,7 +4808,7 @@ CREATE TABLE `civicrm_event` ( `requires_approval` tinyint COMMENT 'Whether participants require approval before they can finish registering.', `expiration_time` int unsigned COMMENT 'Expire pending but unconfirmed registrations after this many hours.', `allow_selfcancelxfer` tinyint DEFAULT 0 COMMENT 'Allow self service cancellation or transfer for event?', - `selfcancelxfer_time` int unsigned DEFAULT 0 COMMENT 'Number of hours prior to event start date to allow self-service cancellation or transfer.', + `selfcancelxfer_time` int DEFAULT 0 COMMENT 'Number of hours prior to event start date to allow self-service cancellation or transfer.', `waitlist_text` text COMMENT 'Text to display when the event is full, but participants can signup for a waitlist.', `approval_req_text` text COMMENT 'Text to display when the approval is required to complete registration for an event.', `is_template` tinyint NOT NULL DEFAULT 0 COMMENT 'whether the event has template', diff --git a/civicrm/sql/civicrm_data.mysql b/civicrm/sql/civicrm_data.mysql index 1aab8338dd..b565cd5f8b 100644 --- a/civicrm/sql/civicrm_data.mysql +++ b/civicrm/sql/civicrm_data.mysql @@ -9408,29 +9408,28 @@ INSERT INTO civicrm_msg_template <td style="text-align:right;white-space: nowrap"><b><font size="1">{ts 1=$defaultCurrency}TOTAL %1{/ts}</font></b></td> <td style="text-align:right;"><font size="1">{$amount|crmMoney:$currency}</font></td> </tr> - {if $amountDue != 0} - <tr> - <td colspan="3"></td> - <td style="text-align:right;white-space: nowrap"><font size="1"> - {if $contribution_status_id == $refundedStatusId} - {ts}Amount Credited{/ts} - {else} - {ts}Amount Paid{/ts} - {/if} - </font> - </td> - <td style="text-align:right;"><font size="1">{$amountPaid|crmMoney:$currency}</font></td> - </tr> - <tr> - <td colspan="3"></td> - <td colspan="2"><hr></hr></td> - </tr> - <tr> - <td colspan="3"></td> - <td style="text-align:right;white-space: nowrap" ><b><font size="1">{ts}AMOUNT DUE:{/ts}</font></b></td> - <td style="text-align:right;"><b><font size="1">{$amountDue|crmMoney:$currency}</font></b></td> - </tr> - {/if} + <tr> + <td colspan="3"></td> + <td style="text-align:right;white-space: nowrap"><font size="1"> + {if $contribution_status_id == $refundedStatusId} + {ts}Amount Credited{/ts} + {else} + {ts}Amount Paid{/ts} + {/if} + </font> + </td> + <td style="text-align:right;"><font size="1">{$amountPaid|crmMoney:$currency}</font></td> + </tr> + <tr> + <td colspan="3"></td> + <td colspan="2"><hr></hr></td> + </tr> + <tr> + <td colspan="3"></td> + <td style="text-align:right;white-space: nowrap" ><b><font size="1">{ts}AMOUNT DUE:{/ts}</font></b></td> + <td style="text-align:right;"><b><font size="1">{$amountDue|crmMoney:$currency}</font></b></td> + </tr> + <br/><br/><br/> <tr> <td colspan="5"></td> @@ -9852,29 +9851,28 @@ INSERT INTO civicrm_msg_template <td style="text-align:right;white-space: nowrap"><b><font size="1">{ts 1=$defaultCurrency}TOTAL %1{/ts}</font></b></td> <td style="text-align:right;"><font size="1">{$amount|crmMoney:$currency}</font></td> </tr> - {if $amountDue != 0} - <tr> - <td colspan="3"></td> - <td style="text-align:right;white-space: nowrap"><font size="1"> - {if $contribution_status_id == $refundedStatusId} - {ts}Amount Credited{/ts} - {else} - {ts}Amount Paid{/ts} - {/if} - </font> - </td> - <td style="text-align:right;"><font size="1">{$amountPaid|crmMoney:$currency}</font></td> - </tr> - <tr> - <td colspan="3"></td> - <td colspan="2"><hr></hr></td> - </tr> - <tr> - <td colspan="3"></td> - <td style="text-align:right;white-space: nowrap" ><b><font size="1">{ts}AMOUNT DUE:{/ts}</font></b></td> - <td style="text-align:right;"><b><font size="1">{$amountDue|crmMoney:$currency}</font></b></td> - </tr> - {/if} + <tr> + <td colspan="3"></td> + <td style="text-align:right;white-space: nowrap"><font size="1"> + {if $contribution_status_id == $refundedStatusId} + {ts}Amount Credited{/ts} + {else} + {ts}Amount Paid{/ts} + {/if} + </font> + </td> + <td style="text-align:right;"><font size="1">{$amountPaid|crmMoney:$currency}</font></td> + </tr> + <tr> + <td colspan="3"></td> + <td colspan="2"><hr></hr></td> + </tr> + <tr> + <td colspan="3"></td> + <td style="text-align:right;white-space: nowrap" ><b><font size="1">{ts}AMOUNT DUE:{/ts}</font></b></td> + <td style="text-align:right;"><b><font size="1">{$amountDue|crmMoney:$currency}</font></b></td> + </tr> + <br/><br/><br/> <tr> <td colspan="5"></td> @@ -14393,7 +14391,7 @@ You were registered by: {$payer.name} {/if} {if $event.allow_selfcancelxfer } -{ts 1=$event.selfcancelxfer_time}You may transfer your registration to another participant or cancel your registration up to %1 hours before the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if} +{ts 1=$selfcancelxfer_time 2=$selfservice_preposition}You may transfer your registration to another participant or cancel your registration up to %1 hours %2 the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if} {capture assign=selfService}{crmURL p=\'civicrm/event/selfsvcupdate\' q="reset=1&pid=`$participant.id`&{contact.checksum}" h=0 a=1 fe=1}{/capture} {ts}Transfer or cancel your registration:{/ts} {$selfService} {/if} @@ -14896,7 +14894,7 @@ You were registered by: {$payer.name} {if $event.allow_selfcancelxfer } <tr> <td colspan="2" {$valueStyle}> - {ts 1=$event.selfcancelxfer_time}You may transfer your registration to another participant or cancel your registration up to %1 hours before the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}<br /> + {ts 1=$selfcancelxfer_time 2=$selfservice_preposition}You may transfer your registration to another participant or cancel your registration up to %1 hours %2 the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}<br /> {capture assign=selfService}{crmURL p=\'civicrm/event/selfsvcupdate\' q="reset=1&pid=`$participant.id`&{contact.checksum}" h=0 a=1 fe=1}{/capture} <a href="{$selfService}">{ts}Click here to transfer or cancel your registration.{/ts}</a> </td> @@ -15202,7 +15200,7 @@ You were registered by: {$payer.name} {/if} {if $event.allow_selfcancelxfer } -{ts 1=$event.selfcancelxfer_time}You may transfer your registration to another participant or cancel your registration up to %1 hours before the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if} +{ts 1=$selfcancelxfer_time 2=$selfservice_preposition}You may transfer your registration to another participant or cancel your registration up to %1 hours %2 the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if} {capture assign=selfService}{crmURL p=\'civicrm/event/selfsvcupdate\' q="reset=1&pid=`$participant.id`&{contact.checksum}" h=0 a=1 fe=1}{/capture} {ts}Transfer or cancel your registration:{/ts} {$selfService} {/if} @@ -15705,7 +15703,7 @@ You were registered by: {$payer.name} {if $event.allow_selfcancelxfer } <tr> <td colspan="2" {$valueStyle}> - {ts 1=$event.selfcancelxfer_time}You may transfer your registration to another participant or cancel your registration up to %1 hours before the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}<br /> + {ts 1=$selfcancelxfer_time 2=$selfservice_preposition}You may transfer your registration to another participant or cancel your registration up to %1 hours %2 the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}<br /> {capture assign=selfService}{crmURL p=\'civicrm/event/selfsvcupdate\' q="reset=1&pid=`$participant.id`&{contact.checksum}" h=0 a=1 fe=1}{/capture} <a href="{$selfService}">{ts}Click here to transfer or cancel your registration.{/ts}</a> </td> @@ -16609,7 +16607,7 @@ Click this link to go to a web page where you can confirm your registration onli {$confirmUrl} {/if} {if $event.allow_selfcancelxfer } -{ts 1=$event.selfcancelxfer_time}You may transfer your registration to another participant or cancel your registration up to %1 hours before the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if} +{ts 1=$selfcancelxfer_time 2=$selfservice_preposition}You may transfer your registration to another participant or cancel your registration up to %1 hours %2 the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if} {capture assign=selfService}{crmURL p=\'civicrm/event/selfsvcupdate\' q="reset=1&pid=`$participant.id`&{contact.checksum}" h=0 a=1 fe=1}{/capture} {ts}Transfer or cancel your registration:{/ts} {$selfService} {/if} @@ -16845,7 +16843,7 @@ Click this link to go to a web page where you can confirm your registration onli {if $event.allow_selfcancelxfer } <tr> <td colspan="2" {$valueStyle}> - {ts 1=$event.selfcancelxfer_time}You may transfer your registration to another participant or cancel your registration up to %1 hours before the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}<br /> + {ts 1=$selfcancelxfer_time 2=$selfservice_preposition}You may transfer your registration to another participant or cancel your registration up to %1 hours %2 the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}<br /> {capture assign=selfService}{crmURL p=\'civicrm/event/selfsvcupdate\' q="reset=1&pid=`$participant.id`&{contact.checksum}" h=0 a=1 fe=1}{/capture} <a href="{$selfService}">{ts}Click here to transfer or cancel your registration.{/ts}</a> </td> @@ -16879,7 +16877,7 @@ Click this link to go to a web page where you can confirm your registration onli {$confirmUrl} {/if} {if $event.allow_selfcancelxfer } -{ts 1=$event.selfcancelxfer_time}You may transfer your registration to another participant or cancel your registration up to %1 hours before the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if} +{ts 1=$selfcancelxfer_time 2=$selfservice_preposition}You may transfer your registration to another participant or cancel your registration up to %1 hours %2 the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if} {capture assign=selfService}{crmURL p=\'civicrm/event/selfsvcupdate\' q="reset=1&pid=`$participant.id`&{contact.checksum}" h=0 a=1 fe=1}{/capture} {ts}Transfer or cancel your registration:{/ts} {$selfService} {/if} @@ -17115,7 +17113,7 @@ Click this link to go to a web page where you can confirm your registration onli {if $event.allow_selfcancelxfer } <tr> <td colspan="2" {$valueStyle}> - {ts 1=$event.selfcancelxfer_time}You may transfer your registration to another participant or cancel your registration up to %1 hours before the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}<br /> + {ts 1=$selfcancelxfer_time 2=$selfservice_preposition}You may transfer your registration to another participant or cancel your registration up to %1 hours %2 the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}<br /> {capture assign=selfService}{crmURL p=\'civicrm/event/selfsvcupdate\' q="reset=1&pid=`$participant.id`&{contact.checksum}" h=0 a=1 fe=1}{/capture} <a href="{$selfService}">{ts}Click here to transfer or cancel your registration.{/ts}</a> </td> @@ -23093,6 +23091,7 @@ VALUES -- do not try this at home folks. INSERT IGNORE INTO civicrm_extension (type, full_name, name, label, file, is_active) VALUES ('module', 'sequentialcreditnotes', 'Sequential credit notes', 'Sequential credit notes', 'sequentialcreditnotes', 1); INSERT IGNORE INTO civicrm_extension (type, full_name, name, label, file, is_active) VALUES ('module', 'eventcart', 'Event cart', 'Event cart', 'eventcart', 1); +INSERT IGNORE INTO civicrm_extension (type, full_name, name, label, file, is_active) VALUES ('module', 'financialacls', 'Financial ACLs', 'Financial ACLs', 'financialacls', 1); -- +--------------------------------------------------------------------+ -- | Copyright CiviCRM LLC. All rights reserved. | -- | | @@ -23897,4 +23896,4 @@ INSERT INTO `civicrm_report_instance` ( `domain_id`, `title`, `report_id`, `description`, `permission`, `form_values`) VALUES ( @domainID, 'Survey Details', 'survey/detail', 'Detailed report for canvassing, phone-banking, walk lists or other surveys.', 'access CiviReport', 'a:39:{s:6:"fields";a:2:{s:9:"sort_name";s:1:"1";s:6:"result";s:1:"1";}s:22:"assignee_contact_id_op";s:2:"eq";s:25:"assignee_contact_id_value";s:0:"";s:12:"sort_name_op";s:3:"has";s:15:"sort_name_value";s:0:"";s:17:"street_number_min";s:0:"";s:17:"street_number_max";s:0:"";s:16:"street_number_op";s:3:"lte";s:19:"street_number_value";s:0:"";s:14:"street_name_op";s:3:"has";s:17:"street_name_value";s:0:"";s:15:"postal_code_min";s:0:"";s:15:"postal_code_max";s:0:"";s:14:"postal_code_op";s:3:"lte";s:17:"postal_code_value";s:0:"";s:7:"city_op";s:3:"has";s:10:"city_value";s:0:"";s:20:"state_province_id_op";s:2:"in";s:23:"state_province_id_value";a:0:{}s:13:"country_id_op";s:2:"in";s:16:"country_id_value";a:0:{}s:12:"survey_id_op";s:2:"in";s:15:"survey_id_value";a:0:{}s:12:"status_id_op";s:2:"eq";s:15:"status_id_value";s:1:"1";s:11:"custom_1_op";s:2:"in";s:14:"custom_1_value";a:0:{}s:11:"custom_2_op";s:2:"in";s:14:"custom_2_value";a:0:{}s:17:"custom_3_relative";s:1:"0";s:13:"custom_3_from";s:0:"";s:11:"custom_3_to";s:0:"";s:11:"description";s:75:"Detailed report for canvassing, phone-banking, walk lists or other surveys.";s:13:"email_subject";s:0:"";s:8:"email_to";s:0:"";s:8:"email_cc";s:0:"";s:10:"permission";s:17:"access CiviReport";s:6:"groups";s:0:"";s:9:"domain_id";i:1;}'); -UPDATE civicrm_domain SET version = '5.29.1'; +UPDATE civicrm_domain SET version = '5.30.0'; diff --git a/civicrm/sql/civicrm_generated.mysql b/civicrm/sql/civicrm_generated.mysql index c1fafd9745..7bea0df4a5 100644 --- a/civicrm/sql/civicrm_generated.mysql +++ b/civicrm/sql/civicrm_generated.mysql @@ -399,7 +399,7 @@ UNLOCK TABLES; LOCK TABLES `civicrm_domain` WRITE; /*!40000 ALTER TABLE `civicrm_domain` DISABLE KEYS */; -INSERT INTO `civicrm_domain` (`id`, `name`, `description`, `version`, `contact_id`, `locales`, `locale_custom_strings`) VALUES (1,'Default Domain Name',NULL,'5.29.1',1,NULL,'a:1:{s:5:\"en_US\";a:0:{}}'); +INSERT INTO `civicrm_domain` (`id`, `name`, `description`, `version`, `contact_id`, `locales`, `locale_custom_strings`) VALUES (1,'Default Domain Name',NULL,'5.30.0',1,NULL,'a:1:{s:5:\"en_US\";a:0:{}}'); /*!40000 ALTER TABLE `civicrm_domain` ENABLE KEYS */; UNLOCK TABLES; @@ -495,7 +495,7 @@ UNLOCK TABLES; LOCK TABLES `civicrm_extension` WRITE; /*!40000 ALTER TABLE `civicrm_extension` DISABLE KEYS */; -INSERT INTO `civicrm_extension` (`id`, `type`, `full_name`, `name`, `label`, `file`, `schema_version`, `is_active`) VALUES (1,'module','sequentialcreditnotes','Sequential credit notes','Sequential credit notes','sequentialcreditnotes',NULL,1),(2,'module','eventcart','Event cart','Event cart','eventcart',NULL,1); +INSERT INTO `civicrm_extension` (`id`, `type`, `full_name`, `name`, `label`, `file`, `schema_version`, `is_active`) VALUES (1,'module','sequentialcreditnotes','Sequential credit notes','Sequential credit notes','sequentialcreditnotes',NULL,1),(2,'module','eventcart','Event cart','Event cart','eventcart',NULL,1),(3,'module','financialacls','Financial ACLs','Financial ACLs','financialacls',NULL,1); /*!40000 ALTER TABLE `civicrm_extension` ENABLE KEYS */; UNLOCK TABLES; diff --git a/civicrm/templates/CRM/Admin/Form/Setting/Mail.hlp b/civicrm/templates/CRM/Admin/Form/Setting/Mail.hlp new file mode 100644 index 0000000000..e33c7912c5 --- /dev/null +++ b/civicrm/templates/CRM/Admin/Form/Setting/Mail.hlp @@ -0,0 +1,14 @@ +{* + +--------------------------------------------------------------------+ + | Copyright CiviCRM LLC. All rights reserved. | + | | + | This work is published under the GNU AGPLv3 license with some | + | permitted exceptions and without any warranty. For full license | + | and copyright information, see https://civicrm.org/licensing | + +--------------------------------------------------------------------+ +*} +{htxt id="civimail-sync-interval-id"} + <p> + {ts}CiviMail records email sent at the frequency you specify. If you set it to 1, it will update the database every time it sends an email. This ensures that emails are not resent if the batch job fails, but this may cause a performance hit, particularly for large jobs.{/ts} + </p> +{/htxt} diff --git a/civicrm/templates/CRM/Case/Form/Activity/OpenCase.tpl b/civicrm/templates/CRM/Case/Form/Activity/OpenCase.tpl index 4627f81de3..c1a955771e 100644 --- a/civicrm/templates/CRM/Case/Form/Activity/OpenCase.tpl +++ b/civicrm/templates/CRM/Case/Form/Activity/OpenCase.tpl @@ -42,15 +42,17 @@ var caseStatusLabels = {/literal}{$caseStatusLabels.values|@json_encode}{literal}; var caseStatusNames = {/literal}{$caseStatusNames.values|@json_encode}{literal}; if ($('#case_type_id, #status_id', $form).length === 2) { - $('#case_type_id', $form).change(function() { - if ($(this).val()) { - var definition = caseTypes[$(this).val()].definition; + updateCaseStatusOptions(); + $('#case_type_id', $form).change(updateCaseStatusOptions); + function updateCaseStatusOptions() { + if ($('#case_type_id', $form).val()) { + var definition = caseTypes[$('#case_type_id', $form).val()].definition; var newOptions = CRM._.filter(caseStatusLabels, function(opt) { return !definition.statuses || !definition.statuses.length || definition.statuses.indexOf(caseStatusNames[opt.key]) > -1; }); CRM.utils.setOptions($('#status_id', $form), newOptions); } - }) + } } }); </script> diff --git a/civicrm/templates/CRM/Case/Form/CaseView.tpl b/civicrm/templates/CRM/Case/Form/CaseView.tpl index 86e23f57af..1301ac5b36 100644 --- a/civicrm/templates/CRM/Case/Form/CaseView.tpl +++ b/civicrm/templates/CRM/Case/Form/CaseView.tpl @@ -95,7 +95,7 @@ <p> {$form.add_activity_type_id.html} {if $hasAccessToAllCases} - {$form.timeline_id.html}{$form._qf_CaseView_next.html} + {$form.timeline_id.html}{*This CaseView_next button is hidden, but gets clicked by the onChange handler for timeline_id in CaseView.js*}{$form._qf_CaseView_next.html} {$form.report_id.html} {/if} </p> @@ -112,7 +112,7 @@ {if $mergeCases} <a href="#mergeCasesDialog" class="action-item no-popup crm-hover-button case-miniform"><i class="crm-i fa-compress" aria-hidden="true"></i> {ts}Merge Case{/ts}</a> - {$form._qf_CaseView_next_merge_case.html} + {*This CaseView_next_merge_case button is hidden, but gets clicked by javascript in CaseView.js when the mergeCasesDialog popup is saved.*}{$form._qf_CaseView_next_merge_case.html} <span id="mergeCasesDialog" class="hiddenElement"> {$form.merge_case_id.html} </span> diff --git a/civicrm/templates/CRM/Contribute/Form/Contribution/Main.tpl b/civicrm/templates/CRM/Contribute/Form/Contribution/Main.tpl index d6ef535aae..0e5a3c2c61 100644 --- a/civicrm/templates/CRM/Contribute/Form/Contribution/Main.tpl +++ b/civicrm/templates/CRM/Contribute/Form/Contribution/Main.tpl @@ -65,16 +65,19 @@ <div class="crm-contribution-page-id-{$contributionPageID} crm-block crm-contribution-main-form-block"> + {crmRegion name='contribution-main-not-you-block'} {if $contact_id && !$ccid} <div class="messages status no-popup crm-not-you-message"> {ts 1=$display_name}Welcome %1{/ts}. (<a href="{crmURL p='civicrm/contribute/transact' q="cid=0&reset=1&id=`$contributionPageID`"}" title="{ts}Click here to do this for a different person.{/ts}">{ts 1=$display_name}Not %1, or want to do this for a different person{/ts}</a>?) </div> {/if} + {/crmRegion} <div id="intro_text" class="crm-public-form-item crm-section intro_text-section"> {$intro_text} </div> {include file="CRM/common/cidzero.tpl"} + {if $islifetime or $ispricelifetime} <div class="help">{ts}You have a current Lifetime Membership which does not need to be renewed.{/ts}</div> {/if} diff --git a/civicrm/templates/CRM/Contribute/Form/Search/Common.tpl b/civicrm/templates/CRM/Contribute/Form/Search/Common.tpl index 657fb77251..5564d23fa0 100644 --- a/civicrm/templates/CRM/Contribute/Form/Search/Common.tpl +++ b/civicrm/templates/CRM/Contribute/Form/Search/Common.tpl @@ -120,7 +120,7 @@ {$form.financial_type_id.html|crmAddClass:twenty} </td> <td> - <label>{ts}Contribution Page{/ts}</label> <br /> + <label>{$form.contribution_page_id.label}</label> <br /> {$form.contribution_page_id.html|crmAddClass:twenty} </td> </tr> diff --git a/civicrm/templates/CRM/Event/Form/ManageEvent/Registration.hlp b/civicrm/templates/CRM/Event/Form/ManageEvent/Registration.hlp index 2aa4f62db7..8fba9c56a9 100644 --- a/civicrm/templates/CRM/Event/Form/ManageEvent/Registration.hlp +++ b/civicrm/templates/CRM/Event/Form/ManageEvent/Registration.hlp @@ -89,5 +89,5 @@ {ts}Cancellation or Transfer Time Limit{/ts} {/htxt} {htxt id="id-selfcancelxfer_time"} - {ts}Number of hours prior to event start date to allow self-service cancellation or transfer. Enter 0 (or leave empty) to allow cancellation or transfer up until the event has started.{/ts} + {ts}Number of hours prior to event start date to allow self-service cancellation or transfer. Enter a negative number of hours to allow cancellation after the event starts. Enter 0 (or leave empty) to allow cancellation or transfer up until the event has started.{/ts} {/htxt} diff --git a/civicrm/templates/CRM/Event/Form/ParticipantFeeSelection.tpl b/civicrm/templates/CRM/Event/Form/ParticipantFeeSelection.tpl index 02246d54cb..16bb9a1938 100644 --- a/civicrm/templates/CRM/Event/Form/ParticipantFeeSelection.tpl +++ b/civicrm/templates/CRM/Event/Form/ParticipantFeeSelection.tpl @@ -18,7 +18,7 @@ function display(totalfee) { // go as a float - CRM-13491 totalfee = Math.round(totalfee*100)/100; // note : some variables used used here are global variables defined inside Calculate.tpl - var totalEventFee = formatMoney( totalfee, 2, seperator, thousandMarker); + var totalEventFee = formatMoney( totalfee, 2, separator, thousandMarker); cj('#pricevalue').html("<b>"+symbol+"</b> "+totalEventFee); scriptfee = totalfee; scriptarray = price; @@ -67,7 +67,7 @@ function populatebalanceFee(updatedAmt, onlyStatusUpdate) { } if (!onlyStatusUpdate) { - balanceAmt = formatMoney(balanceAmt, 2, seperator, thousandMarker); + balanceAmt = formatMoney(balanceAmt, 2, separator, thousandMarker); cj('#balance-fee').text(symbol+" "+balanceAmt); } } diff --git a/civicrm/templates/CRM/Event/Form/Registration/Register.tpl b/civicrm/templates/CRM/Event/Form/Registration/Register.tpl index cc4dedfac3..a76a3c8dfe 100644 --- a/civicrm/templates/CRM/Event/Form/Registration/Register.tpl +++ b/civicrm/templates/CRM/Event/Form/Registration/Register.tpl @@ -33,6 +33,7 @@ </div> {/if} + {crmRegion name='event-register-not-you-block'} {if $contact_id} <div class="messages status no-popup crm-not-you-message" id="crm-event-register-different"> {ts 1=$display_name}Welcome %1{/ts}. (<a @@ -40,6 +41,8 @@ title="{ts}Click here to register a different person for this event.{/ts}">{ts 1=$display_name}Not %1, or want to register a different person{/ts}</a>?) </div> {/if} + {/crmRegion} + {if $event.intro_text} <div id="intro_text" class="crm-public-form-item crm-section intro_text-section"> <p>{$event.intro_text}</p> diff --git a/civicrm/templates/CRM/Mailing/Form/Settings.hlp b/civicrm/templates/CRM/Mailing/Form/Settings.hlp deleted file mode 100644 index 99d515ee39..0000000000 --- a/civicrm/templates/CRM/Mailing/Form/Settings.hlp +++ /dev/null @@ -1,28 +0,0 @@ -{* - +--------------------------------------------------------------------+ - | Copyright CiviCRM LLC. All rights reserved. | - | | - | This work is published under the GNU AGPLv3 license with some | - | permitted exceptions and without any warranty. For full license | - | and copyright information, see https://civicrm.org/licensing | - +--------------------------------------------------------------------+ -*} -{htxt id="mailing-visibility-title"} - {ts}Visibility{/ts} -{/htxt} -{htxt id="mailing-visibility"} -<p> -{assign var=hlpViewURL value="mailing.viewUrl"} -{ts 1=$hlpViewURL}This option makes it possible to make the contents of the email accessible to users as a web page. This option is only relevant if you insert the Mailing permalink token %1 in your message in the next step. This token generates a URL on which the message can be viewed (remember to insert the code in the appropriate HTML tags).{/ts} -</p><p> -{ts}You have two options to specify the visibility of the mailing{/ts}: -</p><p> -{ts}"public pages" will make the content of this mailing be viewable as a web page by everyone who has the permission of "view public CiviMail content"{/ts} -</p><p> -{ts}"user and user admin" only means that only users that received the mailing or administrators can view the content of this email as a web page; the recipients will have to log in to be able to view the message{/ts} -<p></p> -{htxt id="mailing-sync-interval-title"} - {ts}Sync Interval{/ts} -{ts}"civimail_sync_interval" specifies how frequently CiviMail records which emails it has sent. A value of 1 means that it updates the database with every email sent, but this may have a performance hit.{/ts}: -<p></p> -{/htxt} diff --git a/civicrm/templates/CRM/Pledge/Form/Pledge.tpl b/civicrm/templates/CRM/Pledge/Form/Pledge.tpl index 82c7e77087..fa6afe6e6c 100644 --- a/civicrm/templates/CRM/Pledge/Form/Pledge.tpl +++ b/civicrm/templates/CRM/Pledge/Form/Pledge.tpl @@ -217,14 +217,14 @@ function calculatedPaymentAmount( ) { var thousandMarker = {/literal}{crmSetting name="monetaryThousandSeparator" group="CiviCRM Localization"}{literal}; - var seperator = '{/literal}{$config->monetaryDecimalPoint}{literal}'; + var separator = '{/literal}{$config->monetaryDecimalPoint}{literal}'; var amount = document.getElementById("amount").value; - // replace all thousandMarker and change the seperator to a dot - amount = amount.replace(thousandMarker,'').replace(seperator,'.'); + // replace all thousandMarker and change the separator to a dot + amount = amount.replace(thousandMarker,'').replace(separator,'.'); var installments = document.getElementById("installments").value; if ( installments != '' && installments != NaN) { amount = amount/installments; - var installmentAmount = formatMoney( amount, 2, seperator, thousandMarker ); + var installmentAmount = formatMoney( amount, 2, separator, thousandMarker ); document.getElementById("eachPaymentAmount").value = installmentAmount; } } diff --git a/civicrm/templates/CRM/UF/Form/Group.hlp b/civicrm/templates/CRM/UF/Form/Group.hlp index 2bb3fc4ca2..3a0a93d8f1 100644 --- a/civicrm/templates/CRM/UF/Form/Group.hlp +++ b/civicrm/templates/CRM/UF/Form/Group.hlp @@ -63,7 +63,7 @@ {ts}Add to Group{/ts} {/htxt} {htxt id='id-add_group'} -{ts}Select a group if you are using this profile for adding new contacts, AND you want the new contacts to be automatically assigned to a group.{/ts} +{ts}Select a group if you want contacts to be automatically added to that group when the profile is submitted.{/ts} {/htxt} {htxt id='id-notify_email-title'} diff --git a/civicrm/templates/CRM/common/civicrm.settings.php.template b/civicrm/templates/CRM/common/civicrm.settings.php.template index 46ed344835..3d0575569f 100644 --- a/civicrm/templates/CRM/common/civicrm.settings.php.template +++ b/civicrm/templates/CRM/common/civicrm.settings.php.template @@ -106,7 +106,7 @@ if (!defined('CIVICRM_DSN')) { define('CIVICRM_DSN', $GLOBALS['_CV']['TEST_DB_DSN']); } else { - define('CIVICRM_DSN', 'mysql://%%dbUser%%:%%dbPass%%@%%dbHost%%/%%dbName%%?new_link=true'); + define('CIVICRM_DSN', 'mysql://%%dbUser%%:%%dbPass%%@%%dbHost%%/%%dbName%%?new_link=true%%dbSSL%%'); } } @@ -432,6 +432,11 @@ if (!defined('CIVICRM_PSR16_STRICT')) { * configuration option, but wish to, for example, use fr_CA instead of the * default fr_FR (for French), set one or more of the constants below to an * appropriate regional value. + * + * Note that since 5.26.0 specifically https://github.com/civicrm/civicrm-core/pull/16700 + * This generally doesn't get used by WordPress especially if using the Polylang plugin. + * The reason is that the WordPress implementation has been changed to get the full locale + * from the WordPress plugin rather than just the 2 string language code. */ // define('CIVICRM_LANGUAGE_MAPPING_FR', 'fr_CA'); // define('CIVICRM_LANGUAGE_MAPPING_EN', 'en_CA'); diff --git a/civicrm/vendor/autoload.php b/civicrm/vendor/autoload.php index 5333a90fa0..7a74b810e6 100644 --- a/civicrm/vendor/autoload.php +++ b/civicrm/vendor/autoload.php @@ -4,4 +4,4 @@ require_once __DIR__ . '/composer/autoload_real.php'; -return ComposerAutoloaderInitc0c306f7aec196654ddc587ff3d70d96::getLoader(); +return ComposerAutoloaderInit157c8f429cb411f5002e91a721972659::getLoader(); diff --git a/civicrm/vendor/composer/autoload_namespaces.php b/civicrm/vendor/composer/autoload_namespaces.php index 7ae201cfc1..fc4a46d05b 100644 --- a/civicrm/vendor/composer/autoload_namespaces.php +++ b/civicrm/vendor/composer/autoload_namespaces.php @@ -17,6 +17,7 @@ return array( 'Log' => array($vendorDir . '/pear/log'), 'File' => array($vendorDir . '/phpseclib/phpseclib/phpseclib'), 'Dflydev\\ApacheMimeTypes' => array($vendorDir . '/dflydev/apache-mime-types/src'), + 'DB' => array($vendorDir . '/pear/db'), 'Crypt' => array($vendorDir . '/phpseclib/phpseclib/phpseclib'), 'Console' => array($vendorDir . '/pear/console_getopt'), 'Civi\\' => array($baseDir . '/tests/phpunit'), diff --git a/civicrm/vendor/composer/autoload_real.php b/civicrm/vendor/composer/autoload_real.php index 8e5f2d3763..b76ea7c666 100644 --- a/civicrm/vendor/composer/autoload_real.php +++ b/civicrm/vendor/composer/autoload_real.php @@ -2,7 +2,7 @@ // autoload_real.php @generated by Composer -class ComposerAutoloaderInitc0c306f7aec196654ddc587ff3d70d96 +class ComposerAutoloaderInit157c8f429cb411f5002e91a721972659 { private static $loader; @@ -19,9 +19,9 @@ class ComposerAutoloaderInitc0c306f7aec196654ddc587ff3d70d96 return self::$loader; } - spl_autoload_register(array('ComposerAutoloaderInitc0c306f7aec196654ddc587ff3d70d96', 'loadClassLoader'), true, true); + spl_autoload_register(array('ComposerAutoloaderInit157c8f429cb411f5002e91a721972659', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); - spl_autoload_unregister(array('ComposerAutoloaderInitc0c306f7aec196654ddc587ff3d70d96', 'loadClassLoader')); + spl_autoload_unregister(array('ComposerAutoloaderInit157c8f429cb411f5002e91a721972659', 'loadClassLoader')); $includePaths = require __DIR__ . '/include_paths.php'; $includePaths[] = get_include_path(); @@ -31,7 +31,7 @@ class ComposerAutoloaderInitc0c306f7aec196654ddc587ff3d70d96 if ($useStaticLoader) { require_once __DIR__ . '/autoload_static.php'; - call_user_func(\Composer\Autoload\ComposerStaticInitc0c306f7aec196654ddc587ff3d70d96::getInitializer($loader)); + call_user_func(\Composer\Autoload\ComposerStaticInit157c8f429cb411f5002e91a721972659::getInitializer($loader)); } else { $map = require __DIR__ . '/autoload_namespaces.php'; foreach ($map as $namespace => $path) { @@ -52,19 +52,19 @@ class ComposerAutoloaderInitc0c306f7aec196654ddc587ff3d70d96 $loader->register(true); if ($useStaticLoader) { - $includeFiles = Composer\Autoload\ComposerStaticInitc0c306f7aec196654ddc587ff3d70d96::$files; + $includeFiles = Composer\Autoload\ComposerStaticInit157c8f429cb411f5002e91a721972659::$files; } else { $includeFiles = require __DIR__ . '/autoload_files.php'; } foreach ($includeFiles as $fileIdentifier => $file) { - composerRequirec0c306f7aec196654ddc587ff3d70d96($fileIdentifier, $file); + composerRequire157c8f429cb411f5002e91a721972659($fileIdentifier, $file); } return $loader; } } -function composerRequirec0c306f7aec196654ddc587ff3d70d96($fileIdentifier, $file) +function composerRequire157c8f429cb411f5002e91a721972659($fileIdentifier, $file) { if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { require $file; diff --git a/civicrm/vendor/composer/autoload_static.php b/civicrm/vendor/composer/autoload_static.php index c31616f97c..4ef4257dbf 100644 --- a/civicrm/vendor/composer/autoload_static.php +++ b/civicrm/vendor/composer/autoload_static.php @@ -4,7 +4,7 @@ namespace Composer\Autoload; -class ComposerStaticInitc0c306f7aec196654ddc587ff3d70d96 +class ComposerStaticInit157c8f429cb411f5002e91a721972659 { public static $files = array ( '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php', @@ -346,6 +346,10 @@ class ComposerStaticInitc0c306f7aec196654ddc587ff3d70d96 array ( 0 => __DIR__ . '/..' . '/dflydev/apache-mime-types/src', ), + 'DB' => + array ( + 0 => __DIR__ . '/..' . '/pear/db', + ), ), 'C' => array ( @@ -531,11 +535,11 @@ class ComposerStaticInitc0c306f7aec196654ddc587ff3d70d96 public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { - $loader->prefixLengthsPsr4 = ComposerStaticInitc0c306f7aec196654ddc587ff3d70d96::$prefixLengthsPsr4; - $loader->prefixDirsPsr4 = ComposerStaticInitc0c306f7aec196654ddc587ff3d70d96::$prefixDirsPsr4; - $loader->prefixesPsr0 = ComposerStaticInitc0c306f7aec196654ddc587ff3d70d96::$prefixesPsr0; - $loader->fallbackDirsPsr0 = ComposerStaticInitc0c306f7aec196654ddc587ff3d70d96::$fallbackDirsPsr0; - $loader->classMap = ComposerStaticInitc0c306f7aec196654ddc587ff3d70d96::$classMap; + $loader->prefixLengthsPsr4 = ComposerStaticInit157c8f429cb411f5002e91a721972659::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit157c8f429cb411f5002e91a721972659::$prefixDirsPsr4; + $loader->prefixesPsr0 = ComposerStaticInit157c8f429cb411f5002e91a721972659::$prefixesPsr0; + $loader->fallbackDirsPsr0 = ComposerStaticInit157c8f429cb411f5002e91a721972659::$fallbackDirsPsr0; + $loader->classMap = ComposerStaticInit157c8f429cb411f5002e91a721972659::$classMap; }, null, ClassLoader::class); } diff --git a/civicrm/vendor/composer/include_paths.php b/civicrm/vendor/composer/include_paths.php index 975aca43d6..4d88f9589b 100644 --- a/civicrm/vendor/composer/include_paths.php +++ b/civicrm/vendor/composer/include_paths.php @@ -10,9 +10,10 @@ return array( $vendorDir . '/phpseclib/phpseclib/phpseclib', $vendorDir . '/pear/pear_exception', $vendorDir . '/pear/auth_sasl', - $vendorDir . '/pear/log', $vendorDir . '/pear/console_getopt', $vendorDir . '/pear/pear-core-minimal/src', + $vendorDir . '/pear/db', + $vendorDir . '/pear/log', $vendorDir . '/pear/mail', $vendorDir . '/pear/mail_mime', $vendorDir . '/pear/net_socket', diff --git a/civicrm/vendor/composer/installed.json b/civicrm/vendor/composer/installed.json index 3c2cfe85bd..3989c7f86a 100644 --- a/civicrm/vendor/composer/installed.json +++ b/civicrm/vendor/composer/installed.json @@ -1029,6 +1029,68 @@ ], "description": "More info available on: http://pear.php.net/package/Console_Getopt" }, + { + "name": "pear/db", + "version": "v1.10.0", + "version_normalized": "1.10.0.0", + "source": { + "type": "git", + "url": "https://github.com/pear/DB.git", + "reference": "e158c3a48246b67cd8c95856ffbb93de4ef380fe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pear/DB/zipball/e158c3a48246b67cd8c95856ffbb93de4ef380fe", + "reference": "e158c3a48246b67cd8c95856ffbb93de4ef380fe", + "shasum": "" + }, + "require": { + "pear/pear-core-minimal": "*" + }, + "time": "2020-04-19T19:45:59+00:00", + "type": "library", + "extra": { + "patches_applied": { + "Apply CiviCRM Customisations for the pear:db package": "https://raw.githubusercontent.com/civicrm/civicrm-core/a48a43c2b5f6d694fff1cfb99d522c5d9e2459a0/tools/scripts/composer/pear_db_civicrm_changes.patch" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "DB": "./" + } + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "./" + ], + "license": [ + "PHP License v3.01" + ], + "authors": [ + { + "name": "Daniel Convissor", + "email": "danielc@php.net", + "role": "Lead" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net", + "role": "Lead" + }, + { + "name": "Stig Bakken", + "email": "stig@php.net", + "role": "Developer" + }, + { + "name": "Tomas V.V.Cox", + "email": "cox@idecnet.com", + "role": "Developer" + } + ], + "description": "More info available on: http://pear.php.net/package/DB" + }, { "name": "pear/log", "version": "1.13.2", diff --git a/civicrm/packages/DB.php b/civicrm/vendor/pear/db/DB.php similarity index 99% rename from civicrm/packages/DB.php rename to civicrm/vendor/pear/db/DB.php index 0612a1397d..cfac8ee2e6 100644 --- a/civicrm/packages/DB.php +++ b/civicrm/vendor/pear/db/DB.php @@ -440,7 +440,7 @@ define('DB_PORTABILITY_ALL', 63); * @author Daniel Convissor <danielc@php.net> * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.9.3 + * @version Release: @package_version@ * @link http://pear.php.net/package/DB */ class DB @@ -591,7 +591,7 @@ class DB */ function apiVersion() { - return '1.9.3'; + return '@package_version@'; } // }}} @@ -606,7 +606,7 @@ class DB */ public static function isError($value) { - return is_object($value) && is_a($value, 'DB_Error'); + return is_object($value) && is_a($value, 'DB_Error'); } // }}} @@ -896,7 +896,7 @@ class DB * defined, and means that we deal with strings and array in the same * manner. */ $dsnArray = DB::parseDSN($dsn); - + if ($hidePassword) { $dsnArray['password'] = 'PASSWORD'; } @@ -906,7 +906,7 @@ class DB if (is_string($dsn) && strpos($dsn, 'tcp') === false && $dsnArray['protocol'] == 'tcp') { $dsnArray['protocol'] = false; } - + // Now we just have to construct the actual string. This is ugly. $dsnString = $dsnArray['phptype']; if ($dsnArray['dbsyntax']) { @@ -929,7 +929,7 @@ class DB $dsnString .= ':'.$dsnArray['port']; } $dsnString .= '/'.$dsnArray['database']; - + /* Option handling. Unfortunately, parseDSN simply places options into * the top-level array, so we'll first get rid of the fields defined by * DB and see what's left. */ @@ -956,7 +956,7 @@ class DB return $dsnString; } - + // }}} } @@ -972,7 +972,7 @@ class DB * @author Stig Bakken <ssb@php.net> * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.9.3 + * @version Release: @package_version@ * @link http://pear.php.net/package/DB */ class DB_Error extends PEAR_Error @@ -1033,7 +1033,7 @@ class DB_Error extends PEAR_Error * @author Stig Bakken <ssb@php.net> * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.9.3 + * @version Release: @package_version@ * @link http://pear.php.net/package/DB */ class DB_result @@ -1498,7 +1498,7 @@ class DB_result * @author Stig Bakken <ssb@php.net> * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.9.3 + * @version Release: @package_version@ * @link http://pear.php.net/package/DB * @see DB_common::setFetchMode() */ diff --git a/civicrm/packages/DB/common.php b/civicrm/vendor/pear/db/DB/common.php similarity index 99% rename from civicrm/packages/DB/common.php rename to civicrm/vendor/pear/db/DB/common.php index 92a8143b20..b4112e5134 100644 --- a/civicrm/packages/DB/common.php +++ b/civicrm/vendor/pear/db/DB/common.php @@ -42,7 +42,7 @@ require_once 'PEAR.php'; * @author Daniel Convissor <danielc@php.net> * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.9.3 + * @version Release: @package_version@ * @link http://pear.php.net/package/DB */ class DB_common extends PEAR @@ -262,7 +262,7 @@ class DB_common extends PEAR function quoteString($string) { $string = $this->quoteSmart($string); - if ($string{0} == "'") { + if ($string[0] == "'") { return substr($string, 1, -1); } return $string; @@ -471,7 +471,7 @@ class DB_common extends PEAR function quoteBoolean($boolean) { return $boolean ? '1' : '0'; } - + // }}} // {{{ quoteFloat() @@ -487,7 +487,7 @@ class DB_common extends PEAR function quoteFloat($float) { return "'".$this->escapeSimple(str_replace(',', '.', strval(floatval($float))))."'"; } - + // }}} // {{{ escapeSimple() @@ -885,6 +885,9 @@ class DB_common extends PEAR function autoExecute($table, $fields_values, $mode = DB_AUTOQUERY_INSERT, $where = false) { + if ($where) { + $where = strtr($where, array('?' => '\?', '!' => '\!', '&' => '\&',)); + } $sth = $this->autoPrepare($table, array_keys($fields_values), $mode, $where); if (DB::isError($sth)) { diff --git a/civicrm/packages/DB/dbase.php b/civicrm/vendor/pear/db/DB/dbase.php similarity index 99% rename from civicrm/packages/DB/dbase.php rename to civicrm/vendor/pear/db/DB/dbase.php index 8324c86b7a..8368c3d736 100644 --- a/civicrm/packages/DB/dbase.php +++ b/civicrm/vendor/pear/db/DB/dbase.php @@ -41,7 +41,7 @@ require_once 'DB/common.php'; * @author Daniel Convissor <danielc@php.net> * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.9.3 + * @version Release: @package_version@ * @link http://pear.php.net/package/DB */ class DB_dbase extends DB_common @@ -402,7 +402,7 @@ class DB_dbase extends DB_common function quoteBoolean($boolean) { return $boolean ? 'T' : 'F'; } - + // }}} // {{{ tableInfo() diff --git a/civicrm/vendor/pear/db/DB/fbsql.php b/civicrm/vendor/pear/db/DB/fbsql.php new file mode 100644 index 0000000000..62ae2b6a8f --- /dev/null +++ b/civicrm/vendor/pear/db/DB/fbsql.php @@ -0,0 +1,769 @@ +<?php + +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * The PEAR DB driver for PHP's fbsql extension + * for interacting with FrontBase databases + * + * PHP version 5 + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @category Database + * @package DB + * @author Frank M. Kromann <frank@frontbase.com> + * @author Daniel Convissor <danielc@php.net> + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id$ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ +require_once 'DB/common.php'; + +/** + * The methods PEAR DB uses to interact with PHP's fbsql extension + * for interacting with FrontBase databases + * + * These methods overload the ones declared in DB_common. + * + * @category Database + * @package DB + * @author Frank M. Kromann <frank@frontbase.com> + * @author Daniel Convissor <danielc@php.net> + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/DB + * @since Class functional since Release 1.7.0 + */ +class DB_fbsql extends DB_common +{ + // {{{ properties + + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'fbsql'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'fbsql'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * @var array + */ + var $features = array( + 'limit' => 'alter', + 'new_link' => false, + 'numrows' => true, + 'pconnect' => true, + 'prepare' => false, + 'ssl' => false, + 'transactions' => true, + ); + + /** + * A mapping of native error codes to DB error codes + * @var array + */ + var $errorcode_map = array( + 22 => DB_ERROR_SYNTAX, + 85 => DB_ERROR_ALREADY_EXISTS, + 108 => DB_ERROR_SYNTAX, + 116 => DB_ERROR_NOSUCHTABLE, + 124 => DB_ERROR_VALUE_COUNT_ON_ROW, + 215 => DB_ERROR_NOSUCHFIELD, + 217 => DB_ERROR_INVALID_NUMBER, + 226 => DB_ERROR_NOSUCHFIELD, + 231 => DB_ERROR_INVALID, + 239 => DB_ERROR_TRUNCATED, + 251 => DB_ERROR_SYNTAX, + 266 => DB_ERROR_NOT_FOUND, + 357 => DB_ERROR_CONSTRAINT_NOT_NULL, + 358 => DB_ERROR_CONSTRAINT, + 360 => DB_ERROR_CONSTRAINT, + 361 => DB_ERROR_CONSTRAINT, + ); + + /** + * The raw database connection created by PHP + * @var resource + */ + var $connection; + + /** + * The DSN information for connecting to a database + * @var array + */ + var $dsn = array(); + + + // }}} + // {{{ constructor + + /** + * This constructor calls <kbd>parent::__construct()</kbd> + * + * @return void + */ + function __construct() + { + parent::__construct(); + } + + // }}} + // {{{ connect() + + /** + * Connect to the database server, log in and open the database + * + * Don't call this method directly. Use DB::connect() instead. + * + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function connect($dsn, $persistent = false) + { + if (!PEAR::loadExtension('fbsql')) { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; + } + + $params = array( + $dsn['hostspec'] ? $dsn['hostspec'] : 'localhost', + $dsn['username'] ? $dsn['username'] : null, + $dsn['password'] ? $dsn['password'] : null, + ); + + $connect_function = $persistent ? 'fbsql_pconnect' : 'fbsql_connect'; + + $ini = ini_get('track_errors'); + $php_errormsg = ''; + if ($ini) { + $this->connection = @call_user_func_array($connect_function, + $params); + } else { + @ini_set('track_errors', 1); + $this->connection = @call_user_func_array($connect_function, + $params); + @ini_set('track_errors', $ini); + } + + if (!$this->connection) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + $php_errormsg); + } + + if ($dsn['database']) { + if (!@fbsql_select_db($dsn['database'], $this->connection)) { + return $this->fbsqlRaiseError(); + } + } + + return DB_OK; + } + + // }}} + // {{{ disconnect() + + /** + * Disconnects from the database server + * + * @return bool TRUE on success, FALSE on failure + */ + function disconnect() + { + $ret = @fbsql_close($this->connection); + $this->connection = null; + return $ret; + } + + // }}} + // {{{ simpleQuery() + + /** + * Sends a query to the database server + * + * @param string the SQL query string + * + * @return mixed + a PHP result resrouce for successful SELECT queries + * + the DB_OK constant for other successful queries + * + a DB_Error object on failure + */ + function simpleQuery($query) + { + $this->last_query = $query; + $query = $this->modifyQuery($query); + $result = @fbsql_query("$query;", $this->connection); + if (!$result) { + return $this->fbsqlRaiseError(); + } + // Determine which queries that should return data, and which + // should return an error code only. + if ($this->_checkManip($query)) { + return DB_OK; + } + return $result; + } + + // }}} + // {{{ nextResult() + + /** + * Move the internal fbsql result pointer to the next available result + * + * @param a valid fbsql result resource + * + * @access public + * + * @return true if a result is available otherwise return false + */ + function nextResult($result) + { + return @fbsql_next_result($result); + } + + // }}} + // {{{ fetchInto() + + /** + * Places a row from the result set into the given array + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch (0 = first row) + * + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure + * + * @see DB_result::fetchInto() + */ + function fetchInto($result, &$arr, $fetchmode, $rownum = null) + { + if ($rownum !== null) { + if (!@fbsql_data_seek($result, $rownum)) { + return null; + } + } + if ($fetchmode & DB_FETCHMODE_ASSOC) { + $arr = @fbsql_fetch_array($result, FBSQL_ASSOC); + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { + $arr = array_change_key_case($arr, CASE_LOWER); + } + } else { + $arr = @fbsql_fetch_row($result); + } + if (!$arr) { + return null; + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + return DB_OK; + } + + // }}} + // {{{ freeResult() + + /** + * Deletes the result set and frees the memory occupied by the result set + * + * This method is not meant to be called directly. Use + * DB_result::free() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_result::free() + */ + function freeResult($result) + { + return is_resource($result) ? fbsql_free_result($result) : false; + } + + // }}} + // {{{ autoCommit() + + /** + * Enables or disables automatic commits + * + * @param bool $onoff true turns it on, false turns it off + * + * @return int DB_OK on success. A DB_Error object if the driver + * doesn't support auto-committing transactions. + */ + function autoCommit($onoff=false) + { + if ($onoff) { + $this->query("SET COMMIT TRUE"); + } else { + $this->query("SET COMMIT FALSE"); + } + } + + // }}} + // {{{ commit() + + /** + * Commits the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function commit() + { + @fbsql_commit($this->connection); + } + + // }}} + // {{{ rollback() + + /** + * Reverts the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function rollback() + { + @fbsql_rollback($this->connection); + } + + // }}} + // {{{ numCols() + + /** + * Gets the number of columns in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of columns. A DB_Error object on failure. + * + * @see DB_result::numCols() + */ + function numCols($result) + { + $cols = @fbsql_num_fields($result); + if (!$cols) { + return $this->fbsqlRaiseError(); + } + return $cols; + } + + // }}} + // {{{ numRows() + + /** + * Gets the number of rows in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numRows() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of rows. A DB_Error object on failure. + * + * @see DB_result::numRows() + */ + function numRows($result) + { + $rows = @fbsql_num_rows($result); + if ($rows === null) { + return $this->fbsqlRaiseError(); + } + return $rows; + } + + // }}} + // {{{ affectedRows() + + /** + * Determines the number of rows affected by a data maniuplation query + * + * 0 is returned for queries that don't manipulate data. + * + * @return int the number of rows. A DB_Error object on failure. + */ + function affectedRows() + { + if ($this->_last_query_manip) { + $result = @fbsql_affected_rows($this->connection); + } else { + $result = 0; + } + return $result; + } + + // }}} + // {{{ nextId() + + /** + * Returns the next free id in a sequence + * + * @param string $seq_name name of the sequence + * @param boolean $ondemand when true, the seqence is automatically + * created if it does not exist + * + * @return int the next id number in the sequence. + * A DB_Error object on failure. + * + * @see DB_common::nextID(), DB_common::getSequenceName(), + * DB_fbsql::createSequence(), DB_fbsql::dropSequence() + */ + function nextId($seq_name, $ondemand = true) + { + $seqname = $this->getSequenceName($seq_name); + do { + $repeat = 0; + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result = $this->query('SELECT UNIQUE FROM ' . $seqname); + $this->popErrorHandling(); + if ($ondemand && DB::isError($result) && + $result->getCode() == DB_ERROR_NOSUCHTABLE) { + $repeat = 1; + $result = $this->createSequence($seq_name); + if (DB::isError($result)) { + return $result; + } + } else { + $repeat = 0; + } + } while ($repeat); + if (DB::isError($result)) { + return $this->fbsqlRaiseError(); + } + $result->fetchInto($tmp, DB_FETCHMODE_ORDERED); + return $tmp[0]; + } + + /** + * Creates a new sequence + * + * @param string $seq_name name of the new sequence + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::createSequence(), DB_common::getSequenceName(), + * DB_fbsql::nextID(), DB_fbsql::dropSequence() + */ + function createSequence($seq_name) + { + $seqname = $this->getSequenceName($seq_name); + $res = $this->query('CREATE TABLE ' . $seqname + . ' (id INTEGER NOT NULL,' + . ' PRIMARY KEY(id))'); + if ($res) { + $res = $this->query('SET UNIQUE = 0 FOR ' . $seqname); + } + return $res; + } + + // }}} + // {{{ dropSequence() + + /** + * Deletes a sequence + * + * @param string $seq_name name of the sequence to be deleted + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::dropSequence(), DB_common::getSequenceName(), + * DB_fbsql::nextID(), DB_fbsql::createSequence() + */ + function dropSequence($seq_name) + { + return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name) + . ' RESTRICT'); + } + + // }}} + // {{{ modifyLimitQuery() + + /** + * Adds LIMIT clauses to a query string according to current DBMS standards + * + * @param string $query the query to modify + * @param int $from the row to start to fetching (0 = the first row) + * @param int $count the numbers of rows to fetch + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * + * @return string the query string with LIMIT clauses added + * + * @access protected + */ + function modifyLimitQuery($query, $from, $count, $params = array()) + { + if (DB::isManip($query) || $this->_next_query_manip) { + return preg_replace('/^([\s(])*SELECT/i', + "\\1SELECT TOP($count)", $query); + } else { + return preg_replace('/([\s(])*SELECT/i', + "\\1SELECT TOP($from, $count)", $query); + } + } + + // }}} + // {{{ quoteBoolean() + + /** + * Formats a boolean value for use within a query in a locale-independent + * manner. + * + * @param boolean the boolean value to be quoted. + * @return string the quoted string. + * @see DB_common::quoteSmart() + * @since Method available since release 1.7.8. + */ + function quoteBoolean($boolean) { + return $boolean ? 'TRUE' : 'FALSE'; + } + + // }}} + // {{{ quoteFloat() + + /** + * Formats a float value for use within a query in a locale-independent + * manner. + * + * @param float the float value to be quoted. + * @return string the quoted string. + * @see DB_common::quoteSmart() + * @since Method available since release 1.7.8. + */ + function quoteFloat($float) { + return $this->escapeSimple(str_replace(',', '.', strval(floatval($float)))); + } + + // }}} + // {{{ fbsqlRaiseError() + + /** + * Produces a DB_Error object regarding the current problem + * + * @param int $errno if the error is being manually raised pass a + * DB_ERROR* constant here. If this isn't passed + * the error information gathered from the DBMS. + * + * @return object the DB_Error object + * + * @see DB_common::raiseError(), + * DB_fbsql::errorNative(), DB_common::errorCode() + */ + function fbsqlRaiseError($errno = null) + { + if ($errno === null) { + $errno = $this->errorCode(fbsql_errno($this->connection)); + } + return $this->raiseError($errno, null, null, null, + @fbsql_error($this->connection)); + } + + // }}} + // {{{ errorNative() + + /** + * Gets the DBMS' native error code produced by the last query + * + * @return int the DBMS' error code + */ + function errorNative() + { + return @fbsql_errno($this->connection); + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table or a result set + * + * @param object|string $result DB_result object from a query or a + * string containing the name of a table. + * While this also accepts a query result + * resource identifier, this behavior is + * deprecated. + * @param int $mode a valid tableInfo mode + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * + * @see DB_common::tableInfo() + */ + function tableInfo($result, $mode = null) + { + if (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + $id = @fbsql_list_fields($this->dsn['database'], + $result, $this->connection); + $got_string = true; + } elseif (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $id = $result->result; + $got_string = false; + } else { + /* + * Probably received a result resource identifier. + * Copy it. + * Deprecated. Here for compatibility only. + */ + $id = $result; + $got_string = false; + } + + if (!is_resource($id)) { + return $this->fbsqlRaiseError(DB_ERROR_NEED_MORE_DATA); + } + + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; + } else { + $case_func = 'strval'; + } + + $count = @fbsql_num_fields($id); + $res = array(); + + if ($mode) { + $res['num_fields'] = $count; + } + + for ($i = 0; $i < $count; $i++) { + $res[$i] = array( + 'table' => $case_func(@fbsql_field_table($id, $i)), + 'name' => $case_func(@fbsql_field_name($id, $i)), + 'type' => @fbsql_field_type($id, $i), + 'len' => @fbsql_field_len($id, $i), + 'flags' => @fbsql_field_flags($id, $i), + ); + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + } + + // free the result only if we were called on a table + if ($got_string) { + @fbsql_free_result($id); + } + return $res; + } + + // }}} + // {{{ getSpecialQuery() + + /** + * Obtains the query string needed for listing a given type of objects + * + * @param string $type the kind of objects you want to retrieve + * + * @return string the SQL query string or null if the driver doesn't + * support the object type requested + * + * @access protected + * @see DB_common::getListOf() + */ + function getSpecialQuery($type) + { + switch ($type) { + case 'tables': + return 'SELECT "table_name" FROM information_schema.tables' + . ' t0, information_schema.schemata t1' + . ' WHERE t0.schema_pk=t1.schema_pk AND' + . ' "table_type" = \'BASE TABLE\'' + . ' AND "schema_name" = current_schema'; + case 'views': + return 'SELECT "table_name" FROM information_schema.tables' + . ' t0, information_schema.schemata t1' + . ' WHERE t0.schema_pk=t1.schema_pk AND' + . ' "table_type" = \'VIEW\'' + . ' AND "schema_name" = current_schema'; + case 'users': + return 'SELECT "user_name" from information_schema.users'; + case 'functions': + return 'SELECT "routine_name" FROM' + . ' information_schema.psm_routines' + . ' t0, information_schema.schemata t1' + . ' WHERE t0.schema_pk=t1.schema_pk' + . ' AND "routine_kind"=\'FUNCTION\'' + . ' AND "schema_name" = current_schema'; + case 'procedures': + return 'SELECT "routine_name" FROM' + . ' information_schema.psm_routines' + . ' t0, information_schema.schemata t1' + . ' WHERE t0.schema_pk=t1.schema_pk' + . ' AND "routine_kind"=\'PROCEDURE\'' + . ' AND "schema_name" = current_schema'; + default: + return null; + } + } + + // }}} +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/civicrm/vendor/pear/db/DB/ibase.php b/civicrm/vendor/pear/db/DB/ibase.php new file mode 100644 index 0000000000..ca0c948bdb --- /dev/null +++ b/civicrm/vendor/pear/db/DB/ibase.php @@ -0,0 +1,1082 @@ +<?php + +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * The PEAR DB driver for PHP's interbase extension + * for interacting with Interbase and Firebird databases + * + * While this class works with PHP 4, PHP's InterBase extension is + * unstable in PHP 4. Use PHP 5. + * + * PHP version 5 + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @category Database + * @package DB + * @author Sterling Hughes <sterling@php.net> + * @author Daniel Convissor <danielc@php.net> + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id$ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ +require_once 'DB/common.php'; + +/** + * The methods PEAR DB uses to interact with PHP's interbase extension + * for interacting with Interbase and Firebird databases + * + * These methods overload the ones declared in DB_common. + * + * While this class works with PHP 4, PHP's InterBase extension is + * unstable in PHP 4. Use PHP 5. + * + * NOTICE: limitQuery() only works for Firebird. + * + * @category Database + * @package DB + * @author Sterling Hughes <sterling@php.net> + * @author Daniel Convissor <danielc@php.net> + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/DB + * @since Class became stable in Release 1.7.0 + */ +class DB_ibase extends DB_common +{ + // {{{ properties + + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'ibase'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'ibase'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * NOTE: only firebird supports limit. + * + * @var array + */ + var $features = array( + 'limit' => false, + 'new_link' => false, + 'numrows' => 'emulate', + 'pconnect' => true, + 'prepare' => true, + 'ssl' => false, + 'transactions' => true, + ); + + /** + * A mapping of native error codes to DB error codes + * @var array + */ + var $errorcode_map = array( + -104 => DB_ERROR_SYNTAX, + -150 => DB_ERROR_ACCESS_VIOLATION, + -151 => DB_ERROR_ACCESS_VIOLATION, + -155 => DB_ERROR_NOSUCHTABLE, + -157 => DB_ERROR_NOSUCHFIELD, + -158 => DB_ERROR_VALUE_COUNT_ON_ROW, + -170 => DB_ERROR_MISMATCH, + -171 => DB_ERROR_MISMATCH, + -172 => DB_ERROR_INVALID, + // -204 => // Covers too many errors, need to use regex on msg + -205 => DB_ERROR_NOSUCHFIELD, + -206 => DB_ERROR_NOSUCHFIELD, + -208 => DB_ERROR_INVALID, + -219 => DB_ERROR_NOSUCHTABLE, + -297 => DB_ERROR_CONSTRAINT, + -303 => DB_ERROR_INVALID, + -413 => DB_ERROR_INVALID_NUMBER, + -530 => DB_ERROR_CONSTRAINT, + -551 => DB_ERROR_ACCESS_VIOLATION, + -552 => DB_ERROR_ACCESS_VIOLATION, + // -607 => // Covers too many errors, need to use regex on msg + -625 => DB_ERROR_CONSTRAINT_NOT_NULL, + -803 => DB_ERROR_CONSTRAINT, + -804 => DB_ERROR_VALUE_COUNT_ON_ROW, + // -902 => // Covers too many errors, need to use regex on msg + -904 => DB_ERROR_CONNECT_FAILED, + -922 => DB_ERROR_NOSUCHDB, + -923 => DB_ERROR_CONNECT_FAILED, + -924 => DB_ERROR_CONNECT_FAILED + ); + + /** + * The raw database connection created by PHP + * @var resource + */ + var $connection; + + /** + * The DSN information for connecting to a database + * @var array + */ + var $dsn = array(); + + + /** + * The number of rows affected by a data manipulation query + * @var integer + * @access private + */ + var $affected = 0; + + /** + * Should data manipulation queries be committed automatically? + * @var bool + * @access private + */ + var $autocommit = true; + + /** + * The prepared statement handle from the most recently executed statement + * + * {@internal Mainly here because the InterBase/Firebird API is only + * able to retrieve data from result sets if the statemnt handle is + * still in scope.}} + * + * @var resource + */ + var $last_stmt; + + /** + * Is the given prepared statement a data manipulation query? + * @var array + * @access private + */ + var $manip_query = array(); + + + // }}} + // {{{ constructor + + /** + * This constructor calls <kbd>parent::__construct()</kbd> + * + * @return void + */ + function __construct() + { + parent::__construct(); + } + + // }}} + // {{{ connect() + + /** + * Connect to the database server, log in and open the database + * + * Don't call this method directly. Use DB::connect() instead. + * + * PEAR DB's ibase driver supports the following extra DSN options: + * + buffers The number of database buffers to allocate for the + * server-side cache. + * + charset The default character set for a database. + * + dialect The default SQL dialect for any statement + * executed within a connection. Defaults to the + * highest one supported by client libraries. + * Functional only with InterBase 6 and up. + * + role Functional only with InterBase 5 and up. + * + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function connect($dsn, $persistent = false) + { + if (!PEAR::loadExtension('interbase')) { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; + } + if ($this->dbsyntax == 'firebird') { + $this->features['limit'] = 'alter'; + } + + $params = array( + $dsn['hostspec'] + ? ($dsn['hostspec'] . ':' . $dsn['database']) + : $dsn['database'], + $dsn['username'] ? $dsn['username'] : null, + $dsn['password'] ? $dsn['password'] : null, + isset($dsn['charset']) ? $dsn['charset'] : null, + isset($dsn['buffers']) ? $dsn['buffers'] : null, + isset($dsn['dialect']) ? $dsn['dialect'] : null, + isset($dsn['role']) ? $dsn['role'] : null, + ); + + $connect_function = $persistent ? 'ibase_pconnect' : 'ibase_connect'; + + $this->connection = @call_user_func_array($connect_function, $params); + if (!$this->connection) { + return $this->ibaseRaiseError(DB_ERROR_CONNECT_FAILED); + } + return DB_OK; + } + + // }}} + // {{{ disconnect() + + /** + * Disconnects from the database server + * + * @return bool TRUE on success, FALSE on failure + */ + function disconnect() + { + $ret = @ibase_close($this->connection); + $this->connection = null; + return $ret; + } + + // }}} + // {{{ simpleQuery() + + /** + * Sends a query to the database server + * + * @param string the SQL query string + * + * @return mixed + a PHP result resrouce for successful SELECT queries + * + the DB_OK constant for other successful queries + * + a DB_Error object on failure + */ + function simpleQuery($query) + { + $ismanip = $this->_checkManip($query); + $this->last_query = $query; + $query = $this->modifyQuery($query); + $result = @ibase_query($this->connection, $query); + + if (!$result) { + return $this->ibaseRaiseError(); + } + if ($this->autocommit && $ismanip) { + @ibase_commit($this->connection); + } + if ($ismanip) { + $this->affected = $result; + return DB_OK; + } else { + $this->affected = 0; + return $result; + } + } + + // }}} + // {{{ modifyLimitQuery() + + /** + * Adds LIMIT clauses to a query string according to current DBMS standards + * + * Only works with Firebird. + * + * @param string $query the query to modify + * @param int $from the row to start to fetching (0 = the first row) + * @param int $count the numbers of rows to fetch + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * + * @return string the query string with LIMIT clauses added + * + * @access protected + */ + function modifyLimitQuery($query, $from, $count, $params = array()) + { + if ($this->dsn['dbsyntax'] == 'firebird') { + $query = preg_replace('/^([\s(])*SELECT/i', + "SELECT FIRST $count SKIP $from", $query); + } + return $query; + } + + // }}} + // {{{ nextResult() + + /** + * Move the internal ibase result pointer to the next available result + * + * @param a valid fbsql result resource + * + * @access public + * + * @return true if a result is available otherwise return false + */ + function nextResult($result) + { + return false; + } + + // }}} + // {{{ fetchInto() + + /** + * Places a row from the result set into the given array + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch (0 = first row) + * + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure + * + * @see DB_result::fetchInto() + */ + function fetchInto($result, &$arr, $fetchmode, $rownum = null) + { + if ($rownum !== null) { + return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE); + } + if ($fetchmode & DB_FETCHMODE_ASSOC) { + if (function_exists('ibase_fetch_assoc')) { + $arr = @ibase_fetch_assoc($result); + } else { + $arr = get_object_vars(ibase_fetch_object($result)); + } + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { + $arr = array_change_key_case($arr, CASE_LOWER); + } + } else { + $arr = @ibase_fetch_row($result); + } + if (!$arr) { + return null; + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + return DB_OK; + } + + // }}} + // {{{ freeResult() + + /** + * Deletes the result set and frees the memory occupied by the result set + * + * This method is not meant to be called directly. Use + * DB_result::free() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_result::free() + */ + function freeResult($result) + { + return is_resource($result) ? ibase_free_result($result) : false; + } + + // }}} + // {{{ freeQuery() + + function freeQuery($query) + { + return is_resource($query) ? ibase_free_query($query) : false; + } + + // }}} + // {{{ affectedRows() + + /** + * Determines the number of rows affected by a data maniuplation query + * + * 0 is returned for queries that don't manipulate data. + * + * @return int the number of rows. A DB_Error object on failure. + */ + function affectedRows() + { + if (is_integer($this->affected)) { + return $this->affected; + } + return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE); + } + + // }}} + // {{{ numCols() + + /** + * Gets the number of columns in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of columns. A DB_Error object on failure. + * + * @see DB_result::numCols() + */ + function numCols($result) + { + $cols = @ibase_num_fields($result); + if (!$cols) { + return $this->ibaseRaiseError(); + } + return $cols; + } + + // }}} + // {{{ prepare() + + /** + * Prepares a query for multiple execution with execute(). + * + * prepare() requires a generic query as string like <code> + * INSERT INTO numbers VALUES (?, ?, ?) + * </code>. The <kbd>?</kbd> characters are placeholders. + * + * Three types of placeholders can be used: + * + <kbd>?</kbd> a quoted scalar value, i.e. strings, integers + * + <kbd>!</kbd> value is inserted 'as is' + * + <kbd>&</kbd> requires a file name. The file's contents get + * inserted into the query (i.e. saving binary + * data in a db) + * + * Use backslashes to escape placeholder characters if you don't want + * them to be interpreted as placeholders. Example: <code> + * "UPDATE foo SET col=? WHERE col='over \& under'" + * </code> + * + * @param string $query query to be prepared + * @return mixed DB statement resource on success. DB_Error on failure. + */ + function prepare($query) + { + $tokens = preg_split('/((?<!\\\)[&?!])/', $query, -1, + PREG_SPLIT_DELIM_CAPTURE); + $token = 0; + $types = array(); + $newquery = ''; + + foreach ($tokens as $key => $val) { + switch ($val) { + case '?': + $types[$token++] = DB_PARAM_SCALAR; + break; + case '&': + $types[$token++] = DB_PARAM_OPAQUE; + break; + case '!': + $types[$token++] = DB_PARAM_MISC; + break; + default: + $tokens[$key] = preg_replace('/\\\([&?!])/', "\\1", $val); + $newquery .= $tokens[$key] . '?'; + } + } + + $newquery = substr($newquery, 0, -1); + $this->last_query = $query; + $newquery = $this->modifyQuery($newquery); + $stmt = @ibase_prepare($this->connection, $newquery); + + if ($stmt === false) { + $stmt = $this->ibaseRaiseError(); + } else { + $this->prepare_types[(int)$stmt] = $types; + $this->manip_query[(int)$stmt] = DB::isManip($query); + } + + return $stmt; + } + + // }}} + // {{{ execute() + + /** + * Executes a DB statement prepared with prepare(). + * + * @param resource $stmt a DB statement resource returned from prepare() + * @param mixed $data array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 for non-array items or the + * quantity of elements in the array. + * @return object a new DB_Result or a DB_Error when fail + * @see DB_ibase::prepare() + * @access public + */ + function &execute($stmt, $data = array()) + { + $data = (array)$data; + $this->last_parameters = $data; + + $types = $this->prepare_types[(int)$stmt]; + if (count($types) != count($data)) { + $tmp = $this->raiseError(DB_ERROR_MISMATCH); + return $tmp; + } + + $i = 0; + foreach ($data as $key => $value) { + if ($types[$i] == DB_PARAM_MISC) { + /* + * ibase doesn't seem to have the ability to pass a + * parameter along unchanged, so strip off quotes from start + * and end, plus turn two single quotes to one single quote, + * in order to avoid the quotes getting escaped by + * ibase and ending up in the database. + */ + $data[$key] = preg_replace("/^'(.*)'$/", "\\1", $data[$key]); + $data[$key] = str_replace("''", "'", $data[$key]); + } elseif ($types[$i] == DB_PARAM_OPAQUE) { + $fp = @fopen($data[$key], 'rb'); + if (!$fp) { + $tmp = $this->raiseError(DB_ERROR_ACCESS_VIOLATION); + return $tmp; + } + $data[$key] = fread($fp, filesize($data[$key])); + fclose($fp); + } + $i++; + } + + array_unshift($data, $stmt); + + $res = call_user_func_array('ibase_execute', $data); + if (!$res) { + $tmp = $this->ibaseRaiseError(); + return $tmp; + } + /* XXX need this? + if ($this->autocommit && $this->manip_query[(int)$stmt]) { + @ibase_commit($this->connection); + }*/ + $this->last_stmt = $stmt; + if ($this->manip_query[(int)$stmt] || $this->_next_query_manip) { + $this->_last_query_manip = true; + $this->_next_query_manip = false; + $tmp = DB_OK; + } else { + $this->_last_query_manip = false; + $tmp = new DB_result($this, $res); + } + return $tmp; + } + + /** + * Frees the internal resources associated with a prepared query + * + * @param resource $stmt the prepared statement's PHP resource + * @param bool $free_resource should the PHP resource be freed too? + * Use false if you need to get data + * from the result set later. + * + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_ibase::prepare() + */ + function freePrepared($stmt, $free_resource = true) + { + if (!is_resource($stmt)) { + return false; + } + if ($free_resource) { + @ibase_free_query($stmt); + } + unset($this->prepare_tokens[(int)$stmt]); + unset($this->prepare_types[(int)$stmt]); + unset($this->manip_query[(int)$stmt]); + return true; + } + + // }}} + // {{{ autoCommit() + + /** + * Enables or disables automatic commits + * + * @param bool $onoff true turns it on, false turns it off + * + * @return int DB_OK on success. A DB_Error object if the driver + * doesn't support auto-committing transactions. + */ + function autoCommit($onoff = false) + { + $this->autocommit = $onoff ? 1 : 0; + return DB_OK; + } + + // }}} + // {{{ commit() + + /** + * Commits the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function commit() + { + return @ibase_commit($this->connection); + } + + // }}} + // {{{ rollback() + + /** + * Reverts the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function rollback() + { + return @ibase_rollback($this->connection); + } + + // }}} + // {{{ transactionInit() + + function transactionInit($trans_args = 0) + { + return $trans_args + ? @ibase_trans($trans_args, $this->connection) + : @ibase_trans(); + } + + // }}} + // {{{ nextId() + + /** + * Returns the next free id in a sequence + * + * @param string $seq_name name of the sequence + * @param boolean $ondemand when true, the seqence is automatically + * created if it does not exist + * + * @return int the next id number in the sequence. + * A DB_Error object on failure. + * + * @see DB_common::nextID(), DB_common::getSequenceName(), + * DB_ibase::createSequence(), DB_ibase::dropSequence() + */ + function nextId($seq_name, $ondemand = true) + { + $sqn = strtoupper($this->getSequenceName($seq_name)); + $repeat = 0; + do { + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result = $this->query("SELECT GEN_ID(${sqn}, 1) " + . 'FROM RDB$GENERATORS ' + . "WHERE RDB\$GENERATOR_NAME='${sqn}'"); + $this->popErrorHandling(); + if ($ondemand && DB::isError($result)) { + $repeat = 1; + $result = $this->createSequence($seq_name); + if (DB::isError($result)) { + return $result; + } + } else { + $repeat = 0; + } + } while ($repeat); + if (DB::isError($result)) { + return $this->raiseError($result); + } + $arr = $result->fetchRow(DB_FETCHMODE_ORDERED); + $result->free(); + return $arr[0]; + } + + // }}} + // {{{ createSequence() + + /** + * Creates a new sequence + * + * @param string $seq_name name of the new sequence + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::createSequence(), DB_common::getSequenceName(), + * DB_ibase::nextID(), DB_ibase::dropSequence() + */ + function createSequence($seq_name) + { + $sqn = strtoupper($this->getSequenceName($seq_name)); + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result = $this->query("CREATE GENERATOR ${sqn}"); + $this->popErrorHandling(); + + return $result; + } + + // }}} + // {{{ dropSequence() + + /** + * Deletes a sequence + * + * @param string $seq_name name of the sequence to be deleted + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::dropSequence(), DB_common::getSequenceName(), + * DB_ibase::nextID(), DB_ibase::createSequence() + */ + function dropSequence($seq_name) + { + return $this->query('DELETE FROM RDB$GENERATORS ' + . "WHERE RDB\$GENERATOR_NAME='" + . strtoupper($this->getSequenceName($seq_name)) + . "'"); + } + + // }}} + // {{{ _ibaseFieldFlags() + + /** + * Get the column's flags + * + * Supports "primary_key", "unique_key", "not_null", "default", + * "computed" and "blob". + * + * @param string $field_name the name of the field + * @param string $table_name the name of the table + * + * @return string the flags + * + * @access private + */ + function _ibaseFieldFlags($field_name, $table_name) + { + $sql = 'SELECT R.RDB$CONSTRAINT_TYPE CTYPE' + .' FROM RDB$INDEX_SEGMENTS I' + .' JOIN RDB$RELATION_CONSTRAINTS R ON I.RDB$INDEX_NAME=R.RDB$INDEX_NAME' + .' WHERE I.RDB$FIELD_NAME=\'' . $field_name . '\'' + .' AND UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\''; + + $result = @ibase_query($this->connection, $sql); + if (!$result) { + return $this->ibaseRaiseError(); + } + + $flags = ''; + if ($obj = @ibase_fetch_object($result)) { + @ibase_free_result($result); + if (isset($obj->CTYPE) && trim($obj->CTYPE) == 'PRIMARY KEY') { + $flags .= 'primary_key '; + } + if (isset($obj->CTYPE) && trim($obj->CTYPE) == 'UNIQUE') { + $flags .= 'unique_key '; + } + } + + $sql = 'SELECT R.RDB$NULL_FLAG AS NFLAG,' + .' R.RDB$DEFAULT_SOURCE AS DSOURCE,' + .' F.RDB$FIELD_TYPE AS FTYPE,' + .' F.RDB$COMPUTED_SOURCE AS CSOURCE' + .' FROM RDB$RELATION_FIELDS R ' + .' JOIN RDB$FIELDS F ON R.RDB$FIELD_SOURCE=F.RDB$FIELD_NAME' + .' WHERE UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\'' + .' AND R.RDB$FIELD_NAME=\'' . $field_name . '\''; + + $result = @ibase_query($this->connection, $sql); + if (!$result) { + return $this->ibaseRaiseError(); + } + if ($obj = @ibase_fetch_object($result)) { + @ibase_free_result($result); + if (isset($obj->NFLAG)) { + $flags .= 'not_null '; + } + if (isset($obj->DSOURCE)) { + $flags .= 'default '; + } + if (isset($obj->CSOURCE)) { + $flags .= 'computed '; + } + if (isset($obj->FTYPE) && $obj->FTYPE == 261) { + $flags .= 'blob '; + } + } + + return trim($flags); + } + + // }}} + // {{{ ibaseRaiseError() + + /** + * Produces a DB_Error object regarding the current problem + * + * @param int $errno if the error is being manually raised pass a + * DB_ERROR* constant here. If this isn't passed + * the error information gathered from the DBMS. + * + * @return object the DB_Error object + * + * @see DB_common::raiseError(), + * DB_ibase::errorNative(), DB_ibase::errorCode() + */ + function &ibaseRaiseError($errno = null) + { + if ($errno === null) { + $errno = $this->errorCode($this->errorNative()); + } + $tmp = $this->raiseError($errno, null, null, null, @ibase_errmsg()); + return $tmp; + } + + // }}} + // {{{ errorNative() + + /** + * Gets the DBMS' native error code produced by the last query + * + * @return int the DBMS' error code. NULL if there is no error code. + * + * @since Method available since Release 1.7.0 + */ + function errorNative() + { + if (function_exists('ibase_errcode')) { + return @ibase_errcode(); + } + if (preg_match('/^Dynamic SQL Error SQL error code = ([0-9-]+)/i', + @ibase_errmsg(), $m)) { + return (int)$m[1]; + } + return null; + } + + // }}} + // {{{ errorCode() + + /** + * Maps native error codes to DB's portable ones + * + * @param int $nativecode the error code returned by the DBMS + * + * @return int the portable DB error code. Return DB_ERROR if the + * current driver doesn't have a mapping for the + * $nativecode submitted. + * + * @since Method available since Release 1.7.0 + */ + function errorCode($nativecode = null) + { + if (isset($this->errorcode_map[$nativecode])) { + return $this->errorcode_map[$nativecode]; + } + + static $error_regexps; + if (!isset($error_regexps)) { + $error_regexps = array( + '/generator .* is not defined/' + => DB_ERROR_SYNTAX, // for compat. w ibase_errcode() + '/violation of [\w ]+ constraint/i' + => DB_ERROR_CONSTRAINT, + '/table.*(not exist|not found|unknown)/i' + => DB_ERROR_NOSUCHTABLE, + '/table .* already exists/i' + => DB_ERROR_ALREADY_EXISTS, + '/unsuccessful metadata update .* failed attempt to store duplicate value/i' + => DB_ERROR_ALREADY_EXISTS, + '/unsuccessful metadata update .* not found/i' + => DB_ERROR_NOT_FOUND, + '/validation error for column .* value "\*\*\* null/i' + => DB_ERROR_CONSTRAINT_NOT_NULL, + '/conversion error from string/i' + => DB_ERROR_INVALID_NUMBER, + '/no permission for/i' + => DB_ERROR_ACCESS_VIOLATION, + '/arithmetic exception, numeric overflow, or string truncation/i' + => DB_ERROR_INVALID, + '/feature is not supported/i' + => DB_ERROR_NOT_CAPABLE, + ); + } + + $errormsg = @ibase_errmsg(); + foreach ($error_regexps as $regexp => $code) { + if (preg_match($regexp, $errormsg)) { + return $code; + } + } + return DB_ERROR; + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table or a result set + * + * NOTE: only supports 'table' and 'flags' if <var>$result</var> + * is a table name. + * + * @param object|string $result DB_result object from a query or a + * string containing the name of a table. + * While this also accepts a query result + * resource identifier, this behavior is + * deprecated. + * @param int $mode a valid tableInfo mode + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * + * @see DB_common::tableInfo() + */ + function tableInfo($result, $mode = null) + { + if (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + $id = @ibase_query($this->connection, + "SELECT * FROM $result WHERE 1=0"); + $got_string = true; + } elseif (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $id = $result->result; + $got_string = false; + } else { + /* + * Probably received a result resource identifier. + * Copy it. + * Deprecated. Here for compatibility only. + */ + $id = $result; + $got_string = false; + } + + if (!is_resource($id)) { + return $this->ibaseRaiseError(DB_ERROR_NEED_MORE_DATA); + } + + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; + } else { + $case_func = 'strval'; + } + + $count = @ibase_num_fields($id); + $res = array(); + + if ($mode) { + $res['num_fields'] = $count; + } + + for ($i = 0; $i < $count; $i++) { + $info = @ibase_field_info($id, $i); + $res[$i] = array( + 'table' => $got_string ? $case_func($result) : '', + 'name' => $case_func($info['name']), + 'type' => $info['type'], + 'len' => $info['length'], + 'flags' => ($got_string) + ? $this->_ibaseFieldFlags($info['name'], $result) + : '', + ); + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + } + + // free the result only if we were called on a table + if ($got_string) { + @ibase_free_result($id); + } + return $res; + } + + // }}} + // {{{ getSpecialQuery() + + /** + * Obtains the query string needed for listing a given type of objects + * + * @param string $type the kind of objects you want to retrieve + * + * @return string the SQL query string or null if the driver doesn't + * support the object type requested + * + * @access protected + * @see DB_common::getListOf() + */ + function getSpecialQuery($type) + { + switch ($type) { + case 'tables': + return 'SELECT DISTINCT R.RDB$RELATION_NAME FROM ' + . 'RDB$RELATION_FIELDS R WHERE R.RDB$SYSTEM_FLAG=0'; + case 'views': + return 'SELECT DISTINCT RDB$VIEW_NAME from RDB$VIEW_RELATIONS'; + case 'users': + return 'SELECT DISTINCT RDB$USER FROM RDB$USER_PRIVILEGES'; + default: + return null; + } + } + + // }}} + +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/civicrm/vendor/pear/db/DB/ifx.php b/civicrm/vendor/pear/db/DB/ifx.php new file mode 100644 index 0000000000..54eacc45a7 --- /dev/null +++ b/civicrm/vendor/pear/db/DB/ifx.php @@ -0,0 +1,683 @@ +<?php + +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * The PEAR DB driver for PHP's ifx extension + * for interacting with Informix databases + * + * PHP version 5 + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @category Database + * @package DB + * @author Tomas V.V.Cox <cox@idecnet.com> + * @author Daniel Convissor <danielc@php.net> + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id$ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ +require_once 'DB/common.php'; + +/** + * The methods PEAR DB uses to interact with PHP's ifx extension + * for interacting with Informix databases + * + * These methods overload the ones declared in DB_common. + * + * More info on Informix errors can be found at: + * http://www.informix.com/answers/english/ierrors.htm + * + * TODO: + * - set needed env Informix vars on connect + * - implement native prepare/execute + * + * @category Database + * @package DB + * @author Tomas V.V.Cox <cox@idecnet.com> + * @author Daniel Convissor <danielc@php.net> + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/DB + */ +class DB_ifx extends DB_common +{ + // {{{ properties + + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'ifx'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'ifx'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * @var array + */ + var $features = array( + 'limit' => 'emulate', + 'new_link' => false, + 'numrows' => 'emulate', + 'pconnect' => true, + 'prepare' => false, + 'ssl' => false, + 'transactions' => true, + ); + + /** + * A mapping of native error codes to DB error codes + * @var array + */ + var $errorcode_map = array( + '-201' => DB_ERROR_SYNTAX, + '-206' => DB_ERROR_NOSUCHTABLE, + '-217' => DB_ERROR_NOSUCHFIELD, + '-236' => DB_ERROR_VALUE_COUNT_ON_ROW, + '-239' => DB_ERROR_CONSTRAINT, + '-253' => DB_ERROR_SYNTAX, + '-268' => DB_ERROR_CONSTRAINT, + '-292' => DB_ERROR_CONSTRAINT_NOT_NULL, + '-310' => DB_ERROR_ALREADY_EXISTS, + '-316' => DB_ERROR_ALREADY_EXISTS, + '-319' => DB_ERROR_NOT_FOUND, + '-329' => DB_ERROR_NODBSELECTED, + '-346' => DB_ERROR_CONSTRAINT, + '-386' => DB_ERROR_CONSTRAINT_NOT_NULL, + '-391' => DB_ERROR_CONSTRAINT_NOT_NULL, + '-554' => DB_ERROR_SYNTAX, + '-691' => DB_ERROR_CONSTRAINT, + '-692' => DB_ERROR_CONSTRAINT, + '-703' => DB_ERROR_CONSTRAINT_NOT_NULL, + '-1202' => DB_ERROR_DIVZERO, + '-1204' => DB_ERROR_INVALID_DATE, + '-1205' => DB_ERROR_INVALID_DATE, + '-1206' => DB_ERROR_INVALID_DATE, + '-1209' => DB_ERROR_INVALID_DATE, + '-1210' => DB_ERROR_INVALID_DATE, + '-1212' => DB_ERROR_INVALID_DATE, + '-1213' => DB_ERROR_INVALID_NUMBER, + ); + + /** + * The raw database connection created by PHP + * @var resource + */ + var $connection; + + /** + * The DSN information for connecting to a database + * @var array + */ + var $dsn = array(); + + + /** + * Should data manipulation queries be committed automatically? + * @var bool + * @access private + */ + var $autocommit = true; + + /** + * The quantity of transactions begun + * + * {@internal While this is private, it can't actually be designated + * private in PHP 5 because it is directly accessed in the test suite.}} + * + * @var integer + * @access private + */ + var $transaction_opcount = 0; + + /** + * The number of rows affected by a data manipulation query + * @var integer + * @access private + */ + var $affected = 0; + + + // }}} + // {{{ constructor + + /** + * This constructor calls <kbd>parent::__construct()</kbd> + * + * @return void + */ + function __construct() + { + parent::__construct(); + } + + // }}} + // {{{ connect() + + /** + * Connect to the database server, log in and open the database + * + * Don't call this method directly. Use DB::connect() instead. + * + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function connect($dsn, $persistent = false) + { + if (!PEAR::loadExtension('informix') && + !PEAR::loadExtension('Informix')) + { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; + } + + $dbhost = $dsn['hostspec'] ? '@' . $dsn['hostspec'] : ''; + $dbname = $dsn['database'] ? $dsn['database'] . $dbhost : ''; + $user = $dsn['username'] ? $dsn['username'] : ''; + $pw = $dsn['password'] ? $dsn['password'] : ''; + + $connect_function = $persistent ? 'ifx_pconnect' : 'ifx_connect'; + + $this->connection = @$connect_function($dbname, $user, $pw); + if (!is_resource($this->connection)) { + return $this->ifxRaiseError(DB_ERROR_CONNECT_FAILED); + } + return DB_OK; + } + + // }}} + // {{{ disconnect() + + /** + * Disconnects from the database server + * + * @return bool TRUE on success, FALSE on failure + */ + function disconnect() + { + $ret = @ifx_close($this->connection); + $this->connection = null; + return $ret; + } + + // }}} + // {{{ simpleQuery() + + /** + * Sends a query to the database server + * + * @param string the SQL query string + * + * @return mixed + a PHP result resrouce for successful SELECT queries + * + the DB_OK constant for other successful queries + * + a DB_Error object on failure + */ + function simpleQuery($query) + { + $ismanip = $this->_checkManip($query); + $this->last_query = $query; + $this->affected = null; + if (preg_match('/(SELECT|EXECUTE)/i', $query)) { //TESTME: Use !DB::isManip()? + // the scroll is needed for fetching absolute row numbers + // in a select query result + $result = @ifx_query($query, $this->connection, IFX_SCROLL); + } else { + if (!$this->autocommit && $ismanip) { + if ($this->transaction_opcount == 0) { + $result = @ifx_query('BEGIN WORK', $this->connection); + if (!$result) { + return $this->ifxRaiseError(); + } + } + $this->transaction_opcount++; + } + $result = @ifx_query($query, $this->connection); + } + if (!$result) { + return $this->ifxRaiseError(); + } + $this->affected = @ifx_affected_rows($result); + // Determine which queries should return data, and which + // should return an error code only. + if (preg_match('/(SELECT|EXECUTE)/i', $query)) { + return $result; + } + // XXX Testme: free results inside a transaction + // may cause to stop it and commit the work? + + // Result has to be freed even with a insert or update + @ifx_free_result($result); + + return DB_OK; + } + + // }}} + // {{{ nextResult() + + /** + * Move the internal ifx result pointer to the next available result + * + * @param a valid fbsql result resource + * + * @access public + * + * @return true if a result is available otherwise return false + */ + function nextResult($result) + { + return false; + } + + // }}} + // {{{ affectedRows() + + /** + * Determines the number of rows affected by a data maniuplation query + * + * 0 is returned for queries that don't manipulate data. + * + * @return int the number of rows. A DB_Error object on failure. + */ + function affectedRows() + { + if ($this->_last_query_manip) { + return $this->affected; + } else { + return 0; + } + } + + // }}} + // {{{ fetchInto() + + /** + * Places a row from the result set into the given array + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch (0 = first row) + * + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure + * + * @see DB_result::fetchInto() + */ + function fetchInto($result, &$arr, $fetchmode, $rownum = null) + { + if (($rownum !== null) && ($rownum < 0)) { + return null; + } + if ($rownum === null) { + /* + * Even though fetch_row() should return the next row if + * $rownum is null, it doesn't in all cases. Bug 598. + */ + $rownum = 'NEXT'; + } else { + // Index starts at row 1, unlike most DBMS's starting at 0. + $rownum++; + } + if (!$arr = @ifx_fetch_row($result, $rownum)) { + return null; + } + if ($fetchmode !== DB_FETCHMODE_ASSOC) { + $i=0; + $order = array(); + foreach ($arr as $val) { + $order[$i++] = $val; + } + $arr = $order; + } elseif ($fetchmode == DB_FETCHMODE_ASSOC && + $this->options['portability'] & DB_PORTABILITY_LOWERCASE) + { + $arr = array_change_key_case($arr, CASE_LOWER); + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + return DB_OK; + } + + // }}} + // {{{ numCols() + + /** + * Gets the number of columns in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of columns. A DB_Error object on failure. + * + * @see DB_result::numCols() + */ + function numCols($result) + { + if (!$cols = @ifx_num_fields($result)) { + return $this->ifxRaiseError(); + } + return $cols; + } + + // }}} + // {{{ freeResult() + + /** + * Deletes the result set and frees the memory occupied by the result set + * + * This method is not meant to be called directly. Use + * DB_result::free() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_result::free() + */ + function freeResult($result) + { + return is_resource($result) ? ifx_free_result($result) : false; + } + + // }}} + // {{{ autoCommit() + + /** + * Enables or disables automatic commits + * + * @param bool $onoff true turns it on, false turns it off + * + * @return int DB_OK on success. A DB_Error object if the driver + * doesn't support auto-committing transactions. + */ + function autoCommit($onoff = true) + { + // XXX if $this->transaction_opcount > 0, we should probably + // issue a warning here. + $this->autocommit = $onoff ? true : false; + return DB_OK; + } + + // }}} + // {{{ commit() + + /** + * Commits the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function commit() + { + if ($this->transaction_opcount > 0) { + $result = @ifx_query('COMMIT WORK', $this->connection); + $this->transaction_opcount = 0; + if (!$result) { + return $this->ifxRaiseError(); + } + } + return DB_OK; + } + + // }}} + // {{{ rollback() + + /** + * Reverts the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function rollback() + { + if ($this->transaction_opcount > 0) { + $result = @ifx_query('ROLLBACK WORK', $this->connection); + $this->transaction_opcount = 0; + if (!$result) { + return $this->ifxRaiseError(); + } + } + return DB_OK; + } + + // }}} + // {{{ ifxRaiseError() + + /** + * Produces a DB_Error object regarding the current problem + * + * @param int $errno if the error is being manually raised pass a + * DB_ERROR* constant here. If this isn't passed + * the error information gathered from the DBMS. + * + * @return object the DB_Error object + * + * @see DB_common::raiseError(), + * DB_ifx::errorNative(), DB_ifx::errorCode() + */ + function ifxRaiseError($errno = null) + { + if ($errno === null) { + $errno = $this->errorCode(ifx_error()); + } + return $this->raiseError($errno, null, null, null, + $this->errorNative()); + } + + // }}} + // {{{ errorNative() + + /** + * Gets the DBMS' native error code and message produced by the last query + * + * @return string the DBMS' error code and message + */ + function errorNative() + { + return @ifx_error() . ' ' . @ifx_errormsg(); + } + + // }}} + // {{{ errorCode() + + /** + * Maps native error codes to DB's portable ones. + * + * Requires that the DB implementation's constructor fills + * in the <var>$errorcode_map</var> property. + * + * @param string $nativecode error code returned by the database + * @return int a portable DB error code, or DB_ERROR if this DB + * implementation has no mapping for the given error code. + */ + function errorCode($nativecode) + { + if (preg_match('/SQLCODE=(.*)]/', $nativecode, $match)) { + $code = $match[1]; + if (isset($this->errorcode_map[$code])) { + return $this->errorcode_map[$code]; + } + } + return DB_ERROR; + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table or a result set + * + * NOTE: only supports 'table' if <var>$result</var> is a table name. + * + * If analyzing a query result and the result has duplicate field names, + * an error will be raised saying + * <samp>can't distinguish duplicate field names</samp>. + * + * @param object|string $result DB_result object from a query or a + * string containing the name of a table. + * While this also accepts a query result + * resource identifier, this behavior is + * deprecated. + * @param int $mode a valid tableInfo mode + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * + * @see DB_common::tableInfo() + * @since Method available since Release 1.6.0 + */ + function tableInfo($result, $mode = null) + { + if (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + $id = @ifx_query("SELECT * FROM $result WHERE 1=0", + $this->connection); + $got_string = true; + } elseif (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $id = $result->result; + $got_string = false; + } else { + /* + * Probably received a result resource identifier. + * Copy it. + */ + $id = $result; + $got_string = false; + } + + if (!is_resource($id)) { + return $this->ifxRaiseError(DB_ERROR_NEED_MORE_DATA); + } + + $flds = @ifx_fieldproperties($id); + $count = @ifx_num_fields($id); + + if (count($flds) != $count) { + return $this->raiseError("can't distinguish duplicate field names"); + } + + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; + } else { + $case_func = 'strval'; + } + + $i = 0; + $res = array(); + + if ($mode) { + $res['num_fields'] = $count; + } + + foreach ($flds as $key => $value) { + $props = explode(';', $value); + $res[$i] = array( + 'table' => $got_string ? $case_func($result) : '', + 'name' => $case_func($key), + 'type' => $props[0], + 'len' => $props[1], + 'flags' => $props[4] == 'N' ? 'not_null' : '', + ); + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + $i++; + } + + // free the result only if we were called on a table + if ($got_string) { + @ifx_free_result($id); + } + return $res; + } + + // }}} + // {{{ getSpecialQuery() + + /** + * Obtains the query string needed for listing a given type of objects + * + * @param string $type the kind of objects you want to retrieve + * + * @return string the SQL query string or null if the driver doesn't + * support the object type requested + * + * @access protected + * @see DB_common::getListOf() + */ + function getSpecialQuery($type) + { + switch ($type) { + case 'tables': + return 'SELECT tabname FROM systables WHERE tabid >= 100'; + default: + return null; + } + } + + // }}} + +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/civicrm/vendor/pear/db/DB/msql.php b/civicrm/vendor/pear/db/DB/msql.php new file mode 100644 index 0000000000..202f93eded --- /dev/null +++ b/civicrm/vendor/pear/db/DB/msql.php @@ -0,0 +1,831 @@ +<?php + +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * The PEAR DB driver for PHP's msql extension + * for interacting with Mini SQL databases + * + * PHP's mSQL extension did weird things with NULL values prior to PHP + * 4.3.11 and 5.0.4. Make sure your version of PHP meets or exceeds + * those versions. + * + * PHP version 5 + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @category Database + * @package DB + * @author Daniel Convissor <danielc@php.net> + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id$ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ +require_once 'DB/common.php'; + +/** + * The methods PEAR DB uses to interact with PHP's msql extension + * for interacting with Mini SQL databases + * + * These methods overload the ones declared in DB_common. + * + * PHP's mSQL extension did weird things with NULL values prior to PHP + * 4.3.11 and 5.0.4. Make sure your version of PHP meets or exceeds + * those versions. + * + * @category Database + * @package DB + * @author Daniel Convissor <danielc@php.net> + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/DB + * @since Class not functional until Release 1.7.0 + */ +class DB_msql extends DB_common +{ + // {{{ properties + + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'msql'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'msql'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * @var array + */ + var $features = array( + 'limit' => 'emulate', + 'new_link' => false, + 'numrows' => true, + 'pconnect' => true, + 'prepare' => false, + 'ssl' => false, + 'transactions' => false, + ); + + /** + * A mapping of native error codes to DB error codes + * @var array + */ + var $errorcode_map = array( + ); + + /** + * The raw database connection created by PHP + * @var resource + */ + var $connection; + + /** + * The DSN information for connecting to a database + * @var array + */ + var $dsn = array(); + + + /** + * The query result resource created by PHP + * + * Used to make affectedRows() work. Only contains the result for + * data manipulation queries. Contains false for other queries. + * + * @var resource + * @access private + */ + var $_result; + + + // }}} + // {{{ constructor + + /** + * This constructor calls <kbd>parent::__construct()</kbd> + * + * @return void + */ + function __construct() + { + parent::__construct(); + } + + // }}} + // {{{ connect() + + /** + * Connect to the database server, log in and open the database + * + * Don't call this method directly. Use DB::connect() instead. + * + * Example of how to connect: + * <code> + * require_once 'DB.php'; + * + * // $dsn = 'msql://hostname/dbname'; // use a TCP connection + * $dsn = 'msql:///dbname'; // use a socket + * $options = array( + * 'portability' => DB_PORTABILITY_ALL, + * ); + * + * $db = DB::connect($dsn, $options); + * if (PEAR::isError($db)) { + * die($db->getMessage()); + * } + * </code> + * + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function connect($dsn, $persistent = false) + { + if (!PEAR::loadExtension('msql')) { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; + } + + $params = array(); + if ($dsn['hostspec']) { + $params[] = $dsn['port'] + ? $dsn['hostspec'] . ',' . $dsn['port'] + : $dsn['hostspec']; + } + + $connect_function = $persistent ? 'msql_pconnect' : 'msql_connect'; + + $ini = ini_get('track_errors'); + $php_errormsg = ''; + if ($ini) { + $this->connection = @call_user_func_array($connect_function, + $params); + } else { + @ini_set('track_errors', 1); + $this->connection = @call_user_func_array($connect_function, + $params); + @ini_set('track_errors', $ini); + } + + if (!$this->connection) { + if (($err = @msql_error()) != '') { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + $err); + } else { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + $php_errormsg); + } + } + + if (!@msql_select_db($dsn['database'], $this->connection)) { + return $this->msqlRaiseError(); + } + return DB_OK; + } + + // }}} + // {{{ disconnect() + + /** + * Disconnects from the database server + * + * @return bool TRUE on success, FALSE on failure + */ + function disconnect() + { + $ret = @msql_close($this->connection); + $this->connection = null; + return $ret; + } + + // }}} + // {{{ simpleQuery() + + /** + * Sends a query to the database server + * + * @param string the SQL query string + * + * @return mixed + a PHP result resrouce for successful SELECT queries + * + the DB_OK constant for other successful queries + * + a DB_Error object on failure + */ + function simpleQuery($query) + { + $this->last_query = $query; + $query = $this->modifyQuery($query); + $result = @msql_query($query, $this->connection); + if (!$result) { + return $this->msqlRaiseError(); + } + // Determine which queries that should return data, and which + // should return an error code only. + if ($this->_checkManip($query)) { + $this->_result = $result; + return DB_OK; + } else { + $this->_result = false; + return $result; + } + } + + + // }}} + // {{{ nextResult() + + /** + * Move the internal msql result pointer to the next available result + * + * @param a valid fbsql result resource + * + * @access public + * + * @return true if a result is available otherwise return false + */ + function nextResult($result) + { + return false; + } + + // }}} + // {{{ fetchInto() + + /** + * Places a row from the result set into the given array + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * PHP's mSQL extension did weird things with NULL values prior to PHP + * 4.3.11 and 5.0.4. Make sure your version of PHP meets or exceeds + * those versions. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch (0 = first row) + * + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure + * + * @see DB_result::fetchInto() + */ + function fetchInto($result, &$arr, $fetchmode, $rownum = null) + { + if ($rownum !== null) { + if (!@msql_data_seek($result, $rownum)) { + return null; + } + } + if ($fetchmode & DB_FETCHMODE_ASSOC) { + $arr = @msql_fetch_array($result, MSQL_ASSOC); + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { + $arr = array_change_key_case($arr, CASE_LOWER); + } + } else { + $arr = @msql_fetch_row($result); + } + if (!$arr) { + return null; + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + return DB_OK; + } + + // }}} + // {{{ freeResult() + + /** + * Deletes the result set and frees the memory occupied by the result set + * + * This method is not meant to be called directly. Use + * DB_result::free() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_result::free() + */ + function freeResult($result) + { + return is_resource($result) ? msql_free_result($result) : false; + } + + // }}} + // {{{ numCols() + + /** + * Gets the number of columns in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of columns. A DB_Error object on failure. + * + * @see DB_result::numCols() + */ + function numCols($result) + { + $cols = @msql_num_fields($result); + if (!$cols) { + return $this->msqlRaiseError(); + } + return $cols; + } + + // }}} + // {{{ numRows() + + /** + * Gets the number of rows in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numRows() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of rows. A DB_Error object on failure. + * + * @see DB_result::numRows() + */ + function numRows($result) + { + $rows = @msql_num_rows($result); + if ($rows === false) { + return $this->msqlRaiseError(); + } + return $rows; + } + + // }}} + // {{{ affected() + + /** + * Determines the number of rows affected by a data maniuplation query + * + * 0 is returned for queries that don't manipulate data. + * + * @return int the number of rows. A DB_Error object on failure. + */ + function affectedRows() + { + if (!$this->_result) { + return 0; + } + return msql_affected_rows($this->_result); + } + + // }}} + // {{{ nextId() + + /** + * Returns the next free id in a sequence + * + * @param string $seq_name name of the sequence + * @param boolean $ondemand when true, the seqence is automatically + * created if it does not exist + * + * @return int the next id number in the sequence. + * A DB_Error object on failure. + * + * @see DB_common::nextID(), DB_common::getSequenceName(), + * DB_msql::createSequence(), DB_msql::dropSequence() + */ + function nextId($seq_name, $ondemand = true) + { + $seqname = $this->getSequenceName($seq_name); + $repeat = false; + do { + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result = $this->query("SELECT _seq FROM ${seqname}"); + $this->popErrorHandling(); + if ($ondemand && DB::isError($result) && + $result->getCode() == DB_ERROR_NOSUCHTABLE) { + $repeat = true; + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result = $this->createSequence($seq_name); + $this->popErrorHandling(); + if (DB::isError($result)) { + return $this->raiseError($result); + } + } else { + $repeat = false; + } + } while ($repeat); + if (DB::isError($result)) { + return $this->raiseError($result); + } + $arr = $result->fetchRow(DB_FETCHMODE_ORDERED); + $result->free(); + return $arr[0]; + } + + // }}} + // {{{ createSequence() + + /** + * Creates a new sequence + * + * Also creates a new table to associate the sequence with. Uses + * a separate table to ensure portability with other drivers. + * + * @param string $seq_name name of the new sequence + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::createSequence(), DB_common::getSequenceName(), + * DB_msql::nextID(), DB_msql::dropSequence() + */ + function createSequence($seq_name) + { + $seqname = $this->getSequenceName($seq_name); + $res = $this->query('CREATE TABLE ' . $seqname + . ' (id INTEGER NOT NULL)'); + if (DB::isError($res)) { + return $res; + } + $res = $this->query("CREATE SEQUENCE ON ${seqname}"); + return $res; + } + + // }}} + // {{{ dropSequence() + + /** + * Deletes a sequence + * + * @param string $seq_name name of the sequence to be deleted + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::dropSequence(), DB_common::getSequenceName(), + * DB_msql::nextID(), DB_msql::createSequence() + */ + function dropSequence($seq_name) + { + return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); + } + + // }}} + // {{{ quoteIdentifier() + + /** + * mSQL does not support delimited identifiers + * + * @param string $str the identifier name to be quoted + * + * @return object a DB_Error object + * + * @see DB_common::quoteIdentifier() + * @since Method available since Release 1.7.0 + */ + function quoteIdentifier($str) + { + return $this->raiseError(DB_ERROR_UNSUPPORTED); + } + + // }}} + // {{{ quoteFloat() + + /** + * Formats a float value for use within a query in a locale-independent + * manner. + * + * @param float the float value to be quoted. + * @return string the quoted string. + * @see DB_common::quoteSmart() + * @since Method available since release 1.7.8. + */ + function quoteFloat($float) { + return $this->escapeSimple(str_replace(',', '.', strval(floatval($float)))); + } + + // }}} + // {{{ escapeSimple() + + /** + * Escapes a string according to the current DBMS's standards + * + * @param string $str the string to be escaped + * + * @return string the escaped string + * + * @see DB_common::quoteSmart() + * @since Method available since Release 1.7.0 + */ + function escapeSimple($str) + { + return addslashes($str); + } + + // }}} + // {{{ msqlRaiseError() + + /** + * Produces a DB_Error object regarding the current problem + * + * @param int $errno if the error is being manually raised pass a + * DB_ERROR* constant here. If this isn't passed + * the error information gathered from the DBMS. + * + * @return object the DB_Error object + * + * @see DB_common::raiseError(), + * DB_msql::errorNative(), DB_msql::errorCode() + */ + function msqlRaiseError($errno = null) + { + $native = $this->errorNative(); + if ($errno === null) { + $errno = $this->errorCode($native); + } + return $this->raiseError($errno, null, null, null, $native); + } + + // }}} + // {{{ errorNative() + + /** + * Gets the DBMS' native error message produced by the last query + * + * @return string the DBMS' error message + */ + function errorNative() + { + return @msql_error(); + } + + // }}} + // {{{ errorCode() + + /** + * Determines PEAR::DB error code from the database's text error message + * + * @param string $errormsg the error message returned from the database + * + * @return integer the error number from a DB_ERROR* constant + */ + function errorCode($errormsg) + { + static $error_regexps; + + // PHP 5.2+ prepends the function name to $php_errormsg, so we need + // this hack to work around it, per bug #9599. + $errormsg = preg_replace('/^msql[a-z_]+\(\): /', '', $errormsg); + + if (!isset($error_regexps)) { + $error_regexps = array( + '/^Access to database denied/i' + => DB_ERROR_ACCESS_VIOLATION, + '/^Bad index name/i' + => DB_ERROR_ALREADY_EXISTS, + '/^Bad order field/i' + => DB_ERROR_SYNTAX, + '/^Bad type for comparison/i' + => DB_ERROR_SYNTAX, + '/^Can\'t perform LIKE on/i' + => DB_ERROR_SYNTAX, + '/^Can\'t use TEXT fields in LIKE comparison/i' + => DB_ERROR_SYNTAX, + '/^Couldn\'t create temporary table/i' + => DB_ERROR_CANNOT_CREATE, + '/^Error creating table file/i' + => DB_ERROR_CANNOT_CREATE, + '/^Field .* cannot be null$/i' + => DB_ERROR_CONSTRAINT_NOT_NULL, + '/^Index (field|condition) .* cannot be null$/i' + => DB_ERROR_SYNTAX, + '/^Invalid date format/i' + => DB_ERROR_INVALID_DATE, + '/^Invalid time format/i' + => DB_ERROR_INVALID, + '/^Literal value for .* is wrong type$/i' + => DB_ERROR_INVALID_NUMBER, + '/^No Database Selected/i' + => DB_ERROR_NODBSELECTED, + '/^No value specified for field/i' + => DB_ERROR_VALUE_COUNT_ON_ROW, + '/^Non unique value for unique index/i' + => DB_ERROR_CONSTRAINT, + '/^Out of memory for temporary table/i' + => DB_ERROR_CANNOT_CREATE, + '/^Permission denied/i' + => DB_ERROR_ACCESS_VIOLATION, + '/^Reference to un-selected table/i' + => DB_ERROR_SYNTAX, + '/^syntax error/i' + => DB_ERROR_SYNTAX, + '/^Table .* exists$/i' + => DB_ERROR_ALREADY_EXISTS, + '/^Unknown database/i' + => DB_ERROR_NOSUCHDB, + '/^Unknown field/i' + => DB_ERROR_NOSUCHFIELD, + '/^Unknown (index|system variable)/i' + => DB_ERROR_NOT_FOUND, + '/^Unknown table/i' + => DB_ERROR_NOSUCHTABLE, + '/^Unqualified field/i' + => DB_ERROR_SYNTAX, + ); + } + + foreach ($error_regexps as $regexp => $code) { + if (preg_match($regexp, $errormsg)) { + return $code; + } + } + return DB_ERROR; + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table or a result set + * + * @param object|string $result DB_result object from a query or a + * string containing the name of a table. + * While this also accepts a query result + * resource identifier, this behavior is + * deprecated. + * @param int $mode a valid tableInfo mode + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * + * @see DB_common::setOption() + */ + function tableInfo($result, $mode = null) + { + if (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + $id = @msql_query("SELECT * FROM $result", + $this->connection); + $got_string = true; + } elseif (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $id = $result->result; + $got_string = false; + } else { + /* + * Probably received a result resource identifier. + * Copy it. + * Deprecated. Here for compatibility only. + */ + $id = $result; + $got_string = false; + } + + if (!is_resource($id)) { + return $this->raiseError(DB_ERROR_NEED_MORE_DATA); + } + + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; + } else { + $case_func = 'strval'; + } + + $count = @msql_num_fields($id); + $res = array(); + + if ($mode) { + $res['num_fields'] = $count; + } + + for ($i = 0; $i < $count; $i++) { + $tmp = @msql_fetch_field($id); + + $flags = ''; + if ($tmp->not_null) { + $flags .= 'not_null '; + } + if ($tmp->unique) { + $flags .= 'unique_key '; + } + $flags = trim($flags); + + $res[$i] = array( + 'table' => $case_func($tmp->table), + 'name' => $case_func($tmp->name), + 'type' => $tmp->type, + 'len' => msql_field_len($id, $i), + 'flags' => $flags, + ); + + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + } + + // free the result only if we were called on a table + if ($got_string) { + @msql_free_result($id); + } + return $res; + } + + // }}} + // {{{ getSpecialQuery() + + /** + * Obtain a list of a given type of objects + * + * @param string $type the kind of objects you want to retrieve + * + * @return array the array containing the list of objects requested + * + * @access protected + * @see DB_common::getListOf() + */ + function getSpecialQuery($type) + { + switch ($type) { + case 'databases': + $id = @msql_list_dbs($this->connection); + break; + case 'tables': + $id = @msql_list_tables($this->dsn['database'], + $this->connection); + break; + default: + return null; + } + if (!$id) { + return $this->msqlRaiseError(); + } + $out = array(); + while ($row = @msql_fetch_row($id)) { + $out[] = $row[0]; + } + return $out; + } + + // }}} + +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/civicrm/packages/DB/mssql.php b/civicrm/vendor/pear/db/DB/mssql.php similarity index 99% rename from civicrm/packages/DB/mssql.php rename to civicrm/vendor/pear/db/DB/mssql.php index 31798a5f43..82bf63f478 100644 --- a/civicrm/packages/DB/mssql.php +++ b/civicrm/vendor/pear/db/DB/mssql.php @@ -49,7 +49,7 @@ require_once 'DB/common.php'; * @author Daniel Convissor <danielc@php.net> * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.9.3 + * @version Release: @package_version@ * @link http://pear.php.net/package/DB */ class DB_mssql extends DB_common diff --git a/civicrm/packages/DB/mysql.php b/civicrm/vendor/pear/db/DB/mysql.php similarity index 99% rename from civicrm/packages/DB/mysql.php rename to civicrm/vendor/pear/db/DB/mysql.php index 0ff72a4854..1ca86fa699 100644 --- a/civicrm/packages/DB/mysql.php +++ b/civicrm/vendor/pear/db/DB/mysql.php @@ -41,7 +41,7 @@ require_once 'DB/common.php'; * @author Daniel Convissor <danielc@php.net> * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.9.3 + * @version Release: @package_version@ * @link http://pear.php.net/package/DB */ class DB_mysql extends DB_common @@ -251,7 +251,7 @@ class DB_mysql extends DB_common if (!$this->connection) { if (($err = @mysql_error()) != '') { return $this->raiseError(DB_ERROR_CONNECT_FAILED, - null, null, null, + null, null, null, $err); } else { return $this->raiseError(DB_ERROR_CONNECT_FAILED, @@ -930,7 +930,7 @@ class DB_mysql extends DB_common return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED); } } - + /* * Probably received a table name. * Create a result resource identifier. diff --git a/civicrm/packages/DB/mysqli.php b/civicrm/vendor/pear/db/DB/mysqli.php similarity index 99% rename from civicrm/packages/DB/mysqli.php rename to civicrm/vendor/pear/db/DB/mysqli.php index 91707c19d2..2ab5438b14 100644 --- a/civicrm/packages/DB/mysqli.php +++ b/civicrm/vendor/pear/db/DB/mysqli.php @@ -43,7 +43,7 @@ require_once 'DB/common.php'; * @author Daniel Convissor <danielc@php.net> * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.9.3 + * @version Release: @package_version@ * @link http://pear.php.net/package/DB * @since Class functional since Release 1.6.3 */ @@ -256,7 +256,7 @@ class DB_mysqli extends DB_common * Example of how to connect using SSL: * <code> * require_once 'DB.php'; - * + * * $dsn = array( * 'phptype' => 'mysqli', * 'username' => 'someuser', @@ -269,11 +269,11 @@ class DB_mysqli extends DB_common * 'capath' => '/path/to/ca/dir', * 'cipher' => 'AES', * ); - * + * * $options = array( * 'ssl' => true, * ); - * + * * $db = DB::connect($dsn, $options); * if (PEAR::isError($db)) { * die($db->getMessage()); diff --git a/civicrm/vendor/pear/db/DB/oci8.php b/civicrm/vendor/pear/db/DB/oci8.php new file mode 100644 index 0000000000..06ea8c1a65 --- /dev/null +++ b/civicrm/vendor/pear/db/DB/oci8.php @@ -0,0 +1,1155 @@ +<?php + +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * The PEAR DB driver for PHP's oci8 extension + * for interacting with Oracle databases + * + * PHP version 5 + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @category Database + * @package DB + * @author James L. Pine <jlp@valinux.com> + * @author Daniel Convissor <danielc@php.net> + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id$ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ +require_once 'DB/common.php'; + +/** + * The methods PEAR DB uses to interact with PHP's oci8 extension + * for interacting with Oracle databases + * + * Definitely works with versions 8 and 9 of Oracle. + * + * These methods overload the ones declared in DB_common. + * + * Be aware... OCIError() only appears to return anything when given a + * statement, so functions return the generic DB_ERROR instead of more + * useful errors that have to do with feedback from the database. + * + * @category Database + * @package DB + * @author James L. Pine <jlp@valinux.com> + * @author Daniel Convissor <danielc@php.net> + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/DB + */ +class DB_oci8 extends DB_common +{ + // {{{ properties + + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'oci8'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'oci8'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * @var array + */ + var $features = array( + 'limit' => 'alter', + 'new_link' => '5.0.0', + 'numrows' => 'subquery', + 'pconnect' => true, + 'prepare' => true, + 'ssl' => false, + 'transactions' => true, + ); + + /** + * A mapping of native error codes to DB error codes + * @var array + */ + var $errorcode_map = array( + 1 => DB_ERROR_CONSTRAINT, + 900 => DB_ERROR_SYNTAX, + 904 => DB_ERROR_NOSUCHFIELD, + 913 => DB_ERROR_VALUE_COUNT_ON_ROW, + 921 => DB_ERROR_SYNTAX, + 923 => DB_ERROR_SYNTAX, + 942 => DB_ERROR_NOSUCHTABLE, + 955 => DB_ERROR_ALREADY_EXISTS, + 1400 => DB_ERROR_CONSTRAINT_NOT_NULL, + 1401 => DB_ERROR_INVALID, + 1407 => DB_ERROR_CONSTRAINT_NOT_NULL, + 1418 => DB_ERROR_NOT_FOUND, + 1476 => DB_ERROR_DIVZERO, + 1722 => DB_ERROR_INVALID_NUMBER, + 2289 => DB_ERROR_NOSUCHTABLE, + 2291 => DB_ERROR_CONSTRAINT, + 2292 => DB_ERROR_CONSTRAINT, + 2449 => DB_ERROR_CONSTRAINT, + 12899 => DB_ERROR_INVALID, + ); + + /** + * The raw database connection created by PHP + * @var resource + */ + var $connection; + + /** + * The DSN information for connecting to a database + * @var array + */ + var $dsn = array(); + + + /** + * Should data manipulation queries be committed automatically? + * @var bool + * @access private + */ + var $autocommit = true; + + /** + * Stores the $data passed to execute() in the oci8 driver + * + * Gets reset to array() when simpleQuery() is run. + * + * Needed in case user wants to call numRows() after prepare/execute + * was used. + * + * @var array + * @access private + */ + var $_data = array(); + + /** + * The result or statement handle from the most recently executed query + * @var resource + */ + var $last_stmt; + + /** + * Is the given prepared statement a data manipulation query? + * @var array + * @access private + */ + var $manip_query = array(); + + /** + * Store of prepared SQL queries. + * @var array + * @access private + */ + var $_prepared_queries = array(); + + + // }}} + // {{{ constructor + + /** + * This constructor calls <kbd>parent::__construct()</kbd> + * + * @return void + */ + function __construct() + { + parent::__construct(); + } + + // }}} + // {{{ connect() + + /** + * Connect to the database server, log in and open the database + * + * Don't call this method directly. Use DB::connect() instead. + * + * If PHP is at version 5.0.0 or greater: + * + Generally, oci_connect() or oci_pconnect() are used. + * + But if the new_link DSN option is set to true, oci_new_connect() + * is used. + * + * When using PHP version 4.x, OCILogon() or OCIPLogon() are used. + * + * PEAR DB's oci8 driver supports the following extra DSN options: + * + charset The character set to be used on the connection. + * Only used if PHP is at version 5.0.0 or greater + * and the Oracle server is at 9.2 or greater. + * Available since PEAR DB 1.7.0. + * + new_link If set to true, causes subsequent calls to + * connect() to return a new connection link + * instead of the existing one. WARNING: this is + * not portable to other DBMS's. + * Available since PEAR DB 1.7.0. + * + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function connect($dsn, $persistent = false) + { + if (!PEAR::loadExtension('oci8')) { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; + } + + // Backwards compatibility with DB < 1.7.0 + if (empty($dsn['database']) && !empty($dsn['hostspec'])) { + $db = $dsn['hostspec']; + } else { + $db = $dsn['database']; + } + + if (function_exists('oci_connect')) { + if (isset($dsn['new_link']) + && ($dsn['new_link'] == 'true' || $dsn['new_link'] === true)) + { + $connect_function = 'oci_new_connect'; + } else { + $connect_function = $persistent ? 'oci_pconnect' + : 'oci_connect'; + } + if (isset($this->dsn['port']) && $this->dsn['port']) { + $db = '//'.$db.':'.$this->dsn['port']; + } + + $char = empty($dsn['charset']) ? null : $dsn['charset']; + $this->connection = @$connect_function($dsn['username'], + $dsn['password'], + $db, + $char); + $error = OCIError(); + if (!empty($error) && $error['code'] == 12541) { + // Couldn't find TNS listener. Try direct connection. + $this->connection = @$connect_function($dsn['username'], + $dsn['password'], + null, + $char); + } + } else { + $connect_function = $persistent ? 'OCIPLogon' : 'OCILogon'; + if ($db) { + $this->connection = @$connect_function($dsn['username'], + $dsn['password'], + $db); + } elseif ($dsn['username'] || $dsn['password']) { + $this->connection = @$connect_function($dsn['username'], + $dsn['password']); + } + } + + if (!$this->connection) { + $error = OCIError(); + $error = (is_array($error)) ? $error['message'] : null; + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + $error); + } + return DB_OK; + } + + // }}} + // {{{ disconnect() + + /** + * Disconnects from the database server + * + * @return bool TRUE on success, FALSE on failure + */ + function disconnect() + { + if (function_exists('oci_close')) { + $ret = @oci_close($this->connection); + } else { + $ret = @OCILogOff($this->connection); + } + $this->connection = null; + return $ret; + } + + // }}} + // {{{ simpleQuery() + + /** + * Sends a query to the database server + * + * To determine how many rows of a result set get buffered using + * ocisetprefetch(), see the "result_buffering" option in setOptions(). + * This option was added in Release 1.7.0. + * + * @param string the SQL query string + * + * @return mixed + a PHP result resrouce for successful SELECT queries + * + the DB_OK constant for other successful queries + * + a DB_Error object on failure + */ + function simpleQuery($query) + { + $this->_data = array(); + $this->last_parameters = array(); + $this->last_query = $query; + $query = $this->modifyQuery($query); + $result = @OCIParse($this->connection, $query); + if (!$result) { + return $this->oci8RaiseError(); + } + if ($this->autocommit) { + $success = @OCIExecute($result,OCI_COMMIT_ON_SUCCESS); + } else { + $success = @OCIExecute($result,OCI_DEFAULT); + } + if (!$success) { + return $this->oci8RaiseError($result); + } + $this->last_stmt = $result; + if ($this->_checkManip($query)) { + return DB_OK; + } else { + @ocisetprefetch($result, $this->options['result_buffering']); + return $result; + } + } + + // }}} + // {{{ nextResult() + + /** + * Move the internal oracle result pointer to the next available result + * + * @param a valid oci8 result resource + * + * @access public + * + * @return true if a result is available otherwise return false + */ + function nextResult($result) + { + return false; + } + + // }}} + // {{{ fetchInto() + + /** + * Places a row from the result set into the given array + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch (0 = first row) + * + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure + * + * @see DB_result::fetchInto() + */ + function fetchInto($result, &$arr, $fetchmode, $rownum = null) + { + if ($rownum !== null) { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + if ($fetchmode & DB_FETCHMODE_ASSOC) { + $moredata = @OCIFetchInto($result,$arr,OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS); + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && + $moredata) + { + $arr = array_change_key_case($arr, CASE_LOWER); + } + } else { + $moredata = OCIFetchInto($result,$arr,OCI_RETURN_NULLS+OCI_RETURN_LOBS); + } + if (!$moredata) { + return null; + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + return DB_OK; + } + + // }}} + // {{{ freeResult() + + /** + * Deletes the result set and frees the memory occupied by the result set + * + * This method is not meant to be called directly. Use + * DB_result::free() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_result::free() + */ + function freeResult($result) + { + return is_resource($result) ? OCIFreeStatement($result) : false; + } + + /** + * Frees the internal resources associated with a prepared query + * + * @param resource $stmt the prepared statement's resource + * @param bool $free_resource should the PHP resource be freed too? + * Use false if you need to get data + * from the result set later. + * + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_oci8::prepare() + */ + function freePrepared($stmt, $free_resource = true) + { + if (!is_resource($stmt)) { + return false; + } + if (isset($this->prepare_types[(int)$stmt])) { + unset($this->prepare_types[(int)$stmt]); + unset($this->manip_query[(int)$stmt]); + unset($this->_prepared_queries[(int)$stmt]); + } else { + return false; + } + return true; + } + + // }}} + // {{{ numRows() + + /** + * Gets the number of rows in a result set + * + * Only works if the DB_PORTABILITY_NUMROWS portability option + * is turned on. + * + * This method is not meant to be called directly. Use + * DB_result::numRows() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of rows. A DB_Error object on failure. + * + * @see DB_result::numRows(), DB_common::setOption() + */ + function numRows($result) + { + // emulate numRows for Oracle. yuck. + if ($this->options['portability'] & DB_PORTABILITY_NUMROWS && + $result === $this->last_stmt) + { + $countquery = 'SELECT COUNT(*) FROM ('.$this->last_query.')'; + $save_query = $this->last_query; + $save_stmt = $this->last_stmt; + + $count = $this->query($countquery); + + // Restore the last query and statement. + $this->last_query = $save_query; + $this->last_stmt = $save_stmt; + + if (DB::isError($count) || + DB::isError($row = $count->fetchRow(DB_FETCHMODE_ORDERED))) + { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + + return $row[0]; + } + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + + // }}} + // {{{ numCols() + + /** + * Gets the number of columns in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of columns. A DB_Error object on failure. + * + * @see DB_result::numCols() + */ + function numCols($result) + { + $cols = @OCINumCols($result); + if (!$cols) { + return $this->oci8RaiseError($result); + } + return $cols; + } + + // }}} + // {{{ prepare() + + /** + * Prepares a query for multiple execution with execute(). + * + * With oci8, this is emulated. + * + * prepare() requires a generic query as string like <code> + * INSERT INTO numbers VALUES (?, ?, ?) + * </code>. The <kbd>?</kbd> characters are placeholders. + * + * Three types of placeholders can be used: + * + <kbd>?</kbd> a quoted scalar value, i.e. strings, integers + * + <kbd>!</kbd> value is inserted 'as is' + * + <kbd>&</kbd> requires a file name. The file's contents get + * inserted into the query (i.e. saving binary + * data in a db) + * + * Use backslashes to escape placeholder characters if you don't want + * them to be interpreted as placeholders. Example: <code> + * "UPDATE foo SET col=? WHERE col='over \& under'" + * </code> + * + * @param string $query the query to be prepared + * + * @return mixed DB statement resource on success. DB_Error on failure. + * + * @see DB_oci8::execute() + */ + function prepare($query) + { + $tokens = preg_split('/((?<!\\\)[&?!])/', $query, -1, + PREG_SPLIT_DELIM_CAPTURE); + $binds = count($tokens) - 1; + $token = 0; + $types = array(); + $newquery = ''; + + foreach ($tokens as $key => $val) { + switch ($val) { + case '?': + $types[$token++] = DB_PARAM_SCALAR; + unset($tokens[$key]); + break; + case '&': + $types[$token++] = DB_PARAM_OPAQUE; + unset($tokens[$key]); + break; + case '!': + $types[$token++] = DB_PARAM_MISC; + unset($tokens[$key]); + break; + default: + $tokens[$key] = preg_replace('/\\\([&?!])/', "\\1", $val); + if ($key != $binds) { + $newquery .= $tokens[$key] . ':bind' . $token; + } else { + $newquery .= $tokens[$key]; + } + } + } + + $this->last_query = $query; + $newquery = $this->modifyQuery($newquery); + if (!$stmt = @OCIParse($this->connection, $newquery)) { + return $this->oci8RaiseError(); + } + $this->prepare_types[(int)$stmt] = $types; + $this->manip_query[(int)$stmt] = DB::isManip($query); + $this->_prepared_queries[(int)$stmt] = $newquery; + return $stmt; + } + + // }}} + // {{{ execute() + + /** + * Executes a DB statement prepared with prepare(). + * + * To determine how many rows of a result set get buffered using + * ocisetprefetch(), see the "result_buffering" option in setOptions(). + * This option was added in Release 1.7.0. + * + * @param resource $stmt a DB statement resource returned from prepare() + * @param mixed $data array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 for non-array items or the + * quantity of elements in the array. + * + * @return mixed returns an oic8 result resource for successful SELECT + * queries, DB_OK for other successful queries. + * A DB error object is returned on failure. + * + * @see DB_oci8::prepare() + */ + function &execute($stmt, $data = array()) + { + $data = (array)$data; + $this->last_parameters = $data; + $this->last_query = $this->_prepared_queries[(int)$stmt]; + $this->_data = $data; + + $types = $this->prepare_types[(int)$stmt]; + if (count($types) != count($data)) { + $tmp = $this->raiseError(DB_ERROR_MISMATCH); + return $tmp; + } + + $i = 0; + foreach ($data as $key => $value) { + if ($types[$i] == DB_PARAM_MISC) { + /* + * Oracle doesn't seem to have the ability to pass a + * parameter along unchanged, so strip off quotes from start + * and end, plus turn two single quotes to one single quote, + * in order to avoid the quotes getting escaped by + * Oracle and ending up in the database. + */ + $data[$key] = preg_replace("/^'(.*)'$/", "\\1", $data[$key]); + $data[$key] = str_replace("''", "'", $data[$key]); + } elseif ($types[$i] == DB_PARAM_OPAQUE) { + $fp = @fopen($data[$key], 'rb'); + if (!$fp) { + $tmp = $this->raiseError(DB_ERROR_ACCESS_VIOLATION); + return $tmp; + } + $data[$key] = fread($fp, filesize($data[$key])); + fclose($fp); + } elseif ($types[$i] == DB_PARAM_SCALAR) { + // Floats have to be converted to a locale-neutral + // representation. + if (is_float($data[$key])) { + $data[$key] = $this->quoteFloat($data[$key]); + } + } + if (!@OCIBindByName($stmt, ':bind' . $i, $data[$key], -1)) { + $tmp = $this->oci8RaiseError($stmt); + return $tmp; + } + $this->last_query = preg_replace("/:bind$i(?!\d)/", + $this->quoteSmart($data[$key]), $this->last_query, 1); + $i++; + } + if ($this->autocommit) { + $success = @OCIExecute($stmt, OCI_COMMIT_ON_SUCCESS); + } else { + $success = @OCIExecute($stmt, OCI_DEFAULT); + } + if (!$success) { + $tmp = $this->oci8RaiseError($stmt); + return $tmp; + } + $this->last_stmt = $stmt; + if ($this->manip_query[(int)$stmt] || $this->_next_query_manip) { + $this->_last_query_manip = true; + $this->_next_query_manip = false; + $tmp = DB_OK; + } else { + $this->_last_query_manip = false; + @ocisetprefetch($stmt, $this->options['result_buffering']); + $tmp = new DB_result($this, $stmt); + } + return $tmp; + } + + // }}} + // {{{ autoCommit() + + /** + * Enables or disables automatic commits + * + * @param bool $onoff true turns it on, false turns it off + * + * @return int DB_OK on success. A DB_Error object if the driver + * doesn't support auto-committing transactions. + */ + function autoCommit($onoff = false) + { + $this->autocommit = (bool)$onoff;; + return DB_OK; + } + + // }}} + // {{{ commit() + + /** + * Commits the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function commit() + { + $result = @OCICommit($this->connection); + if (!$result) { + return $this->oci8RaiseError(); + } + return DB_OK; + } + + // }}} + // {{{ rollback() + + /** + * Reverts the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function rollback() + { + $result = @OCIRollback($this->connection); + if (!$result) { + return $this->oci8RaiseError(); + } + return DB_OK; + } + + // }}} + // {{{ affectedRows() + + /** + * Determines the number of rows affected by a data maniuplation query + * + * 0 is returned for queries that don't manipulate data. + * + * @return int the number of rows. A DB_Error object on failure. + */ + function affectedRows() + { + if ($this->last_stmt === false) { + return $this->oci8RaiseError(); + } + $result = @OCIRowCount($this->last_stmt); + if ($result === false) { + return $this->oci8RaiseError($this->last_stmt); + } + return $result; + } + + // }}} + // {{{ modifyQuery() + + /** + * Changes a query string for various DBMS specific reasons + * + * "SELECT 2+2" must be "SELECT 2+2 FROM dual" in Oracle. + * + * @param string $query the query string to modify + * + * @return string the modified query string + * + * @access protected + */ + function modifyQuery($query) + { + if (preg_match('/^\s*SELECT/i', $query) && + !preg_match('/\sFROM\s/i', $query)) { + $query .= ' FROM dual'; + } + return $query; + } + + // }}} + // {{{ modifyLimitQuery() + + /** + * Adds LIMIT clauses to a query string according to current DBMS standards + * + * @param string $query the query to modify + * @param int $from the row to start to fetching (0 = the first row) + * @param int $count the numbers of rows to fetch + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * + * @return string the query string with LIMIT clauses added + * + * @access protected + */ + function modifyLimitQuery($query, $from, $count, $params = array()) + { + // Let Oracle return the name of the columns instead of + // coding a "home" SQL parser + + if (count($params)) { + $result = $this->prepare("SELECT * FROM ($query) " + . 'WHERE NULL = NULL'); + $tmp = $this->execute($result, $params); + } else { + $q_fields = "SELECT * FROM ($query) WHERE NULL = NULL"; + + if (!$result = @OCIParse($this->connection, $q_fields)) { + $this->last_query = $q_fields; + return $this->oci8RaiseError(); + } + if (!@OCIExecute($result, OCI_DEFAULT)) { + $this->last_query = $q_fields; + return $this->oci8RaiseError($result); + } + } + + $ncols = OCINumCols($result); + $cols = array(); + for ( $i = 1; $i <= $ncols; $i++ ) { + $cols[] = '"' . OCIColumnName($result, $i) . '"'; + } + $fields = implode(', ', $cols); + // XXX Test that (tip by John Lim) + //if (preg_match('/^\s*SELECT\s+/is', $query, $match)) { + // // Introduce the FIRST_ROWS Oracle query optimizer + // $query = substr($query, strlen($match[0]), strlen($query)); + // $query = "SELECT /* +FIRST_ROWS */ " . $query; + //} + + // Construct the query + // more at: http://marc.theaimsgroup.com/?l=php-db&m=99831958101212&w=2 + // Perhaps this could be optimized with the use of Unions + $query = "SELECT $fields FROM". + " (SELECT rownum as linenum, $fields FROM". + " ($query)". + ' WHERE rownum <= '. ($from + $count) . + ') WHERE linenum >= ' . ++$from; + return $query; + } + + // }}} + // {{{ nextId() + + /** + * Returns the next free id in a sequence + * + * @param string $seq_name name of the sequence + * @param boolean $ondemand when true, the seqence is automatically + * created if it does not exist + * + * @return int the next id number in the sequence. + * A DB_Error object on failure. + * + * @see DB_common::nextID(), DB_common::getSequenceName(), + * DB_oci8::createSequence(), DB_oci8::dropSequence() + */ + function nextId($seq_name, $ondemand = true) + { + $seqname = $this->getSequenceName($seq_name); + $repeat = 0; + do { + $this->expectError(DB_ERROR_NOSUCHTABLE); + $result = $this->query("SELECT ${seqname}.nextval FROM dual"); + $this->popExpect(); + if ($ondemand && DB::isError($result) && + $result->getCode() == DB_ERROR_NOSUCHTABLE) { + $repeat = 1; + $result = $this->createSequence($seq_name); + if (DB::isError($result)) { + return $this->raiseError($result); + } + } else { + $repeat = 0; + } + } while ($repeat); + if (DB::isError($result)) { + return $this->raiseError($result); + } + $arr = $result->fetchRow(DB_FETCHMODE_ORDERED); + return $arr[0]; + } + + /** + * Creates a new sequence + * + * @param string $seq_name name of the new sequence + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::createSequence(), DB_common::getSequenceName(), + * DB_oci8::nextID(), DB_oci8::dropSequence() + */ + function createSequence($seq_name) + { + return $this->query('CREATE SEQUENCE ' + . $this->getSequenceName($seq_name)); + } + + // }}} + // {{{ dropSequence() + + /** + * Deletes a sequence + * + * @param string $seq_name name of the sequence to be deleted + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::dropSequence(), DB_common::getSequenceName(), + * DB_oci8::nextID(), DB_oci8::createSequence() + */ + function dropSequence($seq_name) + { + return $this->query('DROP SEQUENCE ' + . $this->getSequenceName($seq_name)); + } + + // }}} + // {{{ oci8RaiseError() + + /** + * Produces a DB_Error object regarding the current problem + * + * @param int $errno if the error is being manually raised pass a + * DB_ERROR* constant here. If this isn't passed + * the error information gathered from the DBMS. + * + * @return object the DB_Error object + * + * @see DB_common::raiseError(), + * DB_oci8::errorNative(), DB_oci8::errorCode() + */ + function oci8RaiseError($errno = null) + { + if ($errno === null) { + $error = @OCIError($this->connection); + return $this->raiseError($this->errorCode($error['code']), + null, null, null, $error['message']); + } elseif (is_resource($errno)) { + $error = @OCIError($errno); + return $this->raiseError($this->errorCode($error['code']), + null, null, null, $error['message']); + } + return $this->raiseError($this->errorCode($errno)); + } + + // }}} + // {{{ errorNative() + + /** + * Gets the DBMS' native error code produced by the last query + * + * @return int the DBMS' error code. FALSE if the code could not be + * determined + */ + function errorNative() + { + if (is_resource($this->last_stmt)) { + $error = @OCIError($this->last_stmt); + } else { + $error = @OCIError($this->connection); + } + if (is_array($error)) { + return $error['code']; + } + return false; + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table or a result set + * + * NOTE: only supports 'table' and 'flags' if <var>$result</var> + * is a table name. + * + * NOTE: flags won't contain index information. + * + * @param object|string $result DB_result object from a query or a + * string containing the name of a table. + * While this also accepts a query result + * resource identifier, this behavior is + * deprecated. + * @param int $mode a valid tableInfo mode + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * + * @see DB_common::tableInfo() + */ + function tableInfo($result, $mode = null) + { + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; + } else { + $case_func = 'strval'; + } + + $res = array(); + + if (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + $result = strtoupper($result); + $q_fields = 'SELECT column_name, data_type, data_length, ' + . 'nullable ' + . 'FROM user_tab_columns ' + . "WHERE table_name='$result' ORDER BY column_id"; + + $this->last_query = $q_fields; + + if (!$stmt = @OCIParse($this->connection, $q_fields)) { + return $this->oci8RaiseError(DB_ERROR_NEED_MORE_DATA); + } + if (!@OCIExecute($stmt, OCI_DEFAULT)) { + return $this->oci8RaiseError($stmt); + } + + $i = 0; + while (@OCIFetch($stmt)) { + $res[$i] = array( + 'table' => $case_func($result), + 'name' => $case_func(@OCIResult($stmt, 1)), + 'type' => @OCIResult($stmt, 2), + 'len' => @OCIResult($stmt, 3), + 'flags' => (@OCIResult($stmt, 4) == 'N') ? 'not_null' : '', + ); + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + $i++; + } + + if ($mode) { + $res['num_fields'] = $i; + } + @OCIFreeStatement($stmt); + + } else { + if (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $result = $result->result; + } + + $res = array(); + + if ($result === $this->last_stmt) { + $count = @OCINumCols($result); + if ($mode) { + $res['num_fields'] = $count; + } + for ($i = 0; $i < $count; $i++) { + $res[$i] = array( + 'table' => '', + 'name' => $case_func(@OCIColumnName($result, $i+1)), + 'type' => @OCIColumnType($result, $i+1), + 'len' => @OCIColumnSize($result, $i+1), + 'flags' => '', + ); + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + } + } else { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + } + return $res; + } + + // }}} + // {{{ getSpecialQuery() + + /** + * Obtains the query string needed for listing a given type of objects + * + * @param string $type the kind of objects you want to retrieve + * + * @return string the SQL query string or null if the driver doesn't + * support the object type requested + * + * @access protected + * @see DB_common::getListOf() + */ + function getSpecialQuery($type) + { + switch ($type) { + case 'tables': + return 'SELECT table_name FROM user_tables'; + case 'synonyms': + return 'SELECT synonym_name FROM user_synonyms'; + case 'views': + return 'SELECT view_name FROM user_views'; + default: + return null; + } + } + + // }}} + // {{{ quoteFloat() + + /** + * Formats a float value for use within a query in a locale-independent + * manner. + * + * @param float the float value to be quoted. + * @return string the quoted string. + * @see DB_common::quoteSmart() + * @since Method available since release 1.7.8. + */ + function quoteFloat($float) { + return $this->escapeSimple(str_replace(',', '.', strval(floatval($float)))); + } + + // }}} + +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/civicrm/packages/DB/odbc.php b/civicrm/vendor/pear/db/DB/odbc.php similarity index 99% rename from civicrm/packages/DB/odbc.php rename to civicrm/vendor/pear/db/DB/odbc.php index f410a12715..a664331329 100644 --- a/civicrm/packages/DB/odbc.php +++ b/civicrm/vendor/pear/db/DB/odbc.php @@ -44,7 +44,7 @@ require_once 'DB/common.php'; * @author Daniel Convissor <danielc@php.net> * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.9.3 + * @version Release: @package_version@ * @link http://pear.php.net/package/DB */ class DB_odbc extends DB_common diff --git a/civicrm/packages/DB/pgsql.php b/civicrm/vendor/pear/db/DB/pgsql.php similarity index 99% rename from civicrm/packages/DB/pgsql.php rename to civicrm/vendor/pear/db/DB/pgsql.php index 5d045dfdab..8a75ca61f5 100644 --- a/civicrm/packages/DB/pgsql.php +++ b/civicrm/vendor/pear/db/DB/pgsql.php @@ -43,7 +43,7 @@ require_once 'DB/common.php'; * @author Daniel Convissor <danielc@php.net> * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.9.3 + * @version Release: @package_version@ * @link http://pear.php.net/package/DB */ class DB_pgsql extends DB_common @@ -187,12 +187,12 @@ class DB_pgsql extends DB_common * Example of connecting to a new link via a socket: * <code> * require_once 'DB.php'; - * + * * $dsn = 'pgsql://user:pass@unix(/tmp)/dbname?new_link=true'; * $options = array( * 'portability' => DB_PORTABILITY_ALL, * ); - * + * * $db = DB::connect($dsn, $options); * if (PEAR::isError($db)) { * die($db->getMessage()); @@ -480,7 +480,7 @@ class DB_pgsql extends DB_common function quoteBoolean($boolean) { return $boolean ? 'TRUE' : 'FALSE'; } - + // }}} // {{{ escapeSimple() @@ -784,7 +784,7 @@ class DB_pgsql extends DB_common /** * Gets the DBMS' native error message produced by the last query * - * {@internal Error messages are used instead of error codes + * {@internal Error messages are used instead of error codes * in order to support older versions of PostgreSQL.}} * * @return string the DBMS' error message diff --git a/civicrm/vendor/pear/db/DB/sqlite.php b/civicrm/vendor/pear/db/DB/sqlite.php new file mode 100644 index 0000000000..b81a43f135 --- /dev/null +++ b/civicrm/vendor/pear/db/DB/sqlite.php @@ -0,0 +1,963 @@ +<?php + +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * The PEAR DB driver for PHP's sqlite extension + * for interacting with SQLite databases + * + * PHP version 5 + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @category Database + * @package DB + * @author Urs Gehrig <urs@circle.ch> + * @author Mika Tuupola <tuupola@appelsiini.net> + * @author Daniel Convissor <danielc@php.net> + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 3.0 + * @version CVS: $Id$ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ +require_once 'DB/common.php'; + +/** + * The methods PEAR DB uses to interact with PHP's sqlite extension + * for interacting with SQLite databases + * + * These methods overload the ones declared in DB_common. + * + * NOTICE: This driver needs PHP's track_errors ini setting to be on. + * It is automatically turned on when connecting to the database. + * Make sure your scripts don't turn it off. + * + * @category Database + * @package DB + * @author Urs Gehrig <urs@circle.ch> + * @author Mika Tuupola <tuupola@appelsiini.net> + * @author Daniel Convissor <danielc@php.net> + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/DB + */ +class DB_sqlite extends DB_common +{ + // {{{ properties + + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'sqlite'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'sqlite'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * @var array + */ + var $features = array( + 'limit' => 'alter', + 'new_link' => false, + 'numrows' => true, + 'pconnect' => true, + 'prepare' => false, + 'ssl' => false, + 'transactions' => false, + ); + + /** + * A mapping of native error codes to DB error codes + * + * {@internal Error codes according to sqlite_exec. See the online + * manual at http://sqlite.org/c_interface.html for info. + * This error handling based on sqlite_exec is not yet implemented.}} + * + * @var array + */ + var $errorcode_map = array( + ); + + /** + * The raw database connection created by PHP + * @var resource + */ + var $connection; + + /** + * The DSN information for connecting to a database + * @var array + */ + var $dsn = array(); + + + /** + * SQLite data types + * + * @link http://www.sqlite.org/datatypes.html + * + * @var array + */ + var $keywords = array ( + 'BLOB' => '', + 'BOOLEAN' => '', + 'CHARACTER' => '', + 'CLOB' => '', + 'FLOAT' => '', + 'INTEGER' => '', + 'KEY' => '', + 'NATIONAL' => '', + 'NUMERIC' => '', + 'NVARCHAR' => '', + 'PRIMARY' => '', + 'TEXT' => '', + 'TIMESTAMP' => '', + 'UNIQUE' => '', + 'VARCHAR' => '', + 'VARYING' => '', + ); + + /** + * The most recent error message from $php_errormsg + * @var string + * @access private + */ + var $_lasterror = ''; + + + // }}} + // {{{ constructor + + /** + * This constructor calls <kbd>parent::__construct()</kbd> + * + * @return void + */ + function __construct() + { + parent::__construct(); + } + + // }}} + // {{{ connect() + + /** + * Connect to the database server, log in and open the database + * + * Don't call this method directly. Use DB::connect() instead. + * + * PEAR DB's sqlite driver supports the following extra DSN options: + * + mode The permissions for the database file, in four digit + * chmod octal format (eg "0600"). + * + * Example of connecting to a database in read-only mode: + * <code> + * require_once 'DB.php'; + * + * $dsn = 'sqlite:///path/and/name/of/db/file?mode=0400'; + * $options = array( + * 'portability' => DB_PORTABILITY_ALL, + * ); + * + * $db = DB::connect($dsn, $options); + * if (PEAR::isError($db)) { + * die($db->getMessage()); + * } + * </code> + * + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function connect($dsn, $persistent = false) + { + if (!PEAR::loadExtension('sqlite')) { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; + } + + if (!$dsn['database']) { + return $this->sqliteRaiseError(DB_ERROR_ACCESS_VIOLATION); + } + + if ($dsn['database'] !== ':memory:') { + if (!file_exists($dsn['database'])) { + if (!touch($dsn['database'])) { + return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND); + } + if (!isset($dsn['mode']) || + !is_numeric($dsn['mode'])) + { + $mode = 0644; + } else { + $mode = octdec($dsn['mode']); + } + if (!chmod($dsn['database'], $mode)) { + return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND); + } + if (!file_exists($dsn['database'])) { + return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND); + } + } + if (!is_file($dsn['database'])) { + return $this->sqliteRaiseError(DB_ERROR_INVALID); + } + if (!is_readable($dsn['database'])) { + return $this->sqliteRaiseError(DB_ERROR_ACCESS_VIOLATION); + } + } + + $connect_function = $persistent ? 'sqlite_popen' : 'sqlite_open'; + + // track_errors must remain on for simpleQuery() + @ini_set('track_errors', 1); + $php_errormsg = ''; + + if (!$this->connection = @$connect_function($dsn['database'])) { + return $this->raiseError(DB_ERROR_NODBSELECTED, + null, null, null, + $php_errormsg); + } + return DB_OK; + } + + // }}} + // {{{ disconnect() + + /** + * Disconnects from the database server + * + * @return bool TRUE on success, FALSE on failure + */ + function disconnect() + { + $ret = @sqlite_close($this->connection); + $this->connection = null; + return $ret; + } + + // }}} + // {{{ simpleQuery() + + /** + * Sends a query to the database server + * + * NOTICE: This method needs PHP's track_errors ini setting to be on. + * It is automatically turned on when connecting to the database. + * Make sure your scripts don't turn it off. + * + * @param string the SQL query string + * + * @return mixed + a PHP result resrouce for successful SELECT queries + * + the DB_OK constant for other successful queries + * + a DB_Error object on failure + */ + function simpleQuery($query) + { + $ismanip = $this->_checkManip($query); + $this->last_query = $query; + $query = $this->modifyQuery($query); + + $php_errormsg = ''; + + $result = @sqlite_query($query, $this->connection); + $this->_lasterror = $php_errormsg ? $php_errormsg : ''; + + $this->result = $result; + if (!$this->result) { + return $this->sqliteRaiseError(null); + } + + // sqlite_query() seems to allways return a resource + // so cant use that. Using $ismanip instead + if (!$ismanip) { + $numRows = $this->numRows($result); + if (is_object($numRows)) { + // we've got PEAR_Error + return $numRows; + } + return $result; + } + return DB_OK; + } + + // }}} + // {{{ nextResult() + + /** + * Move the internal sqlite result pointer to the next available result + * + * @param resource $result the valid sqlite result resource + * + * @return bool true if a result is available otherwise return false + */ + function nextResult($result) + { + return false; + } + + // }}} + // {{{ fetchInto() + + /** + * Places a row from the result set into the given array + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch (0 = first row) + * + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure + * + * @see DB_result::fetchInto() + */ + function fetchInto($result, &$arr, $fetchmode, $rownum = null) + { + if ($rownum !== null) { + if (!@sqlite_seek($this->result, $rownum)) { + return null; + } + } + if ($fetchmode & DB_FETCHMODE_ASSOC) { + $arr = @sqlite_fetch_array($result, SQLITE_ASSOC); + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { + $arr = array_change_key_case($arr, CASE_LOWER); + } + + /* Remove extraneous " characters from the fields in the result. + * Fixes bug #11716. */ + if (is_array($arr) && count($arr) > 0) { + $strippedArr = array(); + foreach ($arr as $field => $value) { + $strippedArr[trim($field, '"')] = $value; + } + $arr = $strippedArr; + } + } else { + $arr = @sqlite_fetch_array($result, SQLITE_NUM); + } + if (!$arr) { + return null; + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + /* + * Even though this DBMS already trims output, we do this because + * a field might have intentional whitespace at the end that + * gets removed by DB_PORTABILITY_RTRIM under another driver. + */ + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + return DB_OK; + } + + // }}} + // {{{ freeResult() + + /** + * Deletes the result set and frees the memory occupied by the result set + * + * This method is not meant to be called directly. Use + * DB_result::free() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_result::free() + */ + function freeResult(&$result) + { + // XXX No native free? + if (!is_resource($result)) { + return false; + } + $result = null; + return true; + } + + // }}} + // {{{ numCols() + + /** + * Gets the number of columns in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of columns. A DB_Error object on failure. + * + * @see DB_result::numCols() + */ + function numCols($result) + { + $cols = @sqlite_num_fields($result); + if (!$cols) { + return $this->sqliteRaiseError(); + } + return $cols; + } + + // }}} + // {{{ numRows() + + /** + * Gets the number of rows in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numRows() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of rows. A DB_Error object on failure. + * + * @see DB_result::numRows() + */ + function numRows($result) + { + $rows = @sqlite_num_rows($result); + if ($rows === null) { + return $this->sqliteRaiseError(); + } + return $rows; + } + + // }}} + // {{{ affected() + + /** + * Determines the number of rows affected by a data maniuplation query + * + * 0 is returned for queries that don't manipulate data. + * + * @return int the number of rows. A DB_Error object on failure. + */ + function affectedRows() + { + return @sqlite_changes($this->connection); + } + + // }}} + // {{{ dropSequence() + + /** + * Deletes a sequence + * + * @param string $seq_name name of the sequence to be deleted + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::dropSequence(), DB_common::getSequenceName(), + * DB_sqlite::nextID(), DB_sqlite::createSequence() + */ + function dropSequence($seq_name) + { + return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); + } + + /** + * Creates a new sequence + * + * @param string $seq_name name of the new sequence + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::createSequence(), DB_common::getSequenceName(), + * DB_sqlite::nextID(), DB_sqlite::dropSequence() + */ + function createSequence($seq_name) + { + $seqname = $this->getSequenceName($seq_name); + $query = 'CREATE TABLE ' . $seqname . + ' (id INTEGER UNSIGNED PRIMARY KEY) '; + $result = $this->query($query); + if (DB::isError($result)) { + return($result); + } + $query = "CREATE TRIGGER ${seqname}_cleanup AFTER INSERT ON $seqname + BEGIN + DELETE FROM $seqname WHERE id<LAST_INSERT_ROWID(); + END "; + $result = $this->query($query); + if (DB::isError($result)) { + return($result); + } + } + + // }}} + // {{{ nextId() + + /** + * Returns the next free id in a sequence + * + * @param string $seq_name name of the sequence + * @param boolean $ondemand when true, the seqence is automatically + * created if it does not exist + * + * @return int the next id number in the sequence. + * A DB_Error object on failure. + * + * @see DB_common::nextID(), DB_common::getSequenceName(), + * DB_sqlite::createSequence(), DB_sqlite::dropSequence() + */ + function nextId($seq_name, $ondemand = true) + { + $seqname = $this->getSequenceName($seq_name); + + do { + $repeat = 0; + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result = $this->query("INSERT INTO $seqname (id) VALUES (NULL)"); + $this->popErrorHandling(); + if ($result === DB_OK) { + $id = @sqlite_last_insert_rowid($this->connection); + if ($id != 0) { + return $id; + } + } elseif ($ondemand && DB::isError($result) && + $result->getCode() == DB_ERROR_NOSUCHTABLE) + { + $result = $this->createSequence($seq_name); + if (DB::isError($result)) { + return $this->raiseError($result); + } else { + $repeat = 1; + } + } + } while ($repeat); + + return $this->raiseError($result); + } + + // }}} + // {{{ getDbFileStats() + + /** + * Get the file stats for the current database + * + * Possible arguments are dev, ino, mode, nlink, uid, gid, rdev, size, + * atime, mtime, ctime, blksize, blocks or a numeric key between + * 0 and 12. + * + * @param string $arg the array key for stats() + * + * @return mixed an array on an unspecified key, integer on a passed + * arg and false at a stats error + */ + function getDbFileStats($arg = '') + { + $stats = stat($this->dsn['database']); + if ($stats == false) { + return false; + } + if (is_array($stats)) { + if (is_numeric($arg)) { + if (((int)$arg <= 12) & ((int)$arg >= 0)) { + return false; + } + return $stats[$arg ]; + } + if (array_key_exists(trim($arg), $stats)) { + return $stats[$arg ]; + } + } + return $stats; + } + + // }}} + // {{{ escapeSimple() + + /** + * Escapes a string according to the current DBMS's standards + * + * In SQLite, this makes things safe for inserts/updates, but may + * cause problems when performing text comparisons against columns + * containing binary data. See the + * {@link http://php.net/sqlite_escape_string PHP manual} for more info. + * + * @param string $str the string to be escaped + * + * @return string the escaped string + * + * @since Method available since Release 1.6.1 + * @see DB_common::escapeSimple() + */ + function escapeSimple($str) + { + return @sqlite_escape_string($str); + } + + // }}} + // {{{ modifyLimitQuery() + + /** + * Adds LIMIT clauses to a query string according to current DBMS standards + * + * @param string $query the query to modify + * @param int $from the row to start to fetching (0 = the first row) + * @param int $count the numbers of rows to fetch + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * + * @return string the query string with LIMIT clauses added + * + * @access protected + */ + function modifyLimitQuery($query, $from, $count, $params = array()) + { + return "$query LIMIT $count OFFSET $from"; + } + + // }}} + // {{{ modifyQuery() + + /** + * Changes a query string for various DBMS specific reasons + * + * This little hack lets you know how many rows were deleted + * when running a "DELETE FROM table" query. Only implemented + * if the DB_PORTABILITY_DELETE_COUNT portability option is on. + * + * @param string $query the query string to modify + * + * @return string the modified query string + * + * @access protected + * @see DB_common::setOption() + */ + function modifyQuery($query) + { + if ($this->options['portability'] & DB_PORTABILITY_DELETE_COUNT) { + if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) { + $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/', + 'DELETE FROM \1 WHERE 1=1', $query); + } + } + return $query; + } + + // }}} + // {{{ sqliteRaiseError() + + /** + * Produces a DB_Error object regarding the current problem + * + * @param int $errno if the error is being manually raised pass a + * DB_ERROR* constant here. If this isn't passed + * the error information gathered from the DBMS. + * + * @return object the DB_Error object + * + * @see DB_common::raiseError(), + * DB_sqlite::errorNative(), DB_sqlite::errorCode() + */ + function sqliteRaiseError($errno = null) + { + $native = $this->errorNative(); + if ($errno === null) { + $errno = $this->errorCode($native); + } + + $errorcode = @sqlite_last_error($this->connection); + $userinfo = "$errorcode ** $this->last_query"; + + return $this->raiseError($errno, null, null, $userinfo, $native); + } + + // }}} + // {{{ errorNative() + + /** + * Gets the DBMS' native error message produced by the last query + * + * {@internal This is used to retrieve more meaningfull error messages + * because sqlite_last_error() does not provide adequate info.}} + * + * @return string the DBMS' error message + */ + function errorNative() + { + return $this->_lasterror; + } + + // }}} + // {{{ errorCode() + + /** + * Determines PEAR::DB error code from the database's text error message + * + * @param string $errormsg the error message returned from the database + * + * @return integer the DB error number + */ + function errorCode($errormsg) + { + static $error_regexps; + + // PHP 5.2+ prepends the function name to $php_errormsg, so we need + // this hack to work around it, per bug #9599. + $errormsg = preg_replace('/^sqlite[a-z_]+\(\): /', '', $errormsg); + + if (!isset($error_regexps)) { + $error_regexps = array( + '/^no such table:/' => DB_ERROR_NOSUCHTABLE, + '/^no such index:/' => DB_ERROR_NOT_FOUND, + '/^(table|index) .* already exists$/' => DB_ERROR_ALREADY_EXISTS, + '/PRIMARY KEY must be unique/i' => DB_ERROR_CONSTRAINT, + '/is not unique/' => DB_ERROR_CONSTRAINT, + '/columns .* are not unique/i' => DB_ERROR_CONSTRAINT, + '/uniqueness constraint failed/' => DB_ERROR_CONSTRAINT, + '/may not be NULL/' => DB_ERROR_CONSTRAINT_NOT_NULL, + '/^no such column:/' => DB_ERROR_NOSUCHFIELD, + '/no column named/' => DB_ERROR_NOSUCHFIELD, + '/column not present in both tables/i' => DB_ERROR_NOSUCHFIELD, + '/^near ".*": syntax error$/' => DB_ERROR_SYNTAX, + '/[0-9]+ values for [0-9]+ columns/i' => DB_ERROR_VALUE_COUNT_ON_ROW, + ); + } + foreach ($error_regexps as $regexp => $code) { + if (preg_match($regexp, $errormsg)) { + return $code; + } + } + // Fall back to DB_ERROR if there was no mapping. + return DB_ERROR; + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table + * + * @param string $result a string containing the name of a table + * @param int $mode a valid tableInfo mode + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * + * @see DB_common::tableInfo() + * @since Method available since Release 1.7.0 + */ + function tableInfo($result, $mode = null) + { + if (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + $id = @sqlite_array_query($this->connection, + "PRAGMA table_info('$result');", + SQLITE_ASSOC); + $got_string = true; + } else { + $this->last_query = ''; + return $this->raiseError(DB_ERROR_NOT_CAPABLE, null, null, null, + 'This DBMS can not obtain tableInfo' . + ' from result sets'); + } + + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; + } else { + $case_func = 'strval'; + } + + $count = count($id); + $res = array(); + + if ($mode) { + $res['num_fields'] = $count; + } + + for ($i = 0; $i < $count; $i++) { + if (strpos($id[$i]['type'], '(') !== false) { + $bits = explode('(', $id[$i]['type']); + $type = $bits[0]; + $len = rtrim($bits[1],')'); + } else { + $type = $id[$i]['type']; + $len = 0; + } + + $flags = ''; + if ($id[$i]['pk']) { + $flags .= 'primary_key '; + if (strtoupper($type) == 'INTEGER') { + $flags .= 'auto_increment '; + } + } + if ($id[$i]['notnull']) { + $flags .= 'not_null '; + } + if ($id[$i]['dflt_value'] !== null) { + $flags .= 'default_' . rawurlencode($id[$i]['dflt_value']); + } + $flags = trim($flags); + + $res[$i] = array( + 'table' => $case_func($result), + 'name' => $case_func($id[$i]['name']), + 'type' => $type, + 'len' => $len, + 'flags' => $flags, + ); + + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + } + + return $res; + } + + // }}} + // {{{ getSpecialQuery() + + /** + * Obtains the query string needed for listing a given type of objects + * + * @param string $type the kind of objects you want to retrieve + * @param array $args SQLITE DRIVER ONLY: a private array of arguments + * used by the getSpecialQuery(). Do not use + * this directly. + * + * @return string the SQL query string or null if the driver doesn't + * support the object type requested + * + * @access protected + * @see DB_common::getListOf() + */ + function getSpecialQuery($type, $args = array()) + { + if (!is_array($args)) { + return $this->raiseError('no key specified', null, null, null, + 'Argument has to be an array.'); + } + + switch ($type) { + case 'master': + return 'SELECT * FROM sqlite_master;'; + case 'tables': + return "SELECT name FROM sqlite_master WHERE type='table' " + . 'UNION ALL SELECT name FROM sqlite_temp_master ' + . "WHERE type='table' ORDER BY name;"; + case 'schema': + return 'SELECT sql FROM (SELECT * FROM sqlite_master ' + . 'UNION ALL SELECT * FROM sqlite_temp_master) ' + . "WHERE type!='meta' " + . 'ORDER BY tbl_name, type DESC, name;'; + case 'schemax': + case 'schema_x': + /* + * Use like: + * $res = $db->query($db->getSpecialQuery('schema_x', + * array('table' => 'table3'))); + */ + return 'SELECT sql FROM (SELECT * FROM sqlite_master ' + . 'UNION ALL SELECT * FROM sqlite_temp_master) ' + . "WHERE tbl_name LIKE '{$args['table']}' " + . "AND type!='meta' " + . 'ORDER BY type DESC, name;'; + case 'alter': + /* + * SQLite does not support ALTER TABLE; this is a helper query + * to handle this. 'table' represents the table name, 'rows' + * the news rows to create, 'save' the row(s) to keep _with_ + * the data. + * + * Use like: + * $args = array( + * 'table' => $table, + * 'rows' => "id INTEGER PRIMARY KEY, firstname TEXT, surname TEXT, datetime TEXT", + * 'save' => "NULL, titel, content, datetime" + * ); + * $res = $db->query( $db->getSpecialQuery('alter', $args)); + */ + $rows = strtr($args['rows'], $this->keywords); + + $q = array( + 'BEGIN TRANSACTION', + "CREATE TEMPORARY TABLE {$args['table']}_backup ({$args['rows']})", + "INSERT INTO {$args['table']}_backup SELECT {$args['save']} FROM {$args['table']}", + "DROP TABLE {$args['table']}", + "CREATE TABLE {$args['table']} ({$args['rows']})", + "INSERT INTO {$args['table']} SELECT {$rows} FROM {$args['table']}_backup", + "DROP TABLE {$args['table']}_backup", + 'COMMIT', + ); + + /* + * This is a dirty hack, since the above query will not get + * executed with a single query call so here the query method + * will be called directly and return a select instead. + */ + foreach ($q as $query) { + $this->query($query); + } + return "SELECT * FROM {$args['table']};"; + default: + return null; + } + } + + // }}} +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/civicrm/packages/DB/storage.php b/civicrm/vendor/pear/db/DB/storage.php similarity index 99% rename from civicrm/packages/DB/storage.php rename to civicrm/vendor/pear/db/DB/storage.php index 081100486b..f68f3dd321 100644 --- a/civicrm/packages/DB/storage.php +++ b/civicrm/vendor/pear/db/DB/storage.php @@ -38,7 +38,7 @@ require_once 'DB.php'; * @author Stig Bakken <stig@php.net> * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.9.3 + * @version Release: @package_version@ * @link http://pear.php.net/package/DB */ class DB_storage extends PEAR @@ -336,7 +336,7 @@ class DB_storage extends PEAR } reset($rowdata); $found_keycolumn = false; - while (list($key, $value) = each($rowdata)) { + foreach ($rowdata as $key => $value) { if ($key == $this->_keycolumn) { $found_keycolumn = true; } diff --git a/civicrm/vendor/pear/db/DB/sybase.php b/civicrm/vendor/pear/db/DB/sybase.php new file mode 100644 index 0000000000..aa3a1b7c78 --- /dev/null +++ b/civicrm/vendor/pear/db/DB/sybase.php @@ -0,0 +1,942 @@ +<?php + +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * The PEAR DB driver for PHP's sybase extension + * for interacting with Sybase databases + * + * PHP version 5 + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @category Database + * @package DB + * @author Sterling Hughes <sterling@php.net> + * @author Antônio Carlos Venâncio Júnior <floripa@php.net> + * @author Daniel Convissor <danielc@php.net> + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id$ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ +require_once 'DB/common.php'; + +/** + * The methods PEAR DB uses to interact with PHP's sybase extension + * for interacting with Sybase databases + * + * These methods overload the ones declared in DB_common. + * + * WARNING: This driver may fail with multiple connections under the + * same user/pass/host and different databases. + * + * @category Database + * @package DB + * @author Sterling Hughes <sterling@php.net> + * @author Antônio Carlos Venâncio Júnior <floripa@php.net> + * @author Daniel Convissor <danielc@php.net> + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/DB + */ +class DB_sybase extends DB_common +{ + // {{{ properties + + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'sybase'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'sybase'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * @var array + */ + var $features = array( + 'limit' => 'emulate', + 'new_link' => false, + 'numrows' => true, + 'pconnect' => true, + 'prepare' => false, + 'ssl' => false, + 'transactions' => true, + ); + + /** + * A mapping of native error codes to DB error codes + * @var array + */ + var $errorcode_map = array( + ); + + /** + * The raw database connection created by PHP + * @var resource + */ + var $connection; + + /** + * The DSN information for connecting to a database + * @var array + */ + var $dsn = array(); + + + /** + * Should data manipulation queries be committed automatically? + * @var bool + * @access private + */ + var $autocommit = true; + + /** + * The quantity of transactions begun + * + * {@internal While this is private, it can't actually be designated + * private in PHP 5 because it is directly accessed in the test suite.}} + * + * @var integer + * @access private + */ + var $transaction_opcount = 0; + + /** + * The database specified in the DSN + * + * It's a fix to allow calls to different databases in the same script. + * + * @var string + * @access private + */ + var $_db = ''; + + + // }}} + // {{{ constructor + + /** + * This constructor calls <kbd>parent::__construct()</kbd> + * + * @return void + */ + function __construct() + { + parent::__construct(); + } + + // }}} + // {{{ connect() + + /** + * Connect to the database server, log in and open the database + * + * Don't call this method directly. Use DB::connect() instead. + * + * PEAR DB's sybase driver supports the following extra DSN options: + * + appname The application name to use on this connection. + * Available since PEAR DB 1.7.0. + * + charset The character set to use on this connection. + * Available since PEAR DB 1.7.0. + * + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function connect($dsn, $persistent = false) + { + if (!PEAR::loadExtension('sybase') && + !PEAR::loadExtension('sybase_ct')) + { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; + } + + $dsn['hostspec'] = $dsn['hostspec'] ? $dsn['hostspec'] : 'localhost'; + $dsn['password'] = !empty($dsn['password']) ? $dsn['password'] : false; + $dsn['charset'] = isset($dsn['charset']) ? $dsn['charset'] : false; + $dsn['appname'] = isset($dsn['appname']) ? $dsn['appname'] : false; + + $connect_function = $persistent ? 'sybase_pconnect' : 'sybase_connect'; + + if ($dsn['username']) { + $this->connection = @$connect_function($dsn['hostspec'], + $dsn['username'], + $dsn['password'], + $dsn['charset'], + $dsn['appname']); + } else { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + 'The DSN did not contain a username.'); + } + + if (!$this->connection) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + @sybase_get_last_message()); + } + + if ($dsn['database']) { + if (!@sybase_select_db($dsn['database'], $this->connection)) { + return $this->raiseError(DB_ERROR_NODBSELECTED, + null, null, null, + @sybase_get_last_message()); + } + $this->_db = $dsn['database']; + } + + return DB_OK; + } + + // }}} + // {{{ disconnect() + + /** + * Disconnects from the database server + * + * @return bool TRUE on success, FALSE on failure + */ + function disconnect() + { + $ret = @sybase_close($this->connection); + $this->connection = null; + return $ret; + } + + // }}} + // {{{ simpleQuery() + + /** + * Sends a query to the database server + * + * @param string the SQL query string + * + * @return mixed + a PHP result resrouce for successful SELECT queries + * + the DB_OK constant for other successful queries + * + a DB_Error object on failure + */ + function simpleQuery($query) + { + $ismanip = $this->_checkManip($query); + $this->last_query = $query; + if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) { + return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); + } + $query = $this->modifyQuery($query); + if (!$this->autocommit && $ismanip) { + if ($this->transaction_opcount == 0) { + $result = @sybase_query('BEGIN TRANSACTION', $this->connection); + if (!$result) { + return $this->sybaseRaiseError(); + } + } + $this->transaction_opcount++; + } + $result = @sybase_query($query, $this->connection); + if (!$result) { + return $this->sybaseRaiseError(); + } + if (is_resource($result)) { + return $result; + } + // Determine which queries that should return data, and which + // should return an error code only. + return $ismanip ? DB_OK : $result; + } + + // }}} + // {{{ nextResult() + + /** + * Move the internal sybase result pointer to the next available result + * + * @param a valid sybase result resource + * + * @access public + * + * @return true if a result is available otherwise return false + */ + function nextResult($result) + { + return false; + } + + // }}} + // {{{ fetchInto() + + /** + * Places a row from the result set into the given array + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch (0 = first row) + * + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure + * + * @see DB_result::fetchInto() + */ + function fetchInto($result, &$arr, $fetchmode, $rownum = null) + { + if ($rownum !== null) { + if (!@sybase_data_seek($result, $rownum)) { + return null; + } + } + if ($fetchmode & DB_FETCHMODE_ASSOC) { + if (function_exists('sybase_fetch_assoc')) { + $arr = @sybase_fetch_assoc($result); + } else { + if ($arr = @sybase_fetch_array($result)) { + foreach ($arr as $key => $value) { + if (is_int($key)) { + unset($arr[$key]); + } + } + } + } + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { + $arr = array_change_key_case($arr, CASE_LOWER); + } + } else { + $arr = @sybase_fetch_row($result); + } + if (!$arr) { + return null; + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + return DB_OK; + } + + // }}} + // {{{ freeResult() + + /** + * Deletes the result set and frees the memory occupied by the result set + * + * This method is not meant to be called directly. Use + * DB_result::free() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_result::free() + */ + function freeResult($result) + { + return is_resource($result) ? sybase_free_result($result) : false; + } + + // }}} + // {{{ numCols() + + /** + * Gets the number of columns in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of columns. A DB_Error object on failure. + * + * @see DB_result::numCols() + */ + function numCols($result) + { + $cols = @sybase_num_fields($result); + if (!$cols) { + return $this->sybaseRaiseError(); + } + return $cols; + } + + // }}} + // {{{ numRows() + + /** + * Gets the number of rows in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numRows() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of rows. A DB_Error object on failure. + * + * @see DB_result::numRows() + */ + function numRows($result) + { + $rows = @sybase_num_rows($result); + if ($rows === false) { + return $this->sybaseRaiseError(); + } + return $rows; + } + + // }}} + // {{{ affectedRows() + + /** + * Determines the number of rows affected by a data maniuplation query + * + * 0 is returned for queries that don't manipulate data. + * + * @return int the number of rows. A DB_Error object on failure. + */ + function affectedRows() + { + if ($this->_last_query_manip) { + $result = @sybase_affected_rows($this->connection); + } else { + $result = 0; + } + return $result; + } + + // }}} + // {{{ nextId() + + /** + * Returns the next free id in a sequence + * + * @param string $seq_name name of the sequence + * @param boolean $ondemand when true, the seqence is automatically + * created if it does not exist + * + * @return int the next id number in the sequence. + * A DB_Error object on failure. + * + * @see DB_common::nextID(), DB_common::getSequenceName(), + * DB_sybase::createSequence(), DB_sybase::dropSequence() + */ + function nextId($seq_name, $ondemand = true) + { + $seqname = $this->getSequenceName($seq_name); + if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) { + return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); + } + $repeat = 0; + do { + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result = $this->query("INSERT INTO $seqname (vapor) VALUES (0)"); + $this->popErrorHandling(); + if ($ondemand && DB::isError($result) && + ($result->getCode() == DB_ERROR || $result->getCode() == DB_ERROR_NOSUCHTABLE)) + { + $repeat = 1; + $result = $this->createSequence($seq_name); + if (DB::isError($result)) { + return $this->raiseError($result); + } + } elseif (!DB::isError($result)) { + $result = $this->query("SELECT @@IDENTITY FROM $seqname"); + $repeat = 0; + } else { + $repeat = false; + } + } while ($repeat); + if (DB::isError($result)) { + return $this->raiseError($result); + } + $result = $result->fetchRow(DB_FETCHMODE_ORDERED); + return $result[0]; + } + + /** + * Creates a new sequence + * + * @param string $seq_name name of the new sequence + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::createSequence(), DB_common::getSequenceName(), + * DB_sybase::nextID(), DB_sybase::dropSequence() + */ + function createSequence($seq_name) + { + return $this->query('CREATE TABLE ' + . $this->getSequenceName($seq_name) + . ' (id numeric(10, 0) IDENTITY NOT NULL,' + . ' vapor int NULL)'); + } + + // }}} + // {{{ dropSequence() + + /** + * Deletes a sequence + * + * @param string $seq_name name of the sequence to be deleted + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::dropSequence(), DB_common::getSequenceName(), + * DB_sybase::nextID(), DB_sybase::createSequence() + */ + function dropSequence($seq_name) + { + return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); + } + + // }}} + // {{{ quoteFloat() + + /** + * Formats a float value for use within a query in a locale-independent + * manner. + * + * @param float the float value to be quoted. + * @return string the quoted string. + * @see DB_common::quoteSmart() + * @since Method available since release 1.7.8. + */ + function quoteFloat($float) { + return $this->escapeSimple(str_replace(',', '.', strval(floatval($float)))); + } + + // }}} + // {{{ autoCommit() + + /** + * Enables or disables automatic commits + * + * @param bool $onoff true turns it on, false turns it off + * + * @return int DB_OK on success. A DB_Error object if the driver + * doesn't support auto-committing transactions. + */ + function autoCommit($onoff = false) + { + // XXX if $this->transaction_opcount > 0, we should probably + // issue a warning here. + $this->autocommit = $onoff ? true : false; + return DB_OK; + } + + // }}} + // {{{ commit() + + /** + * Commits the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function commit() + { + if ($this->transaction_opcount > 0) { + if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) { + return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); + } + $result = @sybase_query('COMMIT', $this->connection); + $this->transaction_opcount = 0; + if (!$result) { + return $this->sybaseRaiseError(); + } + } + return DB_OK; + } + + // }}} + // {{{ rollback() + + /** + * Reverts the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function rollback() + { + if ($this->transaction_opcount > 0) { + if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) { + return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); + } + $result = @sybase_query('ROLLBACK', $this->connection); + $this->transaction_opcount = 0; + if (!$result) { + return $this->sybaseRaiseError(); + } + } + return DB_OK; + } + + // }}} + // {{{ sybaseRaiseError() + + /** + * Produces a DB_Error object regarding the current problem + * + * @param int $errno if the error is being manually raised pass a + * DB_ERROR* constant here. If this isn't passed + * the error information gathered from the DBMS. + * + * @return object the DB_Error object + * + * @see DB_common::raiseError(), + * DB_sybase::errorNative(), DB_sybase::errorCode() + */ + function sybaseRaiseError($errno = null) + { + $native = $this->errorNative(); + if ($errno === null) { + $errno = $this->errorCode($native); + } + return $this->raiseError($errno, null, null, null, $native); + } + + // }}} + // {{{ errorNative() + + /** + * Gets the DBMS' native error message produced by the last query + * + * @return string the DBMS' error message + */ + function errorNative() + { + return @sybase_get_last_message(); + } + + // }}} + // {{{ errorCode() + + /** + * Determines PEAR::DB error code from the database's text error message. + * + * @param string $errormsg error message returned from the database + * @return integer an error number from a DB error constant + */ + function errorCode($errormsg) + { + static $error_regexps; + + // PHP 5.2+ prepends the function name to $php_errormsg, so we need + // this hack to work around it, per bug #9599. + $errormsg = preg_replace('/^sybase[a-z_]+\(\): /', '', $errormsg); + + if (!isset($error_regexps)) { + $error_regexps = array( + '/Incorrect syntax near/' + => DB_ERROR_SYNTAX, + '/^Unclosed quote before the character string [\"\'].*[\"\']\./' + => DB_ERROR_SYNTAX, + '/Implicit conversion (from datatype|of NUMERIC value)/i' + => DB_ERROR_INVALID_NUMBER, + '/Cannot drop the table [\"\'].+[\"\'], because it doesn\'t exist in the system catalogs\./' + => DB_ERROR_NOSUCHTABLE, + '/Only the owner of object [\"\'].+[\"\'] or a user with System Administrator \(SA\) role can run this command\./' + => DB_ERROR_ACCESS_VIOLATION, + '/^.+ permission denied on object .+, database .+, owner .+/' + => DB_ERROR_ACCESS_VIOLATION, + '/^.* permission denied, database .+, owner .+/' + => DB_ERROR_ACCESS_VIOLATION, + '/[^.*] not found\./' + => DB_ERROR_NOSUCHTABLE, + '/There is already an object named/' + => DB_ERROR_ALREADY_EXISTS, + '/Invalid column name/' + => DB_ERROR_NOSUCHFIELD, + '/does not allow null values/' + => DB_ERROR_CONSTRAINT_NOT_NULL, + '/Command has been aborted/' + => DB_ERROR_CONSTRAINT, + '/^Cannot drop the index .* because it doesn\'t exist/i' + => DB_ERROR_NOT_FOUND, + '/^There is already an index/i' + => DB_ERROR_ALREADY_EXISTS, + '/^There are fewer columns in the INSERT statement than values specified/i' + => DB_ERROR_VALUE_COUNT_ON_ROW, + '/Divide by zero/i' + => DB_ERROR_DIVZERO, + ); + } + + foreach ($error_regexps as $regexp => $code) { + if (preg_match($regexp, $errormsg)) { + return $code; + } + } + return DB_ERROR; + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table or a result set + * + * NOTE: only supports 'table' and 'flags' if <var>$result</var> + * is a table name. + * + * @param object|string $result DB_result object from a query or a + * string containing the name of a table. + * While this also accepts a query result + * resource identifier, this behavior is + * deprecated. + * @param int $mode a valid tableInfo mode + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * + * @see DB_common::tableInfo() + * @since Method available since Release 1.6.0 + */ + function tableInfo($result, $mode = null) + { + if (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) { + return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); + } + $id = @sybase_query("SELECT * FROM $result WHERE 1=0", + $this->connection); + $got_string = true; + } elseif (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $id = $result->result; + $got_string = false; + } else { + /* + * Probably received a result resource identifier. + * Copy it. + * Deprecated. Here for compatibility only. + */ + $id = $result; + $got_string = false; + } + + if (!is_resource($id)) { + return $this->sybaseRaiseError(DB_ERROR_NEED_MORE_DATA); + } + + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; + } else { + $case_func = 'strval'; + } + + $count = @sybase_num_fields($id); + $res = array(); + + if ($mode) { + $res['num_fields'] = $count; + } + + for ($i = 0; $i < $count; $i++) { + $f = @sybase_fetch_field($id, $i); + // column_source is often blank + $res[$i] = array( + 'table' => $got_string + ? $case_func($result) + : $case_func($f->column_source), + 'name' => $case_func($f->name), + 'type' => $f->type, + 'len' => $f->max_length, + 'flags' => '', + ); + if ($res[$i]['table']) { + $res[$i]['flags'] = $this->_sybase_field_flags( + $res[$i]['table'], $res[$i]['name']); + } + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + } + + // free the result only if we were called on a table + if ($got_string) { + @sybase_free_result($id); + } + return $res; + } + + // }}} + // {{{ _sybase_field_flags() + + /** + * Get the flags for a field + * + * Currently supports: + * + <samp>unique_key</samp> (unique index, unique check or primary_key) + * + <samp>multiple_key</samp> (multi-key index) + * + * @param string $table the table name + * @param string $column the field name + * + * @return string space delimited string of flags. Empty string if none. + * + * @access private + */ + function _sybase_field_flags($table, $column) + { + static $tableName = null; + static $flags = array(); + + if ($table != $tableName) { + $flags = array(); + $tableName = $table; + + /* We're running sp_helpindex directly because it doesn't exist in + * older versions of ASE -- unfortunately, we can't just use + * DB::isError() because the user may be using callback error + * handling. */ + $res = @sybase_query("sp_helpindex $table", $this->connection); + + if ($res === false || $res === true) { + // Fake a valid response for BC reasons. + return ''; + } + + while (($val = sybase_fetch_assoc($res)) !== false) { + if (!isset($val['index_keys'])) { + /* No useful information returned. Break and be done with + * it, which preserves the pre-1.7.9 behaviour. */ + break; + } + + $keys = explode(', ', trim($val['index_keys'])); + + if (sizeof($keys) > 1) { + foreach ($keys as $key) { + $this->_add_flag($flags[$key], 'multiple_key'); + } + } + + if (strpos($val['index_description'], 'unique')) { + foreach ($keys as $key) { + $this->_add_flag($flags[$key], 'unique_key'); + } + } + } + + sybase_free_result($res); + + } + + if (array_key_exists($column, $flags)) { + return(implode(' ', $flags[$column])); + } + + return ''; + } + + // }}} + // {{{ _add_flag() + + /** + * Adds a string to the flags array if the flag is not yet in there + * - if there is no flag present the array is created + * + * @param array $array reference of flags array to add a value to + * @param mixed $value value to add to the flag array + * + * @return void + * + * @access private + */ + function _add_flag(&$array, $value) + { + if (!is_array($array)) { + $array = array($value); + } elseif (!in_array($value, $array)) { + array_push($array, $value); + } + } + + // }}} + // {{{ getSpecialQuery() + + /** + * Obtains the query string needed for listing a given type of objects + * + * @param string $type the kind of objects you want to retrieve + * + * @return string the SQL query string or null if the driver doesn't + * support the object type requested + * + * @access protected + * @see DB_common::getListOf() + */ + function getSpecialQuery($type) + { + switch ($type) { + case 'tables': + return "SELECT name FROM sysobjects WHERE type = 'U'" + . ' ORDER BY name'; + case 'views': + return "SELECT name FROM sysobjects WHERE type = 'V'"; + default: + return null; + } + } + + // }}} + +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/civicrm/vendor/pear/db/PATCHES.txt b/civicrm/vendor/pear/db/PATCHES.txt new file mode 100644 index 0000000000..5885f7128c --- /dev/null +++ b/civicrm/vendor/pear/db/PATCHES.txt @@ -0,0 +1,7 @@ +This file was automatically generated by Composer Patches (https://github.com/cweagans/composer-patches) +Patches applied to this directory: + +Apply CiviCRM Customisations for the pear:db package +Source: https://raw.githubusercontent.com/civicrm/civicrm-core/a48a43c2b5f6d694fff1cfb99d522c5d9e2459a0/tools/scripts/composer/pear_db_civicrm_changes.patch + + diff --git a/civicrm/vendor/pear/db/composer.json b/civicrm/vendor/pear/db/composer.json new file mode 100644 index 0000000000..dff216a9d0 --- /dev/null +++ b/civicrm/vendor/pear/db/composer.json @@ -0,0 +1,43 @@ +{ + "authors": [ + { + "email": "danielc@php.net", + "name": "Daniel Convissor", + "role": "Lead" + }, + { + "email": "aharvey@php.net", + "name": "Adam Harvey", + "role": "Lead" + }, + { + "email": "stig@php.net", + "name": "Stig Bakken", + "role": "Developer" + }, + { + "email": "cox@idecnet.com", + "name": "Tomas V.V.Cox", + "role": "Developer" + } + ], + "autoload": { + "psr-0": { + "DB": "./" + } + }, + "description": "More info available on: http://pear.php.net/package/DB", + "include-path": [ + "./" + ], + "license": "PHP License v3.01", + "name": "pear/db", + "support": { + "issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=DB", + "source": "https://github.com/pear/DB" + }, + "type": "library", + "require": { + "pear/pear-core-minimal": "*" + } +} diff --git a/civicrm/xml/schema/Activity/Activity.xml b/civicrm/xml/schema/Activity/Activity.xml index 8a37ea3d76..1fee89be2f 100644 --- a/civicrm/xml/schema/Activity/Activity.xml +++ b/civicrm/xml/schema/Activity/Activity.xml @@ -21,30 +21,6 @@ <name>id</name> <autoincrement>true</autoincrement> </primaryKey> - <field> - <name>source_contact_id</name> - <type>int unsigned</type> - <title>Source Contact</title> - <import>true</import> - <headerPattern>/(activity.)?source(.contact(.id)?)?/i</headerPattern> - <comment>Contact ID of the person scheduling or logging this Activity. Usually the authenticated user.</comment> - <add>1.1</add> - <drop>4.4</drop> - </field> - <foreignKey> - <name>source_contact_id</name> - <table>civicrm_contact</table> - <key>id</key> - <add>1.1</add> - <drop>4.4</drop> - <onDelete>SET NULL</onDelete> - </foreignKey> - <index> - <name>UI_source_contact_id</name> - <fieldName>source_contact_id</fieldName> - <add>2.0</add> - <drop>3.2</drop> - </index> <field> <name>source_record_id</name> <type>int unsigned</type> @@ -59,7 +35,7 @@ </index> <field> <name>activity_type_id</name> - <title>Activity Type</title> + <title>Activity Type ID</title> <import>true</import> <type>int unsigned</type> <required>true</required> @@ -73,6 +49,7 @@ </pseudoconstant> <html> <type>Select</type> + <label>Activity Type</label> </html> </field> <index> @@ -80,23 +57,6 @@ <fieldName>activity_type_id</fieldName> <add>1.6</add> </index> - <field> - <name>target_entity_table</name> - <type>varchar</type> - <length>64</length> - <required>true</required> - <comment>Name of table where item being referenced is stored.</comment> - <add>1.1</add> - <drop>2.0</drop> - </field> - <field> - <name>target_entity_id</name> - <type>int unsigned</type> - <required>true</required> - <comment>Foreign key to the referenced item.</comment> - <add>1.1</add> - <drop>2.0</drop> - </field> <field> <name>subject</name> <uniqueName>activity_subject</uniqueName> @@ -112,13 +72,6 @@ <add>1.1</add> <change>2.0</change> </field> - <field> - <name>scheduled_date</name> - <type>datetime</type> - <comment>Date and time meeting is scheduled to occur.</comment> - <add>1.1</add> - <drop>2.0</drop> - </field> <field> <name>activity_date_time</name> <import>true</import> @@ -140,27 +93,6 @@ <fieldName>activity_date_time</fieldName> <add>4.7</add> </index> - <field> - <name>due_date_time</name> - <type>datetime</type> - <comment>Date and time this activity is due.</comment> - <add>2.0</add> - <drop>3.0</drop> - </field> - <field> - <name>duration_hours</name> - <type>int unsigned</type> - <comment>Planned or actual duration of meeting - hours.</comment> - <add>1.1</add> - <drop>2.0</drop> - </field> - <field> - <name>duration_minutes</name> - <type>int unsigned</type> - <comment>Planned or actual duration of meeting - minutes.</comment> - <add>1.1</add> - <drop>2.0</drop> - </field> <field> <name>duration</name> <uniqueName>activity_duration</uniqueName> @@ -191,11 +123,12 @@ <field> <name>phone_id</name> <type>int unsigned</type> - <title>Phone (called)</title> + <title>Phone (called) ID</title> <comment>Phone ID of the number called (optional - used if an existing phone number is selected).</comment> <add>2.0</add> <html> <type>EntityRef</type> + <label>Phone (called)</label> </html> </field> <foreignKey> @@ -231,15 +164,6 @@ </html> <add>1.1</add> </field> - <field> - <name>status</name> - <type>enum</type> - <title>Status</title> - <values>Scheduled, Completed</values> - <comment>What is the status of this meeting? Completed meeting status results in activity history entry.</comment> - <add>1.1</add> - <drop>2.0</drop> - </field> <field> <name>status_id</name> <uniqueName>activity_status_id</uniqueName> @@ -374,12 +298,6 @@ <add>2.2</add> <onDelete>CASCADE</onDelete> </foreignKey> - <index> - <name>UI_original_id</name> - <fieldName>original_id</fieldName> - <add>2.2</add> - <drop>3.2</drop> - </index> <field> <name>result</name> <uniqueName>activity_result</uniqueName> diff --git a/civicrm/xml/schema/Activity/ActivityAssignment.xml b/civicrm/xml/schema/Activity/ActivityAssignment.xml deleted file mode 100644 index f3df06bb54..0000000000 --- a/civicrm/xml/schema/Activity/ActivityAssignment.xml +++ /dev/null @@ -1,115 +0,0 @@ -<?xml version="1.0" encoding="utf-8" ?> -<table> - <base>CRM/Activity</base> - <class>ActivityAssignment</class> - <name>civicrm_activity_assignment</name> - <comment>Activity assignments</comment> - <add>1.8</add> - <log>true</log> - <field> - <name>id</name> - <type>int unsigned</type> - <title>Activity Assignment ID</title> - <required>true</required> - <comment>Activity assignment id</comment> - <add>1.8</add> - </field> - <primaryKey> - <name>id</name> - <autoincrement>true</autoincrement> - </primaryKey> - - <field> - <name>activity_entity_table</name> - <type>varchar</type> - <length>64</length> - <required>true</required> - <comment>Name of table where item being referenced is stored (activity, phonecall or meeting).</comment> - <add>1.8</add> - <drop>2.0</drop> - </field> - - <field> - <name>activity_entity_id</name> - <type>int unsigned</type> - <required>true</required> - <comment>Entity (activity, phonecall or meeting) id for which the assigment is created</comment> - <add>1.8</add> - <drop>2.0</drop> - </field> - - <dynamicForeignKey> - <idColumn>activity_entity_id</idColumn> - <typeColumn>activity_entity_table</typeColumn> - <add>1.8</add> - <drop>2.0</drop> - </dynamicForeignKey> - - <field> - <name>target_entity_table</name> - <type>varchar</type> - <length>64</length> - <required>true</required> - <comment>Name of table where item being referenced is stored (contact assigned to given activity).</comment> - <add>1.8</add> - <drop>2.0</drop> - </field> - - <field> - <name>target_entity_id</name> - <type>int unsigned</type> - <required>true</required> - <comment>Foreign key to the referenced item.</comment> - <add>1.1</add> - <drop>2.0</drop> - </field> - - <dynamicForeignKey> - <idColumn>target_entity_id</idColumn> - <typeColumn>target_entity_table</typeColumn> - <add>1.8</add> - <drop>2.0</drop> - </dynamicForeignKey> - - <field> - <name>activity_id</name> - <type>int unsigned</type> - <title>Activity ID</title> - <required>true</required> - <comment>Foreign key to the activity for this assignment.</comment> - <add>2.0</add> - </field> - <foreignKey> - <name>activity_id</name> - <table>civicrm_activity</table> - <key>id</key> - <add>2.0</add> - <onDelete>CASCADE</onDelete> - </foreignKey> - - <field> - <name>assignee_contact_id</name> - <type>int unsigned</type> - <title>Assignee Contact ID</title> - <required>true</required> - <comment>Foreign key to the contact for this assignment.</comment> - <add>2.0</add> - </field> - <foreignKey> - <name>assignee_contact_id</name> - <table>civicrm_contact</table> - <key>id</key> - <add>2.0</add> - <onDelete>CASCADE</onDelete> - </foreignKey> - - <index> - <name>UI_activity_assignee_contact_id</name> - <fieldName>assignee_contact_id</fieldName> - <fieldName>activity_id</fieldName> - <unique>true</unique> - <add>2.0</add> - </index> - - -</table> diff --git a/civicrm/xml/schema/Activity/ActivityTarget.xml b/civicrm/xml/schema/Activity/ActivityTarget.xml deleted file mode 100644 index 6b5cba7867..0000000000 --- a/civicrm/xml/schema/Activity/ActivityTarget.xml +++ /dev/null @@ -1,61 +0,0 @@ -<?xml version="1.0" encoding="utf-8" ?> -<table> - <base>CRM/Activity</base> - <class>ActivityTarget</class> - <name>civicrm_activity_target</name> - <comment>Activity targets</comment> - <add>2.0</add> - <log>true</log> - <field> - <name>id</name> - <type>int unsigned</type> - <title>Activity Target ID</title> - <required>true</required> - <comment>Activity target id</comment> - <add>2.0</add> - </field> - <primaryKey> - <name>id</name> - <autoincrement>true</autoincrement> - </primaryKey> - <field> - <name>activity_id</name> - <type>int unsigned</type> - <title>Activity ID</title> - <required>true</required> - <comment>Foreign key to the activity for this target.</comment> - <add>2.0</add> - </field> - <foreignKey> - <name>activity_id</name> - <table>civicrm_activity</table> - <key>id</key> - <add>2.0</add> - <onDelete>CASCADE</onDelete> - </foreignKey> - - <field> - <name>target_contact_id</name> - <title>Contact ID (match to contact)</title> - <import>true</import> - <type>int unsigned</type> - <required>true</required> - <comment>Foreign key to the contact for this target.</comment> - <add>2.0</add> - </field> - <foreignKey> - <name>target_contact_id</name> - <table>civicrm_contact</table> - <key>id</key> - <add>2.0</add> - <onDelete>CASCADE</onDelete> - </foreignKey> - - <index> - <name>UI_activity_target_contact_id</name> - <fieldName>target_contact_id</fieldName> - <fieldName>activity_id</fieldName> - <unique>true</unique> - <add>2.0</add> - </index> -</table> diff --git a/civicrm/xml/schema/Batch/Batch.xml b/civicrm/xml/schema/Batch/Batch.xml index 3bcbc44409..811ecf4f21 100644 --- a/civicrm/xml/schema/Batch/Batch.xml +++ b/civicrm/xml/schema/Batch/Batch.xml @@ -35,15 +35,6 @@ <unique>true</unique> <add>4.2</add> </index> - <field> - <name>label</name> - <type>varchar</type> - <length>64</length> - <localizable>true</localizable> - <comment>Friendly Name.</comment> - <add>3.3</add> - <drop>4.2</drop> - </field> <field> <name>title</name> <title>Batch Title</title> diff --git a/civicrm/xml/schema/Batch/EntityBatch.xml b/civicrm/xml/schema/Batch/EntityBatch.xml index 462c40d1a7..004289557d 100644 --- a/civicrm/xml/schema/Batch/EntityBatch.xml +++ b/civicrm/xml/schema/Batch/EntityBatch.xml @@ -5,6 +5,7 @@ <class>EntityBatch</class> <name>civicrm_entity_batch</name> <comment>Batch entities (Contributions, Participants, Contacts) to a batch.</comment> + <add>3.3</add> <field> <name>id</name> <title>EntityBatch ID</title> diff --git a/civicrm/xml/schema/Case/Case.xml b/civicrm/xml/schema/Case/Case.xml index a91e955a55..492a1df43d 100644 --- a/civicrm/xml/schema/Case/Case.xml +++ b/civicrm/xml/schema/Case/Case.xml @@ -24,23 +24,6 @@ <name>id</name> <autoincrement>true</autoincrement> </primaryKey> - <field> - <name>contact_id</name> - <type>int unsigned</type> - <uniqueName>case_contact_id</uniqueName> - <required>true</required> - <comment>Contact ID of contact record given case belongs to.</comment> - <add>1.8</add> - <drop>2.1</drop> - </field> - <foreignKey> - <name>contact_id</name> - <table>civicrm_contact</table> - <key>id</key> - <add>1.8</add> - <drop>2.1</drop> - <onDelete>CASCADE</onDelete> - </foreignKey> <field> <name>case_type_id</name> <type>int unsigned</type> @@ -70,51 +53,6 @@ <key>id</key> <add>4.5</add> </foreignKey> - <field> - <name>casetag1_id</name> - <type>varchar</type> - <length>128</length> - <required>true</required> - <comment>Id of first case category.</comment> - <add>1.8</add> - <drop>2.0</drop> - </field> - <index> - <name>index_casetag1_id</name> - <fieldName>casetag1_id</fieldName> - <add>1.8</add> - <drop>2.0</drop> - </index> - <field> - <name>casetag2_id</name> - <type>varchar</type> - <length>128</length> - <required>true</required> - <comment>Id of second case category.</comment> - <add>1.8</add> - <drop>2.0</drop> - </field> - <index> - <name>index_casetag2_id</name> - <fieldName>casetag2_id</fieldName> - <add>1.8</add> - <drop>2.0</drop> - </index> - <field> - <name>casetag3_id</name> - <type>varchar</type> - <length>128</length> - <required>true</required> - <comment>Id of third case category.</comment> - <add>1.8</add> - <drop>2.0</drop> - </field> - <index> - <name>index_casetag3_id</name> - <fieldName>casetag3_id</fieldName> - <add>1.8</add> - <drop>2.0</drop> - </index> <field> <name>subject</name> <type>varchar</type> diff --git a/civicrm/xml/schema/Case/CaseActivity.xml b/civicrm/xml/schema/Case/CaseActivity.xml index 0ae6710e4d..daa0a895d2 100644 --- a/civicrm/xml/schema/Case/CaseActivity.xml +++ b/civicrm/xml/schema/Case/CaseActivity.xml @@ -54,31 +54,4 @@ <fieldName>activity_id</fieldName> <add>2.0</add> </index> - - <field> - <name>activity_entity_table</name> - <type>varchar</type> - <length>64</length> - <required>true</required> - <comment>Name of table where item being referenced is stored (activity, phonecall or meeting).</comment> - <add>1.8</add> - <drop>2.0</drop> - </field> - - <field> - <name>activity_entity_id</name> - <type>int unsigned</type> - <required>true</required> - <comment>Entity (activity, phonecall or meeting) id for which the assigment is created</comment> - <add>1.8</add> - <drop>2.0</drop> - </field> - - <dynamicForeignKey> - <idColumn>activity_entity_id</idColumn> - <typeColumn>activity_entity_table</typeColumn> - <add>1.8</add> - <drop>2.0</drop> - </dynamicForeignKey> - </table> diff --git a/civicrm/xml/schema/Contact/Contact.xml b/civicrm/xml/schema/Contact/Contact.xml index dda19acd7f..8200affa64 100644 --- a/civicrm/xml/schema/Contact/Contact.xml +++ b/civicrm/xml/schema/Contact/Contact.xml @@ -246,19 +246,6 @@ <fulltext/> <add>1.1</add> </field> - <field> - <name>home_URL</name> - <rule>url</rule> - <title>Website</title> - <type>varchar</type> - <length>128</length> - <import>true</import> - <headerPattern>/^(home\sURL)|URL|web|site/i</headerPattern> - <dataPattern>/^[\w\/\:\.]+$/</dataPattern> - <comment>optional "home page" URL for this contact.</comment> - <add>1.1</add> - <drop>3.2</drop> - </field> <field> <name>image_URL</name> <type>text</type> @@ -509,32 +496,6 @@ <fieldName>communication_style_id</fieldName> <add>4.4</add> </index> - <field> - <name>greeting_type</name> - <type>varchar</type> - <length>128</length> - <import>true</import> - <comment>Preferred greeting format.</comment> - <add>1.1</add> - <drop>2.2</drop> - </field> - <field> - <name>greeting_type_id</name> - <title>Greeting Type</title> - <type>int unsigned</type> - <comment>FK to civicrm_option_value.id, that has to be valid, registered Greeting type.</comment> - <add>2.2</add> - <drop>3.0</drop> - </field> - <field> - <name>custom_greeting</name> - <type>varchar</type> - <length>128</length> - <import>true</import> - <comment>Custom greeting message.</comment> - <add>1.1</add> - <drop>3.0</drop> - </field> <field> <name>email_greeting_id</name> <type>int unsigned</type> @@ -654,13 +615,14 @@ </field> <field> <name>gender_id</name> - <title>Gender</title> + <title>Gender ID</title> <type>int unsigned</type> <pseudoconstant> <optionGroupName>gender</optionGroupName> </pseudoconstant> <html> <type>Select</type> + <label>Gender</label> </html> <headerPattern>/^gender$/i</headerPattern> <comment>FK to gender ID</comment> @@ -721,23 +683,6 @@ </html> <contactType>Individual</contactType> </field> - <field> - <name>mail_to_household_id</name> - <title>Mail to Household ID</title> - <type>int unsigned</type> - <comment>OPTIONAL FK to civicrm_contact_household record. If NOT NULL, direct mail communications to household rather than individual location. </comment> - <export>true</export> - <add>1.1</add> - <drop>3.3</drop> - </field> - <foreignKey> - <name>mail_to_household_id</name> - <table>civicrm_contact</table> - <key>id</key> - <add>2.1</add> - <onDelete>SET NULL</onDelete> - <drop>3.3</drop> - </foreignKey> <field> <name>household_name</name> <type>varchar</type> @@ -825,7 +770,7 @@ </field> <field> <name>employer_id</name> - <title>Current Employer</title> + <title>Current Employer ID</title> <uniqueName>current_employer_id</uniqueName> <type>int unsigned</type> <comment>OPTIONAL FK to civicrm_contact record.</comment> @@ -833,6 +778,7 @@ <add>2.1</add> <html> <type>EntityRef</type> + <label>Current Employer</label> </html> <contactType>Individual</contactType> </field> @@ -857,12 +803,6 @@ </html> <permission>access deleted contacts</permission> </field> - <index> - <name>index_is_deleted</name> - <fieldName>is_deleted</fieldName> - <add>3.2</add> - <drop>4.4</drop> - </index> <index> <name>index_is_deleted_sort_name</name> <fieldName>is_deleted</fieldName> diff --git a/civicrm/xml/schema/Contact/Individual.xml b/civicrm/xml/schema/Contact/Individual.xml index 45f2720677..9956f17beb 100644 --- a/civicrm/xml/schema/Contact/Individual.xml +++ b/civicrm/xml/schema/Contact/Individual.xml @@ -74,14 +74,6 @@ <fieldName>last_name</fieldName> <add>1.8</add> </index> - <field> - <name>gender</name> - <type>enum</type> - <import>true</import> - <values>Female, Male, Other</values> - <add>1.1</add> - <drop>1.2</drop> - </field> <field> <name>prefix_id</name> <type>int unsigned</type> @@ -108,32 +100,6 @@ <fieldName>suffix_id</fieldName> <add>1.6</add> </index> - <field> - <name>prefix</name> - <type>varchar</type> - <length>64</length> - <import>true</import> - <comment>Prefix to Name.</comment> - <add>1.1</add> - <drop>1.2</drop> - </field> - <field> - <name>suffix</name> - <type>varchar</type> - <length>64</length> - <import>true</import> - <comment>Suffix to Name.</comment> - <add>1.1</add> - <drop>1.2</drop> - </field> - <field> - <name>greeting_type</name> - <type>varchar</type> - <length>128</length> - <comment>Preferred greeting format.</comment> - <add>1.1</add> - <drop>2.2</drop> - </field> <field> <name>greeting_type_id</name> <title>Greeting Type</title> diff --git a/civicrm/xml/schema/Contact/SavedSearch.xml b/civicrm/xml/schema/Contact/SavedSearch.xml index 89f578084b..2d73182ac5 100644 --- a/civicrm/xml/schema/Contact/SavedSearch.xml +++ b/civicrm/xml/schema/Contact/SavedSearch.xml @@ -18,15 +18,6 @@ <name>id</name> <autoincrement>false</autoincrement> </primaryKey> - <field> - <name>query</name> - <title>SQL Query</title> - <type>text</type> - <import>true</import> - <comment>SQL query for this search</comment> - <add>1.1</add> - <drop>1.5</drop> - </field> <field> <name>form_values</name> <title>Submitted Form Values</title> @@ -36,14 +27,6 @@ <serialize>PHP</serialize> <add>1.1</add> </field> - <field> - <name>is_active</name> - <type>boolean</type> - <title>Saved Search Enabled</title> - <comment>Is this entry active?</comment> - <add>1.1</add> - <drop>1.5</drop> - </field> <field> <name>mapping_id</name> <type>int unsigned</type> diff --git a/civicrm/xml/schema/Contribute/Contribution.xml b/civicrm/xml/schema/Contribute/Contribution.xml index 246c55bd32..fed11d358c 100644 --- a/civicrm/xml/schema/Contribute/Contribution.xml +++ b/civicrm/xml/schema/Contribute/Contribution.xml @@ -46,42 +46,9 @@ <add>1.3</add> <onDelete>CASCADE</onDelete> </foreignKey> - <field> - <name>solicitor_id</name> - <title>Solicitor ID</title> - <type>int unsigned</type> - <comment>FK to Solicitor ID</comment> - <add>1.4</add> - <drop>2.2</drop> - </field> - <foreignKey> - <name>solicitor_id</name> - <table>civicrm_contact</table> - <key>id</key> - <add>1.4</add> - <drop>2.2</drop> - <onDelete>SET NULL</onDelete> - </foreignKey> - <field> - <name>contribution_type_id</name> - <title>Contribution Type</title> - <export>false</export> - <type>int unsigned</type> - <comment>FK to Contribution Type</comment> - <add>1.3</add> - <drop>4.3</drop> - </field> - <foreignKey> - <name>contribution_type_id</name> - <table>civicrm_contribution_type</table> - <key>id</key> - <add>1.3</add> - <drop>4.3</drop> - <onDelete>SET NULL</onDelete> - </foreignKey> <field> <name>financial_type_id</name> - <title>Financial Type</title> + <title>Financial Type ID</title> <type>int unsigned</type> <comment>FK to Financial Type for (total_amount - non_deductible_amount).</comment> <pseudoconstant> @@ -92,6 +59,7 @@ <export>true</export> <html> <type>Select</type> + <label>Financial Type</label> </html> <add>4.3</add> </field> @@ -114,6 +82,7 @@ </pseudoconstant> <html> <type>Select</type> + <label>Contribution Page</label> </html> <add>1.5</add> </field> @@ -359,16 +328,6 @@ <type>Text</type> </html> </field> - <field> - <name>note</name> - <type>text</type> - <comment>Note and/or Comment.</comment> - <import>true</import> - <headerPattern>/Note|Comment/i</headerPattern> - <dataPattern>//</dataPattern> - <add>1.4</add> - <drop>1.7</drop> - </field> <index> <name>UI_contrib_trxn_id</name> <fieldName>trxn_id</fieldName> diff --git a/civicrm/xml/schema/Contribute/ContributionPage.xml b/civicrm/xml/schema/Contribute/ContributionPage.xml index 9c08b4738b..fb93703f74 100644 --- a/civicrm/xml/schema/Contribute/ContributionPage.xml +++ b/civicrm/xml/schema/Contribute/ContributionPage.xml @@ -41,20 +41,6 @@ <comment>Text and html allowed. Displayed below title.</comment> <add>1.3</add> </field> - <field> - <name>contribution_type_id</name> - <type>int unsigned</type> - <required>true</required> - <comment>default Contribution type assigned to contributions submitted via this page, e.g. Contribution, Campaign Contribution</comment> - <add>1.3</add> - <drop>4.3</drop> - </field> - <foreignKey> - <name>contribution_type_id</name> - <table>civicrm_contribution_type</table> - <key>id</key> - <drop>4.3</drop> - </foreignKey> <field> <name>financial_type_id</name> <title>Financial Type</title> @@ -80,6 +66,7 @@ <name>payment_processor</name> <type>varchar</type> <length>128</length> + <serialize>SEPARATOR_TRIMMED</serialize> <comment>Payment Processors configured for this contribution Page</comment> <pseudoconstant> <table>civicrm_payment_processor</table> diff --git a/civicrm/xml/schema/Contribute/ContributionProduct.xml b/civicrm/xml/schema/Contribute/ContributionProduct.xml index d5ff804a10..1e3526d27c 100644 --- a/civicrm/xml/schema/Contribute/ContributionProduct.xml +++ b/civicrm/xml/schema/Contribute/ContributionProduct.xml @@ -55,17 +55,6 @@ <comment></comment> <add>1.4</add> </field> - <field> - <name>total_cost</name> - <type>decimal</type> - <required>true</required> - <import>true</import> - <headerPattern>/^total|(.?^am(ou)?nt)/i</headerPattern> - <dataPattern>/^\d+(\.\d{2})?$/</dataPattern> - <comment>quantity X civicrm_product.cost.</comment> - <add>1.3</add> - <drop>4.1</drop> - </field> <field> <name>fulfilled_date</name> <type>date</type> diff --git a/civicrm/xml/schema/Contribute/ContributionRecur.xml b/civicrm/xml/schema/Contribute/ContributionRecur.xml index eddc342d9c..11e70b6afc 100644 --- a/civicrm/xml/schema/Contribute/ContributionRecur.xml +++ b/civicrm/xml/schema/Contribute/ContributionRecur.xml @@ -286,13 +286,6 @@ <html> <type>Text</type> </html> - </field> - <field> - <name>next_sched_contribution</name> - <type>datetime</type> - <comment>At Groundspring this was used by the cron job which triggered payments. If we''re not doing that but we know about payments, it might still be useful to store for display to org andor contributors.</comment> - <add>1.6</add> - <drop>4.4</drop> </field> <field> <name>next_sched_contribution_date</name> @@ -366,23 +359,6 @@ <add>3.3</add> <onDelete>SET NULL</onDelete> </foreignKey> - <field> - <name>contribution_type_id</name> - <title>Contribution Type</title> - <export>false</export> - <type>int unsigned</type> - <comment>FK to Contribution Type</comment> - <add>4.1</add> - <drop>4.3</drop> - </field> - <foreignKey> - <name>contribution_type_id</name> - <table>civicrm_contribution_type</table> - <key>id</key> - <add>4.1</add> - <drop>4.3</drop> - <onDelete>SET NULL</onDelete> - </foreignKey> <field> <name>financial_type_id</name> <title>Financial Type</title> diff --git a/civicrm/xml/schema/Contribute/PremiumsProduct.xml b/civicrm/xml/schema/Contribute/PremiumsProduct.xml index 7f9b866047..6ed7ca1ce6 100644 --- a/civicrm/xml/schema/Contribute/PremiumsProduct.xml +++ b/civicrm/xml/schema/Contribute/PremiumsProduct.xml @@ -47,14 +47,6 @@ <key>id</key> <add>1.4</add> </foreignKey> - <field> - <name>sort_position</name> - <title>Sort Position</title> - <type>int unsigned</type> - <required>true</required> - <add>1.4</add> - <drop>2.0</drop> - </field> <field> <name>weight</name> <title>Order</title> diff --git a/civicrm/xml/schema/Core/ActionSchedule.xml b/civicrm/xml/schema/Core/ActionSchedule.xml index 4c7d78c219..536f174cfb 100644 --- a/civicrm/xml/schema/Core/ActionSchedule.xml +++ b/civicrm/xml/schema/Core/ActionSchedule.xml @@ -51,6 +51,7 @@ <type>varchar</type> <length>255</length> <comment>Entity value</comment> + <serialize>SEPARATOR_TRIMMED</serialize> <add>3.4</add> </field> <field> @@ -58,6 +59,7 @@ <type>varchar</type> <length>64</length> <comment>Entity status</comment> + <serialize>SEPARATOR_TRIMMED</serialize> <add>3.4</add> </field> <field> @@ -169,6 +171,7 @@ <type>varchar</type> <length>128</length> <comment>Contact IDs to which reminder should be sent.</comment> + <serialize>COMMA</serialize> <add>3.4</add> </field> <field> diff --git a/civicrm/xml/schema/Core/Cache.xml b/civicrm/xml/schema/Core/Cache.xml index 564a277329..1e8d9a389f 100644 --- a/civicrm/xml/schema/Core/Cache.xml +++ b/civicrm/xml/schema/Core/Cache.xml @@ -35,14 +35,6 @@ <comment>Unique path name for cache element</comment> <add>2.1</add> </field> - <index> - <name>UI_group_path</name> - <fieldName>group_name</fieldName> - <fieldName>path</fieldName> - <unique>true</unique> - <add>2.1</add> - <drop>4.2</drop> - </index> <index> <name>UI_group_path_date</name> <fieldName>group_name</fieldName> diff --git a/civicrm/xml/schema/Core/CustomField.xml b/civicrm/xml/schema/Core/CustomField.xml index 3e8c2b7d64..44c6322560 100644 --- a/civicrm/xml/schema/Core/CustomField.xml +++ b/civicrm/xml/schema/Core/CustomField.xml @@ -211,14 +211,6 @@ <comment>Date may be up to end_date_years years after the current date.</comment> <add>1.4</add> </field> - <field> - <name>date_parts</name> - <type>varchar</type> - <length>255</length> - <comment>which date part included in display </comment> - <add>1.4</add> - <drop>3.1</drop> - </field> <field> <name>date_format</name> <type>varchar</type> diff --git a/civicrm/xml/schema/Core/CustomGroup.xml b/civicrm/xml/schema/Core/CustomGroup.xml index e0f424ddfc..e21458ecba 100644 --- a/civicrm/xml/schema/Core/CustomGroup.xml +++ b/civicrm/xml/schema/Core/CustomGroup.xml @@ -47,14 +47,6 @@ <comment>Type of object this group extends (can add other options later e.g. contact_address, etc.).</comment> <add>1.1</add> </field> - <field> - <name>extends_entity_column_name</name> - <type>varchar</type> - <length>64</length> - <comment>linking custom group for dynamic object</comment> - <add>1.6</add> - <drop>2.2</drop> - </field> <field> <name>extends_entity_column_id</name> <type>int unsigned</type> diff --git a/civicrm/xml/schema/Core/Dashboard.xml b/civicrm/xml/schema/Core/Dashboard.xml index 00500943a8..6b34614dbc 100644 --- a/civicrm/xml/schema/Core/Dashboard.xml +++ b/civicrm/xml/schema/Core/Dashboard.xml @@ -62,13 +62,6 @@ <comment>url in case of external dashlet</comment> <add>3.1</add> </field> - <field> - <name>content</name> - <type>text</type> - <comment>dashlet content</comment> - <add>3.1</add> - <drop>3.3</drop> - </field> <field> <name>permission</name> <type>varchar</type> @@ -155,12 +148,4 @@ <required>true</required> <add>4.7</add> </field> - <field> - <name>created_date</name> - <type>datetime</type> - <title>Dashlet Created Date</title> - <comment>When was content populated</comment> - <add>3.1</add> - <drop>3.3</drop> - </field> </table> diff --git a/civicrm/xml/schema/Core/Discount.xml b/civicrm/xml/schema/Core/Discount.xml index 4dabdfcf85..54535e4857 100644 --- a/civicrm/xml/schema/Core/Discount.xml +++ b/civicrm/xml/schema/Core/Discount.xml @@ -5,6 +5,7 @@ <class>Discount</class> <name>civicrm_discount</name> <comment>Stores discounts for events on the basis of date</comment> + <add>2.1</add> <log>true</log> <field> <name>id</name> @@ -45,25 +46,6 @@ <fieldName>entity_id</fieldName> <add>2.1</add> </index> - <field> - <name>option_group_id</name> - <uniqueName>participant_discount_name</uniqueName> - <title>Discount Name</title> - <type>int unsigned</type> - <required>true</required> - <export>true</export> - <comment>FK to civicrm_price_set</comment> - <add>2.1</add> - <drop>4.3</drop> - </field> - <foreignKey> - <name>option_group_id</name> - <table>civicrm_price_set</table> - <key>id</key> - <add>2.1</add> - <onDelete>CASCADE</onDelete> - <drop>4.3</drop> - </foreignKey> <field> <name>price_set_id</name> <uniqueName>participant_discount_name</uniqueName> diff --git a/civicrm/xml/schema/Core/Domain.xml b/civicrm/xml/schema/Core/Domain.xml index ddb2a0dc6a..8e4e119a7f 100644 --- a/civicrm/xml/schema/Core/Domain.xml +++ b/civicrm/xml/schema/Core/Domain.xml @@ -46,46 +46,6 @@ <unique>true</unique> <add>1.1</add> </index> - <field> - <name>contact_name</name> - <type>varchar</type> - <length>64</length> - <comment>Name of the person responsible for this domain</comment> - <add>1.1</add> - <drop>1.9</drop> - </field> - <field> - <name>email_name</name> - <type>varchar</type> - <length>255</length> - <comment>The default email name that is used in the from address for all outgoing emails</comment> - <add>1.9</add> - <drop>2.2</drop> - </field> - <field> - <name>email_address</name> - <type>varchar</type> - <length>255</length> - <comment>The default email address that is used as the from address for all outgoing emails</comment> - <add>1.9</add> - <drop>2.2</drop> - </field> - <field> - <name>email_domain</name> - <type>varchar</type> - <length>64</length> - <comment>The domain from which outgoing email for this domain will appear to originate</comment> - <add>1.1</add> - <drop>2.2</drop> - </field> - <field> - <name>email_return_path</name> - <type>varchar</type> - <length>64</length> - <comment>The domain from which outgoing email for this domain will appear to originate</comment> - <add>1.1</add> - <drop>2.2</drop> - </field> <field> <name>config_backend</name> <type>text</type> @@ -103,13 +63,6 @@ <comment>The civicrm version this instance is running</comment> <add>2.0</add> </field> - <field> - <name>loc_block_id</name> - <type>int unsigned</type> - <comment>FK to Location Block ID. This is specifically not an FK to avoid circular constraints</comment> - <add>2.0</add> - <drop>4.3</drop> - </field> <field> <name>contact_id</name> <type>int unsigned</type> diff --git a/civicrm/xml/schema/Core/EntityFile.xml b/civicrm/xml/schema/Core/EntityFile.xml index e4b06ffd25..cad60ba916 100644 --- a/civicrm/xml/schema/Core/EntityFile.xml +++ b/civicrm/xml/schema/Core/EntityFile.xml @@ -5,6 +5,7 @@ <class>EntityFile</class> <name>civicrm_entity_file</name> <comment>Attaches (joins) uploaded files (images, documents, etc.) to entities (Contacts, Groups, Actions).</comment> + <add>1.5</add> <log>true</log> <field> <name>id</name> diff --git a/civicrm/xml/schema/Core/EntityTag.xml b/civicrm/xml/schema/Core/EntityTag.xml index 1f3543dcf0..301706a42b 100644 --- a/civicrm/xml/schema/Core/EntityTag.xml +++ b/civicrm/xml/schema/Core/EntityTag.xml @@ -5,6 +5,7 @@ <class>EntityTag</class> <name>civicrm_entity_tag</name> <comment>Tag entities (Contacts, Groups, Actions) to categories.</comment> + <add>1.1</add> <log>true</log> <field> <name>id</name> @@ -18,22 +19,6 @@ <name>id</name> <autoincrement>true</autoincrement> </primaryKey> - <field> - <name>contact_id</name> - <type>int unsigned</type> - <required>true</required> - <comment>FK to contact table.</comment> - <add>2.0</add> - <drop>3.2</drop> - </field> - <foreignKey> - <name>contact_id</name> - <table>civicrm_contact</table> - <key>id</key> - <add>2.0</add> - <drop>3.2</drop> - <onDelete>CASCADE</onDelete> - </foreignKey> <field> <name>entity_table</name> <type>varchar</type> @@ -58,13 +43,6 @@ <typeColumn>entity_table</typeColumn> <add>3.2</add> </dynamicForeignKey> - <index> - <name>index_entity</name> - <fieldName>entity_table</fieldName> - <fieldName>entity_id</fieldName> - <add>3.2</add> - <drop>3.4</drop> - </index> <field> <name>tag_id</name> <type>int unsigned</type> diff --git a/civicrm/xml/schema/Core/Job.xml b/civicrm/xml/schema/Core/Job.xml index 45429a4aff..a135360f05 100644 --- a/civicrm/xml/schema/Core/Job.xml +++ b/civicrm/xml/schema/Core/Job.xml @@ -85,15 +85,6 @@ <comment>Description of the job</comment> <add>4.1</add> </field> - <field> - <name>api_prefix</name> - <type>varchar</type> - <length>255</length> - <default>"civicrm_api3"</default> - <comment>Prefix of the job api call</comment> - <add>4.1</add> - <drop>4.3</drop> - </field> <field> <name>api_entity</name> <title>API Entity</title> diff --git a/civicrm/xml/schema/Core/Mapping.xml b/civicrm/xml/schema/Core/Mapping.xml index 54ab26d3d0..bd5420ea27 100644 --- a/civicrm/xml/schema/Core/Mapping.xml +++ b/civicrm/xml/schema/Core/Mapping.xml @@ -35,16 +35,6 @@ <comment>Description of Mapping.</comment> <add>1.2</add> </field> - <field> - <name>mapping_type</name> - <type>enum</type> - <values>Export, Import, Export Contributions, Import Contributions, Import Activity, Search Builder, Import - Memberships, Import Participants - </values> - <comment>Type of Mapping.</comment> - <add>1.2</add> - <drop>2.1</drop> - </field> <field> <name>mapping_type_id</name> <type>int unsigned</type> diff --git a/civicrm/xml/schema/Core/MappingField.xml b/civicrm/xml/schema/Core/MappingField.xml index d28b69b30c..0c44414375 100644 --- a/civicrm/xml/schema/Core/MappingField.xml +++ b/civicrm/xml/schema/Core/MappingField.xml @@ -78,14 +78,6 @@ <key>id</key> <add>1.2</add> </foreignKey> - <field> - <name>phone_type</name> - <type>varchar</type> - <length>64</length> - <comment>Phone type, if required</comment> - <add>1.2</add> - <drop>2.2</drop> - </field> <field> <name>phone_type_id</name> <title>Phone Type</title> diff --git a/civicrm/xml/schema/Core/Phone.xml b/civicrm/xml/schema/Core/Phone.xml index 453c64db5f..eb0ad8e1f6 100644 --- a/civicrm/xml/schema/Core/Phone.xml +++ b/civicrm/xml/schema/Core/Phone.xml @@ -36,7 +36,7 @@ </foreignKey> <field> <name>location_type_id</name> - <title>Phone Location Type</title> + <title>Phone Location Type ID</title> <type>int unsigned</type> <comment>Which Location does this phone belong to.</comment> <pseudoconstant> @@ -46,6 +46,7 @@ </pseudoconstant> <html> <type>Select</type> + <label>Phone Location Type</label> </html> <add>2.0</add> </field> @@ -136,19 +137,9 @@ <fieldName>phone_numeric</fieldName> <add>4.3</add> </index> - <field> - <name>phone_type</name> - <type>enum</type> - <values>Phone, Mobile, Fax, Pager</values> - <headerPattern>/phone\s+type/i</headerPattern> - <dataPattern>/phone|mobile|fax|pager/i</dataPattern> - <comment>What type of telecom device is this.</comment> - <add>1.1</add> - <drop>2.2</drop> - </field> <field> <name>phone_type_id</name> - <title>Phone Type</title> + <title>Phone Type ID</title> <type>int unsigned</type> <export>true</export> <comment>Which type of phone does this number belongs.</comment> @@ -157,6 +148,7 @@ </pseudoconstant> <html> <type>Select</type> + <label>Phone Type</label> </html> <add>2.2</add> </field> diff --git a/civicrm/xml/schema/Core/UFField.xml b/civicrm/xml/schema/Core/UFField.xml index fdcdcf83b3..9b012ab4c8 100644 --- a/civicrm/xml/schema/Core/UFField.xml +++ b/civicrm/xml/schema/Core/UFField.xml @@ -103,22 +103,6 @@ <comment>Description and/or help text to display before this field.</comment> <add>3.2</add> </field> - <field> - <name>is_registration</name> - <type>boolean</type> - <default>0</default> - <comment>Is this field included in new user registration forms?</comment> - <add>1.1</add> - <drop>1.3</drop> - </field> - <field> - <name>is_match</name> - <type>boolean</type> - <default>0</default> - <comment>Is this field part of the key for matching users to contacts?</comment> - <add>1.1</add> - <drop>1.3</drop> - </field> <field> <name>visibility</name> <title>Profile Field Visibility</title> @@ -134,14 +118,6 @@ <type>Select</type> </html> </field> - <field> - <name>listings_title</name> - <type>varchar</type> - <length>64</length> - <comment>Page title for listings page (users who share a common value for this property).</comment> - <add>1.1</add> - <drop>1.2</drop> - </field> <field> <name>in_selector</name> <title>Profile Field Is a Filter</title> @@ -172,14 +148,6 @@ <add>1.3</add> <onDelete>SET NULL</onDelete> </foreignKey> - <field> - <name>phone_type</name> - <type>varchar</type> - <length>64</length> - <comment>Phone type, if required</comment> - <add>1.3</add> - <drop>2.2</drop> - </field> <field> <name>phone_type_id</name> <title>Profile Field Phone Type</title> diff --git a/civicrm/xml/schema/Core/UFGroup.xml b/civicrm/xml/schema/Core/UFGroup.xml index cafe160ac6..37d5d0a8aa 100644 --- a/civicrm/xml/schema/Core/UFGroup.xml +++ b/civicrm/xml/schema/Core/UFGroup.xml @@ -37,13 +37,6 @@ <serialize>COMMA</serialize> <add>2.1</add> </field> - <field> - <name>form_type</name> - <type>enum</type> - <values>CiviCRM Profile</values> - <comment>Type of form.</comment> - <drop>2.1</drop> - </field> <field> <name>title</name> <title>Profile Name</title> @@ -81,14 +74,6 @@ <comment>Optional verbose description of the profile.</comment> <add>4.4</add> </field> - <field> - <name>collapse_display</name> - <type>int unsigned</type> - <default>0</default> - <comment>Will this group be in collapsed or expanded mode on initial display ?</comment> - <add>1.1</add> - <drop>2.2</drop> - </field> <field> <name>help_pre</name> <type>text</type> @@ -114,19 +99,6 @@ </html> <add>1.2</add> </field> - <field> - <name>weight</name> - <title>Profile Weight</title> - <type>int</type> - <required>true</required> - <default>1</default> - <html> - <type>Text</type> - </html> - <comment>Controls display order when multiple user framework groups are setup for concurrent display.</comment> - <add>1.2</add> - <drop>1.3</drop> - </field> <field> <name>limit_listings_group_id</name> <title>Profile Search Limit Group</title> diff --git a/civicrm/xml/schema/Core/UFMatch.xml b/civicrm/xml/schema/Core/UFMatch.xml index fb3a05425d..9e573a5a79 100644 --- a/civicrm/xml/schema/Core/UFMatch.xml +++ b/civicrm/xml/schema/Core/UFMatch.xml @@ -73,15 +73,6 @@ <add>1.1</add> <onDelete>CASCADE</onDelete> </foreignKey> - <field> - <name>email</name> - <type>varchar</type> - <length>64</length> - <rule>email</rule> - <comment>Email address</comment> - <add>1.1</add> - <drop>2.0</drop> - </field> <field> <name>language</name> <title>Preferred Language</title> @@ -90,14 +81,6 @@ <comment>UI language preferred by the given user/contact</comment> <add>2.1</add> </field> - <index> - <name>UI_uf_id_domain_id</name> - <fieldName>uf_id</fieldName> - <fieldName>domain_id</fieldName> - <unique>true</unique> - <add>1.5</add> - <drop>1.7</drop> - </index> <index> <name>UI_uf_name_domain_id</name> <fieldName>uf_name</fieldName> diff --git a/civicrm/xml/schema/Dedupe/RuleGroup.xml b/civicrm/xml/schema/Dedupe/RuleGroup.xml index 6adbe3dba4..09a5873628 100644 --- a/civicrm/xml/schema/Dedupe/RuleGroup.xml +++ b/civicrm/xml/schema/Dedupe/RuleGroup.xml @@ -45,15 +45,6 @@ <type>Text</type> </html> </field> - <field> - <name>level</name> - <title>Level</title> - <type>enum</type> - <values>Strict, Fuzzy</values> - <comment>Whether the rule should be used for cases where strict matching of the given contact type is required or a fuzzy one</comment> - <add>2.1</add> - <drop>4.3</drop> - </field> <field> <name>used</name> <type>varchar</type> @@ -69,14 +60,6 @@ <type>Radio</type> </html> </field> - <field> - <name>is_default</name> - <title>Default></title> - <type>boolean</type> - <comment>Is this a default rule (one rule for every contact type + level combination should be default)</comment> - <add>2.1</add> - <drop>4.3</drop> - </field> <field> <name>name</name> <title>Name</title> diff --git a/civicrm/xml/schema/Event/Event.xml b/civicrm/xml/schema/Event/Event.xml index d4c9c65299..979a84df4d 100644 --- a/civicrm/xml/schema/Event/Event.xml +++ b/civicrm/xml/schema/Event/Event.xml @@ -218,14 +218,6 @@ <type>CheckBox</type> </html> </field> - <field> - <name>contribution_type_id</name> - <type>int unsigned</type> - <default>0</default> - <comment>Contribution type assigned to paid event registrations for this event. Required if is_monetary is true.</comment> - <add>1.7</add> - <drop>4.3</drop> - </field> <field> <name>financial_type_id</name> <type>int unsigned</type> @@ -246,6 +238,7 @@ <name>payment_processor</name> <type>varchar</type> <length>128</length> + <serialize>SEPARATOR_TRIMMED</serialize> <comment>Payment Processors configured for this Event (if is_monetary is true)</comment> <pseudoconstant> <table>civicrm_payment_processor</table> @@ -317,19 +310,6 @@ <onDelete>SET NULL</onDelete> <add>2.0</add> </foreignKey> - <field> - <name>receipt_text</name> - <type>varchar</type> - <html> - <type>TextArea</type> - <rows>6</rows> - <cols>50</cols> - </html> - <length>255</length> - <comment>Receipt Text for off-line event participation</comment> - <add>2.0</add> - <drop>2.1</drop> - </field> <field> <name>default_role_id</name> <uniqueName>default_role_id</uniqueName> @@ -679,7 +659,7 @@ </field> <field> <name>selfcancelxfer_time</name> - <type>int unsigned</type> + <type>int</type> <default>0</default> <title>Self-service Cancellation or Transfer Time</title> <comment>Number of hours prior to event start date to allow self-service cancellation or transfer.</comment> diff --git a/civicrm/xml/schema/Financial/EntityFinancialTrxn.xml b/civicrm/xml/schema/Financial/EntityFinancialTrxn.xml index 653fc792d5..f48a3f3c65 100644 --- a/civicrm/xml/schema/Financial/EntityFinancialTrxn.xml +++ b/civicrm/xml/schema/Financial/EntityFinancialTrxn.xml @@ -63,15 +63,6 @@ <comment>allocated amount of transaction to this entity</comment> <add>3.2</add> </field> - <field> - <name>currency</name> - <type>varchar</type> - <length>3</length> - <default>NULL</default> - <comment>3 character string, value from config setting or input via user.</comment> - <add>3.2</add> - <drop>4.3</drop> - </field> <index> <name>UI_entity_financial_trxn_entity_table</name> <fieldName>entity_table</fieldName> diff --git a/civicrm/xml/schema/Financial/FinancialAccount.xml b/civicrm/xml/schema/Financial/FinancialAccount.xml index eedf5c6210..3188e5847e 100644 --- a/civicrm/xml/schema/Financial/FinancialAccount.xml +++ b/civicrm/xml/schema/Financial/FinancialAccount.xml @@ -42,13 +42,6 @@ <add>4.3</add> <onDelete>SET NULL</onDelete> </foreignKey> - <field> - <name>account_type_id</name> - <type>int unsigned</type> - <required>true</required> - <add>3.2</add> - <drop>4.3</drop> - </field> <field> <name>financial_account_type_id</name> <type>int unsigned</type> diff --git a/civicrm/xml/schema/Financial/FinancialTrxn.xml b/civicrm/xml/schema/Financial/FinancialTrxn.xml index 94a2e3f023..e34d9b9112 100644 --- a/civicrm/xml/schema/Financial/FinancialTrxn.xml +++ b/civicrm/xml/schema/Financial/FinancialTrxn.xml @@ -17,34 +17,6 @@ <name>id</name> <autoincrement>true</autoincrement> </primaryKey> - <field> - <name>from_account_id</name> - <type>int unsigned</type> - <comment>FK to financial_account table.</comment> - <add>3.2</add> - <drop>4.3</drop> - </field> - <foreignKey> - <name>from_account_id</name> - <table>civicrm_financial_account</table> - <key>id</key> - <add>3.2</add> - <drop>4.3</drop> - </foreignKey> - <field> - <name>to_account_id</name> - <type>int unsigned</type> - <comment>FK to financial_account table.</comment> - <add>3.2</add> - <drop>4.3</drop> - </field> - <foreignKey> - <name>to_account_id</name> - <table>civicrm_financial_account</table> - <key>id</key> - <add>3.2</add> - <drop>4.3</drop> - </foreignKey> <field> <name>from_financial_account_id</name> <type>int unsigned</type> @@ -99,15 +71,6 @@ <formatType>activityDateTime</formatType> </html> </field> - <field> - <name>trxn_type</name> - <title>Financial Transaction Type</title> - <type>enum</type> - <values>Debit,Credit</values> - <required>true</required> - <add>1.3</add> - <drop>4.3</drop> - </field> <field> <name>total_amount</name> <title>Financial Total Amount</title> @@ -161,15 +124,6 @@ <comment>Is this entry either a payment or a reversal of a payment?</comment> <add>4.7</add> </field> - <field> - <name>payment_processor</name> - <type>varchar</type> - <length>64</length> - <required>true</required> - <comment>derived from Processor setting in civicrm.settings.php.</comment> - <add>1.3</add> - <drop>4.3</drop> - </field> <field> <name>trxn_id</name> <title>Transaction ID</title> diff --git a/civicrm/xml/schema/Financial/FinancialType.xml b/civicrm/xml/schema/Financial/FinancialType.xml index bbdca886da..3d02bf8b09 100644 --- a/civicrm/xml/schema/Financial/FinancialType.xml +++ b/civicrm/xml/schema/Financial/FinancialType.xml @@ -37,16 +37,6 @@ </html> <add>1.3</add> </field> - <field> - <name>accounting_code</name> - <title>Accounting Code</title> - <type>varchar</type> - <length>64</length> - <export>true</export> - <comment>Optional value for mapping contributions to accounting system codes for each type/category of contribution.</comment> - <add>1.3</add> - <drop>4.3</drop> - </field> <field> <name>description</name> <type>varchar</type> diff --git a/civicrm/xml/schema/Financial/PaymentProcessor.xml b/civicrm/xml/schema/Financial/PaymentProcessor.xml index e1aac7cab3..a363ce8784 100644 --- a/civicrm/xml/schema/Financial/PaymentProcessor.xml +++ b/civicrm/xml/schema/Financial/PaymentProcessor.xml @@ -70,14 +70,6 @@ <comment>Payment Processor Description.</comment> <add>1.8</add> </field> - <field> - <name>payment_processor_type</name> - <type>varchar</type> - <length>255</length> - <comment>Payment Processor Type.</comment> - <add>1.8</add> - <drop>4.3</drop> - </field> <field> <name>payment_processor_type_id</name> <title>Payment Processor Type ID</title> diff --git a/civicrm/xml/schema/Grant/Grant.xml b/civicrm/xml/schema/Grant/Grant.xml index 6e222f8e0d..b6c2c89235 100644 --- a/civicrm/xml/schema/Grant/Grant.xml +++ b/civicrm/xml/schema/Grant/Grant.xml @@ -179,15 +179,6 @@ <type>Select</type> </html> </field> - <field> - <name>currency</name> - <type>varchar</type> - <length>8</length> - <default>NULL</default> - <comment>3 character string, value from config setting or input via user.</comment> - <add>3.2</add> - <drop>4.3</drop> - </field> <field> <name>rationale</name> <type>text</type> diff --git a/civicrm/xml/schema/Member/MembershipType.xml b/civicrm/xml/schema/Member/MembershipType.xml index 0007eefaf2..cd528adaf2 100644 --- a/civicrm/xml/schema/Member/MembershipType.xml +++ b/civicrm/xml/schema/Member/MembershipType.xml @@ -84,21 +84,6 @@ <add>1.5</add> <onDelete>RESTRICT</onDelete> </foreignKey> - <field> - <name>contribution_type_id</name> - <type>int unsigned</type> - <required>true</required> - <comment>If membership is paid by a contribution - what contribution type should be used. FK to Contribution Type ID</comment> - <add>1.5</add> - <drop>4.3</drop> - </field> - <foreignKey> - <name>contribution_type_id</name> - <table>civicrm_contribution_type</table> - <key>id</key> - <add>1.5</add> - <drop>4.3</drop> - </foreignKey> <field> <name>financial_type_id</name> <title>Membership Financial Type</title> @@ -192,13 +177,6 @@ <add>1.5</add> <serialize>SEPARATOR_TRIMMED</serialize> </field> - <foreignKey> - <name>relationship_type_id</name> - <table>civicrm_relationship_type</table> - <key>id</key> - <add>1.5</add> - <drop>3.3</drop> - </foreignKey> <index> <name>index_relationship_type_id</name> <fieldName>relationship_type_id</fieldName> diff --git a/civicrm/xml/schema/PCP/PCP.xml b/civicrm/xml/schema/PCP/PCP.xml index dfb8a13f8c..0ef29ee256 100644 --- a/civicrm/xml/schema/PCP/PCP.xml +++ b/civicrm/xml/schema/PCP/PCP.xml @@ -91,20 +91,6 @@ <type>Text</type> </html> </field> - <field> - <name>contribution_page_id</name> - <type>int unsigned</type> - <required>true</required> - <comment>The Contribution Page which triggered this pcp</comment> - <add>2.2</add> - <drop>4.1</drop> - </field> - <foreignKey> - <name>contribution_page_id</name> - <table>civicrm_contribution_page</table> - <key>id</key> - <drop>4.1</drop> - </foreignKey> <field> <name>page_id</name> <title>Contribution Page</title> @@ -182,15 +168,6 @@ <type>Select</type> </html> </field> - <field> - <name>referer</name> - <title>Referer</title> - <type>varchar</type> - <length>255</length> - <default>NULL</default> - <add>2.2</add> - <drop>4.1</drop> - </field> <field> <name>is_active</name> <title>Enabled?</title> diff --git a/civicrm/xml/schema/PCP/PCPBlock.xml b/civicrm/xml/schema/PCP/PCPBlock.xml index b07bc5b035..d25b6eb7c2 100644 --- a/civicrm/xml/schema/PCP/PCPBlock.xml +++ b/civicrm/xml/schema/PCP/PCPBlock.xml @@ -39,13 +39,6 @@ <typeColumn>entity_table</typeColumn> <add>2.2</add> </dynamicForeignKey> - <foreignKey> - <name>entity_id</name> - <table>civicrm_contribution_page</table> - <key>id</key> - <add>2.2</add> - <drop>4.1</drop> - </foreignKey> <field> <name>target_entity_type</name> <title>Target Entity</title> diff --git a/civicrm/xml/schema/Pledge/Pledge.xml b/civicrm/xml/schema/Pledge/Pledge.xml index 39943c58ef..32b806f39f 100644 --- a/civicrm/xml/schema/Pledge/Pledge.xml +++ b/civicrm/xml/schema/Pledge/Pledge.xml @@ -40,23 +40,6 @@ <add>2.1</add> <onDelete>CASCADE</onDelete> </foreignKey> - <field> - <name>contribution_type_id</name> - <uniqueName>pledge_contribution_type_id</uniqueName> - <export>false</export> - <type>int unsigned</type> - <comment>FK to Contribution Type. This is propagated to contribution record when pledge payments are made.</comment> - <add>2.1</add> - <drop>4.3</drop> - </field> - <foreignKey> - <name>contribution_type_id</name> - <table>civicrm_contribution_type</table> - <key>id</key> - <add>2.1</add> - <drop>4.3</drop> - <onDelete>SET NULL</onDelete> - </foreignKey> <field> <name>financial_type_id</name> <title>Type</title> diff --git a/civicrm/xml/schema/Price/LineItem.xml b/civicrm/xml/schema/Price/LineItem.xml index a1f006c9b5..82b9c9a7cc 100644 --- a/civicrm/xml/schema/Price/LineItem.xml +++ b/civicrm/xml/schema/Price/LineItem.xml @@ -67,15 +67,6 @@ <key>id</key> <onDelete>SET NULL</onDelete> </foreignKey> - <field> - <name>option_group_id</name> - <title>Line Item Option Group</title> - <type>int unsigned</type> - <required>true</required> - <comment>FK to option group</comment> - <add>1.7</add> - <drop>3.3</drop> - </field> <field> <name>label</name> <title>Line Item Label</title> diff --git a/civicrm/xml/schema/Price/PriceField.xml b/civicrm/xml/schema/Price/PriceField.xml index 0c2c1af3c1..ce61ecc87c 100644 --- a/civicrm/xml/schema/Price/PriceField.xml +++ b/civicrm/xml/schema/Price/PriceField.xml @@ -221,12 +221,4 @@ <type>Select</type> </html> </field> - <field> - <name>count</name> - <type>int unsigned</type> - <default>NULL</default> - <comment>Number of Participants Per field</comment> - <add>3.2</add> - <drop>3.3</drop> - </field> </table> diff --git a/civicrm/xml/schema/Price/PriceSet.xml b/civicrm/xml/schema/Price/PriceSet.xml index 25266f1dbf..d84ac53392 100644 --- a/civicrm/xml/schema/Price/PriceSet.xml +++ b/civicrm/xml/schema/Price/PriceSet.xml @@ -137,23 +137,6 @@ <type>Text</type> </html> </field> - <field> - <name>contribution_type_id</name> - <title>Price Set Contribution Type</title> - <type>int unsigned</type> - <default>NULL</default> - <comment>FK to Contribution Type(for membership price sets only).</comment> - <add>3.4</add> - <drop>4.3</drop> - </field> - <foreignKey> - <name>contribution_type_id</name> - <table>civicrm_contribution_type</table> - <key>id</key> - <add>3.4</add> - <drop>4.3</drop> - <onDelete>SET NULL</onDelete> - </foreignKey> <field> <name>financial_type_id</name> <title>Financial Type</title> diff --git a/civicrm/xml/templates/civicrm_data.tpl b/civicrm/xml/templates/civicrm_data.tpl index dc9967e4fe..d9891d3e1c 100644 --- a/civicrm/xml/templates/civicrm_data.tpl +++ b/civicrm/xml/templates/civicrm_data.tpl @@ -1781,3 +1781,4 @@ VALUES -- do not try this at home folks. INSERT IGNORE INTO civicrm_extension (type, full_name, name, label, file, is_active) VALUES ('module', 'sequentialcreditnotes', 'Sequential credit notes', 'Sequential credit notes', 'sequentialcreditnotes', 1); INSERT IGNORE INTO civicrm_extension (type, full_name, name, label, file, is_active) VALUES ('module', 'eventcart', 'Event cart', 'Event cart', 'eventcart', 1); +INSERT IGNORE INTO civicrm_extension (type, full_name, name, label, file, is_active) VALUES ('module', 'financialacls', 'Financial ACLs', 'Financial ACLs', 'financialacls', 1); diff --git a/civicrm/xml/templates/dao.tpl b/civicrm/xml/templates/dao.tpl index 3a0c51e328..87a3463531 100644 --- a/civicrm/xml/templates/dao.tpl +++ b/civicrm/xml/templates/dao.tpl @@ -7,12 +7,15 @@ * {$generated} * (GenCodeChecksum:{$genCodeChecksum}) */ - +{$useHelper} /** * Database access object for the {$table.entity} entity. */ class {$table.className} extends CRM_Core_DAO {ldelim} + const EXT = {$ext}; + const TABLE_ADDED = '{$table.add}'; + /** * Static instance to hold the table name. * @@ -60,7 +63,7 @@ class {$table.className} extends CRM_Core_DAO {ldelim} * Returns localized title of this entity. */ public static function getEntityTitle() {ldelim} - return ts('{$table.title}'); + return {$tsFunctionName}('{$table.title}'); {rdelim} diff --git a/civicrm/xml/templates/message_templates/contribution_invoice_receipt_html.tpl b/civicrm/xml/templates/message_templates/contribution_invoice_receipt_html.tpl index d6093e8510..2222bf0b5c 100644 --- a/civicrm/xml/templates/message_templates/contribution_invoice_receipt_html.tpl +++ b/civicrm/xml/templates/message_templates/contribution_invoice_receipt_html.tpl @@ -126,29 +126,28 @@ <td style="text-align:right;white-space: nowrap"><b><font size="1">{ts 1=$defaultCurrency}TOTAL %1{/ts}</font></b></td> <td style="text-align:right;"><font size="1">{$amount|crmMoney:$currency}</font></td> </tr> - {if $amountDue != 0} - <tr> - <td colspan="3"></td> - <td style="text-align:right;white-space: nowrap"><font size="1"> - {if $contribution_status_id == $refundedStatusId} - {ts}Amount Credited{/ts} - {else} - {ts}Amount Paid{/ts} - {/if} - </font> - </td> - <td style="text-align:right;"><font size="1">{$amountPaid|crmMoney:$currency}</font></td> - </tr> - <tr> - <td colspan="3"></td> - <td colspan="2"><hr></hr></td> - </tr> - <tr> - <td colspan="3"></td> - <td style="text-align:right;white-space: nowrap" ><b><font size="1">{ts}AMOUNT DUE:{/ts}</font></b></td> - <td style="text-align:right;"><b><font size="1">{$amountDue|crmMoney:$currency}</font></b></td> - </tr> - {/if} + <tr> + <td colspan="3"></td> + <td style="text-align:right;white-space: nowrap"><font size="1"> + {if $contribution_status_id == $refundedStatusId} + {ts}Amount Credited{/ts} + {else} + {ts}Amount Paid{/ts} + {/if} + </font> + </td> + <td style="text-align:right;"><font size="1">{$amountPaid|crmMoney:$currency}</font></td> + </tr> + <tr> + <td colspan="3"></td> + <td colspan="2"><hr></hr></td> + </tr> + <tr> + <td colspan="3"></td> + <td style="text-align:right;white-space: nowrap" ><b><font size="1">{ts}AMOUNT DUE:{/ts}</font></b></td> + <td style="text-align:right;"><b><font size="1">{$amountDue|crmMoney:$currency}</font></b></td> + </tr> + <br/><br/><br/> <tr> <td colspan="5"></td> diff --git a/civicrm/xml/templates/message_templates/event_online_receipt_html.tpl b/civicrm/xml/templates/message_templates/event_online_receipt_html.tpl index c9e0806256..b9fdec15b4 100644 --- a/civicrm/xml/templates/message_templates/event_online_receipt_html.tpl +++ b/civicrm/xml/templates/message_templates/event_online_receipt_html.tpl @@ -497,7 +497,7 @@ {if $event.allow_selfcancelxfer } <tr> <td colspan="2" {$valueStyle}> - {ts 1=$event.selfcancelxfer_time}You may transfer your registration to another participant or cancel your registration up to %1 hours before the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}<br /> + {ts 1=$selfcancelxfer_time 2=$selfservice_preposition}You may transfer your registration to another participant or cancel your registration up to %1 hours %2 the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}<br /> {capture assign=selfService}{crmURL p='civicrm/event/selfsvcupdate' q="reset=1&pid=`$participant.id`&{contact.checksum}" h=0 a=1 fe=1}{/capture} <a href="{$selfService}">{ts}Click here to transfer or cancel your registration.{/ts}</a> </td> diff --git a/civicrm/xml/templates/message_templates/event_online_receipt_text.tpl b/civicrm/xml/templates/message_templates/event_online_receipt_text.tpl index dbd5f1ba88..05be6dff34 100644 --- a/civicrm/xml/templates/message_templates/event_online_receipt_text.tpl +++ b/civicrm/xml/templates/message_templates/event_online_receipt_text.tpl @@ -291,7 +291,7 @@ You were registered by: {$payer.name} {/if} {if $event.allow_selfcancelxfer } -{ts 1=$event.selfcancelxfer_time}You may transfer your registration to another participant or cancel your registration up to %1 hours before the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if} +{ts 1=$selfcancelxfer_time 2=$selfservice_preposition}You may transfer your registration to another participant or cancel your registration up to %1 hours %2 the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if} {capture assign=selfService}{crmURL p='civicrm/event/selfsvcupdate' q="reset=1&pid=`$participant.id`&{contact.checksum}" h=0 a=1 fe=1}{/capture} {ts}Transfer or cancel your registration:{/ts} {$selfService} {/if} diff --git a/civicrm/xml/templates/message_templates/participant_confirm_html.tpl b/civicrm/xml/templates/message_templates/participant_confirm_html.tpl index ed1d2b7b0d..c4075a04bb 100644 --- a/civicrm/xml/templates/message_templates/participant_confirm_html.tpl +++ b/civicrm/xml/templates/message_templates/participant_confirm_html.tpl @@ -165,7 +165,7 @@ {if $event.allow_selfcancelxfer } <tr> <td colspan="2" {$valueStyle}> - {ts 1=$event.selfcancelxfer_time}You may transfer your registration to another participant or cancel your registration up to %1 hours before the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}<br /> + {ts 1=$selfcancelxfer_time 2=$selfservice_preposition}You may transfer your registration to another participant or cancel your registration up to %1 hours %2 the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if}<br /> {capture assign=selfService}{crmURL p='civicrm/event/selfsvcupdate' q="reset=1&pid=`$participant.id`&{contact.checksum}" h=0 a=1 fe=1}{/capture} <a href="{$selfService}">{ts}Click here to transfer or cancel your registration.{/ts}</a> </td> diff --git a/civicrm/xml/templates/message_templates/participant_confirm_text.tpl b/civicrm/xml/templates/message_templates/participant_confirm_text.tpl index 6296b4594d..2c459e66e2 100644 --- a/civicrm/xml/templates/message_templates/participant_confirm_text.tpl +++ b/civicrm/xml/templates/message_templates/participant_confirm_text.tpl @@ -13,7 +13,7 @@ Click this link to go to a web page where you can confirm your registration onli {$confirmUrl} {/if} {if $event.allow_selfcancelxfer } -{ts 1=$event.selfcancelxfer_time}You may transfer your registration to another participant or cancel your registration up to %1 hours before the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if} +{ts 1=$selfcancelxfer_time 2=$selfservice_preposition}You may transfer your registration to another participant or cancel your registration up to %1 hours %2 the event.{/ts} {if $totalAmount}{ts}Cancellations are not refundable.{/ts}{/if} {capture assign=selfService}{crmURL p='civicrm/event/selfsvcupdate' q="reset=1&pid=`$participant.id`&{contact.checksum}" h=0 a=1 fe=1}{/capture} {ts}Transfer or cancel your registration:{/ts} {$selfService} {/if} diff --git a/civicrm/xml/version.xml b/civicrm/xml/version.xml index b94dfffc42..e8dc6c3e45 100644 --- a/civicrm/xml/version.xml +++ b/civicrm/xml/version.xml @@ -1,4 +1,4 @@ <?xml version="1.0" encoding="iso-8859-1" ?> <version> - <version_no>5.29.1</version_no> + <version_no>5.30.0</version_no> </version> diff --git a/wp-rest/Controller/AuthorizeIPN.php b/wp-rest/Controller/AuthorizeIPN.php index 4cd9da9a97..8771120b79 100644 --- a/wp-rest/Controller/AuthorizeIPN.php +++ b/wp-rest/Controller/AuthorizeIPN.php @@ -31,6 +31,7 @@ class AuthorizeIPN extends Base { register_rest_route( $this->get_namespace(), $this->get_rest_base(), [ [ 'methods' => \WP_REST_Server::ALLMETHODS, + 'permission_callback' => '__return_true', 'callback' => [ $this, 'get_item' ] ] ] ); diff --git a/wp-rest/Controller/Cxn.php b/wp-rest/Controller/Cxn.php index 7f7cca5c56..7a21f23622 100644 --- a/wp-rest/Controller/Cxn.php +++ b/wp-rest/Controller/Cxn.php @@ -29,6 +29,7 @@ class Cxn extends Base { register_rest_route( $this->get_namespace(), $this->get_rest_base(), [ [ 'methods' => \WP_REST_Server::ALLMETHODS, + 'permission_callback' => '__return_true', 'callback' => [ $this, 'get_item' ] ] ] ); diff --git a/wp-rest/Controller/Open.php b/wp-rest/Controller/Open.php index 450ef991a3..9ead546d01 100644 --- a/wp-rest/Controller/Open.php +++ b/wp-rest/Controller/Open.php @@ -28,6 +28,7 @@ class Open extends Base { [ 'methods' => \WP_REST_Server::READABLE, 'callback' => [ $this, 'get_item' ], + 'permission_callback' => '__return_true', 'args' => $this->get_item_args() ], 'schema' => [ $this, 'get_item_schema' ] diff --git a/wp-rest/Controller/PayPalIPN.php b/wp-rest/Controller/PayPalIPN.php index 5b5c380045..a6a4f1fd32 100644 --- a/wp-rest/Controller/PayPalIPN.php +++ b/wp-rest/Controller/PayPalIPN.php @@ -29,6 +29,7 @@ class PayPalIPN extends Base { register_rest_route( $this->get_namespace(), $this->get_rest_base(), [ [ 'methods' => \WP_REST_Server::ALLMETHODS, + 'permission_callback' => '__return_true', 'callback' => [ $this, 'get_item' ] ] ] ); diff --git a/wp-rest/Controller/PxIPN.php b/wp-rest/Controller/PxIPN.php index d68fc8d787..c2aff12c04 100644 --- a/wp-rest/Controller/PxIPN.php +++ b/wp-rest/Controller/PxIPN.php @@ -29,6 +29,7 @@ class PxIPN extends Base { register_rest_route( $this->get_namespace(), $this->get_rest_base(), [ [ 'methods' => \WP_REST_Server::ALLMETHODS, + 'permission_callback' => '__return_true', 'callback' => [ $this, 'get_item' ] ] ] ); diff --git a/wp-rest/Controller/Soap.php b/wp-rest/Controller/Soap.php index 17402cc579..6a8704e24b 100644 --- a/wp-rest/Controller/Soap.php +++ b/wp-rest/Controller/Soap.php @@ -29,6 +29,7 @@ class Soap extends Base { register_rest_route( $this->get_namespace(), $this->get_rest_base(), [ [ 'methods' => \WP_REST_Server::ALLMETHODS, + 'permission_callback' => '__return_true', 'callback' => [ $this, 'get_item' ] ] ] ); diff --git a/wp-rest/Controller/Url.php b/wp-rest/Controller/Url.php index a53d3383bc..5d367adad9 100644 --- a/wp-rest/Controller/Url.php +++ b/wp-rest/Controller/Url.php @@ -28,6 +28,7 @@ class Url extends Base { [ 'methods' => \WP_REST_Server::READABLE, 'callback' => [ $this, 'get_item' ], + 'permission_callback' => '__return_true', 'args' => $this->get_item_args() ], 'schema' => [ $this, 'get_item_schema' ] diff --git a/wp-rest/Controller/Widget.php b/wp-rest/Controller/Widget.php index 13fa1e2add..5a31fc4d54 100644 --- a/wp-rest/Controller/Widget.php +++ b/wp-rest/Controller/Widget.php @@ -30,6 +30,7 @@ class Widget extends Base { [ 'methods' => \WP_REST_Server::READABLE, 'callback' => [ $this, 'get_item' ], + 'permission_callback' => '__return_true', 'args' => $this->get_item_args() ], 'schema' => [ $this, 'get_item_schema' ] -- GitLab