diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000000..ce303ed7de8d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +# CiviCRM editor configuration normalization +# @see http://editorconfig.org/ + +# This is the top-most .editorconfig file; do not search in parent directories. +root = true + +# All files. +[*] +end_of_line = LF +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 9a1e22a5d85f..0e1ed32e174f 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,70 +1,20 @@ -CiviCRM is a community-driven open-source project. It has a small, -full-time "core team" which facilitates development and works on critical -issues. However, many improvements are driven by the active contributors. - -This document provides important information about how to contribute. - -## Review/Release Process - -Releases are developed on a monthly cycle. At the start of the month, the -release-manager will send an invitation to developers who have open PRs, -encouraging them to participate in the release-cycle. Participation -provides a way to exchange feedback with other developers, get PRs merged, -and ensure the next release works -- all with a predictable timeline. - - * For a high-level summary of the release process, see the - [Release Management README](https://github.com/civicrm/release-management/blob/master/README.md). - * For an example invitation, see the previous [invitation for the April-May 2016](https://github.com/civicrm/release-management/issues/1). - -## Pull-Request Subject - -When filing a pull-request, use a descriptive subject. These are good examples: - - * `CRM-12345 - Fix Paypal IPNs when moon is at half-crescent (waxing)` - * `(WIP) CRM-67890 - Refactor SMS callback endpoint` - * `(NFC) CRM_Utils_PDF - Improve docblocks` - -A few elements to include: - - * **CRM-_XXXXX_** - This is a reference to the [CiviCRM issue tracker](http://issues.civicrm.org/) - (JIRA). A bot will setup crosslinks between JIRA and GitHub. - * **Description** - Provide a brief description of what the pull-request does. - * **(WIP)** - "Work in Progress" - If you are still developing a set of - changes, it may be useful to submit a pull-request and flag it as - `(WIP)`. This allows you to have discussion with other developers and - check test results. Once the change is ready, update the subject line - to remove `(WIP)`. - * **(NFC)** - "Non-Functional Change" - Most patches are designed to - change functionality (e.g. fix an error message or add a new button). - However, some changes are non-functional -- e.g. they cleanup the - code-style, improve the comments, or improve the test-suite. - -## Testing - -Pull-requests are tested automatically by a build-bot. Key things to know: - - * If you are a new contributor, the tests may be placed on hold pending a - cursory review. One of the administrators will post a comment like - `jenkins, ok to test` or `jenkins, add to whitelist`. - * The pull-request will have a colored dot indicating its status: - * **Yellow**: The automated tests are running. - * **Red**: The automated tests have failed. - * **Green**: The automated tests have passed. - * If the automated test fails, click on the red dot to investigate details. Check for information in: - * The initial summary. Ordinarily, this will list test failures and error messages. - * The console output. If the test-suite encountered a significant error (such as a PHP crash), - the key details will only appear in the console. - * Code-style tests are executed first. If the code-style in this patch is inconsistent, the remaining tests will be skipped. - * The primary tests may take 20-120 min to execute. This includes the following suites: `api_v3_AllTests`, `CRM_AllTests`, `Civi\AllTests`, `civicrm-upgrade-test`, and `karma` - * There are a handful of unit tests which are time-sensitive and which fail sporadically. See: https://forum.civicrm.org/index.php?topic=36964.0 - * The web test suite (`WebTest_AllTests`) takes several hours to execute. [It runs separately -- after the PR has been merged.](https://test.civicrm.org/job/CiviCRM-WebTest-Matrix/) - -For detailed discussion about automated tests, see http://wiki.civicrm.org/confluence/display/CRMDOC/Testing - -## Updating a pull-request - -During review, there may be some feedback about problems or additional -changes required for acceptance. If you've never updated a pull-request -before, see [Stackoverflow: How to update a pull request](http://stackoverflow.com/questions/9790448/how-to-update-a-pull-request). - -When you push the update to the pull-request, the test suite will re-execute. +CiviCRM is a community-driven open-source project. It has a small, full-time +[core team](https://civicrm.org/core-team) +which facilitates development and works on critical issues. +Additionally, a large community of active contributors and +[partner organizations](https://civicrm.org/partners-contributors) +drive much of the development work. + +For developers, CiviCRM maintains a comprehensive +[Developer Guide](https://docs.civicrm.org/dev/en/latest). +Topics of particular importance while submitting pull requests include: + +* [Contributing to CiviCRM core](https://docs.civicrm.org/dev/en/latest/core/contributing/) +* [Pull requests](https://docs.civicrm.org/dev/en/latest/tools/git/#pr) +* [Git workflow overview](https://docs.civicrm.org/dev/en/latest/tools/git/#contributing) +* [Writing automated tests](https://docs.civicrm.org/dev/en/latest/testing/setup/) +* [Jenkins continuous integration](https://docs.civicrm.org/dev/en/latest/tools/jenkins/) +* [Release Process](https://docs.civicrm.org/dev/en/latest/core/release-process/) +* [Developer Community](https://docs.civicrm.org/dev/en/latest/basics/community/) + +CiviCRM thanks you for your contributions and invites you to [log your time spent](https://civicrm.org/contributor-log) so that you (or your organization) may receive public recognition and promotion for your efforts. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000000..85e7f8f59d2d --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,19 @@ +Overview +---------------------------------------- +_A brief description of the pull request. Try to keep it non-technical._ + +Before +---------------------------------------- +_The current status. Please provide screenshots or gifs ([LICEcap](http://www.cockos.com/licecap/), [SilentCast](https://github.com/colinkeenan/silentcast)) where appropriate._ + +After +---------------------------------------- +_What has been changed. Please provide screenshots or gifs ([LICEcap](http://www.cockos.com/licecap/), [SilentCast](https://github.com/colinkeenan/silentcast)) where appropriate._ + +Technical Details +---------------------------------------- +_If the PR introduces noteworthy technical changes, please describe them here. Provide code snippets if necessary_ + +Comments +---------------------------------------- +_Anything else you would like the reviewer to note_ diff --git a/.gitignore b/.gitignore index 25992eaacb4f..95f9b09cd824 100644 --- a/.gitignore +++ b/.gitignore @@ -1,27 +1,16 @@ *~ *.bak +.use-civicrm-setup +/ext/ backdrop/ bower_components CRM/Case/xml/configuration CRM/Core/DAO/.listAll.php CRM/Core/DAO/listAll.php -CRM/Core/I18n/SchemaStructure.php bin/setup.conf -civicrm-version.php civicrm-version.txt civicrm.config.php -install/langs.php node_modules -packages/.channels -packages/.depdb -packages/.depdblock -packages/.filemap -packages/.lock -packages/.registry -packages/cache -packages/doc -packages/temp -packages/test settings_location.php sql/case_sample.mysql sql/civicrm.mysql @@ -32,7 +21,6 @@ sql/civicrm_data.mysql sql/civicrm_drop.mysql sql/civicrm_navigation.mysql sql/civicrm_sample.mysql -templates/CRM/common/version.tpl tests/phpunit/CiviTest/CiviSeleniumSettings.php tests/phpunit/CiviTest/civicrm.settings.php tools/stats/config.php @@ -54,3 +42,4 @@ civicrm.settings.php sql/dummy_processor.mysql distmaker/distmaker.conf distmaker/out +/tmp diff --git a/.toxic.json b/.toxic.json index 5d1f2a3d854b..d24776f28356 100644 --- a/.toxic.json +++ b/.toxic.json @@ -3,16 +3,79 @@ "toxicAlert": "\"Please:\n\n(Automated notice) This pull-request modifies {SYMBOLS}. That code has been previously identified as hazardous. For advice on dealing with it, please review [Toxic Code Protocol](http://wiki.civicrm.org/confluence/display/CRM/Toxic+Code+Protocol)." }, "checks": { - "CRM_Contact_Import_Parser_Contact::import()": "toxicAlert", - "CRM_Core_BAO_Mapping::buildMappingForm()": "toxicAlert", - "CRM_Event_Form_Participant::postProcess()": "toxicAlert", - "CRM_Export_BAO_Export::exportComponents()": "toxicAlert", - "CRM_Member_Form_Membership::postProcess()": "toxicAlert", - "CRM_Contribute_Form_Contribution::postProcess()": "toxicAlert", + "CRM_Activity_BAO_Activity::create()": "toxicAlert", + "CRM_Activity_Form_Activity::preProcess()": "toxicAlert", + "CRM_Case_BAO_Case::getCaseActivity()": "toxicAlert", + "CRM_Case_BAO_Case::mergeCases()": "toxicAlert", + "CRM_Contact_BAO_Contact::formatProfileContactParams()": "toxicAlert", + "CRM_Contact_BAO_ContactTest::testCreateProfileContact()": "toxicAlert", + "CRM_Contact_BAO_Individual::format()": "toxicAlert", + "CRM_Contact_BAO_Query::addHierarchicalElements()": "toxicAlert", + "CRM_Contact_BAO_Relationship::relatedMemberships()": "toxicAlert", + "CRM_Contact_Form_Contact::postProcess()": "toxicAlert", "CRM_Contact_Import_Form_MapField::buildQuickForm()": "toxicAlert", + "CRM_Contact_Import_Parser::formatCommonData()": "toxicAlert", + "CRM_Contact_Import_Parser::formatContactParameters()": "toxicAlert", + "CRM_Contact_Import_Parser::run()": "toxicAlert", + "CRM_Contact_Import_Parser_Contact::import()": "toxicAlert", + "CRM_Contribute_BAO_Contribution::recordFinancialAccounts()": "toxicAlert", + "CRM_Contribute_BAO_Contribution::transitionComponents()": "toxicAlert", + "CRM_Contribute_BAO_ContributionPage::sendMail()": "toxicAlert", + "CRM_Contribute_BAO_Query::whereClauseSingle()": "toxicAlert", + "CRM_Contribute_Form_Contribution::buildQuickForm()": "toxicAlert", + "CRM_Contribute_Form_Contribution::submit()": "toxicAlert", + "CRM_Contribute_Form_ContributionBase::preProcess()": "toxicAlert", + "CRM_Contribute_Form_ContributionPage_Amount::postProcess()": "toxicAlert", + "CRM_Contribute_Form_Contribution_Confirm::postProcessMembership()": "toxicAlert", + "CRM_Contribute_Form_Contribution_Confirm::processFormSubmission()": "toxicAlert", + "CRM_Contribute_Form_Contribution_Main::buildQuickForm()": "toxicAlert", "CRM_Contribute_Form_Contribution_Main::formRule()": "toxicAlert", + "CRM_Contribute_Form_Contribution_Main::submit()": "toxicAlert", + "CRM_Contribute_Form_Task_Invoice::printPDF()": "toxicAlert", + "CRM_Contribute_Import_Parser::run()": "toxicAlert", + "CRM_Core_BAO_ActionScheduleTest::setUp()": "toxicAlert", + "CRM_Core_BAO_CustomField::formatCustomField()": "toxicAlert", + "CRM_Core_BAO_CustomGroup::getTree()": "toxicAlert", + "CRM_Core_BAO_Mapping::buildMappingForm()": "toxicAlert", + "CRM_Core_BAO_UFGroup::buildProfile()": "toxicAlert", + "CRM_Core_BAO_UFGroup::getValues()": "toxicAlert", + "CRM_Core_I18n_SchemaStructure::widgets()": "toxicAlert", + "CRM_Core_Permission::getEntityActionPermissions()": "toxicAlert", + "CRM_Core_PseudoConstantTest::testOptionValues()": "toxicAlert", + "CRM_Custom_Form_Field::formRule()": "toxicAlert", + "CRM_Dedupe_Merger::getRowsElementsAndInfo()": "toxicAlert", + "CRM_Dedupe_Merger::moveAllBelongings()": "toxicAlert", + "CRM_Event_Form_ManageEvent_Fee::formRule()": "toxicAlert", + "CRM_Event_Form_Participant::submit()": "toxicAlert", "CRM_Event_Form_Registration_Confirm::postProcess()": "toxicAlert", - "CRM_Event_BAO_Event::displayProfile()": "toxicAlert", - "CRM_Contribute_Form_Contribution_Confirm::postProcess()": "toxicAlert" + "CRM_Event_Form_Registration_Register::buildQuickForm()": "toxicAlert", + "CRM_Event_Form_Registration_Register::postProcess()": "toxicAlert", + "CRM_Export_BAO_Export::exportComponents()": "toxicAlert", + "CRM_Mailing_BAO_Mailing::compose()": "toxicAlert", + "CRM_Mailing_BAO_Mailing::getRecipients()": "toxicAlert", + "CRM_Mailing_BAO_Mailing::report()": "toxicAlert", + "CRM_Member_Form_Membership::formRule()": "toxicAlert", + "CRM_Member_Form_Membership::submit()": "toxicAlert", + "CRM_PCP_Page_PCPInfo::run()": "toxicAlert", + "CRM_Price_BAO_PriceField::addQuickFormElement()": "toxicAlert", + "CRM_Price_Form_Field::formRule()": "toxicAlert", + "CRM_Profile_Form::buildQuickForm()": "toxicAlert", + "CRM_Profile_Form::postProcess()": "toxicAlert", + "CRM_Profile_Form::preProcess()": "toxicAlert", + "CRM_Profile_Page_Dynamic::run()": "toxicAlert", + "CRM_Report_Form_Contact_Detail::__construct()": "toxicAlert", + "CRM_Report_Form_Contribute_Bookkeeping::__construct()": "toxicAlert", + "CRM_Report_Form_Contribute_Detail::__construct()": "toxicAlert", + "CRM_Report_Form_Event_ParticipantListing::__construct()": "toxicAlert", + "CRM_Report_Form_Member_ContributionDetail::__construct()": "toxicAlert", + "CRM_UF_Form_Field::buildQuickForm()": "toxicAlert", + "CRM_Upgrade_Incremental_php_FourThree::createFinancialRecords()": "toxicAlert", + "CRM_Utils_Date::relativeToAbsolute()": "toxicAlert", + "CRM_Utils_Mail_EmailProcessor::_process()": "toxicAlert", + "ImportCiviSeleniumTestCase::importContacts()": "toxicAlert", + "WebTest_Contribute_OnBehalfOfOrganization::_testUserWithMoreThanOneRelationship()": "toxicAlert", + "WebTest_Profile_BatchUpdateTest::testBatchUpdate()": "toxicAlert", + "api_v3_JobTest::getMergeLocations()": "toxicAlert", + "api_v3_JobTest::getMergeSets()": "toxicAlert" } } \ No newline at end of file diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 47253a0e1333..d4cbb1448672 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -1,112 +1,248 @@ The following people and organizations sponsored and/or contributed new and improved features to the project. +************************************************ +Code Contributors for 5.x +************************************************ + +CiviCRM - Coleman Watts, Tim Otten + +AGH Strategies - Alice Frumin, Andrew Hunt, Eli Lisseck +Agileware - Alok Patel, Francis Whittle, Justin Freeman +Andrew Thompson +applicado +Australian Greens - Seamus Lee +Bastien Ho +Blackfly Solutions - Alan Dixon +Calibrate - Wannes De Roy +Caltha - Tomasz Pietrzkowski +CEDC - Laryn Kragt Bakker +Chris Burgess +CiviCoop - Jaap Jansma +CiviDesk - Sunil Pawar, Yashodha Chaku +CompuCorp - Camilo Rodriguez, Davi Alexandre, Debarshi Bhaumik, Michael Devery, + Mukesh Ram, Omar Abu Hussein, René Olivo, Vinu Varshith Sekar +Coop SymbioTIC - Mathieu Lutfy, Samuel Vanhove +Davis Media Access - Darrick Servis +Electronic Frontier Foundation - Mark Burdett +eQuality Technology - Greg Rundlett +Freeform Solutions - Herb van den Dool +Fuzion - Jitendra Purohit +Ginkgo Street Labs - Frank Gómez +Hossein Amin +JMA Consulting - Joe Murray, Monish Deb +Johan Vervloet +John Kingsnorth +Joinery - Allen Shaw +Kanzu Code - Carl Andrew Lema +Kompetenzzentrum Technik-Diversity-Chancengleichheit - Niels Heinemann +Left Join Labs - Sean Madsen +Lighthouse Design and Consulting - Brian Shaughnessy +Łukasz Krutul +Megaphone Technology Consulting - Jon Goldberg +MJW Consulting - Matthew Wire +myDropWizard - David Snopek +Naomi Rosenberg +Olivier Tétard +OSSeed Technologies - Madhavi Malgaonkar +Oxfam Germany - Thomas Schüttler, Yuliyana Liyana +Pradeep Nayak +Progressive Technology Project - Jamie McClelland +Richard van Oosterhout +Romain Thouvenin +Squiffle Consulting - Aidan Saunders +Systopia - Björn Endres +Tadpole Collective - Kevin Cristiano +Third Sector Design - Michael McAndrew +Tom Bloor +Wikimedia Foundation - Eileen McNaughton +Wildsight - Lars Sanders-Green +Will Long + ************************************************ Key Contributors and Sponsors for 4.7 ************************************************ -CiviCRM Team - Atif Shaikh, Coleman Watts, David Greenberg, Eileen McNaughton, - Jitendra Purohit, Josh Gowan, Kurund Jalmi, - Michael McAndrew, Monish Deb, Rohan Ramesh Katkar, Tim Otten, - Yashodha Chaku +CiviCRM - Coleman Watts, Josh Gowans, Tim Otten -AGH Strategies - Andrew Hunt, Tyrell Cook, Nikki Murray -Agileware - Francis Whittle +Aaron Jones +Abhikalak Consultants - Amal Sharma +AGH Strategies - Alice Frumin, Andrew Hunt, Eli Lisseck, Nikki Murray, Tommy + Bobo, Tyrell Cook +Agileware - Alok Patel, Francis Whittle, Iris Abarquez, Justin Freeman, Vaibhav + Sagar +András Molnár Andrew West +AppChecker Aputsiaĸ Niels Janussen -Aron Novak -Backoffice Thinking +Arkadiusz Rzadkowolski +Arun Singh +ATD Fourth World - Véronique Gratioulet +Atif Shaikh +Australian Greens - Seamus Lee +Awebon Technologies - Karthikeyan Balasubramanian +BackOffice Thinking - Hassan Farooq Barbara Miller +Black Brick Software - David Hayes +Blackfly Solutions - Alan Dixon +British Humanist Association - Andrew West Borislav Zlatanov Brian Dombrowski +Brooks Digital - Spencer Brooks Caroline Badley -Christian Wach +CEDC - Laryn Kragt Bakker +Chanun Chirattikanon Charlie DeTar +Chirojeugd Vlaanderen - Johan Vervloet +Chris Burgess +Christian Wach Circle Interactive - Dave Jenkins -CiviCoop - Jaap Jansma -CiviDesk - Nicolas Ganivet, Sunil Pawar, Virginie Ganivet -Compucorp - Guanhuan Chen, Jamie Novick -CiviCoop - Jaap Jansma +CiviCoop - Erik Hommel, Jaap Jansma +CiviDesk - Nicolas Ganivet, Sunil Pawar, Virginie Ganivet, Yashodha Chaku +CiviFirst - John Kirk +Community Builders - Dejan Lukic +Community IT Academy - William Mortada +CompuCorp - Camilo Rodriguez, Guanhuan Chen, Kacper Warda, Jamie Novick, Michael + Devery, Mukesh Ram, Omar Abu Hussein Coop SymbioTIC - Mathieu Lutfy, Samuel Vanhove Dave D +Dave Greenberg David Hayes +Deepak Srivastava +Denver DataMan - Steve Kessler Dhanesh Dhuri +Daniël van Vuuren Dmitry Smirnov +Donald Lobo +E-Dynamics - Franky Van Liedekerke +Eaiman Shoshi +Effy Elden +Electronic Frontier Foundation - Mark Burdett Elin Waring -Emphanos LLC - Allen Shaw +Emphanos LLC +Ergon Logic Enterprises - Christopher Gervais +Erich Schulz Esantanche +Francesc Bassas i Bullich Freeform Solutions - Lola Slade, Stephanie Gray, Herb van den Dool Future First - David Knoll, John Prescott -Fuzion NZ - Chris Burgess, Eileen McNaughton, Peter Davis, Torrance Hodgson +Fuzion - Peter Davis, Torrance Hodgson, Jitendra Purohit Giant Rabbit - Peter Haight -Ginkgo Street Labs - Frank Gomez, Galata Tona, Michael Daryabeygi, Roshani Kothari, Toby Lounsbury +Ginkgo Street Labs - Frank Gómez, Galata Tona, Michael Daryabeygi, Roshani + Kothari +Gizra - Aron Novak +Greenleaf Advancement - Guy Iaccarino, Karen Stevenson +Hartmann Computer Consulting - Peter Hartmann +iXiam - Luciano Spiegel Jake Martin White +JazzMan +jernic +Jérôme Lebleu +JMA Consulting - Edsel Lopez, Joe Murray, Monish Deb Joanne Chester Joe McLaughlin -Johan Vervloet -John P Kirk +John Kingsnorth +Joinery - Allen Shaw +Joost Fock Joris -JMA Consulting - Joe Murray, Pradeep Nayak, Edsel Lopez -gah242s -Greenleaf Advancement - Guy Iaccarino -K Sneed Consulting - Kate Sneed +Kanzu Code - Carl Andrew Lema Kemal Bay Ken West Kevin Levie +Kevin Reynen +Klaas Eikelboom +Klangsoft - David Reedy Jr +Kompetenzzentrum Technik-Diversity-Chancengleichheit - Niels Heinemann Korlon - Stuart Gaston -kreynen -Laryn -Lesley Evensen (zorgalina) +K Sneed Consulting - Kate Sneed +Left Join Labs - Sean Madsen +Lemniscus - Noah Miller +Lesley Evensen Lighthouse Consulting and Design - Brian Shaughnessy +Marc Brazeau Marty Wright -Matthew Wire Mattias Michaux +Megaphone Technology Consulting - Jon Goldberg +Michael Hurwicz +Mihael Mladenov +Milton Zurita +MJW Consulting - Matthew Wire Mohit Aggarwal +MongoDB - A. Jesse Jiryu Davis +mountev +myDropWizard - David Snopek +Nathan Brettell National Urban League - Lisa Taliano +Neuwald Tecnologia da Informação - Arthur Almeida Nicholai Burton Niels Heinemann New York City Council New York State Senate - Ken Zalewski +Noah's Light Foundation - Carlos Loeza Northbridge Digital - Oliver Gibson Olaf Buddenhagen -Palante Technology Cooperative - Jon Goldberg, Joseph Lacey +Olivier Hertrich +Oxfam Germany - Thomas Schüttler +Palante Technology Cooperative - Joseph Lacey Paul Campbell -Progressive Tech Project - Alice Aguilar, Jamie McClelland +Pawel Nowak +PowDevel - Beto Aveiga +Pradeep Nayak +Progressive Technology Project - Alice Aguilar, Jamie McClelland +Randy Tobias +Redfin Solutions - Chris Wells Richard Van Oosterhout -RocXa +Rohan Ramesh Katkar +Romain Thouvenin +Rupal Javiya +Samson Alajede Saurabh Batra -Seamus Lee -Seb35 +Sébastien Beyou Semper IT - Karin Gerritsen +Sharique Ahmed Farooqui Shawn Holt -Skvare - Jeremy Proffitt, Peter Petrik +Skvare - Jeremy Proffitt, Mark Hanna, Peter Petrik Smiling Heart Enterprises - Neil Planchon +Spry Digital - Ellen Hendricks Squiffle Consulting - Aidan Saunders +Stan Dragnev Stephen Palmstrom Symbiotic - Mathieu Lutfy, Samuel Vanhove -Systopia - Björn Endres, Niko Bochan -Tadpole - Dana Skallman, Kevin Cristiano +Systopia - Björn Endres, Niko Bochan, Philipp Batroff +Tadpole Collective - Dana Skallman, Kevin Cristiano +Team Expansion - Greg Harris Tech to the People - Xavier Dutoit +Third Sector Design - Michael McAndrew Thomas Leichtuss +Thomas Schüttler Tim Mallezie +Timbsoft Technologies - Tunbola Ogunwande +Tobias Lounsbury Torenware Networks - Rob Thorne -University of Cambridge – Alex Corr, John Kingsnorth -Veda Consulting - Parvez Saleh, Deepak Srivastava, Kajan +University of Cambridge – Alex Corr +Vasantha Kaje +Veda Consulting - Parvez Saleh, Kajan +Vedant Rathore +Vikas Kumar +Vinu Varshith Sekar Wanna Pixel - Nathan Porter, Marisa Porter -Web Access - Sudha Bisht -Wikimedia Foundation - Adam Wight +We Move Europe/Caltha - Tomasz Pietrzkowski +Web Access - Kurund Jalmi, Sudha Bisht +Wikimedia Foundation - Adam Wight, Eileen McNaughton, Elliott Eggleston, Maggie + Epps +Will Long yurg -zarandras ************************************************ Key Contributors and Sponsors for 4.6 ************************************************ CiviCRM Team - Atif Shaikh, Coleman Watts, David Greenberg, Donald Lobo, - Eileen McNaughton, Jitendra Purohit, Josh Gowan, Kurund Jalmi, + Eileen McNaughton, Jitendra Purohit, Josh Gowan, Kurund Jalmi, Michael McAndrew, Monish Deb, Rohan Ramesh Katkar, Tim Otten, Yashodha Chaku -ADG Communications - Steve Binkowski +ADG Communications - Steve Binkowski AGH Strategies - Andrew Hunt, Jane Hanley, Tommy Bobo, Tyrell Cook +Agileware - Justin Freeman, Francis Whittle, Iris Abarquez, Vaibhav Sagar Alex C Allan Chappell Amnesty International Spain - Carlos Capote @@ -116,6 +252,7 @@ Arete Imagine - Marisa Porter, Nate Porter Asylum Hill Congregational Church Blackfly Solutions - Alan Dixon Botanical Society of America - Toby Lounsbury +Chirojeugd Vlaanderen - Johan Vervloet Christian Wach Chris Ward Circle Interactive - Dave Moreton, Andrew Walker, Dave Jenkins, Maya Gibbs @@ -139,7 +276,6 @@ Gnu.org - David Thompson Jaka Kranjc JMA Consulting - Joe Murray, Pradeep Nayak, Edsel Lopez Joanne Chester -Johan Vervloet John Kingsnorth jsnyder83 Kathryn Benedicto diff --git a/CRM/ACL/API.php b/CRM/ACL/API.php index 3d0c090dc9dd..80855ac01b65 100644 --- a/CRM/ACL/API.php +++ b/CRM/ACL/API.php @@ -1,9 +1,9 @@ get('userID'); + $contactID = CRM_Core_Session::getLoggedInContactID(); } if (!$contactID) { @@ -85,6 +84,10 @@ public static function check($str, $contactID = NULL) { * @param bool $skipDeleteClause * Don't add delete clause if this is true,. * this means it is handled by generating query + * @param bool $skipOwnContactClause + * Do not add 'OR contact_id = $userID' to the where clause. + * This is a hideously inefficient query and should be avoided + * wherever possible. * * @return string * the group where clause for this user @@ -95,7 +98,8 @@ public static function whereClause( &$whereTables, $contactID = NULL, $onlyDeleted = FALSE, - $skipDeleteClause = FALSE + $skipDeleteClause = FALSE, + $skipOwnContactClause = FALSE ) { // the default value which is valid for the final AND $deleteClause = ' ( 1 ) '; @@ -109,32 +113,28 @@ public static function whereClause( } } - // first see if the contact has edit / view all contacts - if (CRM_Core_Permission::check('edit all contacts') || - ($type == self::VIEW && CRM_Core_Permission::check('view all contacts')) - ) { - return $deleteClause; - } - if (!$contactID) { $contactID = CRM_Core_Session::getLoggedInContactID(); } $contactID = (int) $contactID; - $where = implode(' AND ', - array( - CRM_ACL_BAO_ACL::whereClause($type, - $tables, - $whereTables, - $contactID - ), - $deleteClause, - ) + // first see if the contact has edit / view all permission + if (CRM_Core_Permission::check('edit all contacts', $contactID) || + ($type == self::VIEW && CRM_Core_Permission::check('view all contacts', $contactID)) + ) { + return $deleteClause; + } + + $whereClause = CRM_ACL_BAO_ACL::whereClause($type, + $tables, + $whereTables, + $contactID ); + $where = implode(' AND ', [$whereClause, $deleteClause]); - // Add permission on self - if ($contactID && (CRM_Core_Permission::check('edit my contact') || - $type == self::VIEW && CRM_Core_Permission::check('view my contact')) + // Add permission on self if we really hate our server or have hardly any contacts. + if (!$skipOwnContactClause && $contactID && (CRM_Core_Permission::check('edit my contact') || + $type == self::VIEW && CRM_Core_Permission::check('view my contact')) ) { $where = "(contact_a.id = $contactID OR ($where))"; } @@ -164,8 +164,7 @@ public static function group( $includedGroups = NULL ) { if ($contactID == NULL) { - $session = CRM_Core_Session::singleton(); - $contactID = $session->get('userID'); + $contactID = CRM_Core_Session::getLoggedInContactID(); } if (!$contactID) { @@ -187,10 +186,8 @@ public static function group( * @param string $tableName * @param null $allGroups * @param null $includedGroups - * @param bool $flush * - * @return array - * the ids of the groups for which the user has permissions + * @return bool */ public static function groupPermission( $type, @@ -198,39 +195,23 @@ public static function groupPermission( $contactID = NULL, $tableName = 'civicrm_saved_search', $allGroups = NULL, - $includedGroups = NULL, - $flush = FALSE + $includedGroups = NULL ) { - static $cache = array(); - $groups = array(); - //@todo this is pretty hacky!!! - //adding a way for unit tests to flush the cache - if ($flush) { - $cache = array(); - return NULL; + if (!isset(Civi::$statics[__CLASS__]) || !isset(Civi::$statics[__CLASS__]['group_permission'])) { + Civi::$statics[__CLASS__]['group_permission'] = []; } + if (!$contactID) { - $session = CRM_Core_Session::singleton(); - $contactID = NULL; - if ($session->get('userID')) { - $contactID = $session->get('userID'); - } + $contactID = CRM_Core_Session::singleton()->getLoggedInContactID(); } $key = "{$tableName}_{$type}_{$contactID}"; - if (array_key_exists($key, $cache)) { - $groups = &$cache[$key]; - } - else { - $groups = self::group($type, $contactID, $tableName, $allGroups, $includedGroups); - $cache[$key] = $groups; - } - if (empty($groups)) { - return FALSE; + if (!array_key_exists($key, Civi::$statics[__CLASS__]['group_permission'])) { + Civi::$statics[__CLASS__]['group_permission'][$key] = self::group($type, $contactID, $tableName, $allGroups, $includedGroups); } - return in_array($groupID, $groups) ? TRUE : FALSE; + return in_array($groupID, Civi::$statics[__CLASS__]['group_permission'][$key]); } } diff --git a/CRM/ACL/BAO/ACL.php b/CRM/ACL/BAO/ACL.php index 205cb0b9ca1b..0051422252ec 100644 --- a/CRM/ACL/BAO/ACL.php +++ b/CRM/ACL/BAO/ACL.php @@ -1,9 +1,9 @@ ts('Contact'), 'civicrm_acl_role' => ts('ACL Role'), - ); + ]; } return self::$_entityTable; } @@ -64,12 +64,12 @@ public static function entityTable() { */ public static function objectTable() { if (!self::$_objectTable) { - self::$_objectTable = array( + self::$_objectTable = [ 'civicrm_contact' => ts('Contact'), 'civicrm_group' => ts('Group'), 'civicrm_saved_search' => ts('Contact Group'), 'civicrm_admin' => ts('Import'), - ); + ]; } return self::$_objectTable; } @@ -79,14 +79,14 @@ public static function objectTable() { */ public static function operation() { if (!self::$_operation) { - self::$_operation = array( + self::$_operation = [ 'View' => ts('View'), 'Edit' => ts('Edit'), 'Create' => ts('Create'), 'Delete' => ts('Delete'), 'Search' => ts('Search'), 'All' => ts('All'), - ); + ]; } return self::$_operation; } @@ -94,6 +94,8 @@ public static function operation() { /** * Construct a WHERE clause to handle permissions to $object_* * + * @deprecated + * * @param array $tables * Any tables that may be needed in the FROM. * @param string $operation @@ -115,19 +117,19 @@ public static function permissionClause( $object_table = NULL, $object_id = NULL, $acl_id = NULL, $acl_role = FALSE ) { + CRM_Core_Error::deprecatedFunctionWarning('unknown - this is really old & not used in core'); $dao = new CRM_ACL_DAO_ACL(); - $t = array( + $t = [ 'ACL' => self::getTableName(), 'ACLRole' => 'civicrm_acl_role', 'ACLEntityRole' => CRM_ACL_DAO_EntityRole::getTableName(), 'Contact' => CRM_Contact_DAO_Contact::getTableName(), 'Group' => CRM_Contact_DAO_Group::getTableName(), 'GroupContact' => CRM_Contact_DAO_GroupContact::getTableName(), - ); + ]; - $session = CRM_Core_Session::singleton(); - $contact_id = $session->get('userID'); + $contact_id = CRM_Core_Session::getLoggedInContactID(); $where = " {$t['ACL']}.operation = '" . CRM_Utils_Type::escape($operation, 'String') . "'"; @@ -156,7 +158,7 @@ public static function permissionClause( } } - $query = array(); + $query = []; /* Query for permissions granted to all contacts in the domain */ @@ -259,9 +261,9 @@ public static function permissionClause( $dao->query($union); - $allow = array(0); - $deny = array(0); - $override = array(); + $allow = [0]; + $deny = [0]; + $override = []; while ($dao->fetch()) { /* Instant bypass for the following cases: @@ -336,7 +338,7 @@ public static function permissionClause( public static function getClause($table, $id, &$tables) { $table = CRM_Utils_Type::escape($table, 'String'); $id = CRM_Utils_Type::escape($id, 'Integer'); - $whereTables = array(); + $whereTables = []; $ssTable = CRM_Contact_BAO_SavedSearch::getTableName(); @@ -365,7 +367,7 @@ public static function getClause($table, $id, &$tables) { * Assoc. array of the ACL rule's properties */ public function toArray($format = '%s', $hideEmpty = FALSE) { - $result = array(); + $result = []; if (!self::$_fieldKeys) { $fields = CRM_ACL_DAO_ACL::fields(); @@ -395,7 +397,7 @@ public function toArray($format = '%s', $hideEmpty = FALSE) { * Array of assoc. arrays of ACL rules */ public static function &getACLs($contact_id = NULL, $group_id = NULL, $aclRoles = FALSE) { - $results = array(); + $results = []; if (empty($contact_id)) { return $results; @@ -503,7 +505,7 @@ public static function &getACLRoles($contact_id = NULL, $group_id = NULL) { } } - $results = array(); + $results = []; $rule->query($query); @@ -533,7 +535,7 @@ public static function &getGroupACLs($contact_id, $aclRoles = FALSE) { $acl = self::getTableName(); $c2g = CRM_Contact_BAO_GroupContact::getTableName(); $group = CRM_Contact_BAO_Group::getTableName(); - $results = array(); + $results = []; if ($contact_id) { $query = " @@ -601,7 +603,7 @@ public static function &getGroupACLRoles($contact_id) { AND $c2g.contact_id = $contact_id AND $c2g.status = 'Added'"; - $results = array(); + $results = []; $rule->query($query); @@ -642,7 +644,7 @@ public static function &getGroupACLRoles($contact_id) { * Assoc array of ACL rules */ public static function &getAllByContact($contact_id) { - $result = array(); + $result = []; /* First, the contact-specific ACLs, including ACL Roles */ $result += self::getACLs($contact_id, NULL, TRUE); @@ -681,8 +683,8 @@ public static function retrieve(&$params, &$defaults) { * @param bool $is_active * Value we want to set the is_active field. * - * @return Object - * DAO object on success, null otherwise + * @return bool + * true if we found and updated the object, else false */ public static function setIsActive($id, $is_active) { // note this also resets any ACL cache @@ -716,65 +718,25 @@ public static function check($str, $contactID) { AND a.object_table = %1 AND a.id IN ( $aclKeys ) "; - $params = array(1 => array($str, 'String')); + $params = [1 => [$str, 'String']]; $count = CRM_Core_DAO::singleValueQuery($query, $params); return ($count) ? TRUE : FALSE; } - /** - * Build a join and where part for a query - * - * @param int $contactId - * @return array - the first key is join part of the query and the second key is the where part of the query - */ - public static function buildAcl($contactId) { - // If there is no $contactId passed return empty ACL join and where clause - if (empty($contactId)) { - return array('', ''); - } - - $tables = array(); - $whereTables = array(); - $whereClause = CRM_ACL_BAO_ACL::whereClause(CRM_Core_Permission::VIEW, $tables, $whereTables, $contactId, TRUE); - if (strlen($whereClause)) { - $whereClause = " AND (" . $whereClause . ")"; - } - - $join = ""; - foreach ($whereTables as $name => $value) { - if (!$value) { - continue; - } - if ($value != 1) { - // if there is already a join statement in value, use value itself - if (strpos($value, 'JOIN')) { - $join .= " $value "; - } - continue; - } - } - - return array( - $join, - $whereClause, - ); - } - /** * @param $type * @param $tables * @param $whereTables * @param int $contactID - * @param bool $strictReturn If there is no where clause build for ACL * * @return null|string */ - public static function whereClause($type, &$tables, &$whereTables, $contactID = NULL, $strictReturn = FALSE) { + public static function whereClause($type, &$tables, &$whereTables, $contactID = NULL) { $acls = CRM_ACL_BAO_Cache::build($contactID); $whereClause = NULL; - $clauses = array(); + $clauses = []; if (!empty($acls)) { $aclKeys = array_keys($acls); @@ -793,12 +755,12 @@ public static function whereClause($type, &$tables, &$whereTables, $contactID = $dao = CRM_Core_DAO::executeQuery($query); // do an or of all the where clauses u see - $ids = array(); + $ids = []; while ($dao->fetch()) { // make sure operation matches the type TODO if (self::matchType($type, $dao->operation)) { if (!$dao->object_id) { - $ids = array(); + $ids = []; $whereClause = ' ( 1 ) '; break; } @@ -815,60 +777,27 @@ public static function whereClause($type, &$tables, &$whereTables, $contactID = AND g.is_active = 1 "; $dao = CRM_Core_DAO::executeQuery($query); - $staticGroupIDs = array(); - $cachedGroupIDs = array(); + $groupIDs = []; + $groupContactCacheClause = FALSE; while ($dao->fetch()) { - // currently operation is restrcited to VIEW/EDIT - if ($dao->where_clause) { - if ($dao->select_tables) { - $tmpTables = array(); - foreach (unserialize($dao->select_tables) as $tmpName => $tmpInfo) { - if ($tmpName == '`civicrm_group_contact-' . $dao->id . '`') { - $tmpName = '`civicrm_group_contact-ACL`'; - $tmpInfo = str_replace('civicrm_group_contact-' . $dao->id, 'civicrm_group_contact-ACL', $tmpInfo); - } - elseif ($tmpName == '`civicrm_group_contact_cache_' . $dao->id . '`') { - $tmpName = '`civicrm_group_contact_cache-ACL`'; - $tmpInfo = str_replace('civicrm_group_contact_cache_' . $dao->id, 'civicrm_group_contact_cache-ACL', $tmpInfo); - } - $tmpTables[$tmpName] = $tmpInfo; - } - $tables = array_merge($tables, - $tmpTables - ); - } - if ($dao->where_tables) { - $tmpTables = array(); - foreach (unserialize($dao->where_tables) as $tmpName => $tmpInfo) { - if ($tmpName == '`civicrm_group_contact-' . $dao->id . '`') { - $tmpName = '`civicrm_group_contact-ACL`'; - $tmpInfo = str_replace('civicrm_group_contact-' . $dao->id, 'civicrm_group_contact-ACL', $tmpInfo); - $staticGroupIDs[] = $dao->id; - } - elseif ($tmpName == '`civicrm_group_contact_cache_' . $dao->id . '`') { - $tmpName = '`civicrm_group_contact_cache-ACL`'; - $tmpInfo = str_replace('civicrm_group_contact_cache_' . $dao->id, 'civicrm_group_contact_cache-ACL', $tmpInfo); - $cachedGroupIDs[] = $dao->id; - } - $tmpTables[$tmpName] = $tmpInfo; - } - $whereTables = array_merge($whereTables, $tmpTables); - } - } + $groupIDs[] = $dao->id; - if (($dao->saved_search_id || $dao->children || $dao->parents) && - $dao->cache_date == NULL - ) { - CRM_Contact_BAO_GroupContactCache::load($dao); + if (($dao->saved_search_id || $dao->children || $dao->parents)) { + if ($dao->cache_date == NULL) { + CRM_Contact_BAO_GroupContactCache::load($dao); + } + $groupContactCacheClause = " UNION SELECT contact_id FROM civicrm_group_contact_cache WHERE group_id IN (" . implode(', ', $groupIDs) . ")"; } - } - if ($staticGroupIDs) { - $clauses[] = '( `civicrm_group_contact-ACL`.group_id IN (' . implode(', ', $staticGroupIDs) . ') AND `civicrm_group_contact-ACL`.status IN ("Added") )'; } - if ($cachedGroupIDs) { - $clauses[] = '`civicrm_group_contact_cache-ACL`.group_id IN (' . implode(', ', $cachedGroupIDs) . ')'; + if ($groupIDs) { + $clauses[] = "( + `contact_a`.id IN ( + SELECT contact_id FROM civicrm_group_contact WHERE group_id IN (" . implode(', ', $groupIDs) . ") AND status = 'Added' + $groupContactCacheClause + ) + )"; } } } @@ -880,7 +809,7 @@ public static function whereClause($type, &$tables, &$whereTables, $contactID = // call the hook to get additional whereClauses CRM_Utils_Hook::aclWhereClause($type, $tables, $whereTables, $contactID, $whereClause); - if (empty($whereClause) && !$strictReturn) { + if (empty($whereClause)) { $whereClause = ' ( 0 ) '; } @@ -903,18 +832,30 @@ public static function group( $allGroups = NULL, $includedGroups = NULL ) { + $userCacheKey = "{$contactID}_{$type}_{$tableName}_" . CRM_Core_Config::domainID() . '_' . md5(implode(',', array_merge((array) $allGroups, (array) $includedGroups))); + if (empty(Civi::$statics[__CLASS__]['permissioned_groups'])) { + Civi::$statics[__CLASS__]['permissioned_groups'] = []; + } + if (!empty(Civi::$statics[__CLASS__]['permissioned_groups'][$userCacheKey])) { + return Civi::$statics[__CLASS__]['permissioned_groups'][$userCacheKey]; + } + + if ($allGroups == NULL) { + $allGroups = CRM_Contact_BAO_Contact::buildOptions('group_id', NULL, ['onlyActive' => FALSE]); + } $acls = CRM_ACL_BAO_Cache::build($contactID); - $ids = array(); + $ids = []; if (!empty($acls)) { $aclKeys = array_keys($acls); $aclKeys = implode(',', $aclKeys); - $cacheKey = "$tableName-$aclKeys"; + $cacheKey = CRM_Core_BAO_Cache::cleanKey("$tableName-$aclKeys"); $cache = CRM_Utils_Cache::singleton(); $ids = $cache->get($cacheKey); if (!$ids) { + $ids = []; $query = " SELECT a.operation, a.object_id FROM civicrm_acl_cache c, civicrm_acl a @@ -925,7 +866,7 @@ public static function group( GROUP BY a.operation,a.object_id ORDER BY a.object_id "; - $params = array(1 => array($tableName, 'String')); + $params = [1 => [$tableName, 'String']]; $dao = CRM_Core_DAO::executeQuery($query, $params); while ($dao->fetch()) { if ($dao->object_id) { @@ -953,9 +894,25 @@ public static function group( ) { $ids = $includedGroups; } + if ($contactID) { + $groupWhere = ''; + if (!empty($allGroups)) { + $groupWhere = " AND id IN (" . implode(',', array_keys($allGroups)) . ")"; + } + // Contacts create hidden groups from search results. They should be able to retrieve their own. + $ownHiddenGroupsList = CRM_Core_DAO::singleValueQuery(" + SELECT GROUP_CONCAT(id) FROM civicrm_group WHERE is_hidden =1 AND created_id = $contactID + $groupWhere + "); + if ($ownHiddenGroupsList) { + $ownHiddenGroups = explode(',', $ownHiddenGroupsList); + $ids = array_merge((array) $ids, $ownHiddenGroups); + } + + } CRM_Utils_Hook::aclGroup($type, $contactID, $tableName, $allGroups, $ids); - + Civi::$statics[__CLASS__]['permissioned_groups'][$userCacheKey] = $ids; return $ids; } diff --git a/CRM/ACL/BAO/Cache.php b/CRM/ACL/BAO/Cache.php index 11ac3d33f1a0..921adfeaf6c0 100644 --- a/CRM/ACL/BAO/Cache.php +++ b/CRM/ACL/BAO/Cache.php @@ -1,9 +1,9 @@ array($id, 'Integer')); + $params = [1 => [$id, 'Integer']]; if ($id == 0) { $query .= " OR contact_id IS NULL"; @@ -83,7 +83,7 @@ public static function retrieve($id) { $dao = CRM_Core_DAO::executeQuery($query, $params); - $cache = array(); + $cache = []; while ($dao->fetch()) { $cache[$dao->acl_id] = 1; } @@ -122,7 +122,7 @@ public static function deleteEntry($id) { DELETE FROM civicrm_acl_cache WHERE contact_id = %1 "; - $params = array(1 => array($id, 'Integer')); + $params = [1 => [$id, 'Integer']]; CRM_Core_DAO::executeQuery($query, $params); } @@ -142,6 +142,9 @@ public static function updateEntry($id) { * Deletes all the cache entries. */ public static function resetCache() { + if (!CRM_Core_Config::isPermitCacheFlushMode()) { + return; + } // reset any static caching self::$_cache = NULL; @@ -151,7 +154,12 @@ public static function resetCache() { WHERE modified_date IS NULL OR (modified_date <= %1) "; - $params = array(1 => array(CRM_Contact_BAO_GroupContactCache::getCacheInvalidDateTime(), 'String')); + $params = [ + 1 => [ + CRM_Contact_BAO_GroupContactCache::getCacheInvalidDateTime(), + 'String', + ], + ]; CRM_Core_DAO::singleValueQuery($query, $params); // CRM_Core_DAO::singleValueQuery("TRUNCATE TABLE civicrm_acl_contact_cache"); // No, force-commits transaction diff --git a/CRM/ACL/BAO/EntityRole.php b/CRM/ACL/BAO/EntityRole.php index 89bcc3f440fd..8ed2b2e0b816 100644 --- a/CRM/ACL/BAO/EntityRole.php +++ b/CRM/ACL/BAO/EntityRole.php @@ -1,9 +1,9 @@ ts('Contact'), 'civicrm_group' => ts('Group'), - ); + ]; } return self::$_entityTable; } @@ -80,8 +80,8 @@ public static function retrieve(&$params, &$defaults) { * @param bool $is_active * Value we want to set the is_active field. * - * @return Object - * DAO object on success, null otherwise + * @return bool + * true if we found and updated the object, else false */ public static function setIsActive($id, $is_active) { return CRM_Core_DAO::setFieldValue('CRM_ACL_DAO_EntityRole', $id, 'is_active', $is_active); diff --git a/CRM/ACL/DAO/ACL.php b/CRM/ACL/DAO/ACL.php index e9ff994dcfa6..51791fb5f5af 100644 --- a/CRM/ACL/DAO/ACL.php +++ b/CRM/ACL/DAO/ACL.php @@ -1,278 +1,318 @@ __table = 'civicrm_acl'; parent::__construct(); } + /** - * Returns foreign keys and entity references + * Returns foreign keys and entity references. * * @return array * [CRM_Core_Reference_Interface] */ - static function getReferenceColumns() { + public static function getReferenceColumns() { if (!isset(Civi::$statics[__CLASS__]['links'])) { - Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Dynamic(self::getTableName() , 'entity_id', NULL, 'id', 'entity_table'); + Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Dynamic(self::getTableName(), 'entity_id', NULL, 'id', 'entity_table'); CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']); } return Civi::$statics[__CLASS__]['links']; } + /** * Returns all the column names of this table * * @return array */ - static function &fields() { + public static function &fields() { if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = array( - 'id' => array( + Civi::$statics[__CLASS__]['fields'] = [ + 'id' => [ 'name' => 'id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('ACL ID') , - 'description' => 'Unique table ID', - 'required' => true, - ) , - 'name' => array( + 'title' => ts('ACL ID'), + 'description' => ts('Unique table ID'), + 'required' => TRUE, + 'table_name' => 'civicrm_acl', + 'entity' => 'ACL', + 'bao' => 'CRM_ACL_BAO_ACL', + 'localizable' => 0, + ], + 'name' => [ 'name' => 'name', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('ACL Name') , - 'description' => 'ACL Name.', + 'title' => ts('ACL Name'), + 'description' => ts('ACL Name.'), 'maxlength' => 64, 'size' => CRM_Utils_Type::BIG, - 'html' => array( + 'table_name' => 'civicrm_acl', + 'entity' => 'ACL', + 'bao' => 'CRM_ACL_BAO_ACL', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'deny' => array( + ], + ], + 'deny' => [ 'name' => 'deny', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Deny ACL?') , - 'description' => 'Is this ACL entry Allow (0) or Deny (1) ?', - 'required' => true, - 'html' => array( + 'title' => ts('Deny ACL?'), + 'description' => ts('Is this ACL entry Allow (0) or Deny (1) ?'), + 'required' => TRUE, + 'default' => '0', + 'table_name' => 'civicrm_acl', + 'entity' => 'ACL', + 'bao' => 'CRM_ACL_BAO_ACL', + 'localizable' => 0, + 'html' => [ 'type' => 'Radio', - ) , - ) , - 'entity_table' => array( + ], + ], + 'entity_table' => [ 'name' => 'entity_table', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('ACL Entity') , - 'description' => 'Table of the object possessing this ACL entry (Contact, Group, or ACL Group)', - 'required' => true, + 'title' => ts('ACL Entity'), + 'description' => ts('Table of the object possessing this ACL entry (Contact, Group, or ACL Group)'), + 'required' => TRUE, 'maxlength' => 64, 'size' => CRM_Utils_Type::BIG, - ) , - 'entity_id' => array( + 'table_name' => 'civicrm_acl', + 'entity' => 'ACL', + 'bao' => 'CRM_ACL_BAO_ACL', + 'localizable' => 0, + ], + 'entity_id' => [ 'name' => 'entity_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Entity ID') , - 'description' => 'ID of the object possessing this ACL', - ) , - 'operation' => array( + 'title' => ts('Entity ID'), + 'description' => ts('ID of the object possessing this ACL'), + 'table_name' => 'civicrm_acl', + 'entity' => 'ACL', + 'bao' => 'CRM_ACL_BAO_ACL', + 'localizable' => 0, + ], + 'operation' => [ 'name' => 'operation', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('ACL Operation') , - 'description' => 'What operation does this ACL entry control?', - 'required' => true, + 'title' => ts('ACL Operation'), + 'description' => ts('What operation does this ACL entry control?'), + 'required' => TRUE, 'maxlength' => 8, 'size' => CRM_Utils_Type::EIGHT, - 'html' => array( + 'table_name' => 'civicrm_acl', + 'entity' => 'ACL', + 'bao' => 'CRM_ACL_BAO_ACL', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'callback' => 'CRM_ACL_BAO_ACL::operation', - ) - ) , - 'object_table' => array( + ] + ], + 'object_table' => [ 'name' => 'object_table', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('ACL Object') , - 'description' => 'The table of the object controlled by this ACL entry', + 'title' => ts('ACL Object'), + 'description' => ts('The table of the object controlled by this ACL entry'), 'maxlength' => 64, 'size' => CRM_Utils_Type::BIG, - ) , - 'object_id' => array( + 'table_name' => 'civicrm_acl', + 'entity' => 'ACL', + 'bao' => 'CRM_ACL_BAO_ACL', + 'localizable' => 0, + ], + 'object_id' => [ 'name' => 'object_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('ACL Object ID') , - 'description' => 'The ID of the object controlled by this ACL entry', - ) , - 'acl_table' => array( + 'title' => ts('ACL Object ID'), + 'description' => ts('The ID of the object controlled by this ACL entry'), + 'table_name' => 'civicrm_acl', + 'entity' => 'ACL', + 'bao' => 'CRM_ACL_BAO_ACL', + 'localizable' => 0, + ], + 'acl_table' => [ 'name' => 'acl_table', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('ACL Table') , - 'description' => 'If this is a grant/revoke entry, what table are we granting?', + 'title' => ts('ACL Table'), + 'description' => ts('If this is a grant/revoke entry, what table are we granting?'), 'maxlength' => 64, 'size' => CRM_Utils_Type::BIG, - ) , - 'acl_id' => array( + 'table_name' => 'civicrm_acl', + 'entity' => 'ACL', + 'bao' => 'CRM_ACL_BAO_ACL', + 'localizable' => 0, + ], + 'acl_id' => [ 'name' => 'acl_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('ACL Group ID') , - 'description' => 'ID of the ACL or ACL group being granted/revoked', - ) , - 'is_active' => array( + 'title' => ts('ACL Group ID'), + 'description' => ts('ID of the ACL or ACL group being granted/revoked'), + 'table_name' => 'civicrm_acl', + 'entity' => 'ACL', + 'bao' => 'CRM_ACL_BAO_ACL', + 'localizable' => 0, + ], + 'is_active' => [ 'name' => 'is_active', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('ACL Is Active?') , - 'description' => 'Is this property active?', - 'html' => array( - 'type' => 'Checkbox', - ) , - ) , - ); + 'title' => ts('ACL Is Active?'), + 'description' => ts('Is this property active?'), + 'table_name' => 'civicrm_acl', + 'entity' => 'ACL', + 'bao' => 'CRM_ACL_BAO_ACL', + 'localizable' => 0, + 'html' => [ + 'type' => 'CheckBox', + ], + ], + ]; CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } return Civi::$statics[__CLASS__]['fields']; } + /** * Return a mapping from field-name to the corresponding key (as used in fields()). * * @return array * Array(string $name => string $uniqueName). */ - static function &fieldKeys() { + public static function &fieldKeys() { if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); } return Civi::$statics[__CLASS__]['fieldKeys']; } + /** * Returns the names of this table * * @return string */ - static function getTableName() { + public static function getTableName() { return self::$_tableName; } + /** * Returns if this table needs to be logged * - * @return boolean + * @return bool */ - function getLog() { + public function getLog() { return self::$_log; } + /** * Returns the list of fields that can be imported * @@ -280,10 +320,11 @@ function getLog() { * * @return array */ - static function &import($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'acl', $prefix, array()); + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'acl', $prefix, []); return $r; } + /** * Returns the list of fields that can be exported * @@ -291,8 +332,30 @@ static function &import($prefix = false) { * * @return array */ - static function &export($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'acl', $prefix, array()); + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'acl', $prefix, []); return $r; } + + /** + * Returns the list of indices + * + * @param bool $localize + * + * @return array + */ + public static function indices($localize = TRUE) { + $indices = [ + 'index_acl_id' => [ + 'name' => 'index_acl_id', + 'field' => [ + 0 => 'acl_id', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_acl::0::acl_id', + ], + ]; + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + } diff --git a/CRM/ACL/DAO/Cache.php b/CRM/ACL/DAO/Cache.php index 6e728c509f08..cd74fd01bba4 100644 --- a/CRM/ACL/DAO/Cache.php +++ b/CRM/ACL/DAO/Cache.php @@ -1,171 +1,175 @@ __table = 'civicrm_acl_cache'; parent::__construct(); } + /** - * Returns foreign keys and entity references + * Returns foreign keys and entity references. * * @return array * [CRM_Core_Reference_Interface] */ - static function getReferenceColumns() { + public static function getReferenceColumns() { if (!isset(Civi::$statics[__CLASS__]['links'])) { - Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'contact_id', 'civicrm_contact', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'acl_id', 'civicrm_acl', 'id'); + Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'contact_id', 'civicrm_contact', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'acl_id', 'civicrm_acl', 'id'); CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']); } return Civi::$statics[__CLASS__]['links']; } + /** * Returns all the column names of this table * * @return array */ - static function &fields() { + public static function &fields() { if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = array( - 'id' => array( + Civi::$statics[__CLASS__]['fields'] = [ + 'id' => [ 'name' => 'id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Cache ID') , - 'description' => 'Unique table ID', - 'required' => true, - ) , - 'contact_id' => array( + 'title' => ts('Cache ID'), + 'description' => ts('Unique table ID'), + 'required' => TRUE, + 'table_name' => 'civicrm_acl_cache', + 'entity' => 'Cache', + 'bao' => 'CRM_ACL_BAO_Cache', + 'localizable' => 0, + ], + 'contact_id' => [ 'name' => 'contact_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Cache Contact') , - 'description' => 'Foreign Key to Contact', + 'title' => ts('Cache Contact'), + 'description' => ts('Foreign Key to Contact'), + 'table_name' => 'civicrm_acl_cache', + 'entity' => 'Cache', + 'bao' => 'CRM_ACL_BAO_Cache', + 'localizable' => 0, 'FKClassName' => 'CRM_Contact_DAO_Contact', - ) , - 'acl_id' => array( + ], + 'acl_id' => [ 'name' => 'acl_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Cache ACL') , - 'description' => 'Foreign Key to ACL', - 'required' => true, + 'title' => ts('Cache ACL'), + 'description' => ts('Foreign Key to ACL'), + 'required' => TRUE, + 'table_name' => 'civicrm_acl_cache', + 'entity' => 'Cache', + 'bao' => 'CRM_ACL_BAO_Cache', + 'localizable' => 0, 'FKClassName' => 'CRM_ACL_DAO_ACL', - ) , - 'modified_date' => array( + ], + 'modified_date' => [ 'name' => 'modified_date', 'type' => CRM_Utils_Type::T_TIMESTAMP, - 'title' => ts('Cache Modified Date') , - 'description' => 'When was this cache entry last modified', - 'required' => false, - ) , - ); + 'title' => ts('Cache Modified Date'), + 'description' => ts('When was this cache entry last modified'), + 'required' => FALSE, + 'table_name' => 'civicrm_acl_cache', + 'entity' => 'Cache', + 'bao' => 'CRM_ACL_BAO_Cache', + 'localizable' => 0, + ], + ]; CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } return Civi::$statics[__CLASS__]['fields']; } + /** * Return a mapping from field-name to the corresponding key (as used in fields()). * * @return array * Array(string $name => string $uniqueName). */ - static function &fieldKeys() { + public static function &fieldKeys() { if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); } return Civi::$statics[__CLASS__]['fieldKeys']; } + /** * Returns the names of this table * * @return string */ - static function getTableName() { + public static function getTableName() { return self::$_tableName; } + /** * Returns if this table needs to be logged * - * @return boolean + * @return bool */ - function getLog() { + public function getLog() { return self::$_log; } + /** * Returns the list of fields that can be imported * @@ -173,10 +177,11 @@ function getLog() { * * @return array */ - static function &import($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'acl_cache', $prefix, array()); + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'acl_cache', $prefix, []); return $r; } + /** * Returns the list of fields that can be exported * @@ -184,8 +189,30 @@ static function &import($prefix = false) { * * @return array */ - static function &export($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'acl_cache', $prefix, array()); + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'acl_cache', $prefix, []); return $r; } + + /** + * Returns the list of indices + * + * @param bool $localize + * + * @return array + */ + public static function indices($localize = TRUE) { + $indices = [ + 'index_acl_id' => [ + 'name' => 'index_acl_id', + 'field' => [ + 0 => 'acl_id', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_acl_cache::0::acl_id', + ], + ]; + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + } diff --git a/CRM/ACL/DAO/EntityRole.php b/CRM/ACL/DAO/EntityRole.php index af50453f9d66..bb0fda29f27a 100644 --- a/CRM/ACL/DAO/EntityRole.php +++ b/CRM/ACL/DAO/EntityRole.php @@ -1,183 +1,192 @@ __table = 'civicrm_acl_entity_role'; parent::__construct(); } + /** - * Returns foreign keys and entity references + * Returns foreign keys and entity references. * * @return array * [CRM_Core_Reference_Interface] */ - static function getReferenceColumns() { + public static function getReferenceColumns() { if (!isset(Civi::$statics[__CLASS__]['links'])) { - Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Dynamic(self::getTableName() , 'entity_id', NULL, 'id', 'entity_table'); + Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Dynamic(self::getTableName(), 'entity_id', NULL, 'id', 'entity_table'); CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']); } return Civi::$statics[__CLASS__]['links']; } + /** * Returns all the column names of this table * * @return array */ - static function &fields() { + public static function &fields() { if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = array( - 'id' => array( + Civi::$statics[__CLASS__]['fields'] = [ + 'id' => [ 'name' => 'id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Entity Role') , - 'description' => 'Unique table ID', - 'required' => true, - ) , - 'acl_role_id' => array( + 'title' => ts('Entity Role'), + 'description' => ts('Unique table ID'), + 'required' => TRUE, + 'table_name' => 'civicrm_acl_entity_role', + 'entity' => 'EntityRole', + 'bao' => 'CRM_ACL_BAO_EntityRole', + 'localizable' => 0, + ], + 'acl_role_id' => [ 'name' => 'acl_role_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('ACL Role ID') , - 'description' => 'Foreign Key to ACL Role (which is an option value pair and hence an implicit FK)', - 'required' => true, - ) , - 'entity_table' => array( + 'title' => ts('ACL Role ID'), + 'description' => ts('Foreign Key to ACL Role (which is an option value pair and hence an implicit FK)'), + 'required' => TRUE, + 'table_name' => 'civicrm_acl_entity_role', + 'entity' => 'EntityRole', + 'bao' => 'CRM_ACL_BAO_EntityRole', + 'localizable' => 0, + ], + 'entity_table' => [ 'name' => 'entity_table', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Entity Table') , - 'description' => 'Table of the object joined to the ACL Role (Contact or Group)', - 'required' => true, + 'title' => ts('Entity Table'), + 'description' => ts('Table of the object joined to the ACL Role (Contact or Group)'), + 'required' => TRUE, 'maxlength' => 64, 'size' => CRM_Utils_Type::BIG, - ) , - 'entity_id' => array( + 'table_name' => 'civicrm_acl_entity_role', + 'entity' => 'EntityRole', + 'bao' => 'CRM_ACL_BAO_EntityRole', + 'localizable' => 0, + ], + 'entity_id' => [ 'name' => 'entity_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('ACL Entity ID') , - 'description' => 'ID of the group/contact object being joined', - 'required' => true, - ) , - 'is_active' => array( + 'title' => ts('ACL Entity ID'), + 'description' => ts('ID of the group/contact object being joined'), + 'required' => TRUE, + 'table_name' => 'civicrm_acl_entity_role', + 'entity' => 'EntityRole', + 'bao' => 'CRM_ACL_BAO_EntityRole', + 'localizable' => 0, + ], + 'is_active' => [ 'name' => 'is_active', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('ACL Entity Role is Active') , - 'description' => 'Is this property active?', - ) , - ); + 'title' => ts('ACL Entity Role is Active'), + 'description' => ts('Is this property active?'), + 'table_name' => 'civicrm_acl_entity_role', + 'entity' => 'EntityRole', + 'bao' => 'CRM_ACL_BAO_EntityRole', + 'localizable' => 0, + ], + ]; CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } return Civi::$statics[__CLASS__]['fields']; } + /** * Return a mapping from field-name to the corresponding key (as used in fields()). * * @return array * Array(string $name => string $uniqueName). */ - static function &fieldKeys() { + public static function &fieldKeys() { if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); } return Civi::$statics[__CLASS__]['fieldKeys']; } + /** * Returns the names of this table * * @return string */ - static function getTableName() { + public static function getTableName() { return self::$_tableName; } + /** * Returns if this table needs to be logged * - * @return boolean + * @return bool */ - function getLog() { + public function getLog() { return self::$_log; } + /** * Returns the list of fields that can be imported * @@ -185,10 +194,11 @@ function getLog() { * * @return array */ - static function &import($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'acl_entity_role', $prefix, array()); + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'acl_entity_role', $prefix, []); return $r; } + /** * Returns the list of fields that can be exported * @@ -196,8 +206,39 @@ static function &import($prefix = false) { * * @return array */ - static function &export($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'acl_entity_role', $prefix, array()); + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'acl_entity_role', $prefix, []); return $r; } + + /** + * Returns the list of indices + * + * @param bool $localize + * + * @return array + */ + public static function indices($localize = TRUE) { + $indices = [ + 'index_role' => [ + 'name' => 'index_role', + 'field' => [ + 0 => 'acl_role_id', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_acl_entity_role::0::acl_role_id', + ], + 'index_entity' => [ + 'name' => 'index_entity', + 'field' => [ + 0 => 'entity_table', + 1 => 'entity_id', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_acl_entity_role::0::entity_table::entity_id', + ], + ]; + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + } diff --git a/CRM/ACL/Form/ACL.php b/CRM/ACL/Form/ACL.php index 7de87c72d51a..b119f7503aed 100644 --- a/CRM/ACL/Form/ACL.php +++ b/CRM/ACL/Form/ACL.php @@ -1,9 +1,9 @@ add('text', 'name', ts('Description'), CRM_Core_DAO::getAttribute('CRM_ACL_DAO_ACL', 'name'), TRUE); - $operations = array('' => ts('- select -')) + CRM_ACL_BAO_ACL::operation(); + $operations = ['' => ts('- select -')] + CRM_ACL_BAO_ACL::operation(); $this->add('select', 'operation', ts('Operation'), $operations, TRUE ); - $objTypes = array( + $objTypes = [ '1' => ts('A group of contacts'), '2' => ts('A profile'), '3' => ts('A set of custom data fields'), - ); + ]; if (CRM_Core_Permission::access('CiviEvent')) { $objTypes['4'] = ts('Events'); } - $extra = array('onclick' => "showObjectSelect();"); + $extra = ['onclick' => "showObjectSelect();"]; $this->addRadio('object_type', ts('Type of Data'), $objTypes, @@ -140,31 +140,31 @@ public function buildQuickForm() { ); $label = ts('Role'); - $role = array( + $role = [ '-1' => ts('- select role -'), '0' => ts('Everyone'), - ) + CRM_Core_OptionGroup::values('acl_role'); + ] + CRM_Core_OptionGroup::values('acl_role'); $this->add('select', 'entity_id', $label, $role, TRUE); - $group = array( + $group = [ '-1' => ts('- select -'), '0' => ts('All Groups'), - ) + CRM_Core_PseudoConstant::group(); + ] + CRM_Core_PseudoConstant::group(); - $customGroup = array( + $customGroup = [ '-1' => ts('- select -'), '0' => ts('All Custom Groups'), - ) + CRM_Core_PseudoConstant::get('CRM_Core_DAO_CustomField', 'custom_group_id'); + ] + CRM_Core_PseudoConstant::get('CRM_Core_DAO_CustomField', 'custom_group_id'); - $ufGroup = array( + $ufGroup = [ '-1' => ts('- select -'), '0' => ts('All Profiles'), - ) + CRM_Core_PseudoConstant::get('CRM_Core_DAO_UFField', 'uf_group_id'); + ] + CRM_Core_PseudoConstant::get('CRM_Core_DAO_UFField', 'uf_group_id'); - $event = array( + $event = [ '-1' => ts('- select -'), '0' => ts('All Events'), - ) + CRM_Event_PseudoConstant::event(NULL, FALSE, "( is_template IS NULL OR is_template != 1 )"); + ] + CRM_Event_PseudoConstant::event(NULL, FALSE, "( is_template IS NULL OR is_template != 1 )"); $this->add('select', 'group_id', ts('Group'), $group); $this->add('select', 'custom_group_id', ts('Custom Data'), $customGroup); @@ -173,7 +173,7 @@ public function buildQuickForm() { $this->add('checkbox', 'is_active', ts('Enabled?')); - $this->addFormRule(array('CRM_ACL_Form_ACL', 'formRule')); + $this->addFormRule(['CRM_ACL_Form_ACL', 'formRule']); } /** @@ -189,7 +189,7 @@ public static function formRule($params) { $errors['entity_id'] = ts('Please assign this permission to a Role.'); } - $validOperations = array('View', 'Edit'); + $validOperations = ['View', 'Edit']; $operationMessage = ts("Only 'View' and 'Edit' operations are valid for this type of data"); // Figure out which type of object we're permissioning on and make sure user has selected a value. diff --git a/CRM/ACL/Form/ACLBasic.php b/CRM/ACL/Form/ACLBasic.php index 17a266616362..263b49592765 100644 --- a/CRM/ACL/Form/ACLBasic.php +++ b/CRM/ACL/Form/ACLBasic.php @@ -1,9 +1,9 @@ _id || $this->_id === '0' @@ -49,9 +49,9 @@ public function setDefaultValues() { WHERE entity_id = %1 AND ( object_table NOT IN ( 'civicrm_saved_search', 'civicrm_uf_group', 'civicrm_custom_group' ) ) "; - $params = array(1 => array($this->_id, 'Integer')); + $params = [1 => [$this->_id, 'Integer']]; $dao = CRM_Core_DAO::executeQuery($query, $params); - $defaults['object_table'] = array(); + $defaults['object_table'] = []; while ($dao->fetch()) { $defaults['object_table'][$dao->object_table] = 1; } @@ -75,14 +75,14 @@ public function buildQuickForm() { ts('ACL Type'), $permissions, NULL, NULL, TRUE, NULL, - array('', '') + ['', ''] ); $label = ts('Role'); - $role = array( + $role = [ '-1' => ts('- select role -'), '0' => ts('Everyone'), - ) + CRM_Core_OptionGroup::values('acl_role'); + ] + CRM_Core_OptionGroup::values('acl_role'); $entityID = &$this->add('select', 'entity_id', $label, $role, TRUE); if ($this->_id) { @@ -90,7 +90,7 @@ public function buildQuickForm() { } $this->add('checkbox', 'is_active', ts('Enabled?')); - $this->addFormRule(array('CRM_ACL_Form_ACLBasic', 'formRule')); + $this->addFormRule(['CRM_ACL_Form_ACLBasic', 'formRule']); } /** @@ -100,7 +100,7 @@ public function buildQuickForm() { */ public static function formRule($params) { if ($params['entity_id'] == -1) { - $errors = array('entity_id' => ts('Role is a required field')); + $errors = ['entity_id' => ts('Role is a required field')]; return $errors; } @@ -123,7 +123,7 @@ public function postProcess() { WHERE entity_id = %1 AND ( object_table NOT IN ( 'civicrm_saved_search', 'civicrm_uf_group', 'civicrm_custom_group' ) ) "; - $deleteParams = array(1 => array($this->_id, 'Integer')); + $deleteParams = [1 => [$this->_id, 'Integer']]; CRM_Core_DAO::executeQuery($query, $deleteParams); if ($this->_action & CRM_Core_Action::DELETE) { diff --git a/CRM/ACL/Form/EntityRole.php b/CRM/ACL/Form/EntityRole.php index 13b709b57317..1da21f89ea2f 100644 --- a/CRM/ACL/Form/EntityRole.php +++ b/CRM/ACL/Form/EntityRole.php @@ -1,9 +1,9 @@ ts('- select -')) + CRM_Core_OptionGroup::values('acl_role'); + $aclRoles = ['' => ts('- select -')] + CRM_Core_OptionGroup::values('acl_role'); $this->add('select', 'acl_role_id', ts('ACL Role'), $aclRoles, TRUE ); $label = ts('Assigned to'); - $group = array('' => ts('- select group -')) + CRM_Core_PseudoConstant::staticGroup(FALSE, 'Access'); - $this->add('select', 'entity_id', $label, $group, TRUE, array('class' => 'crm-select2 huge')); + $group = ['' => ts('- select group -')] + CRM_Core_PseudoConstant::staticGroup(FALSE, 'Access'); + $this->add('select', 'entity_id', $label, $group, TRUE, ['class' => 'crm-select2 huge']); $this->add('checkbox', 'is_active', ts('Enabled?')); } diff --git a/CRM/ACL/Form/WordPress/Permissions.php b/CRM/ACL/Form/WordPress/Permissions.php index 32beb1b9528f..65191fb9793a 100644 --- a/CRM/ACL/Form/WordPress/Permissions.php +++ b/CRM/ACL/Form/WordPress/Permissions.php @@ -1,9 +1,9 @@ role_names as $role => $name) { - // Dont show the permissions options for administrator, as they have all permissions + // Don't show the permissions options for administrator, as they have all permissions if ($role !== 'administrator') { $roleObj = $wp_roles->get_role($role); if (!empty($roleObj->capabilities)) { @@ -78,25 +77,54 @@ public function buildQuickForm() { $this->setDefaults($defaults); - $descArray = array(); + $descArray = []; foreach ($permissionsDesc as $perm => $attr) { if (count($attr) > 1) { $descArray[$perm] = $attr[1]; } } - $this->assign('permDesc', $descArray); + + // build table rows by merging role perms + $rows = []; + foreach ($rolePerms as $role => $perms) { + foreach ($perms as $name => $title) { + $rows[$name] = $title; + } + } + + // Build array keyed by permission + $table = []; + foreach ($rows as $perm => $label) { + + // Init row with permission label + $table[$perm] = [ + 'label' => $label, + 'roles' => [], + ]; + + // Add permission description and role names + foreach ($roles as $key => $label) { + if (isset($descArray[$perm])) { + $table[$perm]['desc'] = $descArray[$perm]; + } + $table[$perm]['roles'][] = $key; + } + + } + + $this->assign('table', $table); $this->assign('rolePerms', $rolePerms); $this->assign('roles', $roles); $this->addButtons( - array( - array( + [ + [ 'type' => 'next', 'name' => ts('Save'), 'spacing' => '', 'isDefault' => FALSE, - ), - ) + ], + ] ); } @@ -134,19 +162,19 @@ public function postProcess() { // Get the permissions into a format that matches what we get from WP $allWarningPermissions = CRM_Core_Permission::getAnonymousPermissionsWarnings(); foreach ($allWarningPermissions as $key => $permission) { - $allWarningPermissions[$key] = CRM_utils_String::munge(strtolower($permission)); + $allWarningPermissions[$key] = CRM_Utils_String::munge(strtolower($permission)); } $warningPermissions = array_intersect($allWarningPermissions, array_keys($rolePermissions)); - $warningPermissionNames = array(); + $warningPermissionNames = []; foreach ($warningPermissions as $permission) { $warningPermissionNames[$permission] = $permissionsArray[$permission]; } if (!empty($warningPermissionNames)) { CRM_Core_Session::setStatus( - ts('The %1 role was assigned one or more permissions that may prove dangerous for users of that role to have. Please reconsider assigning %2 to them.', array( - 1 => $wp_roles->role_names[$role], - 2 => implode(', ', $warningPermissionNames), - )), + ts('The %1 role was assigned one or more permissions that may prove dangerous for users of that role to have. Please reconsider assigning %2 to them.', [ + 1 => $wp_roles->role_names[$role], + 2 => implode(', ', $warningPermissionNames), + ]), ts('Unsafe Permission Settings') ); } @@ -163,7 +191,7 @@ public function postProcess() { CRM_Core_Session::setStatus("", ts('Wordpress Access Control Updated'), "success"); - // rebuild the menus to comply with the new permisssions/capabilites + // rebuild the menus to comply with the new permissions/capabilites CRM_Core_Invoke::rebuildMenuAndCaches(); CRM_Utils_System::redirect('admin.php?page=CiviCRM&q=civicrm/admin/access&reset=1'); @@ -186,7 +214,7 @@ public static function getPermissionArray($descriptions = FALSE) { $permissions = CRM_Core_Permission::basicPermissions(FALSE, $descriptions); - $perms_array = array(); + $perms_array = []; foreach ($permissions as $perm => $title) { //order matters here, but we deal with that later $perms_array[CRM_Utils_String::munge(strtolower($perm))] = $title; diff --git a/CRM/ACL/Page/ACL.php b/CRM/ACL/Page/ACL.php index 0131eee3d067..21ad61efbc41 100644 --- a/CRM/ACL/Page/ACL.php +++ b/CRM/ACL/Page/ACL.php @@ -1,9 +1,9 @@ array( + self::$_links = [ + CRM_Core_Action::UPDATE => [ 'name' => ts('Edit'), 'url' => 'civicrm/acl', 'qs' => 'reset=1&action=update&id=%%id%%', 'title' => ts('Edit ACL'), - ), - CRM_Core_Action::DISABLE => array( + ], + CRM_Core_Action::DISABLE => [ 'name' => ts('Disable'), 'ref' => 'crm-enable-disable', 'title' => ts('Disable ACL'), - ), - CRM_Core_Action::ENABLE => array( + ], + CRM_Core_Action::ENABLE => [ 'name' => ts('Enable'), 'ref' => 'crm-enable-disable', 'title' => ts('Enable ACL'), - ), - CRM_Core_Action::DELETE => array( + ], + CRM_Core_Action::DELETE => [ 'name' => ts('Delete'), 'url' => 'civicrm/acl', 'qs' => 'reset=1&action=delete&id=%%id%%', 'title' => ts('Delete ACL'), - ), - ); + ], + ]; } return self::$_links; } @@ -90,51 +90,17 @@ public function &links() { /** * Run the page. * - * This method is called after the page is created. It checks for the - * type of action and executes that action. - * Finally it calls the parent's run method. + * Set the breadcrumb before beginning the standard page run. */ public function run() { - // get the requested action - $action = CRM_Utils_Request::retrieve('action', 'String', - // default to 'browse' - $this, FALSE, 'browse' - ); - - // assign vars to templates - $this->assign('action', $action); - $id = CRM_Utils_Request::retrieve('id', 'Positive', - $this, FALSE, 0 - ); - // set breadcrumb to append to admin/access - $breadCrumb = array( - array( + $breadCrumb = [ + [ 'title' => ts('Access Control'), - 'url' => CRM_Utils_System::url('civicrm/admin/access', - 'reset=1' - ), - ), - ); + 'url' => CRM_Utils_System::url('civicrm/admin/access', 'reset=1'), + ], + ]; CRM_Utils_System::appendBreadCrumb($breadCrumb); - // what action to take ? - if ($action & (CRM_Core_Action::ADD | CRM_Core_Action::DELETE)) { - $this->edit($action, $id); - } - - if ($action & (CRM_Core_Action::UPDATE)) { - $this->edit($action, $id); - - if (isset($id)) { - $aclName = CRM_Core_DAO::getFieldValue('CRM_ACL_DAO_ACL', $id); - CRM_Utils_System::setTitle(ts('Edit ACL - %1', array(1 => $aclName))); - } - } - - // finally browse the acl's - if ($action & CRM_Core_Action::BROWSE) { - $this->browse(); - } // parent run return parent::run(); @@ -145,7 +111,7 @@ public function run() { */ public function browse() { // get all acl's sorted by weight - $acl = array(); + $acl = []; $query = " SELECT * FROM civicrm_acl @@ -156,26 +122,26 @@ public function browse() { $roles = CRM_Core_OptionGroup::values('acl_role'); - $group = array( + $group = [ '-1' => ts('- select -'), '0' => ts('All Groups'), - ) + CRM_Core_PseudoConstant::group(); - $customGroup = array( + ] + CRM_Core_PseudoConstant::group(); + $customGroup = [ '-1' => ts('- select -'), '0' => ts('All Custom Groups'), - ) + CRM_Core_PseudoConstant::get('CRM_Core_DAO_CustomField', 'custom_group_id'); - $ufGroup = array( + ] + CRM_Core_PseudoConstant::get('CRM_Core_DAO_CustomField', 'custom_group_id'); + $ufGroup = [ '-1' => ts('- select -'), '0' => ts('All Profiles'), - ) + CRM_Core_PseudoConstant::get('CRM_Core_DAO_UFField', 'uf_group_id'); + ] + CRM_Core_PseudoConstant::get('CRM_Core_DAO_UFField', 'uf_group_id'); - $event = array( + $event = [ '-1' => ts('- select -'), '0' => ts('All Events'), - ) + CRM_Event_PseudoConstant::event(); + ] + CRM_Event_PseudoConstant::event(); while ($dao->fetch()) { - $acl[$dao->id] = array(); + $acl[$dao->id] = []; $acl[$dao->id]['name'] = $dao->name; $acl[$dao->id]['operation'] = $dao->operation; $acl[$dao->id]['entity_id'] = $dao->entity_id; @@ -193,22 +159,22 @@ public function browse() { switch ($acl[$dao->id]['object_table']) { case 'civicrm_saved_search': - $acl[$dao->id]['object'] = $group[$acl[$dao->id]['object_id']]; + $acl[$dao->id]['object'] = CRM_Utils_Array::value($acl[$dao->id]['object_id'], $group); $acl[$dao->id]['object_name'] = ts('Group'); break; case 'civicrm_uf_group': - $acl[$dao->id]['object'] = $ufGroup[$acl[$dao->id]['object_id']]; + $acl[$dao->id]['object'] = CRM_Utils_Array::value($acl[$dao->id]['object_id'], $ufGroup); $acl[$dao->id]['object_name'] = ts('Profile'); break; case 'civicrm_custom_group': - $acl[$dao->id]['object'] = $customGroup[$acl[$dao->id]['object_id']]; + $acl[$dao->id]['object'] = CRM_Utils_Array::value($acl[$dao->id]['object_id'], $customGroup); $acl[$dao->id]['object_name'] = ts('Custom Group'); break; case 'civicrm_event': - $acl[$dao->id]['object'] = $event[$acl[$dao->id]['object_id']]; + $acl[$dao->id]['object'] = CRM_Utils_Array::value($acl[$dao->id]['object_id'], $event); $acl[$dao->id]['object_name'] = ts('Event'); break; } @@ -226,7 +192,7 @@ public function browse() { $acl[$dao->id]['action'] = CRM_Core_Action::formLink( self::links(), $action, - array('id' => $dao->id), + ['id' => $dao->id], ts('more'), FALSE, 'ACL.manage.action', @@ -269,4 +235,26 @@ public function userContext($mode = NULL) { return 'civicrm/acl'; } + /** + * Edit an ACL. + * + * @param int $mode + * What mode for the form ?. + * @param int $id + * Id of the entity (for update, view operations). + * @param bool $imageUpload + * Not used in this case, but extended from CRM_Core_Page_Basic. + * @param bool $pushUserContext + * Not used in this case, but extended from CRM_Core_Page_Basic. + */ + public function edit($mode, $id = NULL, $imageUpload = FALSE, $pushUserContext = TRUE) { + if ($mode & (CRM_Core_Action::UPDATE)) { + if (isset($id)) { + $aclName = CRM_Core_DAO::getFieldValue('CRM_ACL_DAO_ACL', $id); + CRM_Utils_System::setTitle(ts('Edit ACL – %1', [1 => $aclName])); + } + } + parent::edit($mode, $id, $imageUpload, $pushUserContext); + } + } diff --git a/CRM/ACL/Page/ACLBasic.php b/CRM/ACL/Page/ACLBasic.php index 1432e1bc352b..e55fb919cd81 100644 --- a/CRM/ACL/Page/ACLBasic.php +++ b/CRM/ACL/Page/ACLBasic.php @@ -1,9 +1,9 @@ array( + self::$_links = [ + CRM_Core_Action::UPDATE => [ 'name' => ts('Edit'), 'url' => 'civicrm/acl/basic', 'qs' => 'reset=1&action=update&id=%%id%%', 'title' => ts('Edit ACL'), - ), - CRM_Core_Action::DELETE => array( + ], + CRM_Core_Action::DELETE => [ 'name' => ts('Delete'), 'url' => 'civicrm/acl/basic', 'qs' => 'reset=1&action=delete&id=%%id%%', 'title' => ts('Delete ACL'), - ), - ); + ], + ]; } return self::$_links; } @@ -83,37 +83,27 @@ public function &links() { * Finally it calls the parent's run method. */ public function run() { - // get the requested action - $action = CRM_Utils_Request::retrieve('action', 'String', - // default to 'browse' - $this, FALSE, 'browse' - ); - - // assign vars to templates - $this->assign('action', $action); - $id = CRM_Utils_Request::retrieve('id', 'Positive', - $this, FALSE, 0 - ); + $id = $this->getIdAndAction(); // set breadcrumb to append to admin/access - $breadCrumb = array( - array( + $breadCrumb = [ + [ 'title' => ts('Access Control'), 'url' => CRM_Utils_System::url('civicrm/admin/access', 'reset=1'), - ), - ); + ], + ]; CRM_Utils_System::appendBreadCrumb($breadCrumb); // what action to take ? - if ($action & (CRM_Core_Action::UPDATE | CRM_Core_Action::ADD | CRM_Core_Action::DELETE)) { - $this->edit($action, $id); + if ($this->_action & (CRM_Core_Action::UPDATE | CRM_Core_Action::ADD | CRM_Core_Action::DELETE)) { + $this->edit($this->_action, $id); } // finally browse the acl's $this->browse(); - // parent run - return parent::run(); + // This replaces parent run, but do parent's parent run + return CRM_Core_Page::run(); } /** @@ -122,7 +112,7 @@ public function run() { public function browse() { // get all acl's sorted by weight - $acl = array(); + $acl = []; $query = " SELECT * FROM civicrm_acl @@ -136,7 +126,7 @@ public function browse() { $permissions = CRM_Core_Permission::basicPermissions(); while ($dao->fetch()) { if (!array_key_exists($dao->entity_id, $acl)) { - $acl[$dao->entity_id] = array(); + $acl[$dao->entity_id] = []; $acl[$dao->entity_id]['name'] = $dao->name; $acl[$dao->entity_id]['entity_id'] = $dao->entity_id; $acl[$dao->entity_id]['entity_table'] = $dao->entity_table; @@ -156,7 +146,7 @@ public function browse() { $acl[$dao->entity_id]['action'] = CRM_Core_Action::formLink( self::links(), $action, - array('id' => $dao->entity_id), + ['id' => $dao->entity_id], ts('more'), FALSE, 'aclRole.manage.action', diff --git a/CRM/ACL/Page/EntityRole.php b/CRM/ACL/Page/EntityRole.php index a98a4cac02e8..d8e70c555b09 100644 --- a/CRM/ACL/Page/EntityRole.php +++ b/CRM/ACL/Page/EntityRole.php @@ -1,9 +1,9 @@ array( + self::$_links = [ + CRM_Core_Action::UPDATE => [ 'name' => ts('Edit'), 'url' => 'civicrm/acl/entityrole', 'qs' => 'action=update&id=%%id%%', 'title' => ts('Edit ACL Role Assignment'), - ), - CRM_Core_Action::DISABLE => array( + ], + CRM_Core_Action::DISABLE => [ 'name' => ts('Disable'), 'ref' => 'crm-enable-disable', 'title' => ts('Disable ACL Role Assignment'), - ), - CRM_Core_Action::ENABLE => array( + ], + CRM_Core_Action::ENABLE => [ 'name' => ts('Enable'), 'ref' => 'crm-enable-disable', 'title' => ts('Enable ACL Role Assignment'), - ), - CRM_Core_Action::DELETE => array( + ], + CRM_Core_Action::DELETE => [ 'name' => ts('Delete'), 'url' => 'civicrm/acl/entityrole', 'qs' => 'action=delete&id=%%id%%', 'title' => ts('Delete ACL Role Assignment'), - ), - ); + ], + ]; } return self::$_links; } @@ -95,46 +95,35 @@ public function &links() { * Finally it calls the parent's run method. */ public function run() { - // get the requested action - $action = CRM_Utils_Request::retrieve('action', 'String', - // default to 'browse' - $this, FALSE, 'browse' - ); - - // assign vars to templates - $this->assign('action', $action); - $id = CRM_Utils_Request::retrieve('id', 'Positive', - $this, FALSE, 0 - ); + $id = $this->getIdAndAction(); // set breadcrumb to append to admin/access - $breadCrumb = array( - array( + $breadCrumb = [ + [ 'title' => ts('Access Control'), - 'url' => CRM_Utils_System::url('civicrm/admin/access', - 'reset=1' - ), - ), - ); + 'url' => CRM_Utils_System::url('civicrm/admin/access', 'reset=1'), + ], + ]; CRM_Utils_System::appendBreadCrumb($breadCrumb); CRM_Utils_System::setTitle(ts('Assign Users to Roles')); // what action to take ? - if ($action & (CRM_Core_Action::UPDATE | CRM_Core_Action::ADD | CRM_Core_Action::DELETE)) { - $this->edit($action, $id); + if ($this->_action & (CRM_Core_Action::UPDATE | CRM_Core_Action::ADD | CRM_Core_Action::DELETE)) { + $this->edit($this->_action, $id); } // reset cache if enabled/disabled - if ($action & (CRM_Core_Action::DISABLE | CRM_Core_Action::ENABLE)) { + if ($this->_action & (CRM_Core_Action::DISABLE | CRM_Core_Action::ENABLE)) { CRM_ACL_BAO_Cache::resetCache(); } // finally browse the acl's - if ($action & CRM_Core_Action::BROWSE) { + if ($this->_action & CRM_Core_Action::BROWSE) { + $this->browse(); } - // parent run - return parent::run(); + // This replaces parent run, but do parent's parent run + return CRM_Core_Page::run(); } /** @@ -143,7 +132,7 @@ public function run() { public function browse() { // get all acl's sorted by weight - $entityRoles = array(); + $entityRoles = []; $dao = new CRM_ACL_DAO_EntityRole(); $dao->find(); @@ -151,7 +140,7 @@ public function browse() { $groups = CRM_Core_PseudoConstant::staticGroup(); while ($dao->fetch()) { - $entityRoles[$dao->id] = array(); + $entityRoles[$dao->id] = []; CRM_Core_DAO::storeValues($dao, $entityRoles[$dao->id]); $entityRoles[$dao->id]['acl_role'] = CRM_Utils_Array::value($dao->acl_role_id, $aclRoles); @@ -169,7 +158,7 @@ public function browse() { $entityRoles[$dao->id]['action'] = CRM_Core_Action::formLink( self::links(), $action, - array('id' => $dao->id), + ['id' => $dao->id], ts('more'), FALSE, 'entityRole.manage.action', diff --git a/CRM/Activity/ActionMapping.php b/CRM/Activity/ActionMapping.php index 67fd6f81486e..5251972ea9ab 100644 --- a/CRM/Activity/ActionMapping.php +++ b/CRM/Activity/ActionMapping.php @@ -1,9 +1,9 @@ register(CRM_Activity_ActionMapping::create(array( + $registrations->register(CRM_Activity_ActionMapping::create([ 'id' => CRM_Activity_ActionMapping::ACTIVITY_MAPPING_ID, 'entity' => 'civicrm_activity', 'entity_label' => ts('Activity'), @@ -65,7 +64,7 @@ public static function onRegisterActionMappings(\Civi\ActionSchedule\Event\Mappi 'entity_status' => 'activity_status', 'entity_status_label' => ts('Activity Status'), 'entity_date_start' => 'activity_date_time', - ))); + ])); } /** @@ -108,7 +107,7 @@ public function createQuery($schedule, $phase, $defaultParams) { $query['casDateField'] = 'e.activity_date_time'; if (!is_null($schedule->limit_to)) { - $activityContacts = \CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityContacts = \CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); if ($schedule->limit_to == 0 || !isset($activityContacts[$schedule->recipient])) { $recipientTypeId = \CRM_Utils_Array::key('Activity Targets', $activityContacts); } diff --git a/CRM/Activity/BAO/Activity.php b/CRM/Activity/BAO/Activity.php index 54a522b1ee64..3f1f37b6e318 100644 --- a/CRM/Activity/BAO/Activity.php +++ b/CRM/Activity/BAO/Activity.php @@ -1,9 +1,9 @@ copyValues($params); if ($activity->find(TRUE)) { - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts); $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts); $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts); @@ -92,7 +104,7 @@ public static function retrieve(&$params, &$defaults) { $assignee_contact_names = CRM_Activity_BAO_ActivityContact::getNames($activity->id, $assigneeID); $defaults['assignee_contact_value'] = implode('; ', $assignee_contact_names); $sourceContactId = self::getActivityContact($activity->id, $sourceID); - if ($activity->activity_type_id != CRM_Core_OptionGroup::getValue('activity_type', 'Bulk Email', 'name')) { + if ($activity->activity_type_id != CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Bulk Email')) { $defaults['target_contact'] = CRM_Activity_BAO_ActivityContact::retrieveContactIdsByActivityId($activity->id, $targetID); $target_contact_names = CRM_Activity_BAO_ActivityContact::getNames($activity->id, $targetID); $defaults['target_contact_value'] = implode('; ', $target_contact_names); @@ -195,9 +207,9 @@ public static function deleteActivity(&$params, $moveToTrash = FALSE) { // CRM-4525 log activity delete $logMsg = 'Case Activity deleted for'; - $msgs = array(); + $msgs = []; - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts); $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts); $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts); @@ -224,10 +236,10 @@ public static function deleteActivity(&$params, $moveToTrash = FALSE) { // delete the recently created Activity if ($result) { - $activityRecent = array( + $activityRecent = [ 'id' => $activity->id, 'type' => 'Activity', - ); + ]; CRM_Utils_Recent::del($activityRecent); } @@ -273,6 +285,10 @@ public static function deleteActivityContact($activityId, $recordTypeID = NULL) * @return CRM_Activity_BAO_Activity|null|object */ public static function create(&$params) { + // CRM-20958 - These fields are managed by MySQL triggers. Watch out for clients resaving stale timestamps. + unset($params['created_date']); + unset($params['modified_date']); + // check required params if (!self::dataExists($params)) { throw new CRM_Core_Exception('Not enough data to create activity object'); @@ -288,10 +304,10 @@ public static function create(&$params) { if (isset($params['activity_date_time']) && strcmp($params['activity_date_time'], CRM_Utils_Date::processDate(date('Ymd')) == -1) ) { - $params['status_id'] = 2; + $params['status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'status_id', 'Completed'); } else { - $params['status_id'] = 1; + $params['status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'status_id', 'Scheduled'); } } @@ -340,17 +356,16 @@ public static function create(&$params) { } $activityId = $activity->id; - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); - $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts); - $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts); - $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts); + $sourceID = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_ActivityContact', 'record_type_id', 'Activity Source'); + $assigneeID = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_ActivityContact', 'record_type_id', 'Activity Assignees'); + $targetID = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_ActivityContact', 'record_type_id', 'Activity Targets'); if (isset($params['source_contact_id'])) { - $acParams = array( + $acParams = [ 'activity_id' => $activityId, 'contact_id' => $params['source_contact_id'], 'record_type_id' => $sourceID, - ); + ]; self::deleteActivityContact($activityId, $sourceID); CRM_Activity_BAO_ActivityContact::create($acParams); } @@ -362,7 +377,7 @@ public static function create(&$params) { $resultAssignment = NULL; if (!empty($params['assignee_contact_id'])) { - $assignmentParams = array('activity_id' => $activityId); + $assignmentParams = ['activity_id' => $activityId]; if (is_array($params['assignee_contact_id'])) { if (CRM_Utils_Array::value('deleteActivityAssignment', $params, TRUE)) { @@ -370,18 +385,16 @@ public static function create(&$params) { self::deleteActivityContact($activityId, $assigneeID); } - $values = array(); foreach ($params['assignee_contact_id'] as $acID) { if ($acID) { - $values[] = "( $activityId, $acID, $assigneeID )"; + $assigneeParams = [ + 'activity_id' => $activityId, + 'contact_id' => $acID, + 'record_type_id' => $assigneeID, + ]; + CRM_Activity_BAO_ActivityContact::create($assigneeParams); } } - while (!empty($values)) { - $input = array_splice($values, 0, CRM_Core_DAO::BULK_INSERT_COUNT); - $str = implode(',', $input); - $sql = "INSERT IGNORE INTO civicrm_activity_contact ( activity_id, contact_id, record_type_id ) VALUES $str;"; - CRM_Core_DAO::executeQuery($sql); - } } else { $assignmentParams['contact_id'] = $params['assignee_contact_id']; @@ -417,27 +430,24 @@ public static function create(&$params) { $resultTarget = NULL; if (!empty($params['target_contact_id'])) { - $targetParams = array('activity_id' => $activityId); - $resultTarget = array(); + $targetParams = ['activity_id' => $activityId]; + $resultTarget = []; if (is_array($params['target_contact_id'])) { if (CRM_Utils_Array::value('deleteActivityTarget', $params, TRUE)) { // first delete existing targets if any self::deleteActivityContact($activityId, $targetID); } - $values = array(); foreach ($params['target_contact_id'] as $tid) { if ($tid) { - $values[] = "( $activityId, $tid, $targetID )"; + $targetContactParams = [ + 'activity_id' => $activityId, + 'contact_id' => $tid, + 'record_type_id' => $targetID, + ]; + CRM_Activity_BAO_ActivityContact::create($targetContactParams); } } - - while (!empty($values)) { - $input = array_splice($values, 0, CRM_Core_DAO::BULK_INSERT_COUNT); - $str = implode(',', $input); - $sql = "INSERT IGNORE INTO civicrm_activity_contact ( activity_id, contact_id, record_type_id ) VALUES $str;"; - CRM_Core_DAO::executeQuery($sql); - } } else { $targetParams['contact_id'] = $params['target_contact_id']; @@ -473,7 +483,7 @@ public static function create(&$params) { $logMsg = "Activity created for "; } - $msgs = array(); + $msgs = []; if (isset($params['source_contact_id'])) { $msgs[] = "source={$params['source_contact_id']}"; } @@ -519,7 +529,7 @@ public static function create(&$params) { $transaction->commit(); if (empty($params['skipRecentView'])) { - $recentOther = array(); + $recentOther = []; if (!empty($params['case_id'])) { $caseContactID = CRM_Core_DAO::getFieldValue('CRM_Case_DAO_CaseContact', $params['case_id'], 'contact_id', 'case_id'); $url = CRM_Utils_System::url('civicrm/case/activity/view', @@ -528,9 +538,9 @@ public static function create(&$params) { } else { $q = "action=view&reset=1&id={$activity->id}&atype={$activity->activity_type_id}&cid=" . CRM_Utils_Array::value('source_contact_id', $params) . "&context=home"; - if ($activity->activity_type_id != CRM_Core_OptionGroup::getValue('activity_type', 'Email', 'name')) { + if ($activity->activity_type_id != CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Email')) { $url = CRM_Utils_System::url('civicrm/activity', $q); - if ($activity->activity_type_id == CRM_Core_OptionGroup::getValue('activity_type', 'Print PDF Letter', 'name')) { + if ($activity->activity_type_id == CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Print PDF Letter')) { $recentOther['editUrl'] = CRM_Utils_System::url('civicrm/activity/pdf/add', "action=update&reset=1&id={$activity->id}&atype={$activity->activity_type_id}&cid={$params['source_contact_id']}&context=home" ); @@ -560,7 +570,7 @@ public static function create(&$params) { if (!isset($activity->parent_id)) { $recentContactDisplay = CRM_Contact_BAO_Contact::displayName($recentContactId); // add the recently created Activity - $activityTypes = CRM_Core_PseudoConstant::activityType(TRUE, TRUE); + $activityTypes = CRM_Activity_BAO_Activity::buildOptions('activity_type_id'); $activitySubject = CRM_Core_DAO::getFieldValue('CRM_Activity_DAO_Activity', $activity->id, 'subject'); $title = ""; @@ -586,30 +596,35 @@ public static function create(&$params) { CRM_Contact_BAO_GroupContactCache::opportunisticCacheFlush(); - if (!empty($params['id'])) { - CRM_Utils_Hook::post('edit', 'Activity', $activity->id, $activity); - } - else { - CRM_Utils_Hook::post('create', 'Activity', $activity->id, $activity); - } - // if the subject contains a ‘[case #…]’ string, file that activity on the related case (CRM-5916) - $matches = array(); - if (preg_match('/\[case #([0-9a-h]{7})\]/', CRM_Utils_Array::value('subject', $params), $matches)) { + $matches = []; + $subjectToMatch = CRM_Utils_Array::value('subject', $params); + if (preg_match('/\[case #([0-9a-h]{7})\]/', $subjectToMatch, $matches)) { $key = CRM_Core_DAO::escapeString(CIVICRM_SITE_KEY); $hash = $matches[1]; - $query = "SELECT id FROM civicrm_case WHERE SUBSTR(SHA1(CONCAT('$key', id)), 1, 7) = '$hash'"; - $caseParams = array( + $query = "SELECT id FROM civicrm_case WHERE SUBSTR(SHA1(CONCAT('$key', id)), 1, 7) = '" . CRM_Core_DAO::escapeString($hash) . "'"; + } + elseif (preg_match('/\[case #(\d+)\]/', $subjectToMatch, $matches)) { + $query = "SELECT id FROM civicrm_case WHERE id = '" . CRM_Core_DAO::escapeString($matches[1]) . "'"; + } + if (!empty($matches)) { + $caseParams = [ 'activity_id' => $activity->id, 'case_id' => CRM_Core_DAO::singleValueQuery($query), - ); + ]; if ($caseParams['case_id']) { CRM_Case_BAO_Case::processCaseActivity($caseParams); } else { - self::logActivityAction($activity, "unknown case hash encountered: $hash"); + self::logActivityAction($activity, "Case details for {$matches[1]} not found while recording an activity on case."); } } + if (!empty($params['id'])) { + CRM_Utils_Hook::post('edit', 'Activity', $activity->id, $activity); + } + else { + CRM_Utils_Hook::post('create', 'Activity', $activity->id, $activity); + } return $result; } @@ -625,20 +640,19 @@ public static function create(&$params) { * @return bool */ public static function logActivityAction($activity, $logMessage = NULL) { - $session = CRM_Core_Session::singleton(); - $id = $session->get('userID'); + $id = CRM_Core_Session::getLoggedInContactID(); if (!$id) { - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts); $id = self::getActivityContact($activity->id, $sourceID); } - $logParams = array( + $logParams = [ 'entity_table' => 'civicrm_activity', 'entity_id' => $activity->id, 'modified_id' => $id, 'modified_date' => date('YmdHis'), 'data' => $logMessage, - ); + ]; CRM_Core_BAO_Log::add($logParams); return TRUE; } @@ -646,7 +660,7 @@ public static function logActivityAction($activity, $logMessage = NULL) { /** * Get the list Activities. * - * @param array $input + * @param array $params * Array of parameters. * Keys include * - contact_id int contact_id whose activities we want to retrieve @@ -660,261 +674,278 @@ public static function logActivityAction($activity, $logMessage = NULL) { * * @return array * Relevant data object values of open activities + * @throws \CiviCRM_API3_Exception */ - public static function &getActivities($input) { - // Step 1: Get the basic activity data. - $bulkActivityTypeID = CRM_Core_OptionGroup::getValue( - 'activity_type', - 'Bulk Email', - 'name' - ); - - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); - $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts); - $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts); - $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts); + public static function getActivities($params) { + $activities = []; - $config = CRM_Core_Config::singleton(); + // Activity.Get API params + $activityParams = self::getActivityParamsForDashboardFunctions($params); - $randomNum = md5(uniqid()); - $activityTempTable = "civicrm_temp_activity_details_{$randomNum}"; - - $tableFields = array( - 'activity_id' => 'int unsigned', - 'activity_date_time' => 'datetime', - 'source_record_id' => 'int unsigned', - 'status_id' => 'int unsigned', - 'subject' => 'varchar(255)', - 'source_contact_name' => 'varchar(255)', - 'activity_type_id' => 'int unsigned', - 'activity_type' => 'varchar(128)', - 'case_id' => 'int unsigned', - 'case_subject' => 'varchar(255)', - 'campaign_id' => 'int unsigned', - ); + if (!empty($params['rowCount']) && + $params['rowCount'] > 0 + ) { + $activityParams['options']['limit'] = $params['rowCount']; + } - $sql = "CREATE TEMPORARY TABLE {$activityTempTable} ( "; - $insertValueSQL = array(); - // The activityTempTable contains the sorted rows - // so in order to maintain the sort order as-is we add an auto_increment - // field; we can sort by this later to ensure the sort order stays correct. - $sql .= " fixed_sort_order INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,"; - foreach ($tableFields as $name => $desc) { - $sql .= "$name $desc,\n"; - $insertValueSQL[] = $name; - } - - // add unique key on activity_id just to be sure - // this cannot be primary key because we need that for the auto_increment - // fixed_sort_order field - $sql .= " - UNIQUE KEY ( activity_id ) - ) ENGINE=HEAP DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci - "; - - CRM_Core_DAO::executeQuery($sql); - - $insertSQL = "INSERT INTO {$activityTempTable} (" . implode(',', $insertValueSQL) . " ) "; - - $order = $limit = $groupBy = ''; - $groupBy = " GROUP BY tbl.activity_id, tbl.activity_type, tbl.case_id, tbl.case_subject "; - - if (!empty($input['sort'])) { - if (is_a($input['sort'], 'CRM_Utils_Sort')) { - $orderBy = $input['sort']->orderBy(); - if (!empty($orderBy)) { - $order = " ORDER BY $orderBy"; - } + if (!empty($params['sort'])) { + if (is_a($params['sort'], 'CRM_Utils_Sort')) { + $order = $params['sort']->orderBy(); } - elseif (trim($input['sort'])) { - $sort = CRM_Utils_Type::escape($input['sort'], 'String'); - $order = " ORDER BY $sort "; + elseif (trim($params['sort'])) { + $order = CRM_Utils_Type::escape($params['sort'], 'String'); } } - if (empty($order)) { - // context = 'activity' in Activities tab. - $order = (CRM_Utils_Array::value('context', $input) == 'activity') ? " ORDER BY tbl.activity_date_time desc " : " ORDER BY tbl.status_id asc, tbl.activity_date_time asc "; - } + $activityParams['options']['sort'] = empty($order) ? "activity_date_time DESC" : str_replace('activity_type ', 'activity_type_id.label ', $order); - if (!empty($input['rowCount']) && - $input['rowCount'] > 0 - ) { - $limit = " LIMIT {$input['offset']}, {$input['rowCount']} "; - } - - $input['count'] = FALSE; - list($sqlClause, $params) = self::getActivitySQLClause($input); - - $query = "{$insertSQL} - SELECT DISTINCT tbl.* from ( {$sqlClause} ) -as tbl "; - - // Filter case activities - CRM-5761. - $components = self::activityComponents(); - if (!in_array('CiviCase', $components)) { - $query .= " -LEFT JOIN civicrm_case_activity ON ( civicrm_case_activity.activity_id = tbl.activity_id ) - WHERE civicrm_case_activity.id IS NULL"; + $activityParams['return'] = [ + 'activity_date_time', + 'source_record_id', + 'source_contact_id', + 'source_contact_name', + 'assignee_contact_id', + 'assignee_contact_name', + 'status_id', + 'subject', + 'activity_type_id', + 'activity_type', + 'case_id', + 'campaign_id', + ]; + foreach (['case_id' => 'CiviCase', 'campaign_id' => 'CiviCampaign'] as $attr => $component) { + if (in_array($component, self::activityComponents())) { + $activityParams['return'][] = $attr; + } } + $result = civicrm_api3('Activity', 'Get', $activityParams)['values']; - $query = $query . $groupBy . $order . $limit; - - $dao = CRM_Core_DAO::executeQuery($query, $params); - - // step 2: Get target and assignee contacts for above activities - // create temp table for target contacts - $activityContactTempTable = "civicrm_temp_activity_contact_{$randomNum}"; - $query = "CREATE TEMPORARY TABLE {$activityContactTempTable} ( - activity_id int unsigned, contact_id int unsigned, record_type_id varchar(16), - contact_name varchar(255), is_deleted int unsigned, counter int unsigned, INDEX index_activity_id( activity_id ) ) - ENGINE=MYISAM DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci"; - - CRM_Core_DAO::executeQuery($query); - - // note that we ignore bulk email for targets, since we don't show it in selector - $query = " -INSERT INTO {$activityContactTempTable} ( activity_id, contact_id, record_type_id, contact_name, is_deleted ) -SELECT ac.activity_id, - ac.contact_id, - ac.record_type_id, - c.sort_name, - c.is_deleted -FROM {$activityTempTable} -INNER JOIN civicrm_activity a ON ( a.id = {$activityTempTable}.activity_id ) -INNER JOIN civicrm_activity_contact ac ON ( ac.activity_id = {$activityTempTable}.activity_id ) -INNER JOIN civicrm_contact c ON c.id = ac.contact_id -WHERE ac.record_type_id != %1 -"; - $params = array(1 => array($targetID, 'Integer')); - CRM_Core_DAO::executeQuery($query, $params); - - $activityFields = array("ac.activity_id", "ac.contact_id", "ac.record_type_id", "c.sort_name", "c.is_deleted"); - $select = CRM_Contact_BAO_Query::appendAnyValueToSelect($activityFields, "ac.activity_id"); - - // for each activity insert one target contact - // if we load all target contacts the performance will suffer a lot for mass-activities. - $query = " -INSERT INTO {$activityContactTempTable} ( activity_id, contact_id, record_type_id, contact_name, is_deleted, counter ) -{$select}, count(ac.contact_id) -FROM {$activityTempTable} -INNER JOIN civicrm_activity a ON ( a.id = {$activityTempTable}.activity_id ) -INNER JOIN civicrm_activity_contact ac ON ( ac.activity_id = {$activityTempTable}.activity_id ) -INNER JOIN civicrm_contact c ON c.id = ac.contact_id -WHERE ac.record_type_id = %1 -GROUP BY ac.activity_id -"; - - CRM_Core_DAO::executeQuery($query, $params); - - // step 3: Combine all temp tables to get final query for activity selector - // sort by the original sort order, stored in fixed_sort_order - $query = " -SELECT {$activityTempTable}.*, - {$activityContactTempTable}.contact_id, - {$activityContactTempTable}.record_type_id, - {$activityContactTempTable}.contact_name, - {$activityContactTempTable}.is_deleted, - {$activityContactTempTable}.counter, - re.parent_id as is_recurring_activity -FROM {$activityTempTable} -INNER JOIN {$activityContactTempTable} on {$activityTempTable}.activity_id = {$activityContactTempTable}.activity_id -LEFT JOIN civicrm_recurring_entity re on {$activityContactTempTable}.activity_id = re.entity_id -ORDER BY fixed_sort_order - "; - - $dao = CRM_Core_DAO::executeQuery($query); + $bulkActivityTypeID = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Bulk Email'); + $allCampaigns = CRM_Campaign_BAO_Campaign::getCampaigns(NULL, NULL, FALSE, FALSE, FALSE, TRUE); // CRM-3553, need to check user has access to target groups. $mailingIDs = CRM_Mailing_BAO_Mailing::mailingACLIDs(); - $accessCiviMail = ( - (CRM_Core_Permission::check('access CiviMail')) || - (CRM_Mailing_Info::workflowEnabled() && - CRM_Core_Permission::check('create mailings')) + $accessCiviMail = ((CRM_Core_Permission::check('access CiviMail')) || + (CRM_Mailing_Info::workflowEnabled() && CRM_Core_Permission::check('create mailings')) ); - // Get all campaigns. - $allCampaigns = CRM_Campaign_BAO_Campaign::getCampaigns(NULL, NULL, FALSE, FALSE, FALSE, TRUE); - $values = array(); - while ($dao->fetch()) { - $activityID = $dao->activity_id; - $values[$activityID]['activity_id'] = $dao->activity_id; - $values[$activityID]['source_record_id'] = $dao->source_record_id; - $values[$activityID]['activity_type_id'] = $dao->activity_type_id; - $values[$activityID]['activity_type'] = $dao->activity_type; - $values[$activityID]['activity_date_time'] = $dao->activity_date_time; - $values[$activityID]['status_id'] = $dao->status_id; - $values[$activityID]['subject'] = $dao->subject; - $values[$activityID]['campaign_id'] = $dao->campaign_id; - $values[$activityID]['is_recurring_activity'] = $dao->is_recurring_activity; - - if ($dao->campaign_id) { - $values[$activityID]['campaign'] = $allCampaigns[$dao->campaign_id]; - } + // @todo - get rid of this & just handle in the array declaration like we do with 'subject' etc. + $mappingParams = [ + 'source_record_id' => 'source_record_id', + 'activity_type_id' => 'activity_type_id', + 'status_id' => 'status_id', + 'campaign_id' => 'campaign_id', + 'case_id' => 'case_id', + ]; - if (empty($values[$activityID]['assignee_contact_name'])) { - $values[$activityID]['assignee_contact_name'] = array(); + if (empty($result)) { + $targetCount = []; + } + else { + $targetCount = CRM_Core_DAO::executeQuery(' + SELECT activity_id, count(*) as target_contact_count + FROM civicrm_activity_contact + INNER JOIN civicrm_contact c ON contact_id = c.id AND c.is_deleted = 0 + WHERE activity_id IN (' . implode(',', array_keys($result)) . ') + AND record_type_id = %1 + GROUP BY activity_id', [ + 1 => [ + CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_ActivityContact', 'record_type_id', 'Activity Targets'), + 'Integer' + ] + ])->fetchAll(); + } + foreach ($targetCount as $activityTarget) { + $result[$activityTarget['activity_id']]['target_contact_count'] = $activityTarget['target_contact_count']; + } + // Iterate through & do basic mappings & determine which ones we want to retrieve target count for. + foreach ($result as $id => $activity) { + $activities[$id] = [ + 'activity_id' => $activity['id'], + 'activity_date_time' => CRM_Utils_Array::value('activity_date_time', $activity), + 'subject' => CRM_Utils_Array::value('subject', $activity), + 'assignee_contact_name' => CRM_Utils_Array::value('assignee_contact_sort_name', $activity, []), + 'source_contact_id' => CRM_Utils_Array::value('source_contact_id', $activity), + 'source_contact_name' => CRM_Utils_Array::value('source_contact_sort_name', $activity), + ]; + $activities[$id]['activity_type_name'] = CRM_Core_PseudoConstant::getName('CRM_Activity_BAO_Activity', 'activity_type_id', $activity['activity_type_id']); + $activities[$id]['activity_type'] = CRM_Core_PseudoConstant::getLabel('CRM_Activity_BAO_Activity', 'activity_type_id', $activity['activity_type_id']); + $activities[$id]['target_contact_count'] = CRM_Utils_Array::value('target_contact_count', $activity, 0); + if (!empty($activity['target_contact_count'])) { + $displayedTarget = civicrm_api3('ActivityContact', 'get', [ + 'activity_id' => $id, + 'check_permissions' => TRUE, + 'options' => ['limit' => 1], + 'record_type_id' => 'Activity Targets', + 'return' => ['contact_id.sort_name', 'contact_id'], + 'sequential' => 1, + ])['values']; + if (empty($displayedTarget[0])) { + $activities[$id]['target_contact_name'] = []; + } + else { + $activities[$id]['target_contact_name'] = [$displayedTarget[0]['contact_id'] => $displayedTarget[0]['contact_id.sort_name']]; + } } + if ($activities[$id]['activity_type_name'] === 'Bulk Email') { + $bulkActivities[] = $id; + // Get the total without permissions being passed but only display names after permissioning. + $activities[$id]['recipients'] = ts('(%1 recipients)', [1 => $activities[$id]['target_contact_count']]); + } + } + + // Eventually this second iteration should just handle the target contacts. It's a bit muddled at + // the moment as the bulk activity stuff needs unravelling & test coverage. + foreach ($result as $id => $activity) { + $isBulkActivity = (!$bulkActivityTypeID || ($bulkActivityTypeID === $activity['activity_type_id'])); + foreach ($mappingParams as $apiKey => $expectedName) { + if (in_array($apiKey, [ + 'target_contact_name', + ])) { + + if ($isBulkActivity) { + // @todo - how is this used? Couldn't we use 'is_bulk' or something clearer? + // or the calling function could handle + $activities[$id]['mailingId'] = FALSE; + if ($accessCiviMail && + ($mailingIDs === TRUE || in_array($activity['source_record_id'], $mailingIDs)) + ) { + $activities[$id]['mailingId'] = TRUE; + } + } + } + // case related fields + elseif ($apiKey == 'case_id' && !$isBulkActivity) { + $activities[$id][$expectedName] = CRM_Utils_Array::value($apiKey, $activity); - if (empty($values[$activityID]['target_contact_name'])) { - $values[$activityID]['target_contact_name'] = array(); - $values[$activityID]['target_contact_counter'] = $dao->counter; + // fetch case subject for case ID found + if (!empty($activity['case_id'])) { + $activities[$id]['case_subject'] = CRM_Core_DAO::executeQuery('CRM_Case_DAO_Case', $activity['case_id'], 'subject'); + } + } + else { + // @todo this generic assign could just be handled in array declaration earlier. + $activities[$id][$expectedName] = CRM_Utils_Array::value($apiKey, $activity); + if ($apiKey == 'campaign_id') { + $activities[$id]['campaign'] = CRM_Utils_Array::value($activities[$id][$expectedName], $allCampaigns); + } + } } - // if deleted, wrap in - if ($dao->is_deleted) { - $dao->contact_name = "{$dao->contact_name}"; + if (!empty($activity['source_contact_id']) && + CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $activity['source_contact_id'], 'is_deleted') + ) { + $activities[$id]['source_contact_name'] = sprintf("%s", $activity['source_contact_name']); } + $activities[$id]['is_recurring_activity'] = CRM_Core_BAO_RecurringEntity::getParentFor($id, 'civicrm_activity'); + } + + return $activities; + } + + /** + * Filter the activity types to only return the ones we actually asked for + * Uses params['activity_type_id'] and params['activity_type_exclude_id'] + * + * @param $params + * @return array|null (Use in Activity.get API activity_type_id) + */ + public static function filterActivityTypes($params) { + $activityTypes = []; + + // If no activity types are specified, get all the active ones + if (empty($params['activity_type_id'])) { + $activityTypes = CRM_Activity_BAO_Activity::buildOptions('activity_type_id', 'get'); + } - if ($dao->record_type_id == $sourceID && $dao->contact_id) { - $values[$activityID]['source_contact_id'] = $dao->contact_id; - $values[$activityID]['source_contact_name'] = $dao->contact_name; + // If no activity types are specified or excluded, return the list of all active ones + if (empty($params['activity_type_id']) && empty($params['activity_type_exclude_id'])) { + if (!empty($activityTypes)) { + return ['IN' => array_keys($activityTypes)]; } + return NULL; + } - if (!$bulkActivityTypeID || ($bulkActivityTypeID != $dao->activity_type_id)) { - // build array of target / assignee names - if ($dao->record_type_id == $targetID && $dao->contact_id) { - $values[$activityID]['target_contact_name'][$dao->contact_id] = $dao->contact_name; - } - if ($dao->record_type_id == $assigneeID && $dao->contact_id) { - $values[$activityID]['assignee_contact_name'][$dao->contact_id] = $dao->contact_name; - } + // If we have specified activity types, build a list to return, excluding the ones we don't want. + if (!empty($params['activity_type_id'])) { + if (!is_array($params['activity_type_id'])) { + // Turn it into array if only one specified, so we don't duplicate processing below + $params['activity_type_id'] = [$params['activity_type_id'] => $params['activity_type_id']]; + } + foreach ($params['activity_type_id'] as $value) { + // Add each activity type that was specified to list + $value = CRM_Utils_Type::escape($value, 'Positive'); + $activityTypes[$value] = $value; + } + } - // case related fields - $values[$activityID]['case_id'] = $dao->case_id; - $values[$activityID]['case_subject'] = $dao->case_subject; + // Build the list of activity types to exclude (from $params['activity_type_exclude_id']) + if (!empty($params['activity_type_exclude_id'])) { + if (!is_array($params['activity_type_exclude_id'])) { + // Turn it into array if only one specified, so we don't duplicate processing below + $params['activity_type_exclude_id'] = [$params['activity_type_exclude_id'] => $params['activity_type_exclude_id']]; } - else { - $values[$activityID]['recipients'] = ts('(%1 recipients)', array(1 => $dao->counter)); - $values[$activityID]['mailingId'] = FALSE; - if ( - $accessCiviMail && - ($mailingIDs === TRUE || in_array($dao->source_record_id, $mailingIDs)) - ) { - $values[$activityID]['mailingId'] = TRUE; + foreach ($params['activity_type_exclude_id'] as $value) { + // Remove each activity type from list if it should be excluded + $value = CRM_Utils_Type::escape($value, 'Positive'); + if (array_key_exists($value, $activityTypes)) { + unset($activityTypes[$value]); } } } - return $values; + return ['IN' => array_keys($activityTypes)]; + } + + /** + * @inheritDoc + */ + public function addSelectWhereClause() { + $clauses = []; + $permittedActivityTypeIDs = self::getPermittedActivityTypes(); + if (empty($permittedActivityTypeIDs)) { + // This just prevents a mysql fail if they have no access - should be extremely edge case. + $permittedActivityTypeIDs = [0]; + } + $clauses['activity_type_id'] = ('IN (' . implode(', ', $permittedActivityTypeIDs) . ')'); + + $contactClause = CRM_Utils_SQL::mergeSubquery('Contact'); + if ($contactClause) { + $contactClause = implode(' AND contact_id ', $contactClause); + $clauses['id'][] = "IN (SELECT activity_id FROM civicrm_activity_contact WHERE contact_id $contactClause)"; + } + CRM_Utils_Hook::selectWhereClause($this, $clauses); + return $clauses; } /** - * Get the component id and name if those are enabled and allowed. + * Get an array of components that are accessible by the currenct user. * - * Checks whether logged in user has permission. - * To decide whether we are going to include - * component related activities with core activity retrieve process. - * (what did that just mean?) + * This means checking if they are enabled and if the user has appropriate permission. + * + * For most components the permission is access component (e.g 'access CiviContribute'). + * Exceptions as CiviCampaign (administer CiviCampaign) and CiviCase + * (accesses a case function which enforces edit all cases or edit my cases. Case + * permissions are also handled on a per activity basis). + * + * Checks whether logged in user has permission to the component. + * + * @param bool $excludeComponentHandledActivities + * Should we exclude components whose display is handled in the components. + * In practice this means should we include CiviCase in the results. Presumbaly + * at the time it was decided case activities should be shown in the case framework and + * that this concept might be extended later. In practice most places that + * call this then re-add CiviCase in some way so it's all a bit... odd. * * @return array * Array of component id and name. */ - public static function activityComponents() { - $components = array(); + public static function activityComponents($excludeComponentHandledActivities = TRUE) { + $components = []; $compInfo = CRM_Core_Component::getEnabledComponents(); foreach ($compInfo as $compObj) { - if (!empty($compObj->info['showActivitiesInCore'])) { + $includeComponent = !$excludeComponentHandledActivities || !empty($compObj->info['showActivitiesInCore']); + if ($includeComponent) { if ($compObj->info['name'] == 'CiviCampaign') { $componentPermission = "administer {$compObj->name}"; } @@ -950,202 +981,9 @@ public static function activityComponents() { * @return int * count of activities */ - public static function &getActivitiesCount($input) { - $input['count'] = TRUE; - list($sqlClause, $params) = self::getActivitySQLClause($input); - - //filter case activities - CRM-5761 - $components = self::activityComponents(); - if (!in_array('CiviCase', $components)) { - $query = " - SELECT COUNT(DISTINCT(tbl.activity_id)) as count - FROM ( {$sqlClause} ) as tbl -LEFT JOIN civicrm_case_activity ON ( civicrm_case_activity.activity_id = tbl.activity_id ) - WHERE civicrm_case_activity.id IS NULL"; - } - else { - $query = "SELECT COUNT(DISTINCT(activity_id)) as count from ( {$sqlClause} ) as tbl"; - } - - return CRM_Core_DAO::singleValueQuery($query, $params); - } - - /** - * Get the activity sql clause to pick activities. - * - * @param array $input - * Array of parameters. - * Keys include - * - contact_id int contact_id whose activities we want to retrieve - * - admin boolean if contact is admin - * - caseId int case ID - * - context string page on which selector is build - * - count boolean are we interested in the count clause only? - * - activity_type_id int|string the activity types we want to restrict by - * - * @return int - * count of activities - */ - public static function getActivitySQLClause($input) { - $params = array(); - $sourceWhere = $targetWhere = $assigneeWhere = $caseWhere = 1; - - $config = CRM_Core_Config::singleton(); - if (!CRM_Utils_Array::value('admin', $input, FALSE)) { - $sourceWhere = ' ac.contact_id = %1 '; - $caseWhere = ' civicrm_case_contact.contact_id = %1 '; - - $params = array(1 => array($input['contact_id'], 'Integer')); - } - - $commonClauses = array( - "civicrm_option_group.name = 'activity_type'", - "civicrm_activity.is_deleted = 0", - "civicrm_activity.is_current_revision = 1", - "civicrm_activity.is_test= 0", - ); - - if ($input['context'] != 'activity') { - $commonClauses[] = "civicrm_activity.status_id = 1"; - } - - // Filter on component IDs. - $components = self::activityComponents(); - if (!empty($components)) { - $componentsIn = implode(',', array_keys($components)); - $commonClauses[] = "( civicrm_option_value.component_id IS NULL OR civicrm_option_value.component_id IN ( $componentsIn ) )"; - } - else { - $commonClauses[] = "civicrm_option_value.component_id IS NULL"; - } - - // activity type ID clause - if (!empty($input['activity_type_id'])) { - if (is_array($input['activity_type_id'])) { - foreach ($input['activity_type_id'] as $idx => $value) { - $input['activity_type_id'][$idx] = CRM_Utils_Type::escape($value, 'Positive'); - } - $commonClauses[] = "civicrm_activity.activity_type_id IN ( " . implode(",", $input['activity_type_id']) . " ) "; - } - else { - $activityTypeID = CRM_Utils_Type::escape($input['activity_type_id'], 'Positive'); - $commonClauses[] = "civicrm_activity.activity_type_id = $activityTypeID"; - } - } - - // exclude by activity type clause - if (!empty($input['activity_type_exclude_id'])) { - if (is_array($input['activity_type_exclude_id'])) { - foreach ($input['activity_type_exclude_id'] as $idx => $value) { - $input['activity_type_exclude_id'][$idx] = CRM_Utils_Type::escape($value, 'Positive'); - } - $commonClauses[] = "civicrm_activity.activity_type_id NOT IN ( " . implode(",", $input['activity_type_exclude_id']) . " ) "; - } - else { - $activityTypeID = CRM_Utils_Type::escape($input['activity_type_exclude_id'], 'Positive'); - $commonClauses[] = "civicrm_activity.activity_type_id != $activityTypeID"; - } - } - - $commonClause = implode(' AND ', $commonClauses); - - $includeCaseActivities = FALSE; - if (in_array('CiviCase', $components)) { - $includeCaseActivities = TRUE; - } - - // build main activity table select clause - $sourceSelect = ''; - - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); - $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts); - $sourceJoin = " -INNER JOIN civicrm_activity_contact ac ON ac.activity_id = civicrm_activity.id -INNER JOIN civicrm_contact contact ON ac.contact_id = contact.id -"; - - if (!$input['count']) { - $sourceSelect = ', - civicrm_activity.activity_date_time, - civicrm_activity.source_record_id, - civicrm_activity.status_id, - civicrm_activity.subject, - contact.sort_name as source_contact_name, - civicrm_option_value.value as activity_type_id, - civicrm_option_value.label as activity_type, - null as case_id, null as case_subject, - civicrm_activity.campaign_id as campaign_id - '; - - $sourceJoin .= " -LEFT JOIN civicrm_activity_contact src ON (src.activity_id = ac.activity_id AND src.record_type_id = {$sourceID} AND src.contact_id = contact.id) -"; - } - - $sourceClause = " - SELECT civicrm_activity.id as activity_id - {$sourceSelect} - from civicrm_activity - left join civicrm_option_value on - civicrm_activity.activity_type_id = civicrm_option_value.value - left join civicrm_option_group on - civicrm_option_group.id = civicrm_option_value.option_group_id - {$sourceJoin} - where - {$sourceWhere} - AND $commonClause - "; - - // Build case clause - // or else exclude Inbound Emails that have been filed on a case. - $caseClause = ''; - - if ($includeCaseActivities) { - $caseSelect = ''; - if (!$input['count']) { - $caseSelect = ', - civicrm_activity.activity_date_time, - civicrm_activity.source_record_id, - civicrm_activity.status_id, - civicrm_activity.subject, - contact.sort_name as source_contact_name, - civicrm_option_value.value as activity_type_id, - civicrm_option_value.label as activity_type, - null as case_id, null as case_subject, - civicrm_activity.campaign_id as campaign_id'; - } - - $caseClause = " - union all - - SELECT civicrm_activity.id as activity_id - {$caseSelect} - from civicrm_activity - inner join civicrm_case_activity on - civicrm_case_activity.activity_id = civicrm_activity.id - inner join civicrm_case on - civicrm_case_activity.case_id = civicrm_case.id - inner join civicrm_case_contact on - civicrm_case_contact.case_id = civicrm_case.id and {$caseWhere} - left join civicrm_option_value on - civicrm_activity.activity_type_id = civicrm_option_value.value - left join civicrm_option_group on - civicrm_option_group.id = civicrm_option_value.option_group_id - {$sourceJoin} - where - {$caseWhere} - AND $commonClause - and ( ( civicrm_case_activity.case_id IS NULL ) OR - ( civicrm_option_value.name <> 'Inbound Email' AND - civicrm_option_value.name <> 'Email' AND civicrm_case_activity.case_id - IS NOT NULL ) - ) - "; - } - - $returnClause = " {$sourceClause} {$caseClause} "; - - return array($returnClause, $params); + public static function getActivitiesCount($input) { + $activityParams = self::getActivityParamsForDashboardFunctions($input); + return civicrm_api3('Activity', 'getcount', $activityParams); } /** @@ -1174,6 +1012,8 @@ public static function getActivitySQLClause($input) { * Contact ids. * @param string $additionalDetails * The additional information of CC and BCC appended to the activity Details. + * @param array $contributionIds + * @param int $campaignId * * @return array * ( sent, activityId) if any email is sent and activityId @@ -1190,17 +1030,18 @@ public static function sendEmail( $cc = NULL, $bcc = NULL, $contactIds = NULL, - $additionalDetails = NULL + $additionalDetails = NULL, + $contributionIds = NULL, + $campaignId = NULL ) { // get the contact details of logged in contact, which we set as from email if ($userID == NULL) { - $session = CRM_Core_Session::singleton(); - $userID = $session->get('userID'); + $userID = CRM_Core_Session::getLoggedInContactID(); } list($fromDisplayName, $fromEmail, $fromDoNotEmail) = CRM_Contact_BAO_Contact::getContactDetails($userID); if (!$fromEmail) { - return array(count($contactDetails), 0, count($contactDetails)); + return [count($contactDetails), 0, count($contactDetails)]; } if (!trim($fromDisplayName)) { $fromDisplayName = $fromEmail; @@ -1219,10 +1060,7 @@ public static function sendEmail( } //create the meta level record first ( email activity ) - $activityTypeID = CRM_Core_OptionGroup::getValue('activity_type', - 'Email', - 'name' - ); + $activityTypeID = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Email'); // CRM-6265: save both text and HTML parts in details (if present) if ($html and $text) { @@ -1233,15 +1071,16 @@ public static function sendEmail( $details .= $additionalDetails; } - $activityParams = array( + $activityParams = [ 'source_contact_id' => $userID, 'activity_type_id' => $activityTypeID, 'activity_date_time' => date('YmdHis'), 'subject' => $subject, 'details' => $details, // FIXME: check for name Completed and get ID from that lookup - 'status_id' => 2, - ); + 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'status_id', 'Completed'), + 'campaign_id' => $campaignId, + ]; // CRM-5916: strip [case #…] before saving the activity (if present in subject) $activityParams['subject'] = preg_replace('/\[case #([0-9a-h]{7})\] /', '', $activityParams['subject']); @@ -1260,7 +1099,7 @@ public static function sendEmail( $attachments = CRM_Core_BAO_File::getEntityFile('civicrm_activity', $activity->id ); - $returnProperties = array(); + $returnProperties = []; if (isset($messageToken['contact'])) { foreach ($messageToken['contact'] as $key => $value) { $returnProperties[$value] = 1; @@ -1276,7 +1115,7 @@ public static function sendEmail( } // get token details for contacts, call only if tokens are used - $details = array(); + $details = []; if (!empty($returnProperties) || !empty($tokens) || !empty($allTokens)) { list($details) = CRM_Utils_Token::getTokenDetails( $contactIds, @@ -1288,7 +1127,7 @@ public static function sendEmail( } // call token hook - $tokens = array(); + $tokens = []; CRM_Utils_Hook::tokens($tokens); $categories = array_keys($tokens); @@ -1298,11 +1137,30 @@ public static function sendEmail( $escapeSmarty = TRUE; } - $sent = $notSent = array(); + $contributionDetails = []; + if (!empty($contributionIds)) { + $contributionDetails = CRM_Contribute_BAO_Contribution::replaceContributionTokens( + $contributionIds, + $subject, + $subjectToken, + $text, + $html, + $messageToken, + $escapeSmarty + ); + } + + $sent = $notSent = []; foreach ($contactDetails as $values) { $contactId = $values['contact_id']; $emailAddress = $values['email']; + if (!empty($contributionDetails)) { + $subject = $contributionDetails[$contactId]['subject']; + $text = $contributionDetails[$contactId]['text']; + $html = $contributionDetails[$contactId]['html']; + } + if (!empty($details) && is_array($details["{$contactId}"])) { // unset email from details since it always returns primary email address unset($details["{$contactId}"]['email']); @@ -1358,79 +1216,89 @@ public static function sendEmail( } } - return array($sent, $activity->id); + return [$sent, $activity->id]; } /** - * Send SMS. + * Send SMS. Returns: bool $sent, int $activityId, int $success (number of sent SMS) * * @param array $contactDetails * @param array $activityParams - * @param array $smsParams - * @param $contactIds - * @param int $userID + * @param array $smsProviderParams + * @param array $contactIds + * @param int $sourceContactId This is the source contact Id * - * @return array + * @return array(bool $sent, int $activityId, int $success) * @throws CRM_Core_Exception */ public static function sendSMS( - &$contactDetails, + &$contactDetails = NULL, &$activityParams, - &$smsParams = array(), - &$contactIds, - $userID = NULL + &$smsProviderParams = [], + &$contactIds = NULL, + $sourceContactId = NULL ) { - if ($userID == NULL) { - $session = CRM_Core_Session::singleton(); - $userID = $session->get('userID'); + if (!CRM_Core_Permission::check('send SMS')) { + throw new CRM_Core_Exception("You do not have the 'send SMS' permission"); } - $text = &$activityParams['sms_text_message']; - - // CRM-4575 - // token replacement of addressee/email/postal greetings - // get the tokens added in subject and message - $messageToken = CRM_Utils_Token::getTokens($text); + if (!isset($contactDetails) && !isset($contactIds)) { + throw new CRM_Core_Exception('You must specify either $contactDetails or $contactIds'); + } + // Populate $contactDetails and $contactIds if only one is set + if (is_array($contactIds) && !empty($contactIds) && empty($contactDetails)) { + foreach ($contactIds as $id) { + try { + $contactDetails[] = civicrm_api3('Contact', 'getsingle', ['contact_id' => $id]); + } + catch (Exception $e) { + // Contact Id doesn't exist + } + } + } + elseif (is_array($contactDetails) && !empty($contactDetails) && empty($contactIds)) { + foreach ($contactDetails as $contact) { + $contactIds[] = $contact['contact_id']; + } + } - // Create the meta level record first ( sms activity ) - $activityTypeID = CRM_Core_OptionGroup::getValue('activity_type', - 'SMS', - 'name' - ); + // Get logged in User Id + if (empty($sourceContactId)) { + $sourceContactId = CRM_Core_Session::getLoggedInContactID(); + } - $details = $text; + $text = &$activityParams['sms_text_message']; - $activitySubject = $activityParams['activity_subject']; - $activityParams = array( - 'source_contact_id' => $userID, - 'activity_type_id' => $activityTypeID, + // Create the meta level record first ( sms activity ) + $activityParams = [ + 'source_contact_id' => $sourceContactId, + 'activity_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'SMS'), 'activity_date_time' => date('YmdHis'), - 'subject' => $activitySubject, - 'details' => $details, - // FIXME: check for name Completed and get ID from that lookup - 'status_id' => 2, - ); - + 'subject' => $activityParams['activity_subject'], + 'details' => $text, + 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'status_id', 'Completed'), + ]; $activity = self::create($activityParams); $activityID = $activity->id; - $returnProperties = array(); - + // Process Tokens + // token replacement of addressee/email/postal greetings + // get the tokens added in subject and message + $messageToken = CRM_Utils_Token::getTokens($text); + $returnProperties = []; if (isset($messageToken['contact'])) { foreach ($messageToken['contact'] as $key => $value) { $returnProperties[$value] = 1; } } - - // call token hook - $tokens = array(); + // Call tokens hook + $tokens = []; CRM_Utils_Hook::tokens($tokens); $categories = array_keys($tokens); - // get token details for contacts, call only if tokens are used - $details = array(); + $tokenDetails = []; if (!empty($returnProperties) || !empty($tokens)) { - list($details) = CRM_Utils_Token::getTokenDetails($contactIds, + list($tokenDetails) = CRM_Utils_Token::getTokenDetails($contactIds, $returnProperties, NULL, NULL, FALSE, $messageToken, @@ -1439,44 +1307,49 @@ public static function sendSMS( } $success = 0; - $escapeSmarty = FALSE; - $errMsgs = array(); - foreach ($contactDetails as $values) { - $contactId = $values['contact_id']; + $errMsgs = []; + foreach ($contactDetails as $contact) { + $contactId = $contact['contact_id']; - if (!empty($details) && is_array($details["{$contactId}"])) { + // Replace tokens + if (!empty($tokenDetails) && is_array($tokenDetails["{$contactId}"])) { // unset phone from details since it always returns primary number - unset($details["{$contactId}"]['phone']); - unset($details["{$contactId}"]['phone_type_id']); - $values = array_merge($values, $details["{$contactId}"]); + unset($tokenDetails["{$contactId}"]['phone']); + unset($tokenDetails["{$contactId}"]['phone_type_id']); + $contact = array_merge($contact, $tokenDetails["{$contactId}"]); } - - $tokenText = CRM_Utils_Token::replaceContactTokens($text, $values, FALSE, $messageToken, FALSE, $escapeSmarty); - $tokenText = CRM_Utils_Token::replaceHookTokens($tokenText, $values, $categories, FALSE, $escapeSmarty); + $tokenText = CRM_Utils_Token::replaceContactTokens($text, $contact, FALSE, $messageToken, FALSE, FALSE); + $tokenText = CRM_Utils_Token::replaceHookTokens($tokenText, $contact, $categories, FALSE, FALSE); // Only send if the phone is of type mobile - $phoneTypes = CRM_Core_OptionGroup::values('phone_type', TRUE, FALSE, FALSE, NULL, 'name'); - if ($values['phone_type_id'] == CRM_Utils_Array::value('Mobile', $phoneTypes)) { - $smsParams['To'] = $values['phone']; + if ($contact['phone_type_id'] == CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_Phone', 'phone_type_id', 'Mobile')) { + $smsProviderParams['To'] = $contact['phone']; } else { - $smsParams['To'] = ''; + $smsProviderParams['To'] = ''; } - $sendResult = self::sendSMSMessage( - $contactId, - $tokenText, - $smsParams, - $activityID, - $userID - ); + $doNotSms = CRM_Utils_Array::value('do_not_sms', $contact, 0); - if (PEAR::isError($sendResult)) { - // Collect all of the PEAR_Error objects - $errMsgs[] = $sendResult; + if ($doNotSms) { + $errMsgs[] = PEAR::raiseError('Contact Does not accept SMS', NULL, PEAR_ERROR_RETURN); } else { - $success++; + $sendResult = self::sendSMSMessage( + $contactId, + $tokenText, + $smsProviderParams, + $activityID, + $sourceContactId + ); + + if (PEAR::isError($sendResult)) { + // Collect all of the PEAR_Error objects + $errMsgs[] = $sendResult; + } + else { + $success++; + } } } @@ -1492,7 +1365,7 @@ public static function sendSMS( $sent = $errMsgs; } - return array($sent, $activity->id, $success); + return [$sent, $activity->id, $success]; } /** @@ -1501,11 +1374,11 @@ public static function sendSMS( * @param int $toID * The contact id of the recipient. * @param $tokenText - * @param array $smsParams + * @param array $smsProviderParams * The params used for sending sms. * @param int $activityID * The activity ID that tracks the message. - * @param int $userID + * @param int $sourceContactID * * @return bool|PEAR_Error * true on success or PEAR_Error object @@ -1513,31 +1386,30 @@ public static function sendSMS( public static function sendSMSMessage( $toID, &$tokenText, - $smsParams = array(), + $smsProviderParams = [], $activityID, - $userID = NULL + $sourceContactID = NULL ) { - $toDoNotSms = ""; - $toPhoneNumber = ""; - - if ($smsParams['To']) { - $toPhoneNumber = trim($smsParams['To']); + $toPhoneNumber = NULL; + if ($smsProviderParams['To']) { + // If phone number is specified use it + $toPhoneNumber = trim($smsProviderParams['To']); } elseif ($toID) { - $filters = array('is_deceased' => 0, 'is_deleted' => 0, 'do_not_sms' => 0); + // No phone number specified, so find a suitable one for the contact + $filters = ['is_deceased' => 0, 'is_deleted' => 0, 'do_not_sms' => 0]; $toPhoneNumbers = CRM_Core_BAO_Phone::allPhones($toID, FALSE, 'Mobile', $filters); - // To get primary mobile phonenumber,if not get the first mobile phonenumber + // To get primary mobile phonenumber, if not get the first mobile phonenumber if (!empty($toPhoneNumbers)) { - $toPhoneNumerDetails = reset($toPhoneNumbers); - $toPhoneNumber = CRM_Utils_Array::value('phone', $toPhoneNumerDetails); + $toPhoneNumberDetails = reset($toPhoneNumbers); + $toPhoneNumber = CRM_Utils_Array::value('phone', $toPhoneNumberDetails); // Contact allows to send sms - $toDoNotSms = 0; } } // make sure both phone are valid // and that the recipient wants to receive sms - if (empty($toPhoneNumber) or $toDoNotSms) { + if (empty($toPhoneNumber)) { return PEAR::raiseError( 'Recipient phone number is invalid or recipient does not want to receive SMS', NULL, @@ -1545,25 +1417,23 @@ public static function sendSMSMessage( ); } - $recipient = $smsParams['To']; - $smsParams['contact_id'] = $toID; - $smsParams['parent_activity_id'] = $activityID; + $recipient = $toPhoneNumber; + $smsProviderParams['contact_id'] = $toID; + $smsProviderParams['parent_activity_id'] = $activityID; - $providerObj = CRM_SMS_Provider::singleton(array('provider_id' => $smsParams['provider_id'])); - $sendResult = $providerObj->send($recipient, $smsParams, $tokenText, NULL, $userID); + $providerObj = CRM_SMS_Provider::singleton(['provider_id' => $smsProviderParams['provider_id']]); + $sendResult = $providerObj->send($recipient, $smsProviderParams, $tokenText, NULL, $sourceContactID); if (PEAR::isError($sendResult)) { return $sendResult; } - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); - $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts); - - // add activity target record for every sms that is send - $activityTargetParams = array( + // add activity target record for every sms that is sent + $targetID = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_ActivityContact', 'record_type_id', 'Activity Targets'); + $activityTargetParams = [ 'activity_id' => $activityID, 'contact_id' => $toID, 'record_type_id' => $targetID, - ); + ]; CRM_Activity_BAO_ActivityContact::create($activityTargetParams); return TRUE; @@ -1619,11 +1489,11 @@ public static function sendMessage( $toDisplayName = $toEmail; } - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts); // create the params array - $mailParams = array( + $mailParams = [ 'groupName' => 'Activity Email Sender', 'from' => $from, 'toName' => $toDisplayName, @@ -1634,18 +1504,18 @@ public static function sendMessage( 'text' => $text_message, 'html' => $html_message, 'attachments' => $attachments, - ); + ]; if (!CRM_Utils_Mail::send($mailParams)) { return FALSE; } // add activity target record for every mail that is send - $activityTargetParams = array( + $activityTargetParams = [ 'activity_id' => $activityID, 'contact_id' => $toID, 'record_type_id' => $targetID, - ); + ]; CRM_Activity_BAO_ActivityContact::create($activityTargetParams); return TRUE; } @@ -1665,26 +1535,26 @@ public static function sendMessage( public static function &importableFields($status = FALSE) { if (!self::$_importableFields) { if (!self::$_importableFields) { - self::$_importableFields = array(); + self::$_importableFields = []; } if (!$status) { - $fields = array('' => array('title' => ts('- do not import -'))); + $fields = ['' => ['title' => ts('- do not import -')]]; } else { - $fields = array('' => array('title' => ts('- Activity Fields -'))); + $fields = ['' => ['title' => ts('- Activity Fields -')]]; } $tmpFields = CRM_Activity_DAO_Activity::import(); $contactFields = CRM_Contact_BAO_Contact::importableFields('Individual', NULL); // Using new Dedupe rule. - $ruleParams = array( + $ruleParams = [ 'contact_type' => 'Individual', 'used' => 'Unsupervised', - ); + ]; $fieldsArray = CRM_Dedupe_BAO_Rule::dedupeRuleFields($ruleParams); - $tmpConatctField = array(); + $tmpConatctField = []; if (is_array($fieldsArray)) { foreach ($fieldsArray as $value) { $customFieldId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomField', @@ -1708,6 +1578,8 @@ public static function &importableFields($status = FALSE) { } /** + * @deprecated - use the api instead. + * * Get the Activities of a target contact. * * @param int $contactId @@ -1717,8 +1589,9 @@ public static function &importableFields($status = FALSE) { * array of activity fields */ public static function getContactActivity($contactId) { - $activities = array(); - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + // @todo remove this function entirely. + $activities = []; + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts); $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts); $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts); @@ -1745,7 +1618,7 @@ public static function getContactActivity($contactId) { $activityIds = array_keys($activities); if (count($activityIds) < 1) { - return array(); + return []; } $activityIds = implode(',', $activityIds); @@ -1758,9 +1631,6 @@ public static function getContactActivity($contactId) { $dao = CRM_Core_DAO::executeQuery($query); - $activityTypes = CRM_Core_OptionGroup::values('activity_type'); - $activityStatuses = CRM_Core_OptionGroup::values('activity_status'); - while ($dao->fetch()) { $activities[$dao->activity_id]['id'] = $dao->activity_id; $activities[$dao->activity_id]['activity_type_id'] = $dao->activity_type_id; @@ -1769,8 +1639,8 @@ public static function getContactActivity($contactId) { $activities[$dao->activity_id]['activity_date_time'] = $dao->activity_date_time; $activities[$dao->activity_id]['details'] = $dao->details; $activities[$dao->activity_id]['status_id'] = $dao->status_id; - $activities[$dao->activity_id]['activity_name'] = $activityTypes[$dao->activity_type_id]; - $activities[$dao->activity_id]['status'] = $activityStatuses[$dao->status_id]; + $activities[$dao->activity_id]['activity_name'] = CRM_Core_PseudoConstant::getLabel('CRM_Activity_BAO_Activity', 'activity_type_id', $dao->activity_type_id); + $activities[$dao->activity_id]['status'] = CRM_Core_PseudoConstant::getLabel('CRM_Activity_BAO_Activity', 'activity_status_id', $dao->status_id); // set to null if not set if (!isset($activities[$dao->activity_id]['source_contact_id'])) { @@ -1788,57 +1658,22 @@ public static function getContactActivity($contactId) { * @param string $activityType * For Membership Signup or Renewal. * @param int $targetContactID + * @param array $params + * Activity params to override. * * @return bool|NULL */ public static function addActivity( &$activity, $activityType = 'Membership Signup', - $targetContactID = NULL + $targetContactID = NULL, + $params = [] ) { + $date = date('YmdHis'); if ($activity->__table == 'civicrm_membership') { - $membershipType = CRM_Member_PseudoConstant::membershipType($activity->membership_type_id); - - if (!$membershipType) { - $membershipType = ts('Membership'); - } - - $subject = "{$membershipType}"; - - if (!empty($activity->source) && $activity->source != 'null') { - $subject .= " - {$activity->source}"; - } - - if ($activity->owner_membership_id) { - $query = " -SELECT display_name - FROM civicrm_contact, civicrm_membership - WHERE civicrm_contact.id = civicrm_membership.contact_id - AND civicrm_membership.id = $activity->owner_membership_id -"; - $displayName = CRM_Core_DAO::singleValueQuery($query); - $subject .= " (by {$displayName})"; - } - - $subject .= " - Status: " . CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipStatus', $activity->status_id, 'label'); - // CRM-72097 changed from start date to today - $date = date('YmdHis'); $component = 'Membership'; } elseif ($activity->__table == 'civicrm_participant') { - $event = CRM_Event_BAO_Event::getEvents(1, $activity->event_id, TRUE, FALSE); - - $roles = CRM_Event_PseudoConstant::participantRole(); - $status = CRM_Event_PseudoConstant::participantStatus(); - - $subject = $event[$activity->event_id]; - if (!empty($roles[$activity->role_id])) { - $subject .= ' - ' . $roles[$activity->role_id]; - } - if (!empty($status[$activity->status_id])) { - $subject .= ' - ' . $status[$activity->status_id]; - } - $date = date('YmdHis'); if ($activityType != 'Email') { $activityType = 'Event Registration'; } @@ -1846,43 +1681,48 @@ public static function addActivity( } elseif ($activity->__table == 'civicrm_contribution') { // create activity record only for Completed Contributions - if ($activity->contribution_status_id != 1) { + $contributionCompletedStatusId = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'); + if ($activity->contribution_status_id != $contributionCompletedStatusId) { return NULL; } + $activityType = $component = 'Contribution'; - $subject = NULL; - - $subject .= CRM_Utils_Money::format($activity->total_amount, $activity->currency); - if (!empty($activity->source) && $activity->source != 'null') { - $subject .= " - {$activity->source}"; + // retrieve existing activity based on source_record_id and activity_type + if (empty($params['id'])) { + $params['id'] = CRM_Utils_Array::value('id', civicrm_api3('Activity', 'Get', [ + 'source_record_id' => $activity->id, + 'activity_type_id' => $activityType, + ])); + } + if (!empty($params['id'])) { + // CRM-13237 : if activity record found, update it with campaign id of contribution + $params['campaign_id'] = $activity->campaign_id; } + $date = CRM_Utils_Date::isoToMysql($activity->receive_date); - $activityType = $component = 'Contribution'; } - $activityParams = array( + + $activityParams = [ 'source_contact_id' => $activity->contact_id, 'source_record_id' => $activity->id, - 'activity_type_id' => CRM_Core_OptionGroup::getValue('activity_type', - $activityType, - 'name' - ), - 'subject' => $subject, + 'activity_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', $activityType), 'activity_date_time' => $date, 'is_test' => $activity->is_test, - 'status_id' => CRM_Core_OptionGroup::getValue('activity_status', - 'Completed', - 'name' - ), + 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', 'Completed'), 'skipRecentView' => TRUE, 'campaign_id' => $activity->campaign_id, - ); + ]; + $activityParams = array_merge($activityParams, $params); + + if (empty($activityParams['subject'])) { + $activityParams['subject'] = self::getActivitySubject($activity); + } if (!empty($activity->activity_id)) { $activityParams['id'] = $activity->activity_id; } // create activity with target contacts - $session = CRM_Core_Session::singleton(); - $id = $session->get('userID'); + $id = CRM_Core_Session::getLoggedInContactID(); if ($id) { $activityParams['source_contact_id'] = $id; $activityParams['target_contact_id'][] = $activity->contact_id; @@ -1896,12 +1736,70 @@ public static function addActivity( if ($targetContactID) { $activityParams['target_contact_id'][] = $targetContactID; } + // @todo - use api - remove lots of wrangling above. Remove deprecated fatal & let form layer + // deal with any exceptions. if (is_a(self::create($activityParams), 'CRM_Core_Error')) { CRM_Core_Error::fatal("Failed creating Activity for $component of id {$activity->id}"); return FALSE; } } + /** + * Get activity subject on basis of component object. + * + * @param object $entityObj + * particular component object. + * + * @return string + */ + public static function getActivitySubject($entityObj) { + switch ($entityObj->__table) { + case 'civicrm_membership': + $membershipType = CRM_Member_PseudoConstant::membershipType($entityObj->membership_type_id); + $subject = $membershipType ? $membershipType : ts('Membership'); + + if (is_array($subject)) { + $subject = implode(", ", $subject); + } + + if (!CRM_Utils_System::isNull($entityObj->source)) { + $subject .= " - {$entityObj->source}"; + } + + if ($entityObj->owner_membership_id) { + list($displayName) = CRM_Contact_BAO_Contact::getDisplayAndImage(CRM_Core_DAO::getFieldValue('CRM_Member_DAO_Membership', $entityObj->owner_membership_id, 'contact_id')); + $subject .= sprintf(' (by %s)', $displayName); + } + + $subject .= " - Status: " . CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipStatus', $entityObj->status_id, 'label'); + return $subject; + + case 'civicrm_participant': + $event = CRM_Event_BAO_Event::getEvents(1, $entityObj->event_id, TRUE, FALSE); + $roles = CRM_Event_PseudoConstant::participantRole(); + $status = CRM_Event_PseudoConstant::participantStatus(); + $subject = $event[$entityObj->event_id]; + + if (!empty($roles[$entityObj->role_id])) { + $subject .= ' - ' . $roles[$entityObj->role_id]; + } + if (!empty($status[$entityObj->status_id])) { + $subject .= ' - ' . $status[$entityObj->status_id]; + } + + return $subject; + + case 'civicrm_contribution': + $subject = CRM_Utils_Money::format($entityObj->total_amount, $entityObj->currency); + if (!CRM_Utils_System::isNull($entityObj->source)) { + $subject .= " - {$entityObj->source}"; + } + + // Amount and source could exceed max length of subject column. + return CRM_Utils_String::ellipsify($subject, 255); + } + } + /** * Get Parent activity for currently viewed activity. * @@ -1912,12 +1810,12 @@ public static function addActivity( * Id of parent activity otherwise false. */ public static function getParentActivity($activityId) { - static $parentActivities = array(); + static $parentActivities = []; $activityId = CRM_Utils_Type::escape($activityId, 'Integer'); if (!array_key_exists($activityId, $parentActivities)) { - $parentActivities[$activityId] = array(); + $parentActivities[$activityId] = []; $parentId = CRM_Core_DAO::getFieldValue('CRM_Activity_DAO_Activity', $activityId, @@ -1940,12 +1838,12 @@ public static function getParentActivity($activityId) { * $params count of prior activities otherwise false. */ public static function getPriorCount($activityID) { - static $priorCounts = array(); + static $priorCounts = []; $activityID = CRM_Utils_Type::escape($activityID, 'Integer'); if (!array_key_exists($activityID, $priorCounts)) { - $priorCounts[$activityID] = array(); + $priorCounts[$activityID] = []; $originalID = CRM_Core_DAO::getFieldValue('CRM_Activity_DAO_Activity', $activityID, 'original_id' @@ -1959,7 +1857,7 @@ public static function getPriorCount($activityID) { AND is_current_revision = 0 AND id < {$activityID} "; - $params = array(1 => array($originalID, 'Integer')); + $params = [1 => [$originalID, 'Integer']]; $count = CRM_Core_DAO::singleValueQuery($query, $params); } $priorCounts[$activityID] = $count ? $count : 0; @@ -1979,13 +1877,13 @@ public static function getPriorCount($activityID) { * prior activities info. */ public static function getPriorAcitivities($activityID, $onlyPriorRevisions = FALSE) { - static $priorActivities = array(); + static $priorActivities = []; $activityID = CRM_Utils_Type::escape($activityID, 'Integer'); $index = $activityID . '_' . (int) $onlyPriorRevisions; if (!array_key_exists($index, $priorActivities)) { - $priorActivities[$index] = array(); + $priorActivities[$index] = []; $originalID = CRM_Core_DAO::getFieldValue('CRM_Activity_DAO_Activity', $activityID, @@ -2008,7 +1906,7 @@ public static function getPriorAcitivities($activityID, $onlyPriorRevisions = FA } $query .= " ORDER BY ca.id DESC"; - $params = array(1 => array($originalID, 'Integer')); + $params = [1 => [$originalID, 'Integer']]; $dao = CRM_Core_DAO::executeQuery($query, $params); while ($dao->fetch()) { @@ -2016,7 +1914,6 @@ public static function getPriorAcitivities($activityID, $onlyPriorRevisions = FA $priorActivities[$index][$dao->activityID]['name'] = $dao->name; $priorActivities[$index][$dao->activityID]['date'] = $dao->date; } - $dao->free(); } } return $priorActivities[$index]; @@ -2032,12 +1929,12 @@ public static function getPriorAcitivities($activityID, $onlyPriorRevisions = FA * current activity id. */ public static function getLatestActivityId($activityID) { - static $latestActivityIds = array(); + static $latestActivityIds = []; $activityID = CRM_Utils_Type::escape($activityID, 'Integer'); if (!array_key_exists($activityID, $latestActivityIds)) { - $latestActivityIds[$activityID] = array(); + $latestActivityIds[$activityID] = []; $originalID = CRM_Core_DAO::getFieldValue('CRM_Activity_DAO_Activity', $activityID, @@ -2046,7 +1943,7 @@ public static function getLatestActivityId($activityID) { if ($originalID) { $activityID = $originalID; } - $params = array(1 => array($activityID, 'Integer')); + $params = [1 => [$activityID, 'Integer']]; $query = "SELECT id from civicrm_activity where original_id = %1 and is_current_revision = 1"; $latestActivityIds[$activityID] = CRM_Core_DAO::singleValueQuery($query, $params); @@ -2069,12 +1966,10 @@ public static function createFollowupActivity($activityId, $params) { return NULL; } - $session = CRM_Core_Session::singleton(); - - $followupParams = array(); + $followupParams = []; $followupParams['parent_id'] = $activityId; - $followupParams['source_contact_id'] = $session->get('userID'); - $followupParams['status_id'] = CRM_Core_OptionGroup::getValue('activity_status', 'Scheduled', 'name'); + $followupParams['source_contact_id'] = CRM_Core_Session::getLoggedInContactID(); + $followupParams['status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', 'Scheduled'); $followupParams['activity_type_id'] = $params['followup_activity_type_id']; // Get Subject of Follow-up Activiity, CRM-4491 @@ -2086,9 +1981,7 @@ public static function createFollowupActivity($activityId, $params) { $followupParams['target_contact_id'] = $params['target_contact_id']; } - $followupParams['activity_date_time'] = CRM_Utils_Date::processDate($params['followup_date'], - $params['followup_date_time'] - ); + $followupParams['activity_date_time'] = $params['followup_date']; $followupActivity = self::create($followupParams); return $followupActivity; @@ -2145,6 +2038,45 @@ public static function restoreActivity(&$params) { return $result; } + /** + * Return list of activity statuses of a given type. + * + * Note: activity status options use the "grouping" field to distinguish status types. + * Types are defined in class constants INCOMPLETE, COMPLETED, CANCELLED + * + * @param int $type + * + * @return array + */ + public static function getStatusesByType($type) { + if (!isset(Civi::$statics[__CLASS__][__FUNCTION__])) { + $statuses = civicrm_api3('OptionValue', 'get', [ + 'option_group_id' => 'activity_status', + 'return' => ['value', 'name', 'filter'], + 'options' => ['limit' => 0], + ]); + Civi::$statics[__CLASS__][__FUNCTION__] = $statuses['values']; + } + $ret = []; + foreach (Civi::$statics[__CLASS__][__FUNCTION__] as $status) { + if ($status['filter'] == $type) { + $ret[$status['value']] = $status['name']; + } + } + return $ret; + } + + /** + * Check if activity is overdue. + * + * @param array $activity + * + * @return bool + */ + public static function isOverdue($activity) { + return array_key_exists($activity['status_id'], self::getStatusesByType(self::INCOMPLETE)) && CRM_Utils_Date::overdue($activity['activity_date_time']); + } + /** * Get the exportable fields for Activities. * @@ -2154,63 +2086,94 @@ public static function restoreActivity(&$params) { * @return array * array of exportable Fields */ - public static function &exportableFields($name = 'Activity') { - if (!isset(self::$_exportableFields[$name])) { - self::$_exportableFields[$name] = array(); - - // TODO: ideally we should retrieve all fields from xml, in this case since activity processing is done - // my case hence we have defined fields as case_* - if ($name == 'Activity') { - $exportableFields = CRM_Activity_DAO_Activity::export(); - $exportableFields['source_contact_id']['title'] = ts('Source Contact ID'); - $exportableFields['source_contact'] = array( - 'title' => ts('Source Contact'), + public static function exportableFields($name = 'Activity') { + self::$_exportableFields[$name] = []; + + // TODO: ideally we should retrieve all fields from xml, in this case since activity processing is done + // my case hence we have defined fields as case_* + if ($name == 'Activity') { + $exportableFields = CRM_Activity_DAO_Activity::export(); + $exportableFields['source_contact_id'] = [ + 'title' => ts('Source Contact ID'), + 'type' => CRM_Utils_Type::T_INT, + ]; + $exportableFields['source_contact'] = [ + 'title' => ts('Source Contact'), + 'type' => CRM_Utils_Type::T_STRING, + ]; + + $Activityfields = [ + 'activity_type' => [ + 'title' => ts('Activity Type'), + 'name' => 'activity_type', 'type' => CRM_Utils_Type::T_STRING, - ); - - $Activityfields = array( - 'activity_type' => array( - 'title' => ts('Activity Type'), - 'name' => 'activity_type', - 'type' => CRM_Utils_Type::T_STRING, - 'searchByLabel' => TRUE, - ), - 'activity_status' => array( - 'title' => ts('Activity Status'), - 'name' => 'activity_status', - 'type' => CRM_Utils_Type::T_STRING, - 'searchByLabel' => TRUE, - ), - ); - $fields = array_merge($Activityfields, $exportableFields); - } - else { - // Set title to activity fields. - $fields = array( - 'case_activity_subject' => array('title' => ts('Activity Subject'), 'type' => CRM_Utils_Type::T_STRING), - 'case_source_contact_id' => array('title' => ts('Activity Reporter'), 'type' => CRM_Utils_Type::T_STRING), - 'case_recent_activity_date' => array('title' => ts('Activity Actual Date'), 'type' => CRM_Utils_Type::T_DATE), - 'case_scheduled_activity_date' => array( - 'title' => ts('Activity Scheduled Date'), - 'type' => CRM_Utils_Type::T_DATE, - ), - 'case_recent_activity_type' => array('title' => ts('Activity Type'), 'type' => CRM_Utils_Type::T_STRING), - 'case_activity_status' => array('title' => ts('Activity Status'), 'type' => CRM_Utils_Type::T_STRING), - 'case_activity_duration' => array('title' => ts('Activity Duration'), 'type' => CRM_Utils_Type::T_INT), - 'case_activity_medium_id' => array('title' => ts('Activity Medium'), 'type' => CRM_Utils_Type::T_INT), - 'case_activity_details' => array('title' => ts('Activity Details'), 'type' => CRM_Utils_Type::T_TEXT), - 'case_activity_is_auto' => array( - 'title' => ts('Activity Auto-generated?'), - 'type' => CRM_Utils_Type::T_BOOLEAN, - ), - ); - } - - // add custom data for case activities - $fields = array_merge($fields, CRM_Core_BAO_CustomField::getFieldsForImport('Activity')); - - self::$_exportableFields[$name] = $fields; + 'searchByLabel' => TRUE, + ], + 'activity_status' => [ + 'title' => ts('Activity Status'), + 'name' => 'activity_status', + 'type' => CRM_Utils_Type::T_STRING, + 'searchByLabel' => TRUE, + ], + 'activity_priority' => [ + 'title' => ts('Activity Priority'), + 'name' => 'activity_priority', + 'type' => CRM_Utils_Type::T_STRING, + 'searchByLabel' => TRUE, + ], + ]; + $fields = array_merge($Activityfields, $exportableFields); } + else { + // Set title to activity fields. + $fields = [ + 'case_activity_subject' => [ + 'title' => ts('Activity Subject'), + 'type' => CRM_Utils_Type::T_STRING, + ], + 'case_source_contact_id' => [ + 'title' => ts('Activity Reporter'), + 'type' => CRM_Utils_Type::T_STRING, + ], + 'case_recent_activity_date' => [ + 'title' => ts('Activity Actual Date'), + 'type' => CRM_Utils_Type::T_DATE, + ], + 'case_scheduled_activity_date' => [ + 'title' => ts('Activity Scheduled Date'), + 'type' => CRM_Utils_Type::T_DATE, + ], + 'case_recent_activity_type' => [ + 'title' => ts('Activity Type'), + 'type' => CRM_Utils_Type::T_STRING, + ], + 'case_activity_status' => [ + 'title' => ts('Activity Status'), + 'type' => CRM_Utils_Type::T_STRING, + ], + 'case_activity_duration' => [ + 'title' => ts('Activity Duration'), + 'type' => CRM_Utils_Type::T_INT, + ], + 'case_activity_medium_id' => [ + 'title' => ts('Activity Medium'), + 'type' => CRM_Utils_Type::T_INT, + ], + 'case_activity_details' => [ + 'title' => ts('Activity Details'), + 'type' => CRM_Utils_Type::T_TEXT, + ], + 'case_activity_is_auto' => [ + 'title' => ts('Activity Auto-generated?'), + 'type' => CRM_Utils_Type::T_BOOLEAN, + ], + ]; + } + + // add custom data for case activities + $fields = array_merge($fields, CRM_Core_BAO_CustomField::getFieldsForImport('Activity')); + + self::$_exportableFields[$name] = $fields; return self::$_exportableFields[$name]; } @@ -2222,7 +2185,7 @@ public static function &exportableFields($name = 'Activity') { */ public static function getProfileFields() { $exportableFields = self::exportableFields('Activity'); - $skipFields = array( + $skipFields = [ 'activity_id', 'activity_type', 'source_contact_id', @@ -2231,7 +2194,7 @@ public static function getProfileFields() { 'activity_is_test', 'is_current_revision', 'activity_is_deleted', - ); + ]; $config = CRM_Core_Config::singleton(); if (!in_array('CiviCampaign', $config->enableComponents)) { $skipFields[] = 'activity_engagement_level'; @@ -2266,7 +2229,7 @@ public static function cleanupActivity($contactId) { if (!$contactId) { return $result; } - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts); $transaction = new CRM_Core_Transaction(); @@ -2287,14 +2250,12 @@ public static function cleanupActivity($contactId) { // delete activity only if no other contacts connected if (!$activityContactOther->find(TRUE)) { - $activityParams = array('id' => $activityContact->activity_id); + $activityParams = ['id' => $activityContact->activity_id]; $result = self::deleteActivity($activityParams); } - $activityContactOther->free(); } - $activityContact->free(); $transaction->commit(); return $result; @@ -2311,88 +2272,28 @@ public static function cleanupActivity($contactId) { * @return bool */ public static function checkPermission($activityId, $action) { - $allow = FALSE; + if (!$activityId || - !in_array($action, array(CRM_Core_Action::UPDATE, CRM_Core_Action::VIEW)) + !in_array($action, [CRM_Core_Action::UPDATE, CRM_Core_Action::VIEW]) ) { - return $allow; + return FALSE; } $activity = new CRM_Activity_DAO_Activity(); $activity->id = $activityId; if (!$activity->find(TRUE)) { - return $allow; - } - - // Component related permissions. - $compPermissions = array( - 'CiviCase' => array( - 'administer CiviCase', - 'access my cases and activities', - 'access all cases and activities', - ), - 'CiviMail' => array('access CiviMail'), - 'CiviEvent' => array('access CiviEvent'), - 'CiviGrant' => array('access CiviGrant'), - 'CiviPledge' => array('access CiviPledge'), - 'CiviMember' => array('access CiviMember'), - 'CiviReport' => array('access CiviReport'), - 'CiviContribute' => array('access CiviContribute'), - 'CiviCampaign' => array('administer CiviCampaign'), - ); - - // Return early when it is case activity. - $isCaseActivity = CRM_Case_BAO_Case::isCaseActivity($activityId); - // Check for civicase related permission. - if ($isCaseActivity) { - $allow = FALSE; - foreach ($compPermissions['CiviCase'] as $per) { - if (CRM_Core_Permission::check($per)) { - $allow = TRUE; - break; - } - } - - // Check for case specific permissions. - if ($allow) { - $oper = 'view'; - if ($action == CRM_Core_Action::UPDATE) { - $oper = 'edit'; - } - $allow = CRM_Case_BAO_Case::checkPermission($activityId, - $oper, - $activity->activity_type_id - ); - } - - return $allow; + return FALSE; } - // First check the component permission. - $sql = " - SELECT component_id - FROM civicrm_option_value val -INNER JOIN civicrm_option_group grp ON ( grp.id = val.option_group_id AND grp.name = %1 ) - WHERE val.value = %2"; - $params = array( - 1 => array('activity_type', 'String'), - 2 => array($activity->activity_type_id, 'Integer'), - ); - $componentId = CRM_Core_DAO::singleValueQuery($sql, $params); - - if ($componentId) { - $componentName = CRM_Core_Component::getComponentName($componentId); - $compPermission = CRM_Utils_Array::value($componentName, $compPermissions); - - // Here we are interesting in any single permission. - if (is_array($compPermission)) { - foreach ($compPermission as $per) { - if (CRM_Core_Permission::check($per)) { - $allow = TRUE; - break; - } - } - } + if (!self::hasPermissionForActivityType($activity->activity_type_id)) { + // this check is redundant for api access / anything that calls the selectWhereClause + // to determine ACLs. + return FALSE; + } + // Return early when it is case activity. + // Check for CiviCase related permission. + if (CRM_Case_BAO_Case::isCaseActivity($activityId)) { + return self::isContactPermittedAccessToCaseActivity($activityId, $action, $activity->activity_type_id); } // Check for this permission related to contact. @@ -2401,55 +2302,169 @@ public static function checkPermission($activityId, $action) { $permission = CRM_Core_Permission::EDIT; } - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts); $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts); $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts); // Check for source contact. - if (!$componentId || $allow) { - $sourceContactId = self::getActivityContact($activity->id, $sourceID); - // Account for possibility of activity not having a source contact (as it may have been deleted). - $allow = $sourceContactId ? CRM_Contact_BAO_Contact_Permission::allow($sourceContactId, $permission) : TRUE; + $sourceContactId = self::getActivityContact($activity->id, $sourceID); + // Account for possibility of activity not having a source contact (as it may have been deleted). + $allow = $sourceContactId ? CRM_Contact_BAO_Contact_Permission::allow($sourceContactId, $permission) : TRUE; + if (!$allow) { + return FALSE; } // Check for target and assignee contacts. - if ($allow) { - // First check for supper permission. - $supPermission = 'view all contacts'; - if ($action == CRM_Core_Action::UPDATE) { - $supPermission = 'edit all contacts'; - } - $allow = CRM_Core_Permission::check($supPermission); - - // User might have sufficient permission, through acls. - if (!$allow) { - $allow = TRUE; - // Get the target contacts. - $targetContacts = CRM_Activity_BAO_ActivityContact::retrieveContactIdsByActivityId($activity->id, $targetID); - foreach ($targetContacts as $cnt => $contactId) { + // First check for supper permission. + $supPermission = 'view all contacts'; + if ($action == CRM_Core_Action::UPDATE) { + $supPermission = 'edit all contacts'; + } + $allow = CRM_Core_Permission::check($supPermission); + + // User might have sufficient permission, through acls. + if (!$allow) { + $allow = TRUE; + // Get the target contacts. + $targetContacts = CRM_Activity_BAO_ActivityContact::retrieveContactIdsByActivityId($activity->id, $targetID); + foreach ($targetContacts as $cnt => $contactId) { + if (!CRM_Contact_BAO_Contact_Permission::allow($contactId, $permission)) { + $allow = FALSE; + break; + } + } + + // Get the assignee contacts. + if ($allow) { + $assigneeContacts = CRM_Activity_BAO_ActivityContact::retrieveContactIdsByActivityId($activity->id, $assigneeID); + foreach ($assigneeContacts as $cnt => $contactId) { if (!CRM_Contact_BAO_Contact_Permission::allow($contactId, $permission)) { $allow = FALSE; break; } } - - // Get the assignee contacts. - if ($allow) { - $assigneeContacts = CRM_Activity_BAO_ActivityContact::retrieveContactIdsByActivityId($activity->id, $assigneeID); - foreach ($assigneeContacts as $cnt => $contactId) { - if (!CRM_Contact_BAO_Contact_Permission::allow($contactId, $permission)) { - $allow = FALSE; - break; - } - } - } } } return $allow; } + /** + * Check if the logged in user has permission for the given case activity. + * + * @param int $activityId + * @param int $action + * @param int $activityTypeID + * + * @return bool + */ + protected static function isContactPermittedAccessToCaseActivity($activityId, $action, $activityTypeID) { + $oper = 'view'; + if ($action == CRM_Core_Action::UPDATE) { + $oper = 'edit'; + } + $allow = CRM_Case_BAO_Case::checkPermission($activityId, + $oper, + $activityTypeID + ); + + return $allow; + } + + /** + * Check if the logged in user has permission to access the given activity type. + * + * @param int $activityTypeID + * + * @return bool + */ + protected static function hasPermissionForActivityType($activityTypeID) { + $permittedActivityTypes = self::getPermittedActivityTypes(); + return isset($permittedActivityTypes[$activityTypeID]); + } + + /** + * Get the activity types the user is permitted to access. + * + * The types are filtered by the components they have access to. ie. a user + * with access CiviContribute but not CiviMember will see contribution related + * activities and activities with no component (e.g meetings) but not member related ones. + * + * @return array + */ + protected static function getPermittedActivityTypes() { + $userID = (int) CRM_Core_Session::getLoggedInContactID(); + if (!isset(Civi::$statics[__CLASS__]['permitted_activity_types'][$userID])) { + $permittedActivityTypes = []; + $components = self::activityComponents(FALSE); + $componentClause = empty($components) ? '' : (' OR component_id IN (' . implode(', ', array_keys($components)) . ')'); + + $types = CRM_Core_DAO::executeQuery( + " + SELECT option_value.value activity_type_id + FROM civicrm_option_value option_value +INNER JOIN civicrm_option_group grp ON (grp.id = option_group_id AND grp.name = 'activity_type') + WHERE component_id IS NULL $componentClause")->fetchAll(); + foreach ($types as $type) { + $permittedActivityTypes[$type['activity_type_id']] = (int) $type['activity_type_id']; + } + Civi::$statics[__CLASS__]['permitted_activity_types'][$userID] = $permittedActivityTypes; + } + return Civi::$statics[__CLASS__]['permitted_activity_types'][$userID]; + } + + /** + * @param $params + * @return array + */ + protected static function getActivityParamsForDashboardFunctions($params) { + $activityParams = [ + 'is_deleted' => 0, + 'is_current_revision' => 1, + 'is_test' => 0, + 'contact_id' => CRM_Utils_Array::value('contact_id', $params), + 'activity_date_time' => CRM_Utils_Array::value('activity_date_time', $params), + 'check_permissions' => 1, + 'options' => [ + 'offset' => CRM_Utils_Array::value('offset', $params, 0), + ], + ]; + + if (!empty($params['activity_status_id'])) { + $activityParams['activity_status_id'] = ['IN' => explode(',', $params['activity_status_id'])]; + } + + $activityParams['activity_type_id'] = self::filterActivityTypes($params); + $enabledComponents = self::activityComponents(); + // @todo - should we move this to activity get api. + foreach ([ + 'case_id' => 'CiviCase', + 'campaign_id' => 'CiviCampaign', + ] as $attr => $component) { + if (!in_array($component, $enabledComponents)) { + $activityParams[$attr] = ['IS NULL' => 1]; + } + } + return $activityParams; + } + + /** + * Checks if user has permissions to edit inbound e-mails, either bsic info + * or both basic information and content. + * + * @return bool + */ + public static function checkEditInboundEmailsPermissions() { + if (CRM_Core_Permission::check('edit inbound email basic information') + || CRM_Core_Permission::check('edit inbound email basic information and content') + ) { + return TRUE; + } + + return FALSE; + } + /** * Wrapper for ajax activity selector. * @@ -2467,6 +2482,17 @@ public static function getContactActivitySelector(&$params) { $params['caseId'] = NULL; $context = CRM_Utils_Array::value('context', $params); $showContactOverlay = !CRM_Utils_String::startsWith($context, "dashlet"); + $activityTypeInfo = civicrm_api3('OptionValue', 'get', [ + 'option_group_id' => "activity_type", + 'options' => ['limit' => 0], + ]); + $activityIcons = []; + foreach ($activityTypeInfo['values'] as $type) { + if (!empty($type['icon'])) { + $activityIcons[$type['value']] = $type['icon']; + } + } + CRM_Utils_Date::convertFormDateToApiFormat($params, 'activity_date_time'); // Get contact activities. $activities = CRM_Activity_BAO_Activity::getActivities($params); @@ -2475,7 +2501,7 @@ public static function getContactActivitySelector(&$params) { $params['total'] = CRM_Activity_BAO_Activity::getActivitiesCount($params); // Format params and add links. - $contactActivities = array(); + $contactActivities = []; if (!empty($activities)) { $activityStatus = CRM_Core_PseudoConstant::activityStatus(); @@ -2483,7 +2509,7 @@ public static function getContactActivitySelector(&$params) { // Check logged in user for permission. $page = new CRM_Core_Page(); CRM_Contact_Page_View::checkUserPermission($page, $params['contact_id']); - $permissions = array($page->_permission); + $permissions = [$page->_permission]; if (CRM_Core_Permission::check('delete activities')) { $permissions[] = CRM_Core_Permission::DELETE; } @@ -2491,27 +2517,24 @@ public static function getContactActivitySelector(&$params) { $mask = CRM_Core_Action::mask($permissions); foreach ($activities as $activityId => $values) { - $activity = array(); + $activity = ['source_contact_name' => '', 'target_contact_name' => '']; $activity['DT_RowId'] = $activityId; // Add class to this row if overdue. - $activity['DT_RowClass'] = 'crm-entity'; - if (CRM_Utils_Date::overdue(CRM_Utils_Array::value('activity_date_time', $values)) - && CRM_Utils_Array::value('status_id', $values) == 1 - ) { + $activity['DT_RowClass'] = "crm-entity status-id-{$values['status_id']}"; + if (self::isOverdue($values)) { $activity['DT_RowClass'] .= ' status-overdue'; } else { $activity['DT_RowClass'] .= ' status-ontime'; } - $activity['DT_RowAttr'] = array(); + $activity['DT_RowAttr'] = []; $activity['DT_RowAttr']['data-entity'] = 'activity'; $activity['DT_RowAttr']['data-id'] = $activityId; - $activity['activity_type'] = $values['activity_type']; + $activity['activity_type'] = (!empty($activityIcons[$values['activity_type_id']]) ? ' ' : '') . $values['activity_type']; $activity['subject'] = $values['subject']; - $activity['source_contact_name'] = ''; if ($params['contact_id'] == $values['source_contact_id']) { $activity['source_contact_name'] = $values['source_contact_name']; } @@ -2524,13 +2547,12 @@ public static function getContactActivitySelector(&$params) { $values['source_contact_id']); } $activity['source_contact_name'] = $srcTypeImage . CRM_Utils_System::href($values['source_contact_name'], - 'civicrm/contact/view', "reset=1&cid={$values['source_contact_id']}"); + 'civicrm/contact/view', "reset=1&cid={$values['source_contact_id']}"); } else { $activity['source_contact_name'] = 'n/a'; } - $activity['target_contact_name'] = ''; if (isset($values['mailingId']) && !empty($values['mailingId'])) { $activity['target_contact'] = CRM_Utils_System::href($values['recipients'], 'civicrm/mailing/report/event', @@ -2539,25 +2561,25 @@ public static function getContactActivitySelector(&$params) { elseif (!empty($values['recipients'])) { $activity['target_contact_name'] = $values['recipients']; } - elseif (isset($values['target_contact_counter']) && $values['target_contact_counter']) { + elseif (isset($values['target_contact_count']) && $values['target_contact_count']) { $activity['target_contact_name'] = ''; - foreach ($values['target_contact_name'] as $tcID => $tcName) { - $targetTypeImage = ""; - $targetLink = CRM_Utils_System::href($tcName, 'civicrm/contact/view', "reset=1&cid={$tcID}"); - if ($showContactOverlay) { - $targetTypeImage = CRM_Contact_BAO_Contact_Utils::getImage( - CRM_Contact_BAO_Contact::getContactType($tcID), - FALSE, - $tcID); - $activity['target_contact_name'] .= "
$targetTypeImage $targetLink"; - } - else { - $activity['target_contact_name'] .= $targetLink; - } + $firstTargetName = reset($values['target_contact_name']); + $firstTargetContactID = key($values['target_contact_name']); + + $targetLink = CRM_Utils_System::href($firstTargetName, 'civicrm/contact/view', "reset=1&cid={$firstTargetContactID}"); + if ($showContactOverlay) { + $targetTypeImage = CRM_Contact_BAO_Contact_Utils::getImage( + CRM_Contact_BAO_Contact::getContactType($firstTargetContactID), + FALSE, + $firstTargetContactID); + $activity['target_contact_name'] .= "
$targetTypeImage $targetLink"; + } + else { + $activity['target_contact_name'] .= $targetLink; } - if ($extraCount = $values['target_contact_counter'] - 1) { - $activity['target_contact_name'] .= ";
" . "(" . ts('%1 more', array(1 => $extraCount)) . ")"; + if ($extraCount = $values['target_contact_count'] - 1) { + $activity['target_contact_name'] .= ";
" . "(" . ts('%1 more', [1 => $extraCount]) . ")"; } if ($showContactOverlay) { $activity['target_contact_name'] .= "
"; @@ -2626,12 +2648,12 @@ public static function getContactActivitySelector(&$params) { $activity['links'] = CRM_Core_Action::formLink($actionLinks, $actionMask, - array( + [ 'id' => $values['activity_id'], 'cid' => $params['contact_id'], 'cxt' => $context, 'caseid' => CRM_Utils_Array::value('case_id', $values), - ), + ], ts('more'), FALSE, 'activity.tab.row', @@ -2647,7 +2669,7 @@ public static function getContactActivitySelector(&$params) { } } - $activitiesDT = array(); + $activitiesDT = []; $activitiesDT['data'] = $contactActivities; $activitiesDT['recordsTotal'] = $params['total']; $activitiesDT['recordsFiltered'] = $params['total']; @@ -2664,7 +2686,7 @@ public static function getContactActivitySelector(&$params) { */ public static function copyExtendedActivityData($params) { // attach custom data to the new activity - $customParams = $htmlType = array(); + $customParams = $htmlType = []; $customValues = CRM_Core_BAO_CustomValueTable::getEntityValues($params['activityID'], 'Activity'); if (!empty($customValues)) { @@ -2681,10 +2703,10 @@ public static function copyExtendedActivityData($params) { // CRM-10542 if (in_array($key, $htmlType)) { $fileValues = CRM_Core_BAO_File::path($value, $params['activityID']); - $customParams["custom_{$key}_-1"] = array( + $customParams["custom_{$key}_-1"] = [ 'name' => $fileValues[0], - 'path' => $fileValues[1], - ); + 'type' => $fileValues[1], + ]; } else { $customParams["custom_{$key}_-1"] = $value; @@ -2731,7 +2753,7 @@ public static function getActivityContact($activityId, $recordTypeID = NULL, $co public static function getSourceContactID($activityId) { static $sourceID = NULL; if (!$sourceID) { - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts); } @@ -2748,7 +2770,7 @@ public static function getSourceContactID($activityId) { public function setApiFilter(&$params) { if (!empty($params['target_contact_id'])) { $this->selectAdd(); - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts); $obj = new CRM_Activity_BAO_ActivityContact(); $params['return.target_contact_id'] = 1; @@ -2767,7 +2789,7 @@ public function setApiFilter(&$params) { * * @return bool */ - public static function sendToAssignee($activity, $mailToContacts, $params = array()) { + public static function sendToAssignee($activity, $mailToContacts, $params = []) { if (!CRM_Utils_Array::crmIsEmptyArray($mailToContacts)) { $clientID = CRM_Utils_Array::value('client_id', $params); $caseID = CRM_Utils_Array::value('case_id', $params); @@ -2783,4 +2805,14 @@ public static function sendToAssignee($activity, $mailToContacts, $params = arra return FALSE; } + /** + * @return array + */ + public static function getEntityRefFilters() { + return [ + ['key' => 'activity_type_id', 'value' => ts('Activity Type')], + ['key' => 'status_id', 'value' => ts('Activity Status')], + ]; + } + } diff --git a/CRM/Activity/BAO/ActivityAssignment.php b/CRM/Activity/BAO/ActivityAssignment.php index 6a45522c8f24..87420a3dc79f 100644 --- a/CRM/Activity/BAO/ActivityAssignment.php +++ b/CRM/Activity/BAO/ActivityAssignment.php @@ -1,9 +1,9 @@ copyValues($params); @@ -71,12 +71,12 @@ public static function create(&$params) { * @return array */ public static function retrieveAssigneeIdsByActivityId($activity_id) { - $assigneeArray = array(); + $assigneeArray = []; if (!CRM_Utils_Rule::positiveInteger($activity_id)) { return $assigneeArray; } - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts); $sql = " @@ -87,7 +87,12 @@ public static function retrieveAssigneeIdsByActivityId($activity_id) { AND record_type_id = $assigneeID AND civicrm_contact.is_deleted = 0 "; - $assignment = CRM_Core_DAO::executeQuery($sql, array(1 => array($activity_id, 'Integer'))); + $assignment = CRM_Core_DAO::executeQuery($sql, [ + 1 => [ + $activity_id, + 'Integer', + ], + ]); while ($assignment->fetch()) { $assigneeArray[] = $assignment->contact_id; } @@ -108,11 +113,11 @@ public static function retrieveAssigneeIdsByActivityId($activity_id) { * @return array */ public static function getAssigneeNames($activityIDs, $isDisplayName = FALSE, $skipDetails = TRUE) { - $assigneeNames = array(); + $assigneeNames = []; if (empty($activityIDs)) { return $assigneeNames; } - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts); $whereClause = ""; diff --git a/CRM/Activity/BAO/ActivityContact.php b/CRM/Activity/BAO/ActivityContact.php index a198afa47f1f..7039fba2f164 100644 --- a/CRM/Activity/BAO/ActivityContact.php +++ b/CRM/Activity/BAO/ActivityContact.php @@ -1,9 +1,9 @@ array($activityID, 'Integer'), - 2 => array($recordTypeID, 'Integer'), - ); + $params = [ + 1 => [$activityID, 'Integer'], + 2 => [$recordTypeID, 'Integer'], + ]; $dao = CRM_Core_DAO::executeQuery($query, $params); while ($dao->fetch()) { @@ -98,7 +98,7 @@ public static function getNames($activityID, $recordTypeID, $alsoIDs = FALSE) { $ids[] = $dao->id; } - return $alsoIDs ? array($names, $ids) : $names; + return $alsoIDs ? [$names, $ids] : $names; } /** @@ -110,7 +110,7 @@ public static function getNames($activityID, $recordTypeID, $alsoIDs = FALSE) { * @return mixed */ public static function retrieveContactIdsByActivityId($activityID, $recordTypeID) { - $activityContact = array(); + $activityContact = []; if (!CRM_Utils_Rule::positiveInteger($activityID) || !CRM_Utils_Rule::positiveInteger($recordTypeID) ) { @@ -124,10 +124,10 @@ public static function retrieveContactIdsByActivityId($activityID, $recordTypeID AND record_type_id = %2 AND civicrm_contact.is_deleted = 0 "; - $params = array( - 1 => array($activityID, 'Integer'), - 2 => array($recordTypeID, 'Integer'), - ); + $params = [ + 1 => [$activityID, 'Integer'], + 2 => [$recordTypeID, 'Integer'], + ]; $dao = CRM_Core_DAO::executeQuery($sql, $params); while ($dao->fetch()) { @@ -147,12 +147,12 @@ public static function retrieveContactIdsByActivityId($activityID, $recordTypeID * @see DB_DataObject::getLink() * * @return array|null - * array = if there are links defined for this table. - * empty array - if there is a links.ini file, but no links on this table - * null - if no links.ini exists for this database (hence try auto_links). + * array if there are links defined for this table. + * empty array - if there is a links.ini file, but no links on this table + * null - if no links.ini exists for this database (hence try auto_links). */ public function links() { - $link = array('activity_id' => 'civicrm_activity:id'); + $link = ['activity_id' => 'civicrm_activity:id']; return $link; } diff --git a/CRM/Activity/BAO/ActivityTarget.php b/CRM/Activity/BAO/ActivityTarget.php index 206e9d682b0f..167857d1c33d 100644 --- a/CRM/Activity/BAO/ActivityTarget.php +++ b/CRM/Activity/BAO/ActivityTarget.php @@ -1,9 +1,9 @@ copyValues($params); @@ -69,12 +69,12 @@ public static function create(&$params) { * @return mixed */ public static function retrieveTargetIdsByActivityId($activity_id) { - $targetArray = array(); + $targetArray = []; if (!CRM_Utils_Rule::positiveInteger($activity_id)) { return $targetArray; } - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts); $sql = " @@ -85,7 +85,12 @@ public static function retrieveTargetIdsByActivityId($activity_id) { AND record_type_id = $targetID AND civicrm_contact.is_deleted = 0 "; - $target = CRM_Core_DAO::executeQuery($sql, array(1 => array($activity_id, 'Integer'))); + $target = CRM_Core_DAO::executeQuery($sql, [ + 1 => [ + $activity_id, + 'Integer', + ], + ]); while ($target->fetch()) { $targetArray[] = $target->contact_id; } @@ -100,12 +105,12 @@ public static function retrieveTargetIdsByActivityId($activity_id) { * @return array */ public static function getTargetNames($activityID) { - $targetNames = array(); + $targetNames = []; if (empty($activityID)) { return $targetNames; } - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts); $query = " @@ -116,7 +121,7 @@ public static function getTargetNames($activityID) { AND civicrm_activity_contact.record_type_id = $targetID AND contact_a.is_deleted = 0 "; - $queryParam = array(1 => array($activityID, 'Integer')); + $queryParam = [1 => [$activityID, 'Integer']]; $dao = CRM_Core_DAO::executeQuery($query, $queryParam); while ($dao->fetch()) { diff --git a/CRM/Activity/BAO/ICalendar.php b/CRM/Activity/BAO/ICalendar.php index 7ccdf0bffd2a..4ebc2b0b54bd 100644 --- a/CRM/Activity/BAO/ICalendar.php +++ b/CRM/Activity/BAO/ICalendar.php @@ -1,9 +1,9 @@ fetch('CRM/Activity/Calendar/ICal.tpl'); if (file_put_contents($this->icsfile, $calendar) !== FALSE) { if (empty($attachments)) { - $attachments = array(); + $attachments = []; } - $attachments['activity_ics'] = array( + $attachments['activity_ics'] = [ 'mime_type' => 'text/calendar', 'fileName' => $icsFileName, 'cleanName' => $icsFileName, 'fullPath' => $this->icsfile, - ); + ]; return 'activity_ics'; } } @@ -105,7 +105,7 @@ public function addAttachment(&$attachments, $contacts) { * Remove temp file. */ public function cleanup() { - if (!empty ($this->icsfile)) { + if (!empty($this->icsfile)) { @unlink($this->icsfile); } } @@ -115,8 +115,7 @@ public function cleanup() { * @return string */ private function getPrimaryEmail() { - $session = &CRM_Core_Session::singleton(); - $uid = $session->get('userID'); + $uid = CRM_Core_Session::getLoggedInContactID(); $primary = ''; $emails = CRM_Core_BAO_Email::allEmails($uid); foreach ($emails as $eid => $e) { diff --git a/CRM/Activity/BAO/Query.php b/CRM/Activity/BAO/Query.php index 370c16dd7030..8f7cd076b8f5 100644 --- a/CRM/Activity/BAO/Query.php +++ b/CRM/Activity/BAO/Query.php @@ -1,9 +1,9 @@ _returnProperties['activity_id'])) { - $query->_select['activity_id'] = "civicrm_activity.id as activity_id"; + $query->_select['activity_id'] = 'civicrm_activity.id as activity_id'; $query->_element['activity_id'] = 1; $query->_tables['civicrm_activity'] = $query->_whereTables['civicrm_activity'] = 1; } if (!empty($query->_returnProperties['activity_type_id'])) { - $query->_select['activity_type_id'] = "activity_type.value as activity_type_id"; + $query->_select['activity_type_id'] = 'activity_type.value as activity_type_id'; $query->_element['activity_type_id'] = 1; $query->_tables['civicrm_activity'] = 1; $query->_tables['activity_type'] = 1; @@ -54,7 +54,7 @@ public static function select(&$query) { } if (!empty($query->_returnProperties['activity_type'])) { - $query->_select['activity_type'] = "activity_type.label as activity_type"; + $query->_select['activity_type'] = 'activity_type.label as activity_type'; $query->_element['activity_type'] = 1; $query->_tables['civicrm_activity'] = 1; $query->_tables['activity_type'] = 1; @@ -63,29 +63,27 @@ public static function select(&$query) { } if (!empty($query->_returnProperties['activity_subject'])) { - $query->_select['activity_subject'] = "civicrm_activity.subject as activity_subject"; + $query->_select['activity_subject'] = 'civicrm_activity.subject as activity_subject'; $query->_element['activity_subject'] = 1; $query->_tables['civicrm_activity'] = $query->_whereTables['civicrm_activity'] = 1; } if (!empty($query->_returnProperties['activity_date_time'])) { - $query->_select['activity_date_time'] = "civicrm_activity.activity_date_time as activity_date_time"; + $query->_select['activity_date_time'] = 'civicrm_activity.activity_date_time as activity_date_time'; $query->_element['activity_date_time'] = 1; $query->_tables['civicrm_activity'] = $query->_whereTables['civicrm_activity'] = 1; } if (!empty($query->_returnProperties['activity_status_id'])) { - $query->_select['activity_status_id'] = "activity_status.value as activity_status_id"; + $query->_select['activity_status_id'] = 'civicrm_activity.status_id as activity_status_id'; $query->_element['activity_status_id'] = 1; $query->_tables['civicrm_activity'] = 1; - $query->_tables['activity_status'] = 1; $query->_whereTables['civicrm_activity'] = 1; - $query->_whereTables['activity_status'] = 1; } if (!empty($query->_returnProperties['activity_status'])) { - $query->_select['activity_status'] = "activity_status.label as activity_status, - civicrm_activity.status_id as status_id"; + $query->_select['activity_status'] = 'activity_status.label as activity_status, + civicrm_activity.status_id as status_id'; $query->_element['activity_status'] = 1; $query->_tables['civicrm_activity'] = 1; $query->_tables['activity_status'] = 1; @@ -94,31 +92,31 @@ public static function select(&$query) { } if (!empty($query->_returnProperties['activity_duration'])) { - $query->_select['activity_duration'] = "civicrm_activity.duration as activity_duration"; + $query->_select['activity_duration'] = 'civicrm_activity.duration as activity_duration'; $query->_element['activity_duration'] = 1; $query->_tables['civicrm_activity'] = $query->_whereTables['civicrm_activity'] = 1; } if (!empty($query->_returnProperties['activity_location'])) { - $query->_select['activity_location'] = "civicrm_activity.location as activity_location"; + $query->_select['activity_location'] = 'civicrm_activity.location as activity_location'; $query->_element['activity_location'] = 1; $query->_tables['civicrm_activity'] = $query->_whereTables['civicrm_activity'] = 1; } if (!empty($query->_returnProperties['activity_details'])) { - $query->_select['activity_details'] = "civicrm_activity.details as activity_details"; + $query->_select['activity_details'] = 'civicrm_activity.details as activity_details'; $query->_element['activity_details'] = 1; $query->_tables['civicrm_activity'] = $query->_whereTables['civicrm_activity'] = 1; } if (!empty($query->_returnProperties['source_record_id'])) { - $query->_select['source_record_id'] = "civicrm_activity.source_record_id as source_record_id"; + $query->_select['source_record_id'] = 'civicrm_activity.source_record_id as source_record_id'; $query->_element['source_record_id'] = 1; $query->_tables['civicrm_activity'] = $query->_whereTables['civicrm_activity'] = 1; } if (!empty($query->_returnProperties['activity_is_test'])) { - $query->_select['activity_is_test'] = "civicrm_activity.is_test as activity_is_test"; + $query->_select['activity_is_test'] = 'civicrm_activity.is_test as activity_is_test'; $query->_element['activity_is_test'] = 1; $query->_tables['civicrm_activity'] = $query->_whereTables['civicrm_activity'] = 1; } @@ -138,7 +136,13 @@ public static function select(&$query) { if (!empty($query->_returnProperties['source_contact'])) { $query->_select['source_contact'] = 'source_contact.sort_name as source_contact'; $query->_element['source_contact'] = 1; - $query->_tables['source_contact'] = $query->_whereTables['source_contact'] = 1; + $query->_tables['civicrm_activity'] = $query->_tables['source_contact'] = $query->_whereTables['source_contact'] = 1; + } + + if (!empty($query->_returnProperties['source_contact_id'])) { + $query->_select['source_contact_id'] = 'source_contact.id as source_contact_id'; + $query->_element['source_contact_id'] = 1; + $query->_tables['civicrm_activity'] = $query->_tables['source_contact'] = $query->_whereTables['source_contact'] = 1; } if (!empty($query->_returnProperties['activity_result'])) { @@ -152,6 +156,15 @@ public static function select(&$query) { $query->_whereTables['parent_id'] = 1; $query->_element['parent_id'] = 1; } + + if (!empty($query->_returnProperties['activity_priority'])) { + $query->_select['activity_priority'] = 'activity_priority.label as activity_priority, + civicrm_activity.priority_id as priority_id'; + $query->_element['activity_priority'] = 1; + $query->_tables['activity_priority'] = 1; + $query->_whereTables['activity_priority'] = 1; + $query->_tables['civicrm_activity'] = $query->_whereTables['civicrm_activity'] = 1; + } } /** @@ -190,34 +203,54 @@ public static function whereClauseSingle(&$values, &$query) { case 'activity_type_id': case 'activity_status_id': case 'activity_engagement_level': - case 'activity_subject': case 'activity_id': case 'activity_campaign_id': + case 'activity_priority_id': + // 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': $qillName = $name; - if (in_array($name, array('activity_engagement_level', 'activity_id'))) { + if (in_array($name, ['activity_engagement_level', 'activity_id'])) { $name = $qillName = str_replace('activity_', '', $name); } - if (in_array($name, array('activity_status_id', 'activity_subject'))) { + if (in_array($name, [ + 'activity_status_id', + 'activity_subject', + 'activity_priority_id', + ])) { $name = str_replace('activity_', '', $name); $qillName = str_replace('_id', '', $qillName); } if ($name == 'activity_campaign_id') { - $name = 'campaign_id'; + $name = 'campaign_id'; } $dataType = !empty($fields[$qillName]['type']) ? CRM_Utils_Type::typeToString($fields[$qillName]['type']) : 'String'; $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause("civicrm_activity.$name", $op, $value, $dataType); list($op, $value) = CRM_Contact_BAO_Query::buildQillForFieldValue('CRM_Activity_DAO_Activity', $name, $value, $op); - $query->_qill[$grouping][] = ts('%1 %2 %3', array(1 => $fields[$qillName]['title'], 2 => $op, 3 => $value)); + $query->_qill[$grouping][] = ts('%1 %2 %3', [ + 1 => $fields[$qillName]['title'], + 2 => $op, + 3 => $value, + ]); + break; + + case 'activity_text': + self::whereClauseSingleActivityText($values, $query); break; case 'activity_type': case 'activity_status': + case 'activity_priority': $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause("$name.label", $op, $value, 'String'); list($op, $value) = CRM_Contact_BAO_Query::buildQillForFieldValue('CRM_Activity_DAO_Activity', $name, $value, $op); - $query->_qill[$grouping][] = ts('%1 %2 %3', array(1 => $fields[$name]['title'], 2 => $op, 3 => $value)); + $query->_qill[$grouping][] = ts('%1 %2 %3', [ + 1 => $fields[$name]['title'], + 2 => $op, + 3 => $value, + ]); $query->_tables[$name] = $query->_whereTables[$name] = 1; break; @@ -232,7 +265,7 @@ public static function whereClauseSingle(&$values, &$query) { case 'activity_role': CRM_Contact_BAO_Query::$_activityRole = $values[2]; - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts); $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts); $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts); @@ -267,14 +300,19 @@ public static function whereClauseSingle(&$values, &$query) { case 'activity_date': case 'activity_date_low': case 'activity_date_high': + case 'activity_date_time_low': + case 'activity_date_time_high': $query->dateQueryBuilder($values, - 'civicrm_activity', 'activity_date', 'activity_date_time', ts('Activity Date') + 'civicrm_activity', str_replace([ + '_high', + '_low', + ], '', $name), 'activity_date_time', ts('Activity Date') ); break; case 'activity_taglist': $taglist = $value; - $value = array(); + $value = []; foreach ($taglist as $val) { if ($val) { $val = explode(',', $val); @@ -288,37 +326,37 @@ public static function whereClauseSingle(&$values, &$query) { case 'activity_tags': $value = array_keys($value); - $activityTags = CRM_Core_PseudoConstant::get('CRM_Core_DAO_EntityTag', 'tag_id', array('onlyActive' => FALSE)); + $activityTags = CRM_Core_PseudoConstant::get('CRM_Core_DAO_EntityTag', 'tag_id', ['onlyActive' => FALSE]); - $names = array(); + $names = []; if (is_array($value)) { foreach ($value as $k => $v) { $names[] = $activityTags[$v]; } } $query->_where[$grouping][] = "civicrm_activity_tag.tag_id IN (" . implode(",", $value) . ")"; - $query->_qill[$grouping][] = ts('Activity Tag %1', array(1 => $op)) . ' ' . implode(' ' . ts('OR') . ' ', $names); + $query->_qill[$grouping][] = ts('Activity Tag %1', [1 => $op]) . ' ' . implode(' ' . ts('OR') . ' ', $names); $query->_tables['civicrm_activity_tag'] = $query->_whereTables['civicrm_activity_tag'] = 1; break; case 'activity_result': if (is_array($value)) { - $safe = NULL; - while (list(, $k) = each($value)) { + $safe = []; + foreach ($value as $id => $k) { $safe[] = "'" . CRM_Utils_Type::escape($k, 'String') . "'"; } $query->_where[$grouping][] = "civicrm_activity.result IN (" . implode(',', $safe) . ")"; - $query->_qill[$grouping][] = ts("Activity Result - %1", array(1 => implode(' or ', $safe))); + $query->_qill[$grouping][] = ts("Activity Result - %1", [1 => implode(' or ', $safe)]); } break; case 'parent_id': if ($value == 1) { - $query->_where[$grouping][] = "parent_id.parent_id IS NOT NULL"; + $query->_where[$grouping][] = "civicrm_activity.parent_id IS NOT NULL"; $query->_qill[$grouping][] = ts('Activities which have Followup Activities'); } elseif ($value == 2) { - $query->_where[$grouping][] = "parent_id.parent_id IS NULL"; + $query->_where[$grouping][] = "civicrm_activity.parent_id IS NULL"; $query->_qill[$grouping][] = ts('Activities without Followup Activities'); } break; @@ -333,6 +371,18 @@ public static function whereClauseSingle(&$values, &$query) { $query->_qill[$grouping][] = ts('Activities which are not Followup Activities'); } break; + + case 'source_contact': + case 'source_contact_id': + $columnName = strstr($name, '_id') ? 'id' : 'sort_name'; + $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause("source_contact.{$columnName}", $op, $value, CRM_Utils_Type::typeToString($fields[$name]['type'])); + list($op, $value) = CRM_Contact_BAO_Query::buildQillForFieldValue('CRM_Contact_DAO_Contact', $columnName, $value, $op); + $query->_qill[$grouping][] = ts('%1 %2 %3', [ + 1 => $fields[$name]['title'], + 2 => $op, + 3 => $value, + ]); + break; } } @@ -373,17 +423,27 @@ public static function from($name, $mode, $side) { AND option_group_activity_type.id = activity_type.option_group_id ) "; break; + case 'activity_priority': + $from .= " $side JOIN civicrm_option_group option_group_activity_priority ON (option_group_activity_priority.name = 'priority')"; + $from .= " $side JOIN civicrm_option_value activity_priority ON (civicrm_activity.priority_id = activity_priority.value + AND option_group_activity_priority.id = activity_priority.option_group_id ) "; + break; + case 'civicrm_activity_tag': $from .= " $side JOIN civicrm_entity_tag as civicrm_activity_tag ON ( civicrm_activity_tag.entity_table = 'civicrm_activity' AND civicrm_activity_tag.entity_id = civicrm_activity.id ) "; break; case 'source_contact': - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); - $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts); + $sourceID = CRM_Core_PseudoConstant::getKey( + 'CRM_Activity_BAO_ActivityContact', + 'record_type_id', + 'Activity Source' + ); $from = " - LEFT JOIN civicrm_activity_contact ac - ON ( ac.activity_id = civicrm_activity_contact.activity_id AND ac.record_type_id = {$sourceID}) - INNER JOIN civicrm_contact source_contact ON (ac.contact_id = source_contact.id)"; + LEFT JOIN civicrm_activity_contact source_activity + ON (source_activity.activity_id = civicrm_activity_contact.activity_id + AND source_activity.record_type_id = {$sourceID}) + LEFT JOIN civicrm_contact source_contact ON (source_activity.contact_id = source_contact.id)"; break; case 'parent_id': @@ -395,48 +455,75 @@ public static function from($name, $mode, $side) { } /** - * Getter for the qill object. + * Get the metadata for fields to be included on the activity search form. * - * @return string + * @todo ideally this would be a trait included on the activity search & advanced search + * rather than a static function. */ - public function qill() { - return (isset($this->_qill)) ? $this->_qill : ""; + public static function getSearchFieldMetadata() { + $fields = ['activity_type_id', 'activity_date_time']; + $metadata = civicrm_api3('Activity', 'getfields', [])['values']; + return array_intersect_key($metadata, array_flip($fields)); } /** * Add all the elements shared between case activity search and advanced search. * - * @param CRM_Core_Form $form + * @param CRM_Core_Form_Search $form */ public static function buildSearchForm(&$form) { - $activityOptions = CRM_Core_PseudoConstant::activityType(TRUE, TRUE, FALSE, 'label', TRUE); - $form->addSelect('activity_type_id', - array('entity' => 'activity', 'label' => ts('Activity Type(s)'), 'multiple' => 'multiple', 'option_url' => NULL, 'placeholder' => ts('- any -')) - ); - - CRM_Core_Form_Date::buildDateRange($form, 'activity_date', 1, '_low', '_high', ts('From'), FALSE, FALSE); - $form->addElement('hidden', 'activity_date_range_error'); - $form->addFormRule(array('CRM_Activity_BAO_Query', 'formRule'), $form); + $form->addSearchFieldMetadata(['Activity' => self::getSearchFieldMetadata()]); + $form->addFormFieldsFromMetadata(); - $followUpActivity = array( + $followUpActivity = [ 1 => ts('Yes'), 2 => ts('No'), - ); - $form->addRadio('parent_id', NULL, $followUpActivity, array('allowClear' => TRUE)); - $form->addRadio('followup_parent_id', NULL, $followUpActivity, array('allowClear' => TRUE)); - $activityRoles = array( + ]; + $form->addRadio('parent_id', NULL, $followUpActivity, ['allowClear' => TRUE]); + $form->addRadio('followup_parent_id', NULL, $followUpActivity, ['allowClear' => TRUE]); + $activityRoles = [ 3 => ts('With'), 2 => ts('Assigned to'), 1 => ts('Added by'), - ); - $form->addRadio('activity_role', NULL, $activityRoles, array('allowClear' => TRUE)); - $form->setDefaults(array('activity_role' => 3)); - $activityStatus = CRM_Core_PseudoConstant::get('CRM_Activity_DAO_Activity', 'status_id', array('flip' => 1, 'labelColumn' => 'name')); + ]; + $form->addRadio('activity_role', NULL, $activityRoles, ['allowClear' => TRUE]); + $form->setDefaults(['activity_role' => 3]); + $activityStatus = CRM_Core_PseudoConstant::get('CRM_Activity_DAO_Activity', 'status_id', [ + 'flip' => 1, + 'labelColumn' => 'name', + ]); $form->addSelect('status_id', - array('entity' => 'activity', 'multiple' => 'multiple', 'option_url' => NULL, 'placeholder' => ts('- any -')) + [ + 'entity' => 'activity', + 'multiple' => 'multiple', + 'option_url' => NULL, + 'placeholder' => ts('- any -'), + ] + ); + $ssID = $form->get('ssID'); + $status = [$activityStatus['Completed'], $activityStatus['Scheduled']]; + //If status is saved in smart group. + if (!empty($ssID) && !empty($form->_formValues['activity_status_id'])) { + $status = $form->_formValues['activity_status_id']; + } + $form->setDefaults(['status_id' => $status]); + + $form->addElement('text', 'activity_text', ts('Activity Text'), CRM_Core_DAO::getAttribute('CRM_Contact_DAO_Contact', 'sort_name')); + + $form->addRadio('activity_option', '', CRM_Core_SelectValues::activityTextOptions()); + $form->setDefaults(['activity_option' => 'both']); + + $priority = CRM_Core_PseudoConstant::get('CRM_Activity_DAO_Activity', 'priority_id'); + $form->addSelect('priority_id', + [ + 'entity' => 'activity', + 'label' => ts('Priority'), + 'multiple' => 'multiple', + 'option_url' => NULL, + 'placeholder' => ts('- any -'), + ] ); - $form->setDefaults(array('status_id' => array($activityStatus['Completed'], $activityStatus['Scheduled']))); - $form->addElement('text', 'activity_subject', ts('Subject'), CRM_Core_DAO::getAttribute('CRM_Contact_DAO_Contact', 'sort_name')); + $form->addYesNo('activity_test', ts('Activity is a Test?')); $activity_tags = CRM_Core_BAO_Tag::getTags('civicrm_activity'); if ($activity_tags) { @@ -453,23 +540,13 @@ public static function buildSearchForm(&$form) { $surveys = CRM_Campaign_BAO_Survey::getSurveys(TRUE, FALSE, FALSE, TRUE); if ($surveys) { $form->add('select', 'activity_survey_id', ts('Survey / Petition'), - array('' => ts('- none -')) + $surveys, FALSE, - array('class' => 'crm-select2') - ); - } - $extends = array('Activity'); - $groupDetails = CRM_Core_BAO_CustomGroup::getGroupDetail(NULL, TRUE, $extends); - if ($groupDetails) { - $form->assign('activityGroupTree', $groupDetails); - foreach ($groupDetails as $group) { - foreach ($group['fields'] as $field) { - $fieldId = $field['id']; - $elementName = 'custom_' . $fieldId; - CRM_Core_BAO_CustomField::addQuickFormElement($form, $elementName, $fieldId, FALSE, TRUE); - } - } + ['' => ts('- none -')] + $surveys, FALSE, + ['class' => 'crm-select2'] + ); } + CRM_Core_BAO_Query::addCustomFormFields($form, ['Activity']); + CRM_Campaign_BAO_Campaign::addCampaignInComponentSearch($form, 'activity_campaign_id'); // Add engagement level CRM-7775. @@ -479,17 +556,19 @@ public static function buildSearchForm(&$form) { CRM_Campaign_BAO_Campaign::accessCampaign() ) { $buildEngagementLevel = TRUE; - $form->addSelect('activity_engagement_level', array('entity' => 'activity', 'context' => 'search')); + $form->addSelect('activity_engagement_level', [ + 'entity' => 'activity', + 'context' => 'search', + ]); // Add survey result field. $optionGroups = CRM_Campaign_BAO_Survey::getResultSets('name'); - $resultOptions = array(); + $resultOptions = []; foreach ($optionGroups as $gid => $name) { if ($name) { - $value = array(); $value = CRM_Core_OptionGroup::values($name); if (!empty($value)) { - while (list($k, $v) = each($value)) { + foreach ($value as $k => $v) { $resultOptions[$v] = $v; } } @@ -502,14 +581,18 @@ public static function buildSearchForm(&$form) { asort($resultOptions); $form->add('select', 'activity_result', ts("Survey Result"), $resultOptions, FALSE, - array('id' => 'activity_result', 'multiple' => 'multiple', 'class' => 'crm-select2') + [ + 'id' => 'activity_result', + 'multiple' => 'multiple', + 'class' => 'crm-select2', + ] ); } } $form->assign('buildEngagementLevel', $buildEngagementLevel); $form->assign('buildSurveyResult', $buildSurveyResult); - $form->setDefaults(array('activity_test' => 0)); + $form->setDefaults(['activity_test' => 0]); } /** @@ -521,7 +604,7 @@ public static function buildSearchForm(&$form) { public static function defaultReturnProperties($mode, $includeCustomFields = TRUE) { $properties = NULL; if ($mode & CRM_Contact_BAO_Query::MODE_ACTIVITY) { - $properties = array( + $properties = [ 'activity_id' => 1, 'contact_type' => 1, 'contact_sub_type' => 1, @@ -535,6 +618,7 @@ public static function defaultReturnProperties($mode, $includeCustomFields = TRU 'activity_location' => 1, 'activity_details' => 1, 'activity_status' => 1, + 'activity_priority' => 1, 'source_contact' => 1, 'source_record_id' => 1, 'activity_is_test' => 1, @@ -542,7 +626,7 @@ public static function defaultReturnProperties($mode, $includeCustomFields = TRU 'result' => 1, 'activity_engagement_level' => 1, 'parent_id' => 1, - ); + ]; if ($includeCustomFields) { // also get all the custom activity properties @@ -559,24 +643,61 @@ public static function defaultReturnProperties($mode, $includeCustomFields = TRU } /** - * Custom form rules. + * Get the list of fields required to populate the selector. * - * @param array $fields - * @param array $files - * @param CRM_Core_Form $form + * The default return properties array returns far too many fields for 'everyday use. Every field you add to this array + * kills a small kitten so add carefully. + */ + public static function selectorReturnProperties() { + $properties = [ + 'activity_id' => 1, + 'contact_type' => 1, + 'contact_sub_type' => 1, + 'sort_name' => 1, + 'display_name' => 1, + 'activity_type_id' => 1, + 'activity_subject' => 1, + 'activity_date_time' => 1, + 'activity_status_id' => 1, + 'source_contact' => 1, + 'source_record_id' => 1, + 'activity_is_test' => 1, + 'activity_campaign_id' => 1, + 'activity_engagement_level' => 1, + ]; + + return $properties; + } + + /** + * Where/qill clause for notes * - * @return bool|array + * @param array $values + * @param object $query */ - public static function formRule($fields, $files, $form) { - $errors = array(); + public static function whereClauseSingleActivityText(&$values, &$query) { + list($name, $op, $value, $grouping, $wildcard) = $values; + $activityOptionValues = $query->getWhereValues('activity_option', $grouping); + $activityOption = CRM_Utils_Array::value(2, $activityOptionValues, 6); - if (empty($fields['activity_date_low']) || empty($fields['activity_date_high'])) { - return TRUE; - } + $query->_useDistinct = TRUE; - CRM_Utils_Rule::validDateRange($fields, 'activity_date', $errors, ts('Activity Date')); + $label = ts('Activity Text (%1)', [1 => CRM_Utils_Array::value($activityOption, CRM_Core_SelectValues::activityTextOptions())]); + $clauses = []; + if ($activityOption % 2 == 0) { + $clauses[] = $query->buildClause('civicrm_activity.details', $op, $value, 'String'); + } + if ($activityOption % 3 == 0) { + $clauses[] = $query->buildClause('civicrm_activity.subject', $op, $value, 'String'); + } - return empty($errors) ? TRUE : $errors; + $query->_where[$grouping][] = "( " . implode(' OR ', $clauses) . " )"; + list($qillOp, $qillVal) = $query->buildQillForFieldValue(NULL, $name, $value, $op); + $query->_qill[$grouping][] = ts("%1 %2 '%3'", [ + 1 => $label, + 2 => $qillOp, + 3 => $qillVal, + ]); } } diff --git a/CRM/Activity/Controller/Search.php b/CRM/Activity/Controller/Search.php index fa7e16feb813..67f358a50d86 100644 --- a/CRM/Activity/Controller/Search.php +++ b/CRM/Activity/Controller/Search.php @@ -1,9 +1,9 @@ __table = 'civicrm_activity'; parent::__construct(); } + /** - * Returns foreign keys and entity references + * Returns foreign keys and entity references. * * @return array * [CRM_Core_Reference_Interface] */ - static function getReferenceColumns() { + public static function getReferenceColumns() { if (!isset(Civi::$statics[__CLASS__]['links'])) { - Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'phone_id', 'civicrm_phone', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'parent_id', 'civicrm_activity', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'relationship_id', 'civicrm_relationship', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'original_id', 'civicrm_activity', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'campaign_id', 'civicrm_campaign', 'id'); + Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'phone_id', 'civicrm_phone', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'parent_id', 'civicrm_activity', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'relationship_id', 'civicrm_relationship', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'original_id', 'civicrm_activity', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'campaign_id', 'civicrm_campaign', 'id'); CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']); } return Civi::$statics[__CLASS__]['links']; } + /** * Returns all the column names of this table * * @return array */ - static function &fields() { + public static function &fields() { if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = array( - 'activity_id' => array( + Civi::$statics[__CLASS__]['fields'] = [ + 'activity_id' => [ 'name' => 'id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Activity ID') , - 'description' => 'Unique Other Activity ID', - 'required' => true, - 'import' => true, + 'title' => ts('Activity ID'), + 'description' => ts('Unique Other Activity ID'), + 'required' => TRUE, + 'import' => TRUE, 'where' => 'civicrm_activity.id', 'headerPattern' => '', 'dataPattern' => '', - 'export' => true, - ) , - 'source_record_id' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_activity', + 'entity' => 'Activity', + 'bao' => 'CRM_Activity_BAO_Activity', + 'localizable' => 0, + ], + 'source_record_id' => [ 'name' => 'source_record_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Source Record') , - 'description' => 'Artificial FK to original transaction (e.g. contribution) IF it is not an Activity. Table can be figured out through activity_type_id, and further through component registry.', - ) , - 'activity_type_id' => array( + 'title' => ts('Source Record'), + 'description' => ts('Artificial FK to original transaction (e.g. contribution) IF it is not an Activity. Table can be figured out through activity_type_id, and further through component registry.'), + 'table_name' => 'civicrm_activity', + 'entity' => 'Activity', + 'bao' => 'CRM_Activity_BAO_Activity', + 'localizable' => 0, + ], + 'activity_type_id' => [ 'name' => 'activity_type_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Activity Type ID') , - 'description' => 'FK to civicrm_option_value.id, that has to be valid, registered activity type.', - 'required' => true, - 'import' => true, + '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, 'where' => 'civicrm_activity.activity_type_id', 'headerPattern' => '/(activity.)?type(.id$)/i', 'dataPattern' => '', - 'export' => true, + 'export' => TRUE, 'default' => '1', - 'html' => array( + 'table_name' => 'civicrm_activity', + 'entity' => 'Activity', + 'bao' => 'CRM_Activity_BAO_Activity', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'optionGroupName' => 'activity_type', 'optionEditPath' => 'civicrm/admin/options/activity_type', - ) - ) , - 'activity_subject' => array( + ] + ], + 'activity_subject' => [ 'name' => 'subject', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Subject') , - 'description' => 'The subject/purpose/short description of the activity.', + 'title' => ts('Subject'), + 'description' => ts('The subject/purpose/short description of the activity.'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - 'import' => true, + 'import' => TRUE, 'where' => 'civicrm_activity.subject', 'headerPattern' => '/(activity.)?subject/i', 'dataPattern' => '', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_activity', + 'entity' => 'Activity', + 'bao' => 'CRM_Activity_BAO_Activity', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'activity_date_time' => array( + ], + ], + 'activity_date_time' => [ 'name' => 'activity_date_time', 'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME, - 'title' => ts('Activity Date') , - 'description' => 'Date and time this activity is scheduled to occur. Formerly named scheduled_date_time.', - 'import' => true, + 'title' => ts('Activity Date'), + 'description' => ts('Date and time this activity is scheduled to occur. Formerly named scheduled_date_time.'), + 'import' => TRUE, 'where' => 'civicrm_activity.activity_date_time', 'headerPattern' => '/(activity.)?date(.time$)?/i', 'dataPattern' => '', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_activity', + 'entity' => 'Activity', + 'bao' => 'CRM_Activity_BAO_Activity', + 'localizable' => 0, + 'html' => [ 'type' => 'Select Date', - ) , - ) , - 'activity_duration' => array( + 'formatType' => 'activityDateTime', + ], + ], + 'activity_duration' => [ 'name' => 'duration', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Duration') , - 'description' => 'Planned or actual duration of activity expressed in minutes. Conglomerate of former duration_hours and duration_minutes.', - 'import' => true, + 'title' => ts('Duration'), + 'description' => ts('Planned or actual duration of activity expressed in minutes. Conglomerate of former duration_hours and duration_minutes.'), + 'import' => TRUE, 'where' => 'civicrm_activity.duration', 'headerPattern' => '/(activity.)?duration(s)?$/i', 'dataPattern' => '', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_activity', + 'entity' => 'Activity', + 'bao' => 'CRM_Activity_BAO_Activity', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'activity_location' => array( + ], + ], + 'activity_location' => [ 'name' => 'location', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Location') , - 'description' => 'Location of the activity (optional, open text).', + 'title' => ts('Location'), + 'description' => ts('Location of the activity (optional, open text).'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - 'import' => true, + 'import' => TRUE, 'where' => 'civicrm_activity.location', 'headerPattern' => '/(activity.)?location$/i', 'dataPattern' => '', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_activity', + 'entity' => 'Activity', + 'bao' => 'CRM_Activity_BAO_Activity', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'phone_id' => array( + ], + ], + 'phone_id' => [ 'name' => 'phone_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Phone (called) ID') , - 'description' => 'Phone ID of the number called (optional - used if an existing phone number is selected).', + 'title' => ts('Phone (called) ID'), + 'description' => ts('Phone ID of the number called (optional - used if an existing phone number is selected).'), + 'table_name' => 'civicrm_activity', + 'entity' => 'Activity', + 'bao' => 'CRM_Activity_BAO_Activity', + 'localizable' => 0, 'FKClassName' => 'CRM_Core_DAO_Phone', - 'html' => array( + 'html' => [ 'type' => 'EntityRef', - ) , - ) , - 'phone_number' => array( + ], + ], + 'phone_number' => [ 'name' => 'phone_number', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Phone (called) Number') , - 'description' => 'Phone number in case the number does not exist in the civicrm_phone table.', + 'title' => ts('Phone (called) Number'), + 'description' => ts('Phone number in case the number does not exist in the civicrm_phone table.'), 'maxlength' => 64, 'size' => CRM_Utils_Type::BIG, - 'html' => array( + 'table_name' => 'civicrm_activity', + 'entity' => 'Activity', + 'bao' => 'CRM_Activity_BAO_Activity', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'activity_details' => array( + ], + ], + 'activity_details' => [ 'name' => 'details', 'type' => CRM_Utils_Type::T_LONGTEXT, - 'title' => ts('Details') , - 'description' => 'Details about the activity (agenda, notes, etc).', - 'import' => true, + 'title' => ts('Details'), + 'description' => ts('Details about the activity (agenda, notes, etc).'), + 'import' => TRUE, 'where' => 'civicrm_activity.details', 'headerPattern' => '/(activity.)?detail(s)?$/i', 'dataPattern' => '', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_activity', + 'entity' => 'Activity', + 'bao' => 'CRM_Activity_BAO_Activity', + 'localizable' => 0, + 'html' => [ 'type' => 'RichTextEditor', - ) , - ) , - 'activity_status_id' => array( + ], + ], + 'activity_status_id' => [ 'name' => 'status_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Activity Status') , - 'description' => 'ID of the status this activity is currently in. Foreign key to civicrm_option_value.', - 'import' => true, + 'title' => ts('Activity Status'), + 'description' => ts('ID of the status this activity is currently in. Foreign key to civicrm_option_value.'), + 'import' => TRUE, 'where' => 'civicrm_activity.status_id', 'headerPattern' => '/(activity.)?status(.label$)?/i', 'dataPattern' => '', - 'export' => false, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_activity', + 'entity' => 'Activity', + 'bao' => 'CRM_Activity_BAO_Activity', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'optionGroupName' => 'activity_status', 'optionEditPath' => 'civicrm/admin/options/activity_status', - ) - ) , - 'priority_id' => array( + ] + ], + 'priority_id' => [ 'name' => 'priority_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Priority') , - 'description' => 'ID of the priority given to this activity. Foreign key to civicrm_option_value.', - 'html' => array( + 'title' => ts('Priority'), + 'description' => ts('ID of the priority given to this activity. Foreign key to civicrm_option_value.'), + 'table_name' => 'civicrm_activity', + 'entity' => 'Activity', + 'bao' => 'CRM_Activity_BAO_Activity', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'optionGroupName' => 'priority', 'optionEditPath' => 'civicrm/admin/options/priority', - ) - ) , - 'parent_id' => array( + ] + ], + 'parent_id' => [ 'name' => 'parent_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Parent Activity Id') , - 'description' => 'Parent meeting ID (if this is a follow-up item). This is not currently implemented', + 'title' => ts('Parent Activity Id'), + 'description' => ts('Parent meeting ID (if this is a follow-up item). This is not currently implemented'), + 'table_name' => 'civicrm_activity', + 'entity' => 'Activity', + 'bao' => 'CRM_Activity_BAO_Activity', + 'localizable' => 0, 'FKClassName' => 'CRM_Activity_DAO_Activity', - ) , - 'activity_is_test' => array( + ], + 'activity_is_test' => [ 'name' => 'is_test', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Test') , - 'import' => true, + 'title' => ts('Test'), + 'import' => TRUE, 'where' => 'civicrm_activity.is_test', 'headerPattern' => '/(is.)?test(.activity)?/i', 'dataPattern' => '', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'default' => '0', + 'table_name' => 'civicrm_activity', + 'entity' => 'Activity', + 'bao' => 'CRM_Activity_BAO_Activity', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - ) , - 'activity_medium_id' => array( + ], + ], + 'activity_medium_id' => [ 'name' => 'medium_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Activity Medium') , - 'description' => 'Activity Medium, Implicit FK to civicrm_option_value where option_group = encounter_medium.', + 'title' => ts('Activity Medium'), + 'description' => ts('Activity Medium, Implicit FK to civicrm_option_value where option_group = encounter_medium.'), 'default' => 'NULL', - 'html' => array( + 'table_name' => 'civicrm_activity', + 'entity' => 'Activity', + 'bao' => 'CRM_Activity_BAO_Activity', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'optionGroupName' => 'encounter_medium', 'optionEditPath' => 'civicrm/admin/options/encounter_medium', - ) - ) , - 'is_auto' => array( + ] + ], + 'is_auto' => [ 'name' => 'is_auto', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Auto') , - ) , - 'relationship_id' => array( + 'title' => ts('Auto'), + 'default' => '0', + 'table_name' => 'civicrm_activity', + 'entity' => 'Activity', + 'bao' => 'CRM_Activity_BAO_Activity', + 'localizable' => 0, + ], + 'relationship_id' => [ 'name' => 'relationship_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Relationship Id') , - 'description' => 'FK to Relationship ID', + 'title' => ts('Relationship Id'), + 'description' => ts('FK to Relationship ID'), 'default' => 'NULL', + 'table_name' => 'civicrm_activity', + 'entity' => 'Activity', + 'bao' => 'CRM_Activity_BAO_Activity', + 'localizable' => 0, 'FKClassName' => 'CRM_Contact_DAO_Relationship', - ) , - 'is_current_revision' => array( + ], + 'is_current_revision' => [ 'name' => 'is_current_revision', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Is this activity a current revision in versioning chain?') , - 'import' => true, + 'title' => ts('Is this activity a current revision in versioning chain?'), + 'import' => TRUE, 'where' => 'civicrm_activity.is_current_revision', 'headerPattern' => '/(is.)?(current.)?(revision|version(ing)?)/i', 'dataPattern' => '', - 'export' => true, + 'export' => TRUE, 'default' => '1', - 'html' => array( + 'table_name' => 'civicrm_activity', + 'entity' => 'Activity', + 'bao' => 'CRM_Activity_BAO_Activity', + 'localizable' => 0, + 'html' => [ 'type' => 'CheckBox', - ) , - ) , - 'original_id' => array( + ], + ], + 'original_id' => [ 'name' => 'original_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Original Activity ID ') , - 'description' => 'Activity ID of the first activity record in versioning chain.', + 'title' => ts('Original Activity ID '), + 'description' => ts('Activity ID of the first activity record in versioning chain.'), + 'table_name' => 'civicrm_activity', + 'entity' => 'Activity', + 'bao' => 'CRM_Activity_BAO_Activity', + 'localizable' => 0, 'FKClassName' => 'CRM_Activity_DAO_Activity', - ) , - 'activity_result' => array( + ], + 'activity_result' => [ 'name' => 'result', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Result') , - 'description' => 'Currently being used to store result id for survey activity, FK to option value.', + 'title' => ts('Result'), + 'description' => ts('Currently being used to store result id for survey activity, FK to option value.'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - 'html' => array( + 'table_name' => 'civicrm_activity', + 'entity' => 'Activity', + 'bao' => 'CRM_Activity_BAO_Activity', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'activity_is_deleted' => array( + ], + ], + 'activity_is_deleted' => [ 'name' => 'is_deleted', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Activity is in the Trash') , - 'import' => true, + 'title' => ts('Activity is in the Trash'), + 'import' => TRUE, 'where' => 'civicrm_activity.is_deleted', 'headerPattern' => '/(activity.)?(trash|deleted)/i', 'dataPattern' => '', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'default' => '0', + 'table_name' => 'civicrm_activity', + 'entity' => 'Activity', + 'bao' => 'CRM_Activity_BAO_Activity', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'activity_campaign_id' => array( + ], + ], + 'activity_campaign_id' => [ 'name' => 'campaign_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Campaign') , - 'description' => 'The campaign for which this activity has been triggered.', - 'import' => true, + 'title' => ts('Campaign'), + 'description' => ts('The campaign for which this activity has been triggered.'), + 'import' => TRUE, 'where' => 'civicrm_activity.campaign_id', 'headerPattern' => '', 'dataPattern' => '', - 'export' => true, + 'export' => TRUE, + 'table_name' => 'civicrm_activity', + 'entity' => 'Activity', + 'bao' => 'CRM_Activity_BAO_Activity', + 'localizable' => 0, 'FKClassName' => 'CRM_Campaign_DAO_Campaign', - 'html' => array( + 'html' => [ 'type' => 'CheckBox', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'table' => 'civicrm_campaign', 'keyColumn' => 'id', 'labelColumn' => 'title', - ) - ) , - 'activity_engagement_level' => array( + ] + ], + 'activity_engagement_level' => [ 'name' => 'engagement_level', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Engagement Index') , - 'description' => 'Assign a specific level of engagement to this activity. Used for tracking constituents in ladder of engagement.', - 'import' => true, + 'title' => ts('Engagement Index'), + 'description' => ts('Assign a specific level of engagement to this activity. Used for tracking constituents in ladder of engagement.'), + 'import' => TRUE, 'where' => 'civicrm_activity.engagement_level', 'headerPattern' => '', 'dataPattern' => '', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_activity', + 'entity' => 'Activity', + 'bao' => 'CRM_Activity_BAO_Activity', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'optionGroupName' => 'engagement_index', 'optionEditPath' => 'civicrm/admin/options/engagement_index', - ) - ) , - 'weight' => array( + ] + ], + 'weight' => [ 'name' => 'weight', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Order') , - 'html' => array( + 'title' => ts('Order'), + 'table_name' => 'civicrm_activity', + 'entity' => 'Activity', + 'bao' => 'CRM_Activity_BAO_Activity', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - ); + ], + ], + 'is_star' => [ + 'name' => 'is_star', + 'type' => CRM_Utils_Type::T_BOOLEAN, + 'title' => ts('Is Starred'), + 'description' => ts('Activity marked as favorite.'), + 'import' => TRUE, + 'where' => 'civicrm_activity.is_star', + 'headerPattern' => '/(activity.)?(star|favorite)/i', + 'dataPattern' => '', + 'export' => TRUE, + 'default' => '0', + 'table_name' => 'civicrm_activity', + 'entity' => 'Activity', + 'bao' => 'CRM_Activity_BAO_Activity', + 'localizable' => 0, + ], + 'activity_created_date' => [ + 'name' => 'created_date', + 'type' => CRM_Utils_Type::T_TIMESTAMP, + 'title' => ts('Created Date'), + 'description' => ts('When was the activity was created.'), + 'required' => FALSE, + 'export' => TRUE, + 'where' => 'civicrm_activity.created_date', + 'headerPattern' => '', + 'dataPattern' => '', + 'default' => 'NULL', + 'table_name' => 'civicrm_activity', + 'entity' => 'Activity', + 'bao' => 'CRM_Activity_BAO_Activity', + 'localizable' => 0, + ], + 'activity_modified_date' => [ + 'name' => 'modified_date', + 'type' => CRM_Utils_Type::T_TIMESTAMP, + 'title' => ts('Modified Date'), + 'description' => ts('When was the activity (or closely related entity) was created or modified or deleted.'), + 'required' => FALSE, + 'export' => TRUE, + 'where' => 'civicrm_activity.modified_date', + 'headerPattern' => '', + 'dataPattern' => '', + 'default' => 'CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP', + 'table_name' => 'civicrm_activity', + 'entity' => 'Activity', + 'bao' => 'CRM_Activity_BAO_Activity', + 'localizable' => 0, + ], + ]; CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } return Civi::$statics[__CLASS__]['fields']; } + /** * Return a mapping from field-name to the corresponding key (as used in fields()). * * @return array * Array(string $name => string $uniqueName). */ - static function &fieldKeys() { + public static function &fieldKeys() { if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); } return Civi::$statics[__CLASS__]['fieldKeys']; } + /** * Returns the names of this table * * @return string */ - static function getTableName() { + public static function getTableName() { return self::$_tableName; } + /** * Returns if this table needs to be logged * - * @return boolean + * @return bool */ - function getLog() { + public function getLog() { return self::$_log; } + /** * Returns the list of fields that can be imported * @@ -564,10 +736,11 @@ function getLog() { * * @return array */ - static function &import($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'activity', $prefix, array()); + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'activity', $prefix, []); return $r; } + /** * Returns the list of fields that can be exported * @@ -575,8 +748,78 @@ static function &import($prefix = false) { * * @return array */ - static function &export($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'activity', $prefix, array()); + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'activity', $prefix, []); return $r; } + + /** + * Returns the list of indices + * + * @param bool $localize + * + * @return array + */ + public static function indices($localize = TRUE) { + $indices = [ + 'UI_source_record_id' => [ + 'name' => 'UI_source_record_id', + 'field' => [ + 0 => 'source_record_id', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_activity::0::source_record_id', + ], + 'UI_activity_type_id' => [ + 'name' => 'UI_activity_type_id', + 'field' => [ + 0 => 'activity_type_id', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_activity::0::activity_type_id', + ], + 'index_activity_date_time' => [ + 'name' => 'index_activity_date_time', + 'field' => [ + 0 => 'activity_date_time', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_activity::0::activity_date_time', + ], + 'index_status_id' => [ + 'name' => 'index_status_id', + 'field' => [ + 0 => 'status_id', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_activity::0::status_id', + ], + 'index_medium_id' => [ + 'name' => 'index_medium_id', + 'field' => [ + 0 => 'medium_id', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_activity::0::medium_id', + ], + 'index_is_current_revision' => [ + 'name' => 'index_is_current_revision', + 'field' => [ + 0 => 'is_current_revision', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_activity::0::is_current_revision', + ], + 'index_is_deleted' => [ + 'name' => 'index_is_deleted', + 'field' => [ + 0 => 'is_deleted', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_activity::0::is_deleted', + ], + ]; + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + } diff --git a/CRM/Activity/DAO/ActivityContact.php b/CRM/Activity/DAO/ActivityContact.php index da7305f527f1..eab2e15f2aa4 100644 --- a/CRM/Activity/DAO/ActivityContact.php +++ b/CRM/Activity/DAO/ActivityContact.php @@ -1,183 +1,187 @@ __table = 'civicrm_activity_contact'; parent::__construct(); } + /** - * Returns foreign keys and entity references + * Returns foreign keys and entity references. * * @return array * [CRM_Core_Reference_Interface] */ - static function getReferenceColumns() { + public static function getReferenceColumns() { if (!isset(Civi::$statics[__CLASS__]['links'])) { - Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'activity_id', 'civicrm_activity', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'contact_id', 'civicrm_contact', 'id'); + Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'activity_id', 'civicrm_activity', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'contact_id', 'civicrm_contact', 'id'); CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']); } return Civi::$statics[__CLASS__]['links']; } + /** * Returns all the column names of this table * * @return array */ - static function &fields() { + public static function &fields() { if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = array( - 'id' => array( + Civi::$statics[__CLASS__]['fields'] = [ + 'id' => [ 'name' => 'id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Activity Contact ID') , - 'description' => 'Activity contact id', - 'required' => true, - ) , - 'activity_id' => array( + 'title' => ts('Activity Contact ID'), + 'description' => ts('Activity contact id'), + 'required' => TRUE, + 'table_name' => 'civicrm_activity_contact', + 'entity' => 'ActivityContact', + 'bao' => 'CRM_Activity_BAO_ActivityContact', + 'localizable' => 0, + ], + 'activity_id' => [ 'name' => 'activity_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Activity ID') , - 'description' => 'Foreign key to the activity for this record.', - 'required' => true, + 'title' => ts('Activity ID'), + 'description' => ts('Foreign key to the activity for this record.'), + 'required' => TRUE, + 'table_name' => 'civicrm_activity_contact', + 'entity' => 'ActivityContact', + 'bao' => 'CRM_Activity_BAO_ActivityContact', + 'localizable' => 0, 'FKClassName' => 'CRM_Activity_DAO_Activity', - ) , - 'contact_id' => array( + ], + 'contact_id' => [ 'name' => 'contact_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Contact ID (match to contact)') , - 'description' => 'Foreign key to the contact for this record.', - 'required' => true, - 'import' => true, + 'title' => ts('Contact ID (match to contact)'), + 'description' => ts('Foreign key to the contact for this record.'), + 'required' => TRUE, + 'import' => TRUE, 'where' => 'civicrm_activity_contact.contact_id', 'headerPattern' => '', 'dataPattern' => '', - 'export' => true, + 'export' => TRUE, + 'table_name' => 'civicrm_activity_contact', + 'entity' => 'ActivityContact', + 'bao' => 'CRM_Activity_BAO_ActivityContact', + 'localizable' => 0, 'FKClassName' => 'CRM_Contact_DAO_Contact', - ) , - 'record_type_id' => array( + ], + 'record_type_id' => [ 'name' => 'record_type_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Record Type ID') , - 'description' => 'Nature of this contact\'s role in the activity: 1 assignee, 2 creator, 3 focus or target.', - 'html' => array( + 'title' => ts('Record Type ID'), + 'description' => ts('Nature of this contact\'s role in the activity: 1 assignee, 2 creator, 3 focus or target.'), + 'table_name' => 'civicrm_activity_contact', + 'entity' => 'ActivityContact', + 'bao' => 'CRM_Activity_BAO_ActivityContact', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'optionGroupName' => 'activity_contacts', 'optionEditPath' => 'civicrm/admin/options/activity_contacts', - ) - ) , - ); + ] + ], + ]; CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } return Civi::$statics[__CLASS__]['fields']; } + /** * Return a mapping from field-name to the corresponding key (as used in fields()). * * @return array * Array(string $name => string $uniqueName). */ - static function &fieldKeys() { + public static function &fieldKeys() { if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); } return Civi::$statics[__CLASS__]['fieldKeys']; } + /** * Returns the names of this table * * @return string */ - static function getTableName() { + public static function getTableName() { return self::$_tableName; } + /** * Returns if this table needs to be logged * - * @return boolean + * @return bool */ - function getLog() { + public function getLog() { return self::$_log; } + /** * Returns the list of fields that can be imported * @@ -185,10 +189,11 @@ function getLog() { * * @return array */ - static function &import($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'activity_contact', $prefix, array()); + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'activity_contact', $prefix, []); return $r; } + /** * Returns the list of fields that can be exported * @@ -196,8 +201,42 @@ static function &import($prefix = false) { * * @return array */ - static function &export($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'activity_contact', $prefix, array()); + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'activity_contact', $prefix, []); return $r; } + + /** + * Returns the list of indices + * + * @param bool $localize + * + * @return array + */ + public static function indices($localize = TRUE) { + $indices = [ + 'UI_activity_contact' => [ + 'name' => 'UI_activity_contact', + 'field' => [ + 0 => 'contact_id', + 1 => 'activity_id', + 2 => 'record_type_id', + ], + 'localizable' => FALSE, + 'unique' => TRUE, + 'sig' => 'civicrm_activity_contact::1::contact_id::activity_id::record_type_id', + ], + 'index_record_type' => [ + 'name' => 'index_record_type', + 'field' => [ + 0 => 'activity_id', + 1 => 'record_type_id', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_activity_contact::0::activity_id::record_type_id', + ], + ]; + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + } diff --git a/CRM/Activity/Form/Activity.php b/CRM/Activity/Form/Activity.php index 9100fc80f22f..680188871e0c 100644 --- a/CRM/Activity/Form/Activity.php +++ b/CRM/Activity/Form/Activity.php @@ -1,9 +1,9 @@ _fields = array( - 'subject' => array( + $this->_fields = [ + 'subject' => [ 'type' => 'text', 'label' => ts('Subject'), - 'attributes' => CRM_Core_DAO::getAttribute('CRM_Activity_DAO_Activity', - 'subject' - ), - ), - 'duration' => array( - 'type' => 'text', + 'attributes' => CRM_Core_DAO::getAttribute('CRM_Activity_DAO_Activity', 'activity_subject'), + ], + 'duration' => [ + 'type' => 'number', 'label' => ts('Duration'), - 'attributes' => array('size' => 4, 'maxlength' => 8), + 'attributes' => ['class' => 'four', 'min' => 1], 'required' => FALSE, - ), - 'location' => array( + ], + 'location' => [ 'type' => 'text', 'label' => ts('Location'), 'attributes' => CRM_Core_DAO::getAttribute('CRM_Activity_DAO_Activity', 'location'), 'required' => FALSE, - ), - 'details' => array( + ], + 'details' => [ 'type' => 'wysiwyg', 'label' => ts('Details'), - 'attributes' => array('class' => 'huge'), + 'attributes' => ['class' => 'huge'], 'required' => FALSE, - ), - 'status_id' => array( + ], + 'status_id' => [ 'type' => 'select', 'required' => TRUE, - ), - 'priority_id' => array( + ], + 'priority_id' => [ 'type' => 'select', 'required' => TRUE, - ), - 'source_contact_id' => array( + ], + 'source_contact_id' => [ 'type' => 'entityRef', 'label' => ts('Added By'), 'required' => FALSE, - ), - 'target_contact_id' => array( + ], + 'target_contact_id' => [ 'type' => 'entityRef', 'label' => ts('With Contact'), - 'attributes' => array('multiple' => TRUE, 'create' => TRUE), - ), - 'assignee_contact_id' => array( + 'attributes' => ['multiple' => TRUE, 'create' => TRUE], + ], + 'assignee_contact_id' => [ 'type' => 'entityRef', 'label' => ts('Assigned to'), - 'attributes' => array( + 'attributes' => [ 'multiple' => TRUE, 'create' => TRUE, - 'api' => array('params' => array('is_deceased' => 0)), - ), - ), - 'followup_assignee_contact_id' => array( + 'api' => ['params' => ['is_deceased' => 0]], + ], + ], + 'activity_date_time' => [ + 'type' => 'datepicker', + 'label' => ts('Date'), + 'required' => TRUE, + ], + 'followup_assignee_contact_id' => [ 'type' => 'entityRef', 'label' => ts('Assigned to'), - 'attributes' => array( + 'attributes' => [ 'multiple' => TRUE, 'create' => TRUE, - 'api' => array('params' => array('is_deceased' => 0)), - ), - ), - 'followup_activity_type_id' => array( + 'api' => ['params' => ['is_deceased' => 0]], + ], + ], + 'followup_activity_type_id' => [ 'type' => 'select', 'label' => ts('Followup Activity'), - 'attributes' => array('' => '- ' . ts('select activity') . ' -') + $activityTypes, - 'extra' => array('class' => 'crm-select2'), - ), + 'attributes' => ['' => '- ' . ts('select activity') . ' -'] + $activityTypes, + 'extra' => ['class' => 'crm-select2'], + ], // Add optional 'Subject' field for the Follow-up Activiity, CRM-4491 - 'followup_activity_subject' => array( + 'followup_activity_subject' => [ 'type' => 'text', 'label' => ts('Subject'), - 'attributes' => CRM_Core_DAO::getAttribute('CRM_Activity_DAO_Activity', - 'subject' - ), - ), - ); + 'attributes' => CRM_Core_DAO::getAttribute('CRM_Activity_DAO_Activity', 'subject'), + ], + ]; } /** @@ -228,7 +249,7 @@ public function preProcess() { } $session = CRM_Core_Session::singleton(); - $this->_currentUserId = $session->get('userID'); + $this->_currentUserId = CRM_Core_Session::getLoggedInContactID(); $this->_currentlyViewedContactId = $this->get('contactId'); if (!$this->_currentlyViewedContactId) { @@ -238,11 +259,11 @@ public function preProcess() { // Give the context. if (!isset($this->_context)) { - $this->_context = CRM_Utils_Request::retrieve('context', 'String', $this); + $this->_context = CRM_Utils_Request::retrieve('context', 'Alphanumeric', $this); if (CRM_Contact_Form_Search::isSearchContext($this->_context)) { $this->_context = 'search'; } - elseif (!in_array($this->_context, array('dashlet', 'dashletFullscreen')) + elseif (!in_array($this->_context, ['dashlet', 'case', 'dashletFullscreen']) && $this->_currentlyViewedContactId ) { $this->_context = 'activity'; @@ -279,10 +300,10 @@ public function preProcess() { // Check for required permissions, CRM-6264. if ($this->_activityId && - in_array($this->_action, array( + in_array($this->_action, [ CRM_Core_Action::UPDATE, CRM_Core_Action::VIEW, - )) && + ]) && !CRM_Activity_BAO_Activity::checkPermission($this->_activityId, $this->_action) ) { CRM_Core_Error::fatal(ts('You do not have permission to access this page.')); @@ -291,6 +312,7 @@ public function preProcess() { CRM_Activity_BAO_Activity::checkPermission($this->_activityId, CRM_Core_Action::UPDATE) ) { $this->assign('permission', 'edit'); + $this->assign('allow_edit_inbound_emails', CRM_Activity_BAO_Activity::checkEditInboundEmailsPermissions()); } if (!$this->_activityTypeId && $this->_activityId) { @@ -312,7 +334,6 @@ public function preProcess() { // Set title. if (isset($activityTName)) { $activityName = CRM_Utils_Array::value($this->_activityTypeId, $activityTName); - $this->assign('pageTitle', ts('%1 Activity', array(1 => $activityName))); if ($this->_currentlyViewedContactId) { $displayName = CRM_Contact_BAO_Contact::displayName($this->_currentlyViewedContactId); @@ -323,7 +344,7 @@ public function preProcess() { CRM_Utils_System::setTitle($displayName . ' - ' . $activityName); } else { - CRM_Utils_System::setTitle(ts('%1 Activity', array(1 => $activityName))); + CRM_Utils_System::setTitle(ts('%1 Activity', [1 => $activityName])); } } @@ -377,7 +398,7 @@ public function preProcess() { if ($this->_action & CRM_Core_Action::VIEW) { // Get the tree of custom fields. - $this->_groupTree = CRM_Core_BAO_CustomGroup::getTree('Activity', $this, + $this->_groupTree = CRM_Core_BAO_CustomGroup::getTree('Activity', NULL, $this->_activityId, 0, $this->_activityTypeId ); } @@ -415,12 +436,12 @@ public function preProcess() { } $this->assign('searchKey', $qfKey); } - elseif (in_array($this->_context, array( + elseif (in_array($this->_context, [ 'standalone', 'home', 'dashlet', 'dashletFullscreen', - )) + ]) ) { $urlParams = 'reset=1'; $urlString = 'civicrm/dashboard'; @@ -494,17 +515,40 @@ public function preProcess() { $this->_values = $this->get('values'); if (!is_array($this->_values)) { - $this->_values = array(); + $this->_values = []; if (isset($this->_activityId) && $this->_activityId) { - $params = array('id' => $this->_activityId); + $params = ['id' => $this->_activityId]; CRM_Activity_BAO_Activity::retrieve($params, $this->_values); } + $this->set('values', $this->_values); } if ($this->_action & CRM_Core_Action::UPDATE) { + // We filter out alternatives, in case this is a stored e-mail, before sending to front-end + if (isset($this->_values['details'])) { + $this->_values['details'] = CRM_Utils_String::stripAlternatives($this->_values['details']) ?: ''; + } + + if ($this->_activityTypeName === 'Inbound Email' && + !CRM_Core_Permission::check('edit inbound email basic information and content') + ) { + $this->_fields['details']['type'] = 'static'; + } + CRM_Core_Form_RecurringEntity::preProcess('civicrm_activity'); } + + if ($this->_action & CRM_Core_Action::VIEW) { + $url = CRM_Utils_System::url(implode("/", $this->urlPath), "reset=1&id={$this->_activityId}&action=view&cid={$this->_values['source_contact_id']}"); + CRM_Utils_Recent::add(CRM_Utils_Array::value('subject', $this->_values, ts('(no subject)')), + $url, + $this->_values['id'], + 'Activity', + $this->_values['source_contact_id'], + $this->_values['source_contact'] + ); + } } /** @@ -519,16 +563,6 @@ public function setDefaultValues() { $defaults = $this->_values + CRM_Core_Form_RecurringEntity::setDefaultValues(); // if we're editing... if (isset($this->_activityId)) { - if (empty($defaults['activity_date_time'])) { - list($defaults['activity_date_time'], $defaults['activity_date_time_time']) = CRM_Utils_Date::setDateDefaults(NULL, 'activityDateTime'); - } - elseif ($this->_action & CRM_Core_Action::UPDATE) { - $this->assign('current_activity_date_time', $defaults['activity_date_time']); - list($defaults['activity_date_time'], - $defaults['activity_date_time_time'] - ) = CRM_Utils_Date::setDateDefaults($defaults['activity_date_time'], 'activityDateTime'); - list($defaults['repetition_start_date'], $defaults['repetition_start_date_time']) = CRM_Utils_Date::setDateDefaults($defaults['activity_date_time'], 'activityDateTime'); - } if ($this->_context != 'standalone') { $this->assign('target_contact_value', @@ -544,7 +578,7 @@ public function setDefaultValues() { $defaults['assignee_contact_id'] = CRM_Utils_Array::value('assignee_contact', $defaults); // set default tags if exists - $defaults['tag'] = CRM_Core_BAO_EntityTag::getTag($this->_activityId, 'civicrm_activity'); + $defaults['tag'] = implode(',', CRM_Core_BAO_EntityTag::getTag($this->_activityId, 'civicrm_activity')); } else { // if it's a new activity, we need to set default values for associated contact fields @@ -553,9 +587,10 @@ public function setDefaultValues() { $defaults['source_contact_id'] = $this->_sourceContactId; $defaults['target_contact_id'] = $this->_targetContactId; + } - list($defaults['activity_date_time'], $defaults['activity_date_time_time']) - = CRM_Utils_Date::setDateDefaults(NULL, 'activityDateTime'); + if (empty($defaults['activity_date_time'])) { + $defaults['activity_date_time'] = date('Y-m-d H:i:s'); } if ($this->_activityTypeId) { @@ -567,10 +602,10 @@ public function setDefaultValues() { } // CRM-15472 - 50 is around the practical limit of how many items a select2 entityRef can handle - if (!empty($defaults['target_contact_id'])) { + if ($this->_action == 2 && !empty($defaults['target_contact_id'])) { $count = count(is_array($defaults['target_contact_id']) ? $defaults['target_contact_id'] : explode(',', $defaults['target_contact_id'])); if ($count > 50) { - $this->freeze(array('target_contact_id')); + $this->freeze(['target_contact_id']); } } @@ -601,18 +636,18 @@ public function buildQuickForm() { if ($this->_action & CRM_Core_Action::RENEW) { $button = ts('Restore'); } - $this->addButtons(array( - array( + $this->addButtons([ + [ 'type' => 'next', 'name' => $button, 'spacing' => '         ', 'isDefault' => TRUE, - ), - array( + ], + [ 'type' => 'cancel', 'name' => ts('Cancel'), - ), - )); + ], + ]); return; } @@ -623,11 +658,11 @@ public function buildQuickForm() { $this->assign('suppressForm', FALSE); $element = &$this->add('select', 'activity_type_id', ts('Activity Type'), - array('' => '- ' . ts('select') . ' -') + $this->_fields['followup_activity_type_id']['attributes'], - FALSE, array( + ['' => '- ' . ts('select') . ' -'] + $this->_fields['followup_activity_type_id']['attributes'], + FALSE, [ 'onchange' => "CRM.buildCustomData( 'Activity', this.value );", 'class' => 'crm-select2 required', - ) + ] ); // Freeze for update mode. @@ -646,7 +681,7 @@ public function buildQuickForm() { $required = !empty($values['required']); if ($values['type'] == 'select' && empty($attribute)) { - $this->addSelect($field, array('entity' => 'activity'), $required); + $this->addSelect($field, ['entity' => 'activity'], $required); } elseif ($values['type'] == 'entityRef') { $this->addEntityRef($field, $values['label'], $attribute, $required); @@ -666,7 +701,7 @@ public function buildQuickForm() { CRM_Campaign_BAO_Campaign::accessCampaign() ) { $buildEngagementLevel = TRUE; - $this->addSelect('engagement_level', array('entity' => 'activity')); + $this->addSelect('engagement_level', ['entity' => 'activity']); $this->addRule('engagement_level', ts('Please enter the engagement index as a number (integers only).'), 'positiveInteger' @@ -690,7 +725,7 @@ public function buildQuickForm() { $responseOptions = CRM_Campaign_BAO_Survey::getResponsesOptions($surveyId); if ($responseOptions) { $this->add('select', 'result', ts('Result'), - array('' => ts('- select -')) + array_combine($responseOptions, $responseOptions) + ['' => ts('- select -')] + array_combine($responseOptions, $responseOptions) ); } $surveyTitle = NULL; @@ -702,18 +737,26 @@ public function buildQuickForm() { } $this->assign('surveyActivity', $this->_isSurveyActivity); - // this option should be available only during add mode - if ($this->_action != CRM_Core_Action::UPDATE) { - $this->add('advcheckbox', 'is_multi_activity', ts('Create a separate activity for each contact.')); + // Add the "Activity Separation" field + $actionIsAdd = $this->_action != CRM_Core_Action::UPDATE; + $separationIsPossible = $this->supportsActivitySeparation; + if ($actionIsAdd && $separationIsPossible) { + $this->addRadio( + 'separation', + ts('Activity Separation'), + [ + 'separate' => ts('Create separate activities for each contact'), + 'combined' => ts('Create one activity with all contacts together'), + ] + ); } $this->addRule('duration', ts('Please enter the duration as number of minutes (integers only).'), 'positiveInteger' ); - $this->addDateTime('activity_date_time', ts('Date'), TRUE, array('formatType' => 'activityDateTime')); // Add followup date. - $this->addDateTime('followup_date', ts('in'), FALSE, array('formatType' => 'activityDateTime')); + $this->add('datepicker', 'followup_date', ts('in')); // Only admins and case-workers can change the activity source if (!CRM_Core_Permission::check('administer CiviCRM') && $this->_context != 'caseActivity') { @@ -725,17 +768,18 @@ public function buildQuickForm() { $this->assign('customDataSubType', $this->_activityTypeId); $this->assign('entityID', $this->_activityId); - CRM_Core_BAO_Tag::getTags('civicrm_activity', $tags, NULL, - '  ', TRUE); + $tags = CRM_Core_BAO_Tag::getColorTags('civicrm_activity'); if (!empty($tags)) { - $this->add('select', 'tag', ts('Tags'), $tags, FALSE, - array('id' => 'tags', 'multiple' => 'multiple', 'class' => 'crm-select2 huge') - ); + $this->add('select2', 'tag', ts('Tags'), $tags, FALSE, [ + 'class' => 'huge', + 'placeholder' => ts('- select -'), + 'multiple' => TRUE, + ]); } // we need to hide activity tagset for special activities - $specialActivities = array('Open Case'); + $specialActivities = ['Open Case']; if (!in_array($this->_activityTypeName, $specialActivities)) { // build tag widget @@ -751,48 +795,47 @@ public function buildQuickForm() { // form should be frozen for view mode $this->freeze(); - $buttons = array(); - $buttons[] = array( - 'type' => 'cancel', - 'name' => ts('Done'), - ); - $this->addButtons($buttons); + $this->addButtons([ + [ + 'type' => 'cancel', + 'name' => ts('Done'), + ], + ]); } else { - $message = array( - 'completed' => ts('Are you sure? This is a COMPLETED activity with the DATE in the FUTURE. Click Cancel to change the date / status. Otherwise, click OK to save.'), - 'scheduled' => ts('Are you sure? This is a SCHEDULED activity with the DATE in the PAST. Click Cancel to change the date / status. Otherwise, click OK to save.'), - ); - $js = array('onclick' => "return activityStatus(" . json_encode($message) . ");"); - $this->addButtons(array( - array( + $this->addButtons([ + [ 'type' => 'upload', 'name' => ts('Save'), - 'js' => $js, 'isDefault' => TRUE, - ), - array( + 'submitOnce' => TRUE, + ], + [ 'type' => 'cancel', 'name' => ts('Cancel'), - ), - )); + ], + ]); } if ($this->_activityTypeFile) { $className = "CRM_{$this->_crmDir}_Form_Activity_{$this->_activityTypeFile}"; $className::buildQuickForm($this); - $this->addFormRule(array($className, 'formRule'), $this); + $this->addFormRule([$className, 'formRule'], $this); } - $this->addFormRule(array('CRM_Activity_Form_Activity', 'formRule'), $this); + $this->addFormRule(['CRM_Activity_Form_Activity', 'formRule'], $this); - if (Civi::settings()->get('activity_assignee_notification')) { - $this->assign('activityAssigneeNotification', TRUE); + $doNotNotifyAssigneeFor = (array) Civi::settings() + ->get('do_not_notify_assignees_for'); + if (($this->_activityTypeId && in_array($this->_activityTypeId, $doNotNotifyAssigneeFor)) || !Civi::settings() + ->get('activity_assignee_notification')) { + $this->assign('activityAssigneeNotification', FALSE); } else { - $this->assign('activityAssigneeNotification', FALSE); + $this->assign('activityAssigneeNotification', TRUE); } + $this->assign('doNotNotifyAssigneeFor', $doNotNotifyAssigneeFor); } /** @@ -812,7 +855,7 @@ public static function formRule($fields, $files, $self) { if (CRM_Utils_Array::value('_qf_Activity_next_', $fields) == 'Delete') { return TRUE; } - $errors = array(); + $errors = []; if ((array_key_exists('activity_type_id', $fields) || !$self->_single) && empty($fields['activity_type_id'])) { $errors['activity_type_id'] = ts('Activity Type is a required field'); } @@ -829,12 +872,22 @@ public static function formRule($fields, $files, $self) { } if (!empty($fields['followup_activity_type_id']) && empty($fields['followup_date'])) { - $errors['followup_date_time'] = ts('Followup date is a required field.'); + $errors['followup_date'] = ts('Followup date is a required field.'); } // Activity type is mandatory if subject or follow-up date is specified for an Follow-up activity, CRM-4515. if ((!empty($fields['followup_activity_subject']) || !empty($fields['followup_date'])) && empty($fields['followup_activity_type_id'])) { $errors['followup_activity_subject'] = ts('Follow-up Activity type is a required field.'); } + + // Check that a value has been set for the "activity separation" field if needed + $separationIsPossible = $self->supportsActivitySeparation; + $actionIsAdd = $self->_action == CRM_Core_Action::ADD; + $hasMultipleTargetContacts = !empty($fields['target_contact_id']) && strpos($fields['target_contact_id'], ',') !== FALSE; + $separationFieldIsEmpty = empty($fields['separation']); + if ($separationIsPossible && $actionIsAdd && $hasMultipleTargetContacts && $separationFieldIsEmpty) { + $errors['separation'] = ts('Activity Separation is a required field.'); + } + return $errors; } @@ -847,15 +900,15 @@ public static function formRule($fields, $files, $self) { */ public function postProcess($params = NULL) { if ($this->_action & CRM_Core_Action::DELETE) { - $deleteParams = array('id' => $this->_activityId); + $deleteParams = ['id' => $this->_activityId]; $moveToTrash = CRM_Case_BAO_Case::isCaseActivity($this->_activityId); CRM_Activity_BAO_Activity::deleteActivity($deleteParams, $moveToTrash); // delete tags for the entity - $tagParams = array( + $tagParams = [ 'entity_table' => 'civicrm_activity', 'entity_id' => $this->_activityId, - ); + ]; CRM_Core_BAO_EntityTag::del($tagParams); @@ -890,16 +943,13 @@ public function postProcess($params = NULL) { ); } - // store the date with proper format - $params['activity_date_time'] = CRM_Utils_Date::processDate($params['activity_date_time'], $params['activity_date_time_time']); - // format params as arrays - foreach (array('target', 'assignee', 'followup_assignee') as $name) { + foreach (['target', 'assignee', 'followup_assignee'] as $name) { if (!empty($params["{$name}_contact_id"])) { $params["{$name}_contact_id"] = explode(',', $params["{$name}_contact_id"]); } else { - $params["{$name}_contact_id"] = array(); + $params["{$name}_contact_id"] = []; } } @@ -919,13 +969,15 @@ public function postProcess($params = NULL) { $this->_activityId ); - $activity = array(); + $params['is_multi_activity'] = CRM_Utils_Array::value('separation', $params) == 'separate'; + + $activity = []; if (!empty($params['is_multi_activity']) && !CRM_Utils_Array::crmIsEmptyArray($params['target_contact_id']) ) { $targetContacts = $params['target_contact_id']; foreach ($targetContacts as $targetContactId) { - $params['target_contact_id'] = array($targetContactId); + $params['target_contact_id'] = [$targetContactId]; // save activity $activity[] = $this->processActivity($params); } @@ -935,7 +987,7 @@ public function postProcess($params = NULL) { $activity = $this->processActivity($params); } - $activityIds = empty($this->_activityIds) ? array($this->_activityId) : $this->_activityIds; + $activityIds = empty($this->_activityIds) ? [$this->_activityId] : $this->_activityIds; foreach ($activityIds as $activityId) { // set params for repeat configuration in create mode $params['entity_id'] = $activityId; @@ -954,7 +1006,7 @@ public function postProcess($params = NULL) { $params['schedule_reminder_id'] = $scheduleReminderDetails->id; } } - $params['dateColumns'] = array('activity_date_time'); + $params['dateColumns'] = ['activity_date_time']; // Set default repetition start if it was not provided. if (empty($params['repetition_start_date'])) { @@ -963,20 +1015,20 @@ public function postProcess($params = NULL) { // unset activity id unset($params['id']); - $linkedEntities = array( - array( + $linkedEntities = [ + [ 'table' => 'civicrm_activity_contact', - 'findCriteria' => array( + 'findCriteria' => [ 'activity_id' => $activityId, - ), - 'linkedColumns' => array('activity_id'), + ], + 'linkedColumns' => ['activity_id'], 'isRecurringEntityRecord' => FALSE, - ), - ); + ], + ]; CRM_Core_Form_RecurringEntity::postProcess($params, 'civicrm_activity', $linkedEntities); } - return array('activity' => $activity); + return ['activity' => $activity]; } /** @@ -988,8 +1040,8 @@ public function postProcess($params = NULL) { * @return self|null|object */ protected function processActivity(&$params) { - $activityAssigned = array(); - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityAssigned = []; + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts); // format assignee params if (!CRM_Utils_Array::crmIsEmptyArray($params['assignee_contact_id'])) { @@ -1009,11 +1061,13 @@ protected function processActivity(&$params) { $activity = CRM_Activity_BAO_Activity::create($params); // add tags if exists - $tagParams = array(); + $tagParams = []; if (!empty($params['tag'])) { - foreach ($params['tag'] as $tag) { - $tagParams[$tag] = 1; + if (!is_array($params['tag'])) { + $params['tag'] = explode(',', $params['tag']); } + + $tagParams = array_fill_keys($params['tag'], 1); } // Save static tags. @@ -1047,15 +1101,17 @@ protected function processActivity(&$params) { // send copy to assignee contacts.CRM-4509 $mailStatus = ''; - if (Civi::settings()->get('activity_assignee_notification')) { - $activityIDs = array($activity->id); + if (Civi::settings()->get('activity_assignee_notification') + && !in_array($activity->activity_type_id, Civi::settings() + ->get('do_not_notify_assignees_for'))) { + $activityIDs = [$activity->id]; if ($followupActivity) { - $activityIDs = array_merge($activityIDs, array($followupActivity->id)); + $activityIDs = array_merge($activityIDs, [$followupActivity->id]); } $assigneeContacts = CRM_Activity_BAO_ActivityAssignment::getAssigneeNames($activityIDs, TRUE, FALSE); if (!CRM_Utils_Array::crmIsEmptyArray($params['assignee_contact_id'])) { - $mailToContacts = array(); + $mailToContacts = []; // Build an associative array with unique email addresses. foreach ($activityAssigned as $id => $dnc) { @@ -1072,7 +1128,7 @@ protected function processActivity(&$params) { // Also send email to follow-up activity assignees if set if ($followupActivity) { - $mailToFollowupContacts = array(); + $mailToFollowupContacts = []; foreach ($assigneeContacts as $values) { if ($values['activity_id'] == $followupActivity->id) { $mailToFollowupContacts[$values['email']] = $values; @@ -1093,11 +1149,11 @@ protected function processActivity(&$params) { } CRM_Core_Session::setStatus(ts('Activity %1 has been saved. %2 %3', - array( + [ 1 => $subject, 2 => $followupStatus, 3 => $mailStatus, - ) + ] ), ts('Saved'), 'success'); return $activity; diff --git a/CRM/Activity/Form/ActivityFilter.php b/CRM/Activity/Form/ActivityFilter.php index 19b19acdc642..472c5b542c60 100644 --- a/CRM/Activity/Form/ActivityFilter.php +++ b/CRM/Activity/Form/ActivityFilter.php @@ -1,9 +1,9 @@ add('select', 'activity_type_filter_id', ts('Include'), array('' => ts('- all activity type(s) -')) + $activityOptions); - $this->add('select', 'activity_type_exclude_filter_id', ts('Exclude'), array('' => ts('- select activity type -')) + $activityOptions); + $this->add('select', 'activity_type_filter_id', ts('Include'), $activityOptions, FALSE, ['class' => 'crm-select2', 'multiple' => TRUE, 'placeholder' => ts('- all activity type(s) -')]); + $this->add('select', 'activity_type_exclude_filter_id', ts('Exclude'), $activityOptions, FALSE, ['class' => 'crm-select2', 'multiple' => TRUE, 'placeholder' => ts('- no types excluded -')]); + $this->addDatePickerRange('activity_date_time', ts('Date')); + $this->addSelect('status_id', + ['entity' => 'activity', 'multiple' => 'multiple', 'option_url' => NULL, 'placeholder' => ts('- any -')] + ); + $this->assign('suppressForm', TRUE); } @@ -56,13 +62,13 @@ public function buildQuickForm() { */ public function setDefaultValues() { // CRM-11761 retrieve user's activity filter preferences - $defaults = array(); - $session = CRM_Core_Session::singleton(); - $userID = $session->get('userID'); - if ($userID) { - $defaults = Civi::service('settings_manager') - ->getBagByContact(NULL, $userID) - ->get('activity_tab_filter'); + $defaults = []; + if (Civi::settings()->get('preserve_activity_tab_filter') && (CRM_Core_Session::getLoggedInContactID())) { + $defaults = Civi::contactSettings()->get('activity_tab_filter'); + } + // set Activity status 'Scheduled' by default only for dashlet + elseif (strstr(CRM_Utils_Array::value('q', $_GET), 'dashlet')) { + $defaults['status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'status_id', 'Scheduled'); } return $defaults; } diff --git a/CRM/Activity/Form/ActivityLinks.php b/CRM/Activity/Form/ActivityLinks.php index f0252ec75da0..36a27d14b39c 100644 --- a/CRM/Activity/Form/ActivityLinks.php +++ b/CRM/Activity/Form/ActivityLinks.php @@ -1,9 +1,9 @@ ts('Send an Email')); - } - } - - if ($contactId && CRM_SMS_BAO_Provider::activeProviderCount()) { - // Check for existence of a mobile phone and ! do not SMS privacy setting - $mobileTypeID = CRM_Core_OptionGroup::getValue('phone_type', 'Mobile', 'name'); - list($name, $phone, $doNotSMS) = CRM_Contact_BAO_Contact_Location::getPhoneDetails($contactId, $mobileTypeID); - - if (!$doNotSMS && $phone) { - $sendSMS = array($SMSId => ts('Send SMS')); - $activityTypes += $sendSMS; - } - } - // this returns activity types sorted by weight - $otherTypes = CRM_Core_PseudoConstant::activityType(FALSE); + $allTypes = CRM_Utils_Array::value('values', civicrm_api3('OptionValue', 'get', [ + 'option_group_id' => 'activity_type', + 'is_active' => 1, + 'options' => ['limit' => 0, 'sort' => 'weight'], + ])); - $activityTypes += $otherTypes; + $activityTypes = []; - foreach (array_keys($activityTypes) as $typeId) { - if ($typeId == $emailTypeId) { - $urls[$typeId] = CRM_Utils_System::url('civicrm/activity/email/add', - "{$urlParams}{$typeId}", FALSE, NULL, FALSE - ); + foreach ($allTypes as $act) { + $url = 'civicrm/activity/add'; + if ($act['name'] == 'Email') { + if (!CRM_Utils_Mail::validOutBoundMail() || !$contactId) { + continue; + } + list($name, $email, $doNotEmail, $onHold, $isDeceased) = CRM_Contact_BAO_Contact::getContactDetails($contactId); + if (!$doNotEmail && $email && !$isDeceased) { + $url = 'civicrm/activity/email/add'; + $act['label'] = ts('Send an Email'); + } + else { + continue; + } } - elseif ($typeId == $SMSId) { - $urls[$typeId] = CRM_Utils_System::url('civicrm/activity/sms/add', - "{$urlParams}{$typeId}", FALSE, NULL, FALSE - ); + elseif ($act['name'] == 'SMS') { + if (!$contactId || !CRM_SMS_BAO_Provider::activeProviderCount() || !CRM_Core_Permission::check('send SMS')) { + continue; + } + // Check for existence of a mobile phone and ! do not SMS privacy setting + try { + $phone = civicrm_api3('Phone', 'getsingle', [ + 'contact_id' => $contactId, + 'phone_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_Phone', 'phone_type_id', 'Mobile'), + 'return' => ['phone', 'contact_id'], + 'options' => ['limit' => 1, 'sort' => "is_primary DESC"], + 'api.Contact.getsingle' => [ + 'id' => '$value.contact_id', + 'return' => 'do_not_sms', + ], + ]); + } + catch (CiviCRM_API3_Exception $e) { + continue; + } + if (!$phone['api.Contact.getsingle']['do_not_sms'] && $phone['phone']) { + $url = 'civicrm/activity/sms/add'; + } + else { + continue; + } } - elseif ($typeId == $letterTypeId) { - $urls[$typeId] = CRM_Utils_System::url('civicrm/activity/pdf/add', - "{$urlParams}{$typeId}", FALSE, NULL, FALSE - ); + elseif ($act['name'] == 'Print PDF Letter') { + $url = 'civicrm/activity/pdf/add'; } - else { - $urls[$typeId] = CRM_Utils_System::url('civicrm/activity/add', - "{$urlParams}{$typeId}", FALSE, NULL, FALSE - ); + elseif (!empty($act['filter']) || (!empty($act['component_id']) && $act['component_id'] != '1')) { + continue; } + $act['url'] = CRM_Utils_System::url($url, + "{$urlParams}{$act['value']}", FALSE, NULL, FALSE + ); + $act += ['icon' => 'fa-plus-square-o']; + $activityTypes[$act['value']] = $act; } $self->assign('activityTypes', $activityTypes); - $self->assign('urls', $urls); $self->assign('suppressForm', TRUE); } diff --git a/CRM/Activity/Form/ActivityView.php b/CRM/Activity/Form/ActivityView.php index 0174a83f62f3..d0e884397168 100644 --- a/CRM/Activity/Form/ActivityView.php +++ b/CRM/Activity/Form/ActivityView.php @@ -1,9 +1,9 @@ pushUserContext($url); - $defaults = array(); - $params = array('id' => $activityId); + $defaults = []; + $params = ['id' => $activityId]; CRM_Activity_BAO_Activity::retrieve($params, $defaults); // Set activity type name and description to template. @@ -108,20 +108,29 @@ public function preProcess() { $values['attachment'] = CRM_Core_BAO_File::attachmentInfo('civicrm_activity', $activityId); $this->assign('values', $values); + + $url = CRM_Utils_System::url(implode("/", $this->urlPath), "reset=1&id={$activityId}&action=view&cid={$values['source_contact_id']}"); + CRM_Utils_Recent::add($this->_values['subject'], + $url, + $values['id'], + 'Activity', + $values['source_contact_id'], + $values['source_contact'] + ); } /** * Build the form object. */ public function buildQuickForm() { - $this->addButtons(array( - array( + $this->addButtons([ + [ 'type' => 'cancel', 'name' => ts('Done'), 'spacing' => '         ', 'isDefault' => TRUE, - ), - ) + ], + ] ); } diff --git a/CRM/Activity/Form/Search.php b/CRM/Activity/Form/Search.php index 14d23bdff827..4aa16252d5d6 100644 --- a/CRM/Activity/Form/Search.php +++ b/CRM/Activity/Form/Search.php @@ -1,9 +1,9 @@ _actionButtonName = $this->getButtonName('next', 'action'); $this->_done = FALSE; - $this->defaults = array(); - // we allow the controller to set force/reset externally, useful when we are being - // driven by the wizard framework - $this->_reset = CRM_Utils_Request::retrieve('reset', 'Boolean', CRM_Core_DAO::$_nullObject); - $this->_force = CRM_Utils_Request::retrieve('force', 'Boolean', $this, FALSE); - $this->_limit = CRM_Utils_Request::retrieve('limit', 'Positive', $this); - $this->_context = CRM_Utils_Request::retrieve('context', 'String', $this, FALSE, 'search'); - - $this->assign("context", $this->_context); + $this->loadStandardSearchOptionsFromUrl(); // get user submitted values // get it from controller only if form has been submitted, else preProcess has set this @@ -98,6 +98,15 @@ public function preProcess() { } else { $this->_formValues = $this->get('formValues'); + + if ($this->_force) { + // If we force the search then merge form values with url values + // and set submit values to form values. + // @todo this is not good security practice. Instead define the fields in metadata & use + // getEntityDefaults. + $this->_formValues = array_merge((array) $this->_formValues, CRM_Utils_Request::exportValues()); + $this->_submitValues = $this->_formValues; + } } if (empty($this->_formValues)) { @@ -163,9 +172,7 @@ public function buildQuickForm() { $this->addRowSelectors($rows); } - $permission = CRM_Core_Permission::getPermission(); - - $this->addTaskMenu(CRM_Activity_Task::permissionedTaskTitles($permission)); + $this->addTaskMenu(CRM_Activity_Task::permissionedTaskTitles(CRM_Core_Permission::getPermission())); } } @@ -192,15 +199,19 @@ public function postProcess() { if (!empty($_POST)) { $this->_formValues = $this->controller->exportValues($this->_name); - $specialParams = array( + $specialParams = [ 'activity_type_id', 'status_id', - 'activity_subject', - ); - $changeNames = array('status_id' => 'activity_status_id'); + 'priority_id', + 'activity_text', + ]; + $changeNames = [ + 'status_id' => 'activity_status_id', + 'priority_id' => 'activity_priority_id', + ]; + CRM_Contact_BAO_Query::processSpecialFormValue($this->_formValues, $specialParams, $changeNames); } - $this->fixFormValues(); if (isset($this->_ssID) && empty($_POST)) { @@ -281,7 +292,7 @@ public function fixFormValues() { $this->_defaults['activity_status_id'] = $status; } - $survey = CRM_Utils_Request::retrieve('survey', 'Positive', CRM_Core_DAO::$_nullObject); + $survey = CRM_Utils_Request::retrieve('survey', 'Positive'); if ($survey) { $this->_formValues['activity_survey_id'] = $this->_defaults['activity_survey_id'] = $survey; @@ -312,66 +323,9 @@ public function fixFormValues() { } } - // Added for membership search - - $signupType = CRM_Utils_Request::retrieve('signupType', 'Positive', - CRM_Core_DAO::$_nullObject - ); - - if ($signupType) { - $this->_formValues['activity_role'] = 1; - $this->_defaults['activity_role'] = 1; - $activityTypes = CRM_Core_PseudoConstant::activityType(TRUE, FALSE, FALSE, 'name'); - - $renew = CRM_Utils_Array::key('Membership Renewal', $activityTypes); - $signup = CRM_Utils_Array::key('Membership Signup', $activityTypes); - - switch ($signupType) { - case 3: // signups and renewals - $this->_formValues['activity_type_id'][$renew] = 1; - $this->_defaults['activity_type_id'][$renew] = 1; - case 1: // signups only - $this->_formValues['activity_type_id'][$signup] = 1; - $this->_defaults['activity_type_id'][$signup] = 1; - break; - - case 2: // renewals only - $this->_formValues['activity_type_id'][$renew] = 1; - $this->_defaults['activity_type_id'][$renew] = 1; - break; - } - } - - $dateLow = CRM_Utils_Request::retrieve('dateLow', 'String', - CRM_Core_DAO::$_nullObject - ); - - if ($dateLow) { - $dateLow = date('m/d/Y', strtotime($dateLow)); - $this->_formValues['activity_date_relative'] = 0; - $this->_defaults['activity_date_relative'] = 0; - $this->_formValues['activity_date_low'] = $dateLow; - $this->_defaults['activity_date_low'] = $dateLow; - } - - $dateHigh = CRM_Utils_Request::retrieve('dateHigh', 'String', - CRM_Core_DAO::$_nullObject - ); - - if ($dateHigh) { - // Activity date time assumes midnight at the beginning of the date - // This sets it to almost midnight at the end of the date - /* if ($dateHigh <= 99999999) { - $dateHigh = 1000000 * $dateHigh + 235959; - } */ - $dateHigh = date('m/d/Y', strtotime($dateHigh)); - $this->_formValues['activity_date_relative'] = 0; - $this->_defaults['activity_date_relative'] = 0; - $this->_formValues['activity_date_high'] = $dateHigh; - $this->_defaults['activity_date_high'] = $dateHigh; - } - // Enable search activity by custom value + // @todo this is not good security practice. Instead define entity fields in metadata & + // use getEntity Defaults $requestParams = CRM_Utils_Request::exportValues(); foreach (array_keys($requestParams) as $key) { if (substr($key, 0, 7) != 'custom_') { @@ -399,6 +353,16 @@ public function getFormValues() { return NULL; } + /** + * This virtual function is used to set the default values of various form elements. + * + * @return array|NULL + * reference to the array of default values + */ + public function setDefaultValues() { + return array_merge($this->getEntityDefaults($this->getDefaultEntity()), (array) $this->_formValues); + } + /** * Return a descriptive name for the page, used in wizard header * @@ -408,4 +372,8 @@ public function getTitle() { return ts('Find Activities'); } + protected function getEntityMetadata() { + return CRM_Activity_BAO_Query::getSearchFieldMetadata(); + } + } diff --git a/CRM/Activity/Form/Task.php b/CRM/Activity/Form/Task.php index bb64bcb7bcc4..d64c173297b1 100644 --- a/CRM/Activity/Form/Task.php +++ b/CRM/Activity/Form/Task.php @@ -1,9 +1,9 @@ _activityHolderIds = array(); + public static function preProcessCommon(&$form) { + $form->_activityHolderIds = []; $values = $form->controller->exportValues($form->get('searchFormName')); @@ -93,7 +65,7 @@ public static function preProcessCommon(&$form, $useTable = FALSE) { $activityTasks = CRM_Activity_Task::tasks(); $form->assign('taskName', $activityTasks[$form->_task]); - $ids = array(); + $ids = []; if ($values['radio_ts'] == 'ts_sel') { foreach ($values as $name => $value) { if (substr($name, 0, CRM_Core_Form::CB_PREFIX_LEN) == CRM_Core_Form::CB_PREFIX) { @@ -113,9 +85,9 @@ public static function preProcessCommon(&$form, $useTable = FALSE) { $activityClause = NULL; $components = CRM_Core_Component::getNames(); - $componentClause = array(); + $componentClause = []; foreach ($components as $componentID => $componentName) { - if (!CRM_Core_Permission::check("access $componentName")) { + if ($componentName != 'CiviCase' && !CRM_Core_Permission::check("access $componentName")) { $componentClause[] = " (activity_type.component_id IS NULL OR activity_type.component_id <> {$componentID}) "; } } @@ -164,7 +136,7 @@ public static function preProcessCommon(&$form, $useTable = FALSE) { public function setContactIDs() { $IDs = implode(',', $this->_activityHolderIds); - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts); $query = " SELECT contact_id @@ -191,17 +163,17 @@ public function setContactIDs() { * @param bool $submitOnce */ public function addDefaultButtons($title, $nextType = 'next', $backType = 'back', $submitOnce = FALSE) { - $this->addButtons(array( - array( + $this->addButtons([ + [ 'type' => $nextType, 'name' => $title, 'isDefault' => TRUE, - ), - array( + ], + [ 'type' => $backType, 'name' => ts('Cancel'), - ), - )); + ], + ]); } } diff --git a/CRM/Activity/Form/Task/AddToTag.php b/CRM/Activity/Form/Task/AddToTag.php index 528d593d2f8b..f712d449246d 100644 --- a/CRM/Activity/Form/Task/AddToTag.php +++ b/CRM/Activity/Form/Task/AddToTag.php @@ -1,9 +1,9 @@ addFormRule(array('CRM_Activity_Form_Task_AddToTag', 'formRule')); + $this->addFormRule(['CRM_Activity_Form_Task_AddToTag', 'formRule']); } /** @@ -81,7 +81,7 @@ public function addRules() { * @return array */ public static function formRule($form, $rule) { - $errors = array(); + $errors = []; if (empty($form['tag']) && empty($form['activity_taglist'])) { $errors['_qf_default'] = ts("Please select at least one tag."); } @@ -94,7 +94,7 @@ public static function formRule($form, $rule) { public function postProcess() { // Get the submitted values in an array. $params = $this->controller->exportValues($this->_name); - $activityTags = $tagList = array(); + $activityTags = $tagList = []; // check if contact tags exists if (!empty($params['tag'])) { @@ -131,22 +131,22 @@ public function postProcess() { // merge activity and taglist tags $allTags = CRM_Utils_Array::crmArrayMerge($activityTags, $tagList); - $this->_name = array(); + $this->_name = []; foreach ($allTags as $key => $dnc) { $this->_name[] = $this->_tags[$key]; list($total, $added, $notAdded) = CRM_Core_BAO_EntityTag::addEntitiesToTag($this->_activityHolderIds, $key, 'civicrm_activity', FALSE); - $status = array(ts('Activity tagged', array('count' => $added, 'plural' => '%count activities tagged'))); + $status = [ts('Activity tagged', ['count' => $added, 'plural' => '%count activities tagged'])]; if ($notAdded) { - $status[] = ts('1 activity already had this tag', array( + $status[] = ts('1 activity already had this tag', [ 'count' => $notAdded, 'plural' => '%count activities already had this tag', - )); + ]); } $status = '
  • ' . implode('
  • ', $status) . '
'; - CRM_Core_Session::setStatus($status, ts("Added Tag %1", array(1 => $this->_tags[$key])), 'success', array('expires' => 0)); + CRM_Core_Session::setStatus($status, ts("Added Tag %1", [1 => $this->_tags[$key]]), 'success', ['expires' => 0]); } } diff --git a/CRM/Activity/Form/Task/Batch.php b/CRM/Activity/Form/Task/Batch.php index afc3eb931c7c..9bdb768b8b50 100644 --- a/CRM/Activity/Form/Task/Batch.php +++ b/CRM/Activity/Form/Task/Batch.php @@ -1,9 +1,9 @@ ts('Added By'), 'target_sort_name' => ts('With Contact')), + $readOnlyFields = array_merge(['sort_name' => ts('Added By'), 'target_sort_name' => ts('With Contact')], CRM_Core_BAO_Setting::valueOptions(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'contact_autocomplete_options', TRUE, NULL, FALSE, 'name', TRUE @@ -78,8 +80,8 @@ public function preProcess() { if (!empty($contactDetails)) { foreach ($contactDetails as $key => $value) { $assignee = CRM_Activity_BAO_ActivityAssignment::retrieveAssigneeIdsByActivityId($key); - foreach ($assignee as $keys => $values) { - $assigneeContact[] = CRM_Contact_BAO_Contact::displayname($values); + foreach ($assignee as $values) { + $assigneeContact[] = CRM_Contact_BAO_Contact::displayName($values); } $contactDetails[$key]['assignee_display_name'] = !empty($assigneeContact) ? implode(';', $assigneeContact) : NULL; } @@ -95,20 +97,20 @@ public function buildQuickForm() { $ufGroupId = $this->get('ufGroupId'); if (!$ufGroupId) { - CRM_Core_Error::fatal('ufGroupId is missing'); + throw new CRM_Core_Exception('The profile id is missing'); } $this->_title = ts('Update multiple activities') . ' - ' . CRM_Core_BAO_UFGroup::getTitle($ufGroupId); CRM_Utils_System::setTitle($this->_title); $this->addDefaultButtons(ts('Save')); - $this->_fields = array(); + $this->_fields = []; $this->_fields = CRM_Core_BAO_UFGroup::getFields($ufGroupId, FALSE, CRM_Core_Action::VIEW); // remove file type field and then limit fields $suppressFields = FALSE; - $removehtmlTypes = array('File', 'Autocomplete-Select'); + $removehtmlTypes = ['File']; foreach ($this->_fields as $name => $field) { - if ($cfID = CRM_Core_BAO_CustomField::getKeyID($name) && + if (CRM_Core_BAO_CustomField::getKeyID($name) && in_array($this->_fields[$name]['html_type'], $removehtmlTypes) ) { $suppressFields = TRUE; @@ -124,25 +126,24 @@ public function buildQuickForm() { $this->_fields = array_slice($this->_fields, 0, $this->_maxFields); - $this->addButtons(array( - array( + $this->addButtons([ + [ 'type' => 'submit', 'name' => ts('Update Activities'), 'isDefault' => TRUE, - ), - array( + ], + [ 'type' => 'cancel', 'name' => ts('Cancel'), - ), - )); + ], + ]); $this->assign('profileTitle', $this->_title); $this->assign('componentIds', $this->_activityHolderIds); - $fileFieldExists = FALSE; // Load all campaigns. if (array_key_exists('activity_campaign_id', $this->_fields)) { - $this->_componentCampaigns = array(); + $this->_componentCampaigns = []; CRM_Core_PseudoConstant::populate($this->_componentCampaigns, 'CRM_Activity_DAO_Activity', TRUE, 'campaign_id', 'id', @@ -151,6 +152,10 @@ public function buildQuickForm() { } $customFields = CRM_Core_BAO_CustomField::getFields('Activity'); + // It is possible to have fields that are required in CiviCRM not be required in the + // profile. Overriding that here. Perhaps a better approach would be to + // make them required in the schema & read that up through getFields functionality. + $requiredFields = ['activity_date_time']; foreach ($this->_activityHolderIds as $activityId) { $typeId = CRM_Core_DAO::getFieldValue("CRM_Activity_DAO_Activity", $activityId, 'activity_type_id'); @@ -170,6 +175,9 @@ public function buildQuickForm() { } else { // Handle non custom fields. + if (in_array($field['name'], $requiredFields)) { + $field['is_required'] = TRUE; + } CRM_Core_BAO_UFGroup::buildProfile($this, $field, NULL, $activityId); } } @@ -181,7 +189,7 @@ public function buildQuickForm() { // $buttonName = $this->controller->getButtonName('submit'); if ($suppressFields) { - CRM_Core_Session::setStatus(ts("File or Autocomplete-Select type field(s) in the selected profile are not supported for Update multiple activities."), ts('Some Fields Excluded'), 'info'); + CRM_Core_Session::setStatus(ts("File type field(s) in the selected profile are not supported for Update multiple activities."), ts('Some Fields Excluded'), 'info'); } $this->addDefaultButtons(ts('Update Activities')); @@ -195,9 +203,8 @@ public function setDefaultValues() { return; } - $defaults = array(); + $defaults = []; foreach ($this->_activityHolderIds as $activityId) { - $details[$activityId] = array(); CRM_Core_BAO_UFGroup::setProfileDefaults(NULL, $this->_fields, $defaults, FALSE, $activityId, 'Activity'); } @@ -218,10 +225,6 @@ public function postProcess() { ); $value['id'] = $key; - if (!empty($value['activity_date_time'])) { - $value['activity_date_time'] = CRM_Utils_Date::processDate($value['activity_date_time'], $value['activity_date_time_time']); - } - if (!empty($value['activity_status_id'])) { $value['status_id'] = $value['activity_status_id']; } @@ -230,10 +233,6 @@ public function postProcess() { $value['details'] = $value['activity_details']; } - if (!empty($value['activity_duration'])) { - $value['duration'] = $value['activity_duration']; - } - if (!empty($value['activity_location'])) { $value['location'] = $value['activity_location']; } @@ -242,30 +241,12 @@ public function postProcess() { $value['subject'] = $value['activity_subject']; } - $query = " -SELECT a.activity_type_id, ac.contact_id -FROM civicrm_activity a -JOIN civicrm_activity_contact ac ON ( ac.activity_id = a.id -AND ac.record_type_id = %2 ) -WHERE a.id = %1 "; - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); - $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts); - $params = array(1 => array($key, 'Integer'), 2 => array($sourceID, 'Integer')); - $dao = CRM_Core_DAO::executeQuery($query, $params); - $dao->fetch(); - - // Get Activity Type ID - $value['activity_type_id'] = $dao->activity_type_id; - - // Get Conatct ID - $value['source_contact_id'] = $dao->contact_id; - - // make call use API 3 - $value['version'] = 3; - - $activityId = civicrm_api('activity', 'update', $value); + $activityId = civicrm_api3('activity', 'create', $value); - // add custom field values + // @todo this would be done by the api call above if the parames were passed through. + // @todo extract submit functions & + // extend CRM_Event_Form_Task_BatchTest::testSubmit with a data provider to test + // handling of custom data, specifically checkbox fields. if (!empty($value['custom']) && is_array($value['custom']) ) { diff --git a/CRM/Activity/Form/Task/Delete.php b/CRM/Activity/Form/Task/Delete.php index 1339a4b0e05f..7fa2bef6ca80 100644 --- a/CRM/Activity/Form/Task/Delete.php +++ b/CRM/Activity/Form/Task/Delete.php @@ -1,9 +1,9 @@ '%count activities deleted.', 'count' => $deleted)); + $msg = ts('%count activity deleted.', ['plural' => '%count activities deleted.', 'count' => $deleted]); CRM_Core_Session::setStatus($msg, ts('Removed'), 'success'); } if ($failed) { - CRM_Core_Session::setStatus(ts('1 could not be deleted.', array('plural' => '%count could not be deleted.', 'count' => $failed)), ts('Error'), 'error'); + CRM_Core_Session::setStatus(ts('1 could not be deleted.', ['plural' => '%count could not be deleted.', 'count' => $failed]), ts('Error'), 'error'); } } diff --git a/CRM/Activity/Form/Task/Email.php b/CRM/Activity/Form/Task/Email.php index 019bc7731315..4f6db5136b1a 100644 --- a/CRM/Activity/Form/Task/Email.php +++ b/CRM/Activity/Form/Task/Email.php @@ -1,9 +1,9 @@ addEntityRef('unclosed_case_id', ts('Select Case'), array('entity' => 'Case'), TRUE); + $this->addEntityRef('unclosed_case_id', ts('Select Case'), ['entity' => 'Case'], TRUE); $this->addDefaultButtons(ts('Save')); } @@ -80,8 +82,8 @@ public function postProcess() { $caseId = $formparams['unclosed_case_id']; $filedActivities = 0; foreach ($this->_activityHolderIds as $key => $id) { - $targetContactValues = $defaults = array(); - $params = array('id' => $id); + $targetContactValues = $defaults = []; + $params = ['id' => $id]; CRM_Activity_BAO_Activity::retrieve($params, $defaults); if (CRM_Case_BAO_Case::checkPermission($id, 'File On Case', $defaults['activity_type_id'])) { @@ -92,13 +94,13 @@ public function postProcess() { $targetContactValues = implode(',', array_keys($targetContactValues)); } - $params = array( + $params = [ 'caseID' => $caseId, 'activityID' => $id, 'newSubject' => empty($defaults['subject']) ? '' : $defaults['subject'], 'targetContactIds' => $targetContactValues, 'mode' => 'file', - ); + ]; $error_msg = CRM_Activity_Page_AJAX::_convertToCaseActivity($params); if (empty($error_msg['error_msg'])) { @@ -109,16 +111,17 @@ public function postProcess() { } } else { - CRM_Core_Session::setStatus(ts('Not permitted to file activity %1 %2.', array( + CRM_Core_Session::setStatus( + ts('Not permitted to file activity %1 %2.', [ 1 => empty($defaults['subject']) ? '' : $defaults['subject'], 2 => $defaults['activity_date_time'], - )), + ]), ts("Error"), "error"); } } CRM_Core_Session::setStatus($filedActivities, ts("Filed Activities"), "success"); - CRM_Core_Session::setStatus("", ts('Total Selected Activities: %1', array(1 => count($this->_activityHolderIds))), "info"); + CRM_Core_Session::setStatus("", ts('Total Selected Activities: %1', [1 => count($this->_activityHolderIds)]), "info"); } } diff --git a/CRM/Activity/Form/Task/PickOption.php b/CRM/Activity/Form/Task/PickOption.php index 342e4d7541c7..bef28a6b3b4a 100644 --- a/CRM/Activity/Form/Task/PickOption.php +++ b/CRM/Activity/Form/Task/PickOption.php @@ -1,9 +1,9 @@ _activityHolderIds) > $this->_maxActivities) { - CRM_Core_Session::setStatus(ts("The maximum number of Activities you can select to send an email is %1. You have selected %2. Please select fewer Activities from your search results and try again.", array( + CRM_Core_Session::setStatus(ts("The maximum number of Activities you can select to send an email is %1. You have selected %2. Please select fewer Activities from your search results and try again.", [ 1 => $this->_maxActivities, 2 => count($this->_activityHolderIds), - )), ts("Maximum Exceeded"), "error"); + ]), ts("Maximum Exceeded"), "error"); $validate = TRUE; } // then redirect @@ -92,7 +95,7 @@ public function buildQuickForm() { $this->addElement('checkbox', 'with_contact', ts('With Contact')); $this->addElement('checkbox', 'assigned_to', ts('Assigned to Contact')); $this->addElement('checkbox', 'created_by', ts('Created by')); - $this->setDefaults(array('with_contact' => 1)); + $this->setDefaults(['with_contact' => 1]); $this->addDefaultButtons(ts('Continue')); } @@ -100,7 +103,7 @@ public function buildQuickForm() { * Add local and global form rules. */ public function addRules() { - $this->addFormRule(array('CRM_Activity_Form_Task_PickOption', 'formRule')); + $this->addFormRule(['CRM_Activity_Form_Task_PickOption', 'formRule']); } /** @@ -117,7 +120,7 @@ public static function formRule($fields) { !isset($fields['assigned_to']) && !isset($fields['created_by']) ) { - return array('with_contact' => ts('You must select at least one email recipient type.')); + return ['with_contact' => ts('You must select at least one email recipient type.')]; } return TRUE; } @@ -129,9 +132,9 @@ public function postProcess() { // Clear any formRule errors from Email form in case they came back here via Cancel button $this->controller->resetPage('Email'); $params = $this->exportValues(); - $this->_contacts = array(); + $this->_contacts = []; - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts); $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts); // Get assignee contacts. diff --git a/CRM/Activity/Form/Task/PickProfile.php b/CRM/Activity/Form/Task/PickProfile.php index de0d799c8018..1221a21c4abd 100644 --- a/CRM/Activity/Form/Task/PickProfile.php +++ b/CRM/Activity/Form/Task/PickProfile.php @@ -1,9 +1,9 @@ _activityHolderIds) > $this->_maxActivities) { - CRM_Core_Session::setStatus(ts("The maximum number of activities you can select for Update multiple activities is %1. You have selected %2. Please select fewer Activities from your search results and try again.", array( + CRM_Core_Session::setStatus(ts("The maximum number of activities you can select for Update multiple activities is %1. You have selected %2. Please select fewer Activities from your search results and try again.", [ 1 => $this->_maxActivities, 2 => count($this->_activityHolderIds), - )), ts('Maximum Exceeded'), 'error'); + ]), ts('Maximum Exceeded'), 'error'); $validate = TRUE; } @@ -85,11 +87,11 @@ public function preProcess() { * Build the form object. */ public function buildQuickForm() { - $types = array('Activity'); + $types = ['Activity']; $profiles = CRM_Core_BAO_UFGroup::getProfiles($types, TRUE); $activityTypeIds = array_flip(CRM_Core_PseudoConstant::activityType(TRUE, FALSE, FALSE, 'name')); - $nonEditableActivityTypeIds = array( + $nonEditableActivityTypeIds = [ $activityTypeIds['Email'], $activityTypeIds['Bulk Email'], $activityTypeIds['Contribution'], @@ -99,7 +101,7 @@ public function buildQuickForm() { $activityTypeIds['Membership Renewal'], $activityTypeIds['Event Registration'], $activityTypeIds['Pledge Acknowledgment'], - ); + ]; $notEditable = FALSE; foreach ($this->_activityHolderIds as $activityId) { $typeId = CRM_Core_DAO::getFieldValue("CRM_Activity_DAO_Activity", $activityId, 'activity_type_id'); @@ -110,7 +112,7 @@ public function buildQuickForm() { } if (empty($profiles)) { - CRM_Core_Session::setStatus(ts("You will need to create a Profile containing the %1 fields you want to edit before you can use Update multiple activities. Navigate to Administer > Customize Data and Screens > Profiles to configure a Profile. Consult the online Administrator documentation for more information.", array(1 => $types[0])), ts("No Profile Configured"), "alert"); + CRM_Core_Session::setStatus(ts("You will need to create a Profile containing the %1 fields you want to edit before you can use Update multiple activities. Navigate to Administer > Customize Data and Screens > Profiles to configure a Profile. Consult the online Administrator documentation for more information.", [1 => $types[0]]), ts("No Profile Configured"), "alert"); CRM_Utils_System::redirect($this->_userContext); } elseif ($notEditable) { @@ -119,9 +121,9 @@ public function buildQuickForm() { } $ufGroupElement = $this->add('select', 'uf_group_id', ts('Select Profile'), - array( + [ '' => ts('- select profile -'), - ) + $profiles, TRUE + ] + $profiles, TRUE ); $this->addDefaultButtons(ts('Continue')); } @@ -130,7 +132,7 @@ public function buildQuickForm() { * Add local and global form rules. */ public function addRules() { - $this->addFormRule(array('CRM_Activity_Form_Task_PickProfile', 'formRule')); + $this->addFormRule(['CRM_Activity_Form_Task_PickProfile', 'formRule']); } /** diff --git a/CRM/Activity/Form/Task/Print.php b/CRM/Activity/Form/Task/Print.php index 9b085ed232d7..69f822bd74b7 100644 --- a/CRM/Activity/Form/Task/Print.php +++ b/CRM/Activity/Form/Task/Print.php @@ -1,9 +1,9 @@ addButtons(array( - array( + $this->addButtons([ + [ 'type' => 'next', 'name' => ts('Print Activities'), - 'js' => array('onclick' => 'window.print()'), + 'js' => ['onclick' => 'window.print()'], 'isDefault' => TRUE, - ), - array( + ], + [ 'type' => 'back', 'name' => ts('Done'), - ), - )); + ], + ]); } } diff --git a/CRM/Activity/Form/Task/RemoveFromTag.php b/CRM/Activity/Form/Task/RemoveFromTag.php index d04ab4dbff3e..2590ba073d73 100644 --- a/CRM/Activity/Form/Task/RemoveFromTag.php +++ b/CRM/Activity/Form/Task/RemoveFromTag.php @@ -1,9 +1,9 @@ addFormRule(array('CRM_Activity_Form_Task_RemoveFromTag', 'formRule')); + $this->addFormRule(['CRM_Activity_Form_Task_RemoveFromTag', 'formRule']); } /** @@ -77,7 +77,7 @@ public function addRules() { * @return array */ public static function formRule($form, $rule) { - $errors = array(); + $errors = []; if (empty($form['tag']) && empty($form['activity_taglist'])) { $errors['_qf_default'] = "Please select atleast one tag."; } @@ -91,7 +91,7 @@ public function postProcess() { //get the submitted values in an array $params = $this->controller->exportValues($this->_name); - $activityTags = $tagList = array(); + $activityTags = $tagList = []; // check if contact tags exists if (!empty($params['tag'])) { @@ -120,27 +120,27 @@ public function postProcess() { // merge contact and taglist tags $allTags = CRM_Utils_Array::crmArrayMerge($activityTags, $tagList); - $this->_name = array(); + $this->_name = []; foreach ($allTags as $key => $dnc) { $this->_name[] = $this->_tags[$key]; list($total, $removed, $notRemoved) = CRM_Core_BAO_EntityTag::removeEntitiesFromTag($this->_activityHolderIds, $key, 'civicrm_activity', FALSE); - $status = array( - ts('%count activity un-tagged', array( + $status = [ + ts('%count activity un-tagged', [ 'count' => $removed, 'plural' => '%count activities un-tagged', - )), - ); + ]), + ]; if ($notRemoved) { - $status[] = ts('1 activity already did not have this tag', array( + $status[] = ts('1 activity already did not have this tag', [ 'count' => $notRemoved, 'plural' => '%count activities already did not have this tag', - )); + ]); } $status = '
  • ' . implode('
  • ', $status) . '
'; - CRM_Core_Session::setStatus($status, ts("Removed Tag %1", array(1 => $this->_tags[$key])), 'success', array('expires' => 0)); + CRM_Core_Session::setStatus($status, ts("Removed Tag %1", [1 => $this->_tags[$key]]), 'success', ['expires' => 0]); } } diff --git a/CRM/Activity/Form/Task/SMS.php b/CRM/Activity/Form/Task/SMS.php index a78159eedf15..42d9f6aa3f23 100644 --- a/CRM/Activity/Form/Task/SMS.php +++ b/CRM/Activity/Form/Task/SMS.php @@ -1,9 +1,9 @@ _activityHolderIds); - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts); $query = " SELECT at.subject as subject, @@ -63,12 +63,12 @@ public function preProcess() { $dao = CRM_Core_DAO::executeQuery($query); while ($dao->fetch()) { - $rows[] = array( + $rows[] = [ 'subject' => $dao->subject, 'activity_type' => $dao->activity_type, 'activity_date' => $dao->activity_date, 'display_name' => $dao->display_name, - ); + ]; } $this->assign('rows', $rows); } @@ -77,13 +77,13 @@ public function preProcess() { * Build the form object. */ public function buildQuickForm() { - $this->addButtons(array( - array( + $this->addButtons([ + [ 'type' => 'done', 'name' => ts('Done'), 'isDefault' => TRUE, - ), - )); + ], + ]); } } diff --git a/CRM/Activity/Import/Controller.php b/CRM/Activity/Import/Controller.php index 4740fb2bbb23..9edc8e66b4e3 100644 --- a/CRM/Activity/Import/Controller.php +++ b/CRM/Activity/Import/Controller.php @@ -1,9 +1,9 @@ addActions($config->uploadDir, array('uploadFile')); + $this->addActions($config->uploadDir, ['uploadFile']); } } diff --git a/CRM/Activity/Import/Field.php b/CRM/Activity/Import/Field.php index d19ee7e6e473..b19a6002de94 100644 --- a/CRM/Activity/Import/Field.php +++ b/CRM/Activity/Import/Field.php @@ -1,9 +1,9 @@ createElement('radio', NULL, NULL, ts('Skip'), CRM_Import_Parser::DUPLICATE_SKIP ); @@ -67,11 +67,11 @@ public function buildQuickForm() { * Process the uploaded file. */ public function postProcess() { - $this->storeFormValues(array( + $this->storeFormValues([ 'onDuplicate', 'dateFormats', 'savedMapping', - )); + ]); $this->submitFileForMapping('CRM_Activity_Import_Parser_Activity'); } diff --git a/CRM/Activity/Import/Form/MapField.php b/CRM/Activity/Import/Form/MapField.php index baec4b1f6846..2b2e78053b6f 100644 --- a/CRM/Activity/Import/Form/MapField.php +++ b/CRM/Activity/Import/Form/MapField.php @@ -1,9 +1,9 @@ assign('rowDisplayCount', 2); } - $highlightedFields = array(); - $requiredFields = array( + $highlightedFields = []; + $requiredFields = [ 'activity_date_time', 'activity_type_id', 'activity_label', 'target_contact_id', 'activity_subject', - ); + ]; foreach ($requiredFields as $val) { $highlightedFields[] = $val; } @@ -103,8 +102,8 @@ public function buildQuickForm() { $this->assign('loadedMapping', $savedMapping); $this->set('loadedMapping', $savedMapping); - $params = array('id' => $savedMapping); - $temp = array(); + $params = ['id' => $savedMapping]; + $temp = []; $mappingDetails = CRM_Core_BAO_Mapping::retrieve($params, $temp); $this->assign('savedName', $mappingDetails->name); @@ -117,13 +116,13 @@ public function buildQuickForm() { $this->add('text', 'saveMappingDesc', ts('Description')); } - $this->addElement('checkbox', 'saveMapping', $saveDetailsName, NULL, array('onclick' => "showSaveDetails(this)")); + $this->addElement('checkbox', 'saveMapping', $saveDetailsName, NULL, ['onclick' => "showSaveDetails(this)"]); - $this->addFormRule(array('CRM_Activity_Import_Form_MapField', 'formRule')); + $this->addFormRule(['CRM_Activity_Import_Form_MapField', 'formRule']); //-------- end of saved mapping stuff --------- - $defaults = array(); + $defaults = []; $mapperKeys = array_keys($this->_mapperFields); $hasHeaders = !empty($this->_columnHeaders); @@ -148,7 +147,7 @@ public function buildQuickForm() { $warning = 0; for ($i = 0; $i < $this->_columnCount; $i++) { - $sel = &$this->addElement('hierselect', "mapper[$i]", ts('Mapper for Field %1', array(1 => $i)), NULL); + $sel = &$this->addElement('hierselect', "mapper[$i]", ts('Mapper for Field %1', [1 => $i]), NULL); $jsSet = FALSE; if ($this->get('savedMapping')) { if (isset($mappingName[$i])) { @@ -165,15 +164,15 @@ public function buildQuickForm() { } $js .= "{$formName}['mapper[$i][3]'].style.display = 'none';\n"; - $defaults["mapper[$i]"] = array( + $defaults["mapper[$i]"] = [ $mappingHeader[0], (isset($locationId)) ? $locationId : "", (isset($phoneType)) ? $phoneType : "", - ); + ]; $jsSet = TRUE; } else { - $defaults["mapper[$i]"] = array(); + $defaults["mapper[$i]"] = []; } if (!$jsSet) { for ($k = 1; $k < 4; $k++) { @@ -186,14 +185,10 @@ public function buildQuickForm() { $js .= "swapOptions($formName, 'mapper[$i]', 0, 3, 'hs_mapper_" . $i . "_');\n"; if ($hasHeaders) { - $defaults["mapper[$i]"] = array( - $this->defaultFromHeader($this->_columnHeaders[$i], - $headerPatterns - ), - ); + $defaults["mapper[$i]"] = [$this->defaultFromHeader($this->_columnHeaders[$i], $headerPatterns)]; } else { - $defaults["mapper[$i]"] = array($this->defaultFromData($dataPatterns, $i)); + $defaults["mapper[$i]"] = [$this->defaultFromData($dataPatterns, $i)]; } } // End of load mapping. @@ -202,23 +197,23 @@ public function buildQuickForm() { $js .= "swapOptions($formName, 'mapper[$i]', 0, 3, 'hs_mapper_" . $i . "_');\n"; if ($hasHeaders) { // Infer the default from the skipped headers if we have them - $defaults["mapper[$i]"] = array( - $this->defaultFromHeader($this->_columnHeaders[$i], - $headerPatterns - ), + $defaults["mapper[$i]"] = [ + $this->defaultFromHeader($this->_columnHeaders[$i], $headerPatterns), 0, - ); + ]; } else { // Otherwise guess the default from the form of the data - $defaults["mapper[$i]"] = array( - $this->defaultFromData($dataPatterns, $i), - 0, - ); + $defaults["mapper[$i]"] = [$this->defaultFromData($dataPatterns, $i), 0]; } } - $sel->setOptions(array($sel1, $sel2, (isset($sel3)) ? $sel3 : "", (isset($sel4)) ? $sel4 : "")); + $sel->setOptions([ + $sel1, + $sel2, + (isset($sel3)) ? $sel3 : "", + (isset($sel4)) ? $sel4 : "", + ]); } $js .= "\n"; $this->assign('initHideBoxes', $js); @@ -240,22 +235,22 @@ public function buildQuickForm() { $this->setDefaults($defaults); - $this->addButtons(array( - array( + $this->addButtons([ + [ 'type' => 'back', 'name' => ts('Previous'), - ), - array( + ], + [ 'type' => 'next', 'name' => ts('Continue'), 'spacing' => '          ', 'isDefault' => TRUE, - ), - array( + ], + [ 'type' => 'cancel', 'name' => ts('Cancel'), - ), - ) + ], + ] ); } @@ -269,28 +264,28 @@ public function buildQuickForm() { * list of errors to be posted back to the form */ public static function formRule($fields) { - $errors = array(); + $errors = []; // define so we avoid notices below $errors['_qf_default'] = ''; $fieldMessage = NULL; if (!array_key_exists('savedMapping', $fields)) { - $importKeys = array(); + $importKeys = []; foreach ($fields['mapper'] as $mapperPart) { $importKeys[] = $mapperPart[0]; } // FIXME: should use the schema titles, not redeclare them - $requiredFields = array( + $requiredFields = [ 'target_contact_id' => ts('Contact ID'), 'activity_date_time' => ts('Activity Date'), 'activity_subject' => ts('Activity Subject'), 'activity_type_id' => ts('Activity Type ID'), - ); + ]; - $params = array( + $params = [ 'used' => 'Unsupervised', 'contact_type' => 'Individual', - ); + ]; list($ruleFields, $threshold) = CRM_Dedupe_BAO_RuleGroup::dedupeRuleFieldsWeight($params); $weightSum = 0; foreach ($importKeys as $key => $val) { @@ -310,7 +305,7 @@ public static function formRule($fields) { else { $errors['_qf_default'] .= ts('Missing required contact matching fields.') . $fieldMessage . ' ' - . ts('(Sum of all weights should be greater than or equal to threshold: %1).', array(1 => $threshold)) + . ts('(Sum of all weights should be greater than or equal to threshold: %1).', [1 => $threshold]) . '
'; } } @@ -320,14 +315,14 @@ public static function formRule($fields) { } else { $errors['_qf_default'] .= ts('Missing required field: Provide %1 or %2', - array( + [ 1 => $title, 2 => 'Activity Type Label', - )) . '
'; + ]) . '
'; } } else { - $errors['_qf_default'] .= ts('Missing required field: %1', array(1 => $title)) . '
'; + $errors['_qf_default'] .= ts('Missing required field: %1', [1 => $title]) . '
'; } } } @@ -339,8 +334,7 @@ public static function formRule($fields) { $errors['saveMappingName'] = ts('Name is required to save Import Mapping'); } else { - $mappingTypeId = CRM_Core_OptionGroup::getValue('mapping_type', 'Import Activity', 'name'); - if (CRM_Core_BAO_Mapping::checkMapping($nameField, $mappingTypeId)) { + if (CRM_Core_BAO_Mapping::checkMapping($nameField, CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_Mapping', 'mapping_type_id', 'Import Activity'))) { $errors['saveMappingName'] = ts('Duplicate Import Mapping Name'); } } @@ -376,17 +370,15 @@ public function postProcess() { } $fileName = $this->controller->exportValue('DataSource', 'uploadFile'); + $seperator = $this->controller->exportValue('DataSource', 'fieldSeparator'); $skipColumnHeader = $this->controller->exportValue('DataSource', 'skipColumnHeader'); - $config = CRM_Core_Config::singleton(); - $seperator = $config->fieldSeparator; - - $mapperKeys = array(); - $mapper = array(); + $mapperKeys = []; + $mapper = []; $mapperKeys = $this->controller->exportValue($this->_name, 'mapper'); - $mapperKeysMain = array(); - $mapperLocType = array(); - $mapperPhoneType = array(); + $mapperKeysMain = []; + $mapperLocType = []; + $mapperPhoneType = []; for ($i = 0; $i < $this->_columnCount; $i++) { $mapper[$i] = $this->_mapperFields[$mapperKeys[$i][0]]; @@ -420,7 +412,7 @@ public function postProcess() { $mappingFields->mapping_id = $params['mappingId']; $mappingFields->find(); - $mappingFieldsId = array(); + $mappingFieldsId = []; while ($mappingFields->fetch()) { if ($mappingFields->id) { $mappingFieldsId[$mappingFields->column_number] = $mappingFields->id; @@ -440,14 +432,11 @@ public function postProcess() { // Saving Mapping Details and Records. if (!empty($params['saveMapping'])) { - $mappingParams = array( + $mappingParams = [ 'name' => $params['saveMappingName'], 'description' => $params['saveMappingDesc'], - 'mapping_type_id' => CRM_Core_OptionGroup::getValue('mapping_type', - 'Import Activity', - 'name' - ), - ); + 'mapping_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_Mapping', 'mapping_type_id', 'Import Activity'), + ]; $saveMapping = CRM_Core_BAO_Mapping::add($mappingParams); for ($i = 0; $i < $this->_columnCount; $i++) { diff --git a/CRM/Activity/Import/Form/Preview.php b/CRM/Activity/Import/Form/Preview.php index 3e1bebe60301..6a2352d06213 100644 --- a/CRM/Activity/Import/Form/Preview.php +++ b/CRM/Activity/Import/Form/Preview.php @@ -1,9 +1,9 @@ set('downloadMismatchRecordsUrl', CRM_Utils_System::url('civicrm/export', $urlParams)); } - $properties = array( + $properties = [ 'mapper', 'dataValues', 'columnCount', @@ -93,7 +93,8 @@ public function preProcess() { 'downloadErrorRecordsUrl', 'downloadConflictRecordsUrl', 'downloadMismatchRecordsUrl', - ); + ]; + $this->setStatusUrl(); foreach ($properties as $property) { $this->assign($property, $this->get($property)); @@ -107,18 +108,16 @@ public function preProcess() { */ public function postProcess() { $fileName = $this->controller->exportValue('DataSource', 'uploadFile'); + $seperator = $this->controller->exportValue('DataSource', 'fieldSeparator'); $skipColumnHeader = $this->controller->exportValue('DataSource', 'skipColumnHeader'); $invalidRowCount = $this->get('invalidRowCount'); $conflictRowCount = $this->get('conflictRowCount'); $onDuplicate = $this->get('onDuplicate'); - $config = CRM_Core_Config::singleton(); - $seperator = $config->fieldSeparator; - $mapper = $this->controller->exportValue('MapField', 'mapper'); - $mapperKeys = array(); - $mapperLocType = array(); - $mapperPhoneType = array(); + $mapperKeys = []; + $mapperLocType = []; + $mapperPhoneType = []; foreach ($mapper as $key => $value) { $mapperKeys[$key] = $mapper[$key][0]; @@ -143,7 +142,7 @@ public function postProcess() { $mapFields = $this->get('fields'); foreach ($mapper as $key => $value) { - $header = array(); + $header = []; if (isset($mapFields[$mapper[$key][0]])) { $header[] = $mapFields[$mapper[$key][0]]; } @@ -153,7 +152,9 @@ public function postProcess() { $mapperFields, $skipColumnHeader, CRM_Import_Parser::MODE_IMPORT, - $onDuplicate + $onDuplicate, + $this->get('statusID'), + $this->get('totalRowCount') ); // add all the necessary variables to the form @@ -163,7 +164,7 @@ public function postProcess() { $errorStack = CRM_Core_Error::singleton(); $errors = $errorStack->getErrors(); - $errorMessage = array(); + $errorMessage = []; if (is_array($errors)) { foreach ($errors as $key => $value) { diff --git a/CRM/Activity/Import/Form/Summary.php b/CRM/Activity/Import/Form/Summary.php index 02a9c223501e..5e263c5edbc7 100644 --- a/CRM/Activity/Import/Form/Summary.php +++ b/CRM/Activity/Import/Form/Summary.php @@ -1,9 +1,9 @@ assign('dupeActionString', $dupeActionString); - $properties = array( + $properties = [ 'totalRowCount', 'validRowCount', 'invalidRowCount', @@ -101,7 +101,7 @@ public function preProcess() { 'downloadMismatchRecordsUrl', 'groupAdditions', 'unMatchCount', - ); + ]; foreach ($properties as $property) { $this->assign($property, $this->get($property)); } diff --git a/CRM/Activity/Import/Parser.php b/CRM/Activity/Import/Parser.php index 497be5ec2892..fc4aca0c20dc 100644 --- a/CRM/Activity/Import/Parser.php +++ b/CRM/Activity/Import/Parser.php @@ -1,9 +1,9 @@ _invalidRowCount = $this->_validCount = 0; $this->_totalCount = $this->_conflictCount = 0; - $this->_errors = array(); - $this->_warnings = array(); - $this->_conflicts = array(); + $this->_errors = []; + $this->_warnings = []; + $this->_conflicts = []; $this->_fileSize = number_format(filesize($fileName) / 1024.0, 2); if ($mode == self::MODE_MAPFIELD) { - $this->_rows = array(); + $this->_rows = []; } else { $this->_activeFieldCount = count($this->_activeFields); } + if ($statusID) { + $this->progressImport($statusID); + $startTimestamp = $currTimestamp = $prevTimestamp = time(); + } while (!feof($fd)) { $this->_lineCount++; @@ -148,6 +159,9 @@ public function run( } elseif ($mode == self::MODE_IMPORT) { $returnCode = $this->import($onDuplicate, $values); + if ($statusID && (($this->_lineCount % 50) == 0)) { + $prevTimestamp = $this->progressImport($statusID, FALSE, $startTimestamp, $prevTimestamp, $totalRowCount); + } } else { $returnCode = self::ERROR; @@ -171,14 +185,12 @@ public function run( if ($returnCode & self::ERROR) { $this->_invalidRowCount++; - if ($this->_invalidRowCount < $this->_maxErrorCount) { - $recordNumber = $this->_lineCount; - if ($this->_haveColumnHeader) { - $recordNumber--; - } - array_unshift($values, $recordNumber); - $this->_errors[] = $values; + $recordNumber = $this->_lineCount; + if ($this->_haveColumnHeader) { + $recordNumber--; } + array_unshift($values, $recordNumber); + $this->_errors[] = $values; } if ($returnCode & self::CONFLICT) { @@ -233,30 +245,24 @@ public function run( } if ($this->_invalidRowCount) { // removed view url for invlaid contacts - $headers = array_merge(array( - ts('Line Number'), - ts('Reason'), - ), + $headers = array_merge( + [ts('Line Number'), ts('Reason')], $customHeaders ); $this->_errorFileName = self::errorFileName(self::ERROR); self::exportCSV($this->_errorFileName, $headers, $this->_errors); } if ($this->_conflictCount) { - $headers = array_merge(array( - ts('Line Number'), - ts('Reason'), - ), + $headers = array_merge( + [ts('Line Number'), ts('Reason')], $customHeaders ); $this->_conflictFileName = self::errorFileName(self::CONFLICT); self::exportCSV($this->_conflictFileName, $headers, $this->_conflicts); } if ($this->_duplicateCount) { - $headers = array_merge(array( - ts('Line Number'), - ts('View Activity History URL'), - ), + $headers = array_merge( + [ts('Line Number'), ts('View Activity History URL')], $customHeaders ); @@ -291,7 +297,7 @@ public function setActiveFields($fieldKeys) { * (reference ) associative array of name/value pairs */ public function &getActiveFieldParams() { - $params = array(); + $params = []; for ($i = 0; $i < $this->_activeFieldCount; $i++) { if (isset($this->_activeFields[$i]->_value) && !isset($params[$this->_activeFields[$i]->_name]) @@ -376,7 +382,7 @@ public function set($store, $mode = self::MODE_SUMMARY) { * @param array $data */ public static function exportCSV($fileName, $header, $data) { - $output = array(); + $output = []; $fd = fopen($fileName, 'w'); foreach ($header as $key => $value) { diff --git a/CRM/Activity/Import/Parser/Activity.php b/CRM/Activity/Import/Parser/Activity.php index 22dbddcb6552..de829240c3d7 100644 --- a/CRM/Activity/Import/Parser/Activity.php +++ b/CRM/Activity/Import/Parser/Activity.php @@ -1,9 +1,9 @@ array( + $fields = array_merge($fields, [ + 'source_contact_id' => [ 'title' => ts('Source Contact'), 'headerPattern' => '/Source.Contact?/i', - ), - 'activity_label' => array( + ], + 'activity_label' => [ 'title' => ts('Activity Type Label'), 'headerPattern' => '/(activity.)?type label?/i', - ), - )); + ], + ]); foreach ($fields as $name => $field) { $field['type'] = CRM_Utils_Array::value('type', $field, CRM_Utils_Type::T_INT); @@ -96,7 +96,7 @@ public function init() { $this->addField($name, $field['title'], $field['type'], $field['headerPattern'], $field['dataPattern']); } - $this->_newActivity = array(); + $this->_newActivity = []; $this->setActiveFields($this->_mapperKeys); @@ -258,7 +258,7 @@ public function import($onDuplicate, &$values) { $params = &$this->getActiveFieldParams(); $activityLabel = array_search('activity_label', $this->_mapperKeys); if ($activityLabel) { - $params = array_merge($params, array('activity_label' => $values[$activityLabel])); + $params = array_merge($params, ['activity_label' => $values[$activityLabel]]); } // For date-Formats. $session = CRM_Core_Session::singleton(); @@ -267,7 +267,7 @@ public function import($onDuplicate, &$values) { $params['source_contact_id'] = $session->get('userID'); } - $customFields = CRM_Core_BAO_CustomField::getFields(CRM_Utils_Array::value('contact_type', $params)); + $customFields = CRM_Core_BAO_CustomField::getFields('Activity'); foreach ($params as $key => $val) { if ($customFieldID = CRM_Core_BAO_CustomField::getKeyID($key)) { @@ -332,10 +332,10 @@ public function import($onDuplicate, &$values) { } else { // Using new Dedupe rule. - $ruleParams = array( + $ruleParams = [ 'contact_type' => 'Individual', 'used' => 'Unsupervised', - ); + ]; $fieldsArray = CRM_Dedupe_BAO_Rule::dedupeRuleFields($ruleParams); $disp = NULL; diff --git a/CRM/Activity/Page/AJAX.php b/CRM/Activity/Page/AJAX.php index 200cc013e560..1da789c2734a 100644 --- a/CRM/Activity/Page/AJAX.php +++ b/CRM/Activity/Page/AJAX.php @@ -1,9 +1,9 @@ 'Integer', 'status_id' => 'Integer', 'activity_deleted' => 'Boolean', 'activity_type_id' => 'Integer', - 'activity_date_low' => 'Date', - 'activity_date_high' => 'Date', - ); + // "Date" validation fails because it expects only numbers with no hyphens + 'activity_date_low' => 'Alphanumeric', + 'activity_date_high' => 'Alphanumeric', + ]; $params = CRM_Core_Page_AJAX::defaultSortAndPagerParams(); - $params += CRM_Core_Page_AJAX::validateParams(array(), $optionalParameters); + $params += CRM_Core_Page_AJAX::validateParams([], $optionalParameters); // get the activities related to given case $activities = CRM_Case_BAO_Case::getCaseActivity($caseID, $params, $contactID, $context, $userID); @@ -65,17 +67,17 @@ public static function getCaseGlobalRelationships() { $params = CRM_Core_Page_AJAX::defaultSortAndPagerParams(); // get the activities related to given case - $globalGroupInfo = array(); + $globalGroupInfo = []; // get the total row count CRM_Case_BAO_Case::getGlobalContacts($globalGroupInfo, NULL, FALSE, TRUE, NULL, NULL); // limit the rows $relGlobal = CRM_Case_BAO_Case::getGlobalContacts($globalGroupInfo, $params['sortBy'], $showLinks = TRUE, FALSE, $params['offset'], $params['rp']); - $relationships = array(); + $relationships = []; // after sort we can update username fields to be a url foreach ($relGlobal as $key => $value) { - $relationship = array(); + $relationship = []; $relationship['sort_name'] = $value['sort_name']; $relationship['phone'] = $value['phone']; $relationship['email'] = $value['email']; @@ -83,7 +85,7 @@ public static function getCaseGlobalRelationships() { array_push($relationships, $relationship); } - $globalRelationshipsDT = array(); + $globalRelationshipsDT = []; $globalRelationshipsDT['data'] = $relationships; $globalRelationshipsDT['recordsTotal'] = count($relationships); $globalRelationshipsDT['recordsFiltered'] = count($relationships); @@ -107,7 +109,7 @@ public static function getCaseClientRelationships() { // Now build 'Other Relationships' array by removing relationships that are already listed under Case Roles // so they don't show up twice. - $clientRelationships = array(); + $clientRelationships = []; foreach ($relClient as $r) { if (!array_key_exists($r['id'], $caseRelationships)) { $clientRelationships[] = $r; @@ -121,10 +123,10 @@ public static function getCaseClientRelationships() { $sort_type = "SORT_" . strtoupper($params['_raw_values']['order'][0]); array_multisort($sortArray, constant($sort_type), $clientRelationships); - $relationships = array(); + $relationships = []; // after sort we can update username fields to be a url foreach ($clientRelationships as $key => $value) { - $relationship = array(); + $relationship = []; $relationship['relation'] = $value['relation']; $relationship['name'] = '' . $clientRelationships[$key]['name'] . ''; @@ -134,7 +136,7 @@ public static function getCaseClientRelationships() { array_push($relationships, $relationship); } - $clientRelationshipsDT = array(); + $clientRelationshipsDT = []; $clientRelationshipsDT['data'] = $relationships; $clientRelationshipsDT['recordsTotal'] = count($relationships); $clientRelationshipsDT['recordsFiltered'] = count($relationships); @@ -142,7 +144,6 @@ public static function getCaseClientRelationships() { CRM_Utils_JSON::output($clientRelationshipsDT); } - public static function getCaseRoles() { $caseID = CRM_Utils_Type::escape($_GET['caseID'], 'Integer'); $contactID = CRM_Utils_Type::escape($_GET['cid'], 'Integer'); @@ -172,7 +173,7 @@ public static function getCaseRoles() { // CRM-14466 added cid to the non-client array to avoid php notice foreach ($caseRoles as $id => $value) { if ($id != "client") { - $rel = array(); + $rel = []; $rel['relation'] = $value; $rel['relation_type'] = $id; $rel['name'] = '(not assigned)'; @@ -183,7 +184,7 @@ public static function getCaseRoles() { } else { foreach ($value as $clientRole) { - $relClient = array(); + $relClient = []; $relClient['relation'] = 'Client'; $relClient['name'] = $clientRole['sort_name']; $relClient['phone'] = $clientRole['phone']; @@ -202,13 +203,13 @@ public static function getCaseRoles() { $sort_type = "SORT_" . strtoupper($params['_raw_values']['order'][0]); array_multisort($sortArray, constant($sort_type), $caseRelationships); - $relationships = array(); + $relationships = []; // set user name, email and edit columns links foreach ($caseRelationships as $key => &$row) { $typeLabel = $row['relation']; // Add "
(Case Manager)" to label - if ($row['relation_type'] == $managerRoleId) { + if (!empty($row['relation_type']) && $row['relation_type'] == $managerRoleId) { $row['relation'] .= '
' . '(' . ts('Case Manager') . ')'; } // view user links @@ -227,16 +228,16 @@ public static function getCaseRoles() { $contactType = $contactType == 'Contact' ? '' : $contactType; switch ($row['source']) { case 'caseRel': - $row['actions'] = '' . + $row['actions'] = '' . '' . '' . - '' . + '' . '' . ''; break; case 'caseRoles': - $row['actions'] = '' . + $row['actions'] = '' . '' . ''; break; @@ -251,7 +252,7 @@ public static function getCaseRoles() { } $params['total'] = count($relationships); - $caseRelationshipsDT = array(); + $caseRelationshipsDT = []; $caseRelationshipsDT['data'] = $relationships; $caseRelationshipsDT['recordsTotal'] = $params['total']; $caseRelationshipsDT['recordsFiltered'] = $params['total']; @@ -261,8 +262,8 @@ public static function getCaseRoles() { } public static function convertToCaseActivity() { - $params = array('caseID', 'activityID', 'contactID', 'newSubject', 'targetContactIds', 'mode'); - $vals = array(); + $params = ['caseID', 'activityID', 'contactID', 'newSubject', 'targetContactIds', 'mode']; + $vals = []; foreach ($params as $param) { $vals[$param] = CRM_Utils_Array::value($param, $_POST); } @@ -277,19 +278,19 @@ public static function convertToCaseActivity() { */ public static function _convertToCaseActivity($params) { if (!$params['activityID'] || !$params['caseID']) { - return (array('error_msg' => 'required params missing.')); + return (['error_msg' => 'required params missing.']); } $otherActivity = new CRM_Activity_DAO_Activity(); $otherActivity->id = $params['activityID']; if (!$otherActivity->find(TRUE)) { - return (array('error_msg' => 'activity record is missing.')); + return (['error_msg' => 'activity record is missing.']); } $actDateTime = CRM_Utils_Date::isoToMysql($otherActivity->activity_date_time); // Create new activity record. $mainActivity = new CRM_Activity_DAO_Activity(); - $mainActVals = array(); + $mainActVals = []; CRM_Core_DAO::storeValues($otherActivity, $mainActVals); // Get new activity subject. @@ -302,20 +303,19 @@ public static function _convertToCaseActivity($params) { $mainActivity->activity_date_time = $actDateTime; // Make sure this is current revision. $mainActivity->is_current_revision = TRUE; - // Drop all relations. - $mainActivity->parent_id = $mainActivity->original_id = NULL; + $mainActivity->original_id = $otherActivity->id; + $otherActivity->is_current_revision = FALSE; $mainActivity->save(); $mainActivityId = $mainActivity->id; CRM_Activity_BAO_Activity::logActivityAction($mainActivity); - $mainActivity->free(); // Mark previous activity as deleted. If it was a non-case activity // then just change the subject. - if (in_array($params['mode'], array( + if (in_array($params['mode'], [ 'move', 'file', - ))) { + ])) { $caseActivity = new CRM_Case_DAO_CaseActivity(); $caseActivity->case_id = $params['caseID']; $caseActivity->activity_id = $otherActivity->id; @@ -323,55 +323,52 @@ public static function _convertToCaseActivity($params) { $otherActivity->is_deleted = 1; } else { - $otherActivity->subject = ts('(Filed on case %1)', array( + $otherActivity->subject = ts('(Filed on case %1)', [ 1 => $params['caseID'], - )) . ' ' . $otherActivity->subject; + ]) . ' ' . $otherActivity->subject; } - $otherActivity->activity_date_time = $actDateTime; $otherActivity->save(); - $caseActivity->free(); } - $otherActivity->free(); - $targetContacts = array(); + $targetContacts = []; if (!empty($params['targetContactIds'])) { $targetContacts = array_unique(explode(',', $params['targetContactIds'])); } - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts); $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts); $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts); $sourceContactID = CRM_Activity_BAO_Activity::getSourceContactID($params['activityID']); - $src_params = array( + $src_params = [ 'activity_id' => $mainActivityId, 'contact_id' => $sourceContactID, 'record_type_id' => $sourceID, - ); + ]; CRM_Activity_BAO_ActivityContact::create($src_params); foreach ($targetContacts as $key => $value) { - $targ_params = array( + $targ_params = [ 'activity_id' => $mainActivityId, 'contact_id' => $value, 'record_type_id' => $targetID, - ); + ]; CRM_Activity_BAO_ActivityContact::create($targ_params); } - // typically this will be empty, since assignees on another case may be completely different - $assigneeContacts = array(); + //CRM-21114 retrieve assignee contacts from original case; allow overriding from params + $assigneeContacts = CRM_Activity_BAO_ActivityContact::retrieveContactIdsByActivityId($params['activityID'], $assigneeID); if (!empty($params['assigneeContactIds'])) { $assigneeContacts = array_unique(explode(',', $params['assigneeContactIds'])); } foreach ($assigneeContacts as $key => $value) { - $assigneeParams = array( + $assigneeParams = [ 'activity_id' => $mainActivityId, 'contact_id' => $value, 'record_type_id' => $assigneeID, - ); + ]; CRM_Activity_BAO_ActivityContact::create($assigneeParams); } @@ -381,24 +378,33 @@ public static function _convertToCaseActivity($params) { $caseActivity->activity_id = $mainActivityId; $caseActivity->save(); $error_msg = $caseActivity->_lastError; - $caseActivity->free(); $params['mainActivityId'] = $mainActivityId; CRM_Activity_BAO_Activity::copyExtendedActivityData($params); + CRM_Utils_Hook::post('create', 'CaseActivity', $caseActivity->id, $caseActivity); - return (array('error_msg' => $error_msg, 'newId' => $mainActivity->id)); + return (['error_msg' => $error_msg, 'newId' => $mainActivity->id]); } + /** + * Get activities for the contact. + * + * @return array + */ public static function getContactActivity() { - $requiredParameters = array( + $requiredParameters = [ 'cid' => 'Integer', - ); + ]; - $optionalParameters = array( + $optionalParameters = [ 'context' => 'String', 'activity_type_id' => 'Integer', 'activity_type_exclude_id' => 'Integer', - ); + 'activity_status_id' => 'String', + 'activity_date_time_relative' => 'String', + 'activity_date_time_low' => 'String', + 'activity_date_time_high' => 'String', + ]; $params = CRM_Core_Page_AJAX::defaultSortAndPagerParams(); $params += CRM_Core_Page_AJAX::validateParams($requiredParameters, $optionalParameters); @@ -411,32 +417,40 @@ public static function getContactActivity() { // get the contact activities $activities = CRM_Activity_BAO_Activity::getContactActivitySelector($params); - if (!empty($_GET['is_unit_test'])) { - return $activities; - } - foreach ($activities['data'] as $key => $value) { // Check if recurring activity. if (!empty($value['is_recurring_activity'])) { $repeat = $value['is_recurring_activity']; - $activities['data'][$key]['activity_type'] .= '
' . ts('Repeating (%1 of %2)', array(1 => $repeat[0], 2 => $repeat[1])) . ''; + $activities['data'][$key]['activity_type'] .= '
' . ts('Repeating (%1 of %2)', [1 => $repeat[0], 2 => $repeat[1]]) . ''; } } // store the activity filter preference CRM-11761 - $session = CRM_Core_Session::singleton(); - $userID = $session->get('userID'); - if ($userID) { - $activityFilter = array( - 'activity_type_filter_id' => empty($params['activity_type_id']) ? '' : CRM_Utils_Type::escape($params['activity_type_id'], 'Integer'), - 'activity_type_exclude_filter_id' => empty($params['activity_type_exclude_id']) ? '' : CRM_Utils_Type::escape($params['activity_type_exclude_id'], 'Integer'), - ); - - /** - * @var \Civi\Core\SettingsBag $cSettings - */ - $cSettings = Civi::service('settings_manager')->getBagByContact(CRM_Core_Config::domainID(), $userID); - $cSettings->set('activity_tab_filter', $activityFilter); + if (Civi::settings()->get('preserve_activity_tab_filter') && ($userID = CRM_Core_Session::getLoggedInContactID())) { + unset($optionalParameters['context']); + foreach ($optionalParameters as $searchField => $dataType) { + $formSearchField = $searchField; + if ($searchField == 'activity_type_id') { + $formSearchField = 'activity_type_filter_id'; + } + elseif ($searchField == 'activity_type_exclude_id') { + $formSearchField = 'activity_type_exclude_filter_id'; + } + if (!empty($params[$searchField])) { + $activityFilter[$formSearchField] = $params[$searchField]; + if (in_array($searchField, ['activity_date_time_low', 'activity_date_time_high'])) { + $activityFilter['activity_date_time_relative'] = 0; + } + elseif ($searchField == 'activity_status_id') { + $activityFilter['status_id'] = explode(',', $activityFilter[$searchField]); + } + } + } + + Civi::contactSettings()->set('activity_tab_filter', $activityFilter); + } + if (!empty($_GET['is_unit_test'])) { + return [$activities, $activityFilter]; } CRM_Utils_JSON::output($activities); diff --git a/CRM/Activity/Page/Tab.php b/CRM/Activity/Page/Tab.php index 349be6abd21c..4415618a7e2f 100644 --- a/CRM/Activity/Page/Tab.php +++ b/CRM/Activity/Page/Tab.php @@ -1,9 +1,9 @@ assign('context', $context); $this->_id = CRM_Utils_Request::retrieve('id', 'Integer', $this); @@ -73,25 +73,25 @@ public function edit() { $activityTypeId = CRM_Utils_Request::retrieve('atype', 'Positive', $this); // Email and Create Letter activities use a different form class - $emailTypeValue = CRM_Core_OptionGroup::getValue('activity_type', - 'Email', - 'name' + $emailTypeValue = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', + 'activity_type_id', + 'Email' ); - $letterTypeValue = CRM_Core_OptionGroup::getValue('activity_type', - 'Print PDF Letter', - 'name' + $letterTypeValue = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', + 'activity_type_id', + 'Print PDF Letter' ); switch ($activityTypeId) { case $emailTypeValue: $wrapper = new CRM_Utils_Wrapper(); - $arguments = array('attachUpload' => 1); + $arguments = ['attachUpload' => 1]; return $wrapper->run('CRM_Contact_Form_Task_Email', ts('Email a Contact'), $arguments); case $letterTypeValue: $wrapper = new CRM_Utils_Wrapper(); - $arguments = array('attachUpload' => 1); + $arguments = ['attachUpload' => 1]; return $wrapper->run('CRM_Contact_Form_Task_PDF', ts('Create PDF Letter'), $arguments); default: @@ -131,6 +131,7 @@ public function preProcess() { $this->_action = CRM_Utils_Request::retrieve('action', 'String', $this, FALSE, 'browse'); $this->assign('action', $this->_action); + $this->assign('allow_edit_inbound_emails', CRM_Activity_BAO_Activity::checkEditInboundEmailsPermissions()); // also create the form element for the activity links box $controller = new CRM_Core_Controller_Simple( @@ -159,14 +160,14 @@ public function delete() { * Perform actions and display for activities. */ public function run() { - $context = CRM_Utils_Request::retrieve('context', 'String', $this); + $context = CRM_Utils_Request::retrieve('context', 'Alphanumeric', $this); $contactId = CRM_Utils_Request::retrieve('cid', 'Positive', $this); $action = CRM_Utils_Request::retrieve('action', 'String', $this); $this->_id = CRM_Utils_Request::retrieve('id', 'Positive', $this); // Do check for view/edit operation. if ($this->_id && - in_array($action, array(CRM_Core_Action::UPDATE, CRM_Core_Action::VIEW)) + in_array($action, [CRM_Core_Action::UPDATE, CRM_Core_Action::VIEW]) ) { if (!CRM_Activity_BAO_Activity::checkPermission($this->_id, $action)) { CRM_Core_Error::fatal(ts('You are not authorized to access this page.')); @@ -189,20 +190,20 @@ public function run() { $activityTypeId = CRM_Utils_Request::retrieve('atype', 'Positive', $this); // Email and Create Letter activities use a different form class - $emailTypeValue = CRM_Core_OptionGroup::getValue('activity_type', - 'Email', - 'name' + $emailTypeValue = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', + 'activity_type_id', + 'Email' ); - $letterTypeValue = CRM_Core_OptionGroup::getValue('activity_type', - 'Print PDF Letter', - 'name' + $letterTypeValue = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', + 'activity_type_id', + 'Print PDF Letter' ); - if (in_array($activityTypeId, array( + if (in_array($activityTypeId, [ $emailTypeValue, $letterTypeValue, - ))) { + ])) { return; } } diff --git a/CRM/Activity/Page/UserDashboard.php b/CRM/Activity/Page/UserDashboard.php index 5804056d0039..d16a23d32ef2 100644 --- a/CRM/Activity/Page/UserDashboard.php +++ b/CRM/Activity/Page/UserDashboard.php @@ -1,9 +1,9 @@ set('context', 'user'); $controller->set('cid', $this->_contactId); // Limit to status "Scheduled" and "Available" - $controller->set('status', array('IN' => array(1, 7))); + $controller->set('status', ['IN' => [1, 7]]); $controller->set('activity_role', 2); $controller->set('force', 1); $controller->process(); diff --git a/CRM/Activity/Selector/Activity.php b/CRM/Activity/Selector/Activity.php index ad26e32c1b8c..e03072dbdd17 100644 --- a/CRM/Activity/Selector/Activity.php +++ b/CRM/Activity/Selector/Activity.php @@ -1,9 +1,9 @@ array( + VIEW => [ 'name' => ts('View'), 'url' => $url, 'qs' => $qsView, 'title' => ts('View Activity'), - ), - ); + ], + ]; } if ($showUpdate) { @@ -231,15 +237,15 @@ public static function actionLinks( $updateUrl = 'civicrm/activity/pdf/add'; } if (CRM_Activity_BAO_Activity::checkPermission($activityId, CRM_Core_Action::UPDATE)) { - $actionLinks += array( + $actionLinks += [ CRM_Core_Action:: - UPDATE => array( + UPDATE => [ 'name' => ts('Edit'), 'url' => $updateUrl, 'qs' => $qsUpdate, 'title' => ts('Update Activity'), - ), - ); + ], + ]; } } @@ -247,42 +253,42 @@ public static function actionLinks( $activityTypeName && CRM_Case_BAO_Case::checkPermission($activityId, 'File On Case', $activityTypeId) ) { - $actionLinks += array( + $actionLinks += [ CRM_Core_Action:: - ADD => array( + ADD => [ 'name' => ts('File on Case'), 'url' => '#', 'extra' => 'onclick="javascript:fileOnCase( \'file\', \'%%id%%\', null, this ); return false;"', 'title' => ts('File on Case'), - ), - ); + ], + ]; } if ($showDelete) { if (!isset($delUrl) || !$delUrl) { $delUrl = $url; } - $actionLinks += array( + $actionLinks += [ CRM_Core_Action:: - DELETE => array( + DELETE => [ 'name' => ts('Delete'), 'url' => $delUrl, 'qs' => $qsDelete, 'title' => ts('Delete Activity'), - ), - ); + ], + ]; } if ($accessMailingReport) { - $actionLinks += array( + $actionLinks += [ CRM_Core_Action:: - BROWSE => array( + BROWSE => [ 'name' => ts('Mailing Report'), 'url' => 'civicrm/mailing/report', 'qs' => "mid={$sourceRecordId}&reset=1&cid=%%cid%%&context=activitySelector", 'title' => ts('View Mailing Report'), - ), - ); + ], + ]; } return $actionLinks; @@ -317,7 +323,7 @@ public function getPagerParams($action, &$params) { */ public function &getColumnHeaders($action = NULL, $output = NULL) { if ($output == CRM_Core_Selector_Controller::EXPORT || $output == CRM_Core_Selector_Controller::SCREEN) { - $csvHeaders = array(ts('Activity Type'), ts('Description'), ts('Activity Date')); + $csvHeaders = [ts('Activity Type'), ts('Description'), ts('Activity Date')]; foreach (self::_getColumnHeaders() as $column) { if (array_key_exists('name', $column)) { $csvHeaders[] = $column['name']; @@ -342,7 +348,7 @@ public function &getColumnHeaders($action = NULL, $output = NULL) { * Total number of rows */ public function getTotalCount($action, $case = NULL) { - $params = array( + $params = [ 'contact_id' => $this->_contactId, 'admin' => $this->_admin, 'caseId' => $case, @@ -351,7 +357,7 @@ public function getTotalCount($action, $case = NULL) { 'offset' => 0, 'rowCount' => 0, 'sort' => NULL, - ); + ]; return CRM_Activity_BAO_Activity::getActivitiesCount($params); } @@ -375,7 +381,7 @@ public function getTotalCount($action, $case = NULL) { * the total number of rows for this action */ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL, $case = NULL) { - $params = array( + $params = [ 'contact_id' => $this->_contactId, 'admin' => $this->_admin, 'caseId' => $case, @@ -384,7 +390,7 @@ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL, $ca 'offset' => $offset, 'rowCount' => $rowCount, 'sort' => $sort, - ); + ]; $config = CRM_Core_Config::singleton(); $rows = CRM_Activity_BAO_Activity::getActivities($params); @@ -397,7 +403,7 @@ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL, $ca $engagementLevels = CRM_Campaign_PseudoConstant::engagementLevel(); // CRM-4418 - $permissions = array($this->_permission); + $permissions = [$this->_permission]; if (CRM_Core_Permission::check('delete activities')) { $permissions[] = CRM_Core_Permission::DELETE; } @@ -467,12 +473,12 @@ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL, $ca if ($output != CRM_Core_Selector_Controller::EXPORT && $output != CRM_Core_Selector_Controller::SCREEN) { $row['action'] = CRM_Core_Action::formLink($actionLinks, $actionMask, - array( + [ 'id' => $row['activity_id'], 'cid' => $this->_contactId, 'cxt' => $this->_context, 'caseid' => CRM_Utils_Array::value('case_id', $row), - ), + ], ts('more'), FALSE, 'activity.selector.action', @@ -508,36 +514,36 @@ public function getExportFileName($output = 'csv') { */ private static function &_getColumnHeaders() { if (!isset(self::$_columnHeaders)) { - self::$_columnHeaders = array( - array( + self::$_columnHeaders = [ + [ 'name' => ts('Type'), 'sort' => 'activity_type', 'direction' => CRM_Utils_Sort::DONTCARE, - ), - array( + ], + [ 'name' => ts('Subject'), 'sort' => 'subject', 'direction' => CRM_Utils_Sort::DONTCARE, - ), - array( + ], + [ 'name' => ts('Added By'), 'sort' => 'source_contact_name', 'direction' => CRM_Utils_Sort::DONTCARE, - ), - array('name' => ts('With')), - array('name' => ts('Assigned')), - array( + ], + ['name' => ts('With')], + ['name' => ts('Assigned')], + [ 'name' => ts('Date'), 'sort' => 'activity_date_time', 'direction' => CRM_Utils_Sort::DONTCARE, - ), - array( + ], + [ 'name' => ts('Status'), 'sort' => 'status_id', 'direction' => CRM_Utils_Sort::DONTCARE, - ), - array('desc' => ts('Actions')), - ); + ], + ['desc' => ts('Actions')], + ]; } return self::$_columnHeaders; diff --git a/CRM/Activity/Selector/Search.php b/CRM/Activity/Selector/Search.php index 0e2549c63ca5..8a21dde81faa 100644 --- a/CRM/Activity/Selector/Search.php +++ b/CRM/Activity/Selector/Search.php @@ -1,9 +1,9 @@ $componentName) { // CRM-19201: Add support for searching CiviCampaign and CiviCase // activities. For CiviCase, "access all cases and activities" is @@ -197,9 +197,7 @@ public function __construct( // type of selector $this->_action = $action; $this->_query = new CRM_Contact_BAO_Query($this->_queryParams, - CRM_Activity_BAO_Query::defaultReturnProperties(CRM_Contact_BAO_Query::MODE_ACTIVITY, - FALSE - ), + CRM_Activity_BAO_Query::selectorReturnProperties(), NULL, FALSE, FALSE, CRM_Contact_BAO_Query::MODE_ACTIVITY ); @@ -263,7 +261,7 @@ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) { FALSE, $this->_activityClause ); - $rows = array(); + $rows = []; $mailingIDs = CRM_Mailing_BAO_Mailing::mailingACLIDs(); $accessCiviMail = CRM_Core_Permission::check('access CiviMail'); @@ -271,20 +269,20 @@ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) { $allCampaigns = CRM_Campaign_BAO_Campaign::getCampaigns(NULL, NULL, FALSE, FALSE, FALSE, TRUE); $engagementLevels = CRM_Campaign_PseudoConstant::engagementLevel(); - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts); $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts); $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts); - // Get all activity types - $activityTypes = CRM_Core_PseudoConstant::activityType(TRUE, TRUE, FALSE, 'name', TRUE); + $bulkActivityTypeID = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Bulk Email'); while ($result->fetch()) { - $row = array(); + $row = []; // Ignore rows where we dont have an activity id. if (empty($result->activity_id)) { continue; } + $this->_query->convertToPseudoNames($result); // the columns we are interested in foreach (self::$_properties as $property) { @@ -312,9 +310,8 @@ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) { $accessMailingReport = FALSE; $activityTypeId = $row['activity_type_id']; if ($row['activity_is_test']) { - $row['activity_type'] = $row['activity_type'] . " (test)"; + $row['activity_type'] = CRM_Core_TestEntity::appendTestText($row['activity_type']); } - $bulkActivityTypeID = CRM_Utils_Array::key('Bulk Email', $activityTypes); $row['mailingId'] = ''; if ( $accessCiviMail && @@ -338,11 +335,11 @@ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) { $this->_compContext ); $row['action'] = CRM_Core_Action::formLink($actionLinks, NULL, - array( + [ 'id' => $result->activity_id, 'cid' => $contactId, 'cxt' => $this->_context, - ), + ], ts('more'), FALSE, 'activity.selector.row', @@ -364,7 +361,7 @@ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) { $repeat = CRM_Core_BAO_RecurringEntity::getPositionAndCount($row['activity_id'], 'civicrm_activity'); $row['repeat'] = ''; if ($repeat) { - $row['repeat'] = ts('Repeating (%1 of %2)', array(1 => $repeat[0], 2 => $repeat[1])); + $row['repeat'] = ts('Repeating (%1 of %2)', [1 => $repeat[0], 2 => $repeat[1]]); } $rows[] = $row; } @@ -394,38 +391,38 @@ public function getQILL() { */ public function &getColumnHeaders($action = NULL, $output = NULL) { if (!isset(self::$_columnHeaders)) { - self::$_columnHeaders = array( - array( + self::$_columnHeaders = [ + [ 'name' => ts('Type'), 'sort' => 'activity_type_id', 'direction' => CRM_Utils_Sort::DONTCARE, - ), - array( + ], + [ 'name' => ts('Subject'), 'sort' => 'activity_subject', 'direction' => CRM_Utils_Sort::DONTCARE, - ), - array( + ], + [ 'name' => ts('Added By'), 'sort' => 'source_contact', 'direction' => CRM_Utils_Sort::DONTCARE, - ), - array('name' => ts('With')), - array('name' => ts('Assigned')), - array( + ], + ['name' => ts('With')], + ['name' => ts('Assigned')], + [ 'name' => ts('Date'), 'sort' => 'activity_date_time', 'direction' => CRM_Utils_Sort::DESCENDING, - ), - array( + ], + [ 'name' => ts('Status'), 'sort' => 'activity_status', 'direction' => CRM_Utils_Sort::DONTCARE, - ), - array( + ], + [ 'desc' => ts('Actions'), - ), - ); + ], + ]; } return self::$_columnHeaders; } @@ -434,7 +431,7 @@ public function &getColumnHeaders($action = NULL, $output = NULL) { * @return mixed */ public function alphabetQuery() { - return $this->_query->searchQuery(NULL, NULL, NULL, FALSE, FALSE, TRUE); + return $this->_query->alphabetQuery(); } /** diff --git a/CRM/Activity/StateMachine/Search.php b/CRM/Activity/StateMachine/Search.php index 1f06cb7a9c40..237e9704fdf3 100644 --- a/CRM/Activity/StateMachine/Search.php +++ b/CRM/Activity/StateMachine/Search.php @@ -1,9 +1,9 @@ _pages = array(); + $this->_pages = []; $this->_pages['CRM_Activity_Form_Search'] = NULL; list($task, $result) = $this->taskName($controller, 'Search'); @@ -76,7 +76,7 @@ public function __construct($controller, $action = CRM_Core_Action::NONE) { * * @param string $formName * - * @return string + * @return array * the name of the form that will handle the task */ public function taskName($controller, $formName = 'Search') { diff --git a/CRM/Activity/Task.php b/CRM/Activity/Task.php index 526944883667..00bfcbffd952 100644 --- a/CRM/Activity/Task.php +++ b/CRM/Activity/Task.php @@ -1,9 +1,9 @@ array( + self::$_tasks = [ + self::TASK_DELETE => [ 'title' => ts('Delete activities'), 'class' => 'CRM_Activity_Form_Task_Delete', 'result' => FALSE, - ), - 2 => array( + ], + self::TASK_PRINT => [ 'title' => ts('Print selected rows'), 'class' => 'CRM_Activity_Form_Task_Print', 'result' => FALSE, - ), - 3 => array( + ], + self::TASK_EXPORT => [ 'title' => ts('Export activities'), - 'class' => array( + 'class' => [ 'CRM_Export_Form_Select', 'CRM_Export_Form_Map', - ), + ], 'result' => FALSE, - ), - 4 => array( + ], + self::BATCH_UPDATE => [ 'title' => ts('Update multiple activities'), - 'class' => array( + 'class' => [ 'CRM_Activity_Form_Task_PickProfile', 'CRM_Activity_Form_Task_Batch', - ), + ], 'result' => FALSE, - ), - 5 => array( - 'title' => ts('Email - send now'), - 'class' => array( + ], + self::TASK_EMAIL => [ + 'title' => ts('Email - send now (to %1 or less)', [ + 1 => Civi::settings() + ->get('simple_mail_limit'), + ]), + 'class' => [ 'CRM_Activity_Form_Task_PickOption', 'CRM_Activity_Form_Task_Email', - ), + ], 'result' => FALSE, - ), - 6 => array( + ], + self::TASK_SMS => [ 'title' => ts('SMS - send reply'), 'class' => 'CRM_Activity_Form_Task_SMS', 'result' => FALSE, - ), - 7 => array( + ], + self::TAG_ADD => [ 'title' => ts('Tag - add to activities'), 'class' => 'CRM_Activity_Form_Task_AddToTag', 'result' => FALSE, - ), - 8 => array( + ], + self::TAG_REMOVE => [ 'title' => ts('Tag - remove from activities'), 'class' => 'CRM_Activity_Form_Task_RemoveFromTag', 'result' => FALSE, - ), - ); + ], + ]; $config = CRM_Core_Config::singleton(); if (in_array('CiviCase', $config->enableComponents)) { if (CRM_Core_Permission::check('access all cases and activities') || CRM_Core_Permission::check('access my cases and activities') ) { - self::$_tasks[6] = array( + self::$_tasks[self::TASK_SMS] = [ 'title' => ts('File on case'), 'class' => 'CRM_Activity_Form_Task_FileOnCase', 'result' => FALSE, - ); + ]; } } // CRM-4418, check for delete if (!CRM_Core_Permission::check('delete activities')) { - unset(self::$_tasks[1]); + unset(self::$_tasks[self::TASK_DELETE]); } - } - CRM_Utils_Hook::searchTasks('activity', self::$_tasks); - asort(self::$_tasks); - return self::$_tasks; - } - /** - * These tasks are the core set of task titles on activity. - * - * @return array - * the set of task titles - */ - public static function &taskTitles() { - self::tasks(); - $titles = array(); - foreach (self::$_tasks as $id => $value) { - $titles[$id] = $value['title']; + parent::tasks(); } - return $titles; + + return self::$_tasks; } /** * Show tasks selectively based on the permission level of the user. * * @param int $permission + * @param array $params * * @return array * set of tasks that are valid for the user */ - public static function &permissionedTaskTitles($permission) { - $tasks = array(); + public static function permissionedTaskTitles($permission, $params = []) { if ($permission == CRM_Core_Permission::EDIT) { $tasks = self::taskTitles(); } else { - $tasks = array( - 3 => self::$_tasks[3]['title'], - ); + $tasks = [ + self::TASK_EXPORT => self::$_tasks[self::TASK_EXPORT]['title'], + ]; //CRM-4418, if (CRM_Core_Permission::check('delete activities')) { - $tasks[1] = self::$_tasks[1]['title']; + $tasks[self::TASK_DELETE] = self::$_tasks[self::TASK_DELETE]['title']; } } + + $tasks = parent::corePermissionedTaskTitles($tasks, $permission, $params); return $tasks; } @@ -193,12 +165,13 @@ public static function getTask($value) { self::tasks(); if (!$value || !CRM_Utils_Array::value($value, self::$_tasks)) { // make the print task by default - $value = 2; + $value = self::TASK_PRINT; } - return array( + + return [ self::$_tasks[$value]['class'], self::$_tasks[$value]['result'], - ); + ]; } } diff --git a/CRM/Activity/Tokens.php b/CRM/Activity/Tokens.php index def80cbd850b..3271b965922f 100644 --- a/CRM/Activity/Tokens.php +++ b/CRM/Activity/Tokens.php @@ -2,9 +2,9 @@ /* +--------------------------------------------------------------------+ - | CiviCRM version 4.7 | + | CiviCRM version 5 | +--------------------------------------------------------------------+ - | Copyright CiviCRM LLC (c) 2004-2016 | + | Copyright CiviCRM LLC (c) 2004-2019 | +--------------------------------------------------------------------+ | This file is a part of CiviCRM. | | | @@ -28,7 +28,7 @@ /** * @package CRM - * @copyright CiviCRM LLC (c) 2004-2016 + * @copyright CiviCRM LLC (c) 2004-2019 */ /** @@ -45,30 +45,28 @@ */ class CRM_Activity_Tokens extends \Civi\Token\AbstractTokenSubscriber { + /** + * CRM_Activity_Tokens constructor. + */ public function __construct() { - parent::__construct('activity', array( - 'activity_id' => ts('Activity ID'), - 'activity_type' => ts('Activity Type'), - 'subject' => ts('Activity Subject'), - 'details' => ts('Activity Details'), - 'activity_date_time' => ts('Activity Date-Time'), + parent::__construct('activity', array_merge( + $this->getBasicTokens(), + $this->getCustomFieldTokens() )); } /** - * Check is active. - * - * @param \Civi\Token\TokenProcessor $processor - * - * @return bool + * @inheritDoc */ public function checkActive(\Civi\Token\TokenProcessor $processor) { // Extracted from scheduled-reminders code. See the class description. - return - !empty($processor->context['actionMapping']) + return !empty($processor->context['actionMapping']) && $processor->context['actionMapping']->getEntity() === 'civicrm_activity'; } + /** + * @inheritDoc + */ public function alterActionScheduleQuery(\Civi\ActionSchedule\Event\MailingQueryEvent $e) { if ($e->mapping->getEntity() !== 'civicrm_activity') { return; @@ -79,7 +77,8 @@ public function alterActionScheduleQuery(\Civi\ActionSchedule\Event\MailingQuery // Q: Could we simplify & move the extra AND clauses into `where(...)`? $e->query->param('casEntityJoinExpr', 'e.id = reminder.entity_id AND e.is_current_revision = 1 AND e.is_deleted = 0'); - $e->query->select('e.*'); // FIXME: seems too broad. + // FIXME: seems too broad. + $e->query->select('e.*'); $e->query->select('ov.label as activity_type, e.id as activity_id'); $e->query->join("og", "!casMailingJoinType civicrm_option_group og ON og.name = 'activity_type'"); @@ -94,17 +93,7 @@ public function alterActionScheduleQuery(\Civi\ActionSchedule\Event\MailingQuery } /** - * Evaluate the content of a single token. - * - * @param \Civi\Token\TokenRow $row - * The record for which we want token values. - * @param string $entity - * @param string $field - * The name of the token field. - * @param mixed $prefetch - * Any data that was returned by the prefetch(). - * - * @return mixed + * @inheritDoc */ public function evaluateToken(\Civi\Token\TokenRow $row, $entity, $field, $prefetch = NULL) { $actionSearchResult = $row->context['actionSearchResult']; @@ -115,9 +104,35 @@ public function evaluateToken(\Civi\Token\TokenRow $row, $entity, $field, $prefe elseif (isset($actionSearchResult->$field)) { $row->tokens($entity, $field, $actionSearchResult->$field); } + elseif ($cfID = \CRM_Core_BAO_CustomField::getKeyID($field)) { + $row->customToken($entity, $cfID, $actionSearchResult->entity_id); + } else { $row->tokens($entity, $field, ''); } } + /** + * Get the basic tokens provided. + * + * @return array token name => token label + */ + protected function getBasicTokens() { + return [ + 'activity_id' => ts('Activity ID'), + 'activity_type' => ts('Activity Type'), + 'subject' => ts('Activity Subject'), + 'details' => ts('Activity Details'), + 'activity_date_time' => ts('Activity Date-Time'), + ]; + } + + /** + * Get the tokens for custom fields + * @return array token name => token label + */ + protected function getCustomFieldTokens() { + return CRM_Utils_Token::getCustomFieldTokens('Activity'); + } + } diff --git a/CRM/Admin/Form.php b/CRM/Admin/Form.php index c6b535c079ff..e4edfb0a7c60 100644 --- a/CRM/Admin/Form.php +++ b/CRM/Admin/Form.php @@ -1,9 +1,9 @@ addStyleFile('civicrm', 'css/admin.css'); + Civi::resources()->addScriptFile('civicrm', 'js/jquery/jquery.crmIconPicker.js'); $this->_id = $this->get('id'); $this->_BAOName = $this->get('BAOName'); - $this->_values = array(); + $this->_values = []; if (isset($this->_id)) { - $params = array('id' => $this->_id); + $params = ['id' => $this->_id]; // this is needed if the form is outside the CRM name space $baoName = $this->_BAOName; $baoName::retrieve($params, $this->_values); @@ -89,14 +90,24 @@ public function preProcess() { * @return array */ public function setDefaultValues() { - if (isset($this->_id) && empty($this->_values)) { - $this->_values = array(); - $params = array('id' => $this->_id); + // Fetch defaults from the db + if (!empty($this->_id) && empty($this->_values) && CRM_Utils_Rule::positiveInteger($this->_id)) { + $this->_values = []; + $params = ['id' => $this->_id]; $baoName = $this->_BAOName; $baoName::retrieve($params, $this->_values); } $defaults = $this->_values; + // Allow defaults to be set from the url + if (empty($this->_id) && $this->_action & CRM_Core_Action::ADD) { + foreach ($_GET as $key => $val) { + if ($this->elementExists($key)) { + $defaults[$key] = $val; + } + } + } + if ($this->_action == CRM_Core_Action::DELETE && isset($defaults['name']) ) { @@ -116,28 +127,26 @@ public function setDefaultValues() { */ public function buildQuickForm() { if ($this->_action & CRM_Core_Action::VIEW || $this->_action & CRM_Core_Action::PREVIEW) { - $this->addButtons(array( - array( - 'type' => 'cancel', - 'name' => ts('Done'), - 'isDefault' => TRUE, - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'cancel', + 'name' => ts('Done'), + 'isDefault' => TRUE, + ], + ]); } else { - $this->addButtons(array( - array( - 'type' => 'next', - 'name' => $this->_action & CRM_Core_Action::DELETE ? ts('Delete') : ts('Save'), - 'isDefault' => TRUE, - ), - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'next', + 'name' => $this->_action & CRM_Core_Action::DELETE ? ts('Delete') : ts('Save'), + 'isDefault' => TRUE, + ], + [ + 'type' => 'cancel', + 'name' => ts('Cancel'), + ], + ]); } } diff --git a/CRM/Admin/Form/CMSUser.php b/CRM/Admin/Form/CMSUser.php index 9571716d9f9d..44305056b654 100644 --- a/CRM/Admin/Form/CMSUser.php +++ b/CRM/Admin/Form/CMSUser.php @@ -1,9 +1,9 @@ addButtons(array( - array( - 'type' => 'next', - 'name' => ts('OK'), - 'isDefault' => TRUE, - ), - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'next', + 'name' => ts('OK'), + 'isDefault' => TRUE, + ], + [ + 'type' => 'cancel', + 'name' => ts('Cancel'), + ], + ]); } /** @@ -62,25 +61,25 @@ public function postProcess() { $result = CRM_Utils_System::synchronizeUsers(); $status = ts('Checked one user record.', - array( + [ 'count' => $result['contactCount'], 'plural' => 'Checked %count user records.', - ) + ] ); if ($result['contactMatching']) { $status .= '
' . ts('Found one matching contact record.', - array( + [ 'count' => $result['contactMatching'], 'plural' => 'Found %count matching contact records.', - ) + ] ); } $status .= '
' . ts('Created one new contact record.', - array( + [ 'count' => $result['contactCreated'], 'plural' => 'Created %count new contact records.', - ) + ] ); CRM_Core_Session::setStatus($status, ts('Synchronize Complete'), 'success'); CRM_Core_Session::singleton()->pushUserContext(CRM_Utils_System::url('civicrm/admin', 'reset=1')); diff --git a/CRM/Admin/Form/ContactType.php b/CRM/Admin/Form/ContactType.php index d54229af9ab3..3e6f2a753646 100644 --- a/CRM/Admin/Form/ContactType.php +++ b/CRM/Admin/Form/ContactType.php @@ -1,9 +1,9 @@ assign('cid', $this->_id); - $this->addFormRule(array('CRM_Admin_Form_ContactType', 'formRule'), $this); + $this->addFormRule(['CRM_Admin_Form_ContactType', 'formRule'], $this); } /** @@ -90,7 +90,7 @@ public function buildQuickForm() { */ public static function formRule($fields, $files, $self) { - $errors = array(); + $errors = []; if ($self->_id) { $contactName = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_ContactType', $self->_id, 'name'); @@ -142,7 +142,7 @@ public function postProcess() { } $contactType = CRM_Contact_BAO_ContactType::add($params); CRM_Core_Session::setStatus(ts("The Contact Type '%1' has been saved.", - array(1 => $contactType->label) + [1 => $contactType->label] ), ts('Saved'), 'success'); } diff --git a/CRM/Admin/Form/Extensions.php b/CRM/Admin/Form/Extensions.php index 7aa2b80bb931..852ebb082083 100644 --- a/CRM/Admin/Form/Extensions.php +++ b/CRM/Admin/Form/Extensions.php @@ -1,9 +1,9 @@ formatLocalExtensionRows(); + $this->assign('localExtensionRows', $localExtensionRows); + + $remoteExtensionRows = $mainPage->formatRemoteExtensionRows($localExtensionRows); + $this->assign('remoteExtensionRows', $remoteExtensionRows); + $this->_key = CRM_Utils_Request::retrieve('key', 'String', $this, FALSE, 0 ); - + if (!CRM_Utils_Type::validate($this->_key, 'ExtensionKey') && !empty($this->_key)) { + throw new CRM_Core_Exception('Extension Key does not match expected standard'); + } $session = CRM_Core_Session::singleton(); $url = CRM_Utils_System::url('civicrm/admin/extensions', 'reset=1&action=browse'); $session->pushUserContext($url); @@ -81,7 +90,7 @@ public function preProcess() { * Set default values for the form. */ public function setDefaultValues() { - $defaults = array(); + $defaults = []; return $defaults; } @@ -92,53 +101,52 @@ public function buildQuickForm() { switch ($this->_action) { case CRM_Core_Action::ADD: $buttonName = ts('Install'); - $title = ts('Install "%1"?', array( + $title = ts('Install "%1"?', [ 1 => $this->_key, - )); + ]); break; case CRM_Core_Action::UPDATE: $buttonName = ts('Download and Install'); - $title = ts('Download and Install "%1"?', array( + $title = ts('Download and Install "%1"?', [ 1 => $this->_key, - )); + ]); break; case CRM_Core_Action::DELETE: $buttonName = ts('Uninstall'); - $title = ts('Uninstall "%1"?', array( + $title = ts('Uninstall "%1"?', [ 1 => $this->_key, - )); + ]); break; case CRM_Core_Action::ENABLE: $buttonName = ts('Enable'); - $title = ts('Enable "%1"?', array( + $title = ts('Enable "%1"?', [ 1 => $this->_key, - )); + ]); break; case CRM_Core_Action::DISABLE: $buttonName = ts('Disable'); - $title = ts('Disable "%1"?', array( + $title = ts('Disable "%1"?', [ 1 => $this->_key, - )); + ]); break; } $this->assign('title', $title); - $this->addButtons(array( - array( - 'type' => 'next', - 'name' => $buttonName, - 'isDefault' => TRUE, - ), - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'next', + 'name' => $buttonName, + 'isDefault' => TRUE, + ], + [ + 'type' => 'cancel', + 'name' => ts('Cancel'), + ], + ]); } /** @@ -155,7 +163,7 @@ public function buildQuickForm() { * true if no errors, else an array of errors */ public static function formRule($fields, $files, $self) { - $errors = array(); + $errors = []; return empty($errors) ? TRUE : $errors; } @@ -168,7 +176,7 @@ public function postProcess() { if ($this->_action & CRM_Core_Action::DELETE) { try { - CRM_Extension_System::singleton()->getManager()->uninstall(array($this->_key)); + CRM_Extension_System::singleton()->getManager()->uninstall([$this->_key]); CRM_Core_Session::setStatus("", ts('Extension Uninstalled'), "success"); } catch (CRM_Extension_Exception_DependencyException $e) { @@ -178,25 +186,25 @@ public function postProcess() { } if ($this->_action & CRM_Core_Action::ADD) { - CRM_Extension_System::singleton()->getManager()->install(array($this->_key)); + civicrm_api3('Extension', 'install', ['keys' => $this->_key]); CRM_Core_Session::setStatus("", ts('Extension Installed'), "success"); } if ($this->_action & CRM_Core_Action::ENABLE) { - CRM_Extension_System::singleton()->getManager()->enable(array($this->_key)); + civicrm_api3('Extension', 'enable', ['keys' => $this->_key]); CRM_Core_Session::setStatus("", ts('Extension Enabled'), "success"); } if ($this->_action & CRM_Core_Action::DISABLE) { - CRM_Extension_System::singleton()->getManager()->disable(array($this->_key)); + CRM_Extension_System::singleton()->getManager()->disable([$this->_key]); CRM_Core_Session::setStatus("", ts('Extension Disabled'), "success"); } if ($this->_action & CRM_Core_Action::UPDATE) { - $result = civicrm_api('Extension', 'download', array( + $result = civicrm_api('Extension', 'download', [ 'version' => 3, 'key' => $this->_key, - )); + ]); if (!CRM_Utils_Array::value('is_error', $result, FALSE)) { CRM_Core_Session::setStatus("", ts('Extension Upgraded'), "success"); } diff --git a/CRM/Admin/Form/Generic.php b/CRM/Admin/Form/Generic.php new file mode 100644 index 000000000000..04b29ecd9793 --- /dev/null +++ b/CRM/Admin/Form/Generic.php @@ -0,0 +1,112 @@ +setDefaultsForMetadataDefinedFields(); + return $this->_defaults; + } + + /** + * Build the form object. + */ + public function buildQuickForm() { + $filter = $this->getSettingPageFilter(); + $settings = civicrm_api3('Setting', 'getfields', [])['values']; + foreach ($settings as $key => $setting) { + if (isset($setting['settings_pages'][$filter])) { + $this->_settings[$key] = $setting; + } + } + + $this->addFieldsDefinedInSettingsMetadata(); + + // @todo look at sharing the code below in the settings trait. + if ($this->includesReadOnlyFields) { + CRM_Core_Session::setStatus(ts("Some fields are loaded as 'readonly' as they have been set (overridden) in civicrm.settings.php."), '', 'info', ['expires' => 0]); + } + + // @todo - do we still like this redirect? + CRM_Core_Session::singleton()->pushUserContext(CRM_Utils_System::url('civicrm/admin', 'reset=1')); + $this->addButtons([ + [ + 'type' => 'next', + 'name' => ts('Save'), + 'isDefault' => TRUE, + ], + [ + 'type' => 'cancel', + 'name' => ts('Cancel'), + ], + ]); + } + + /** + * Process the form submission. + */ + public function postProcess() { + $params = $this->controller->exportValues($this->_name); + try { + $this->saveMetadataDefinedSettings($params); + } + catch (CiviCRM_API3_Exception $e) { + CRM_Core_Session::setStatus($e->getMessage(), ts('Save Failed'), 'error'); + } + } + +} diff --git a/CRM/Admin/Form/Job.php b/CRM/Admin/Form/Job.php index 46f221e7c18d..42f4a3c3861d 100644 --- a/CRM/Admin/Form/Job.php +++ b/CRM/Admin/Form/Job.php @@ -1,9 +1,9 @@ addRule('name', ts('Name already exists in Database.'), 'objectExists', array( - 'CRM_Core_DAO_Job', - $this->_id, - )); + $this->addRule('name', ts('Name already exists in Database.'), 'objectExists', [ + 'CRM_Core_DAO_Job', + $this->_id, + ]); $this->add('text', 'description', ts('Description'), $attributes['description'] @@ -97,7 +97,7 @@ public function buildQuickForm($check = FALSE) { $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, array('minDate' => time())); + $this->add('datepicker', 'scheduled_run_date', ts('Scheduled Run Date'), NULL, FALSE, ['minDate' => time()]); $this->add('textarea', 'parameters', ts('Command parameters'), "cols=50 rows=6" @@ -106,7 +106,7 @@ public function buildQuickForm($check = FALSE) { // is this job active ? $this->add('checkbox', 'is_active', ts('Is this Scheduled Job active?')); - $this->addFormRule(array('CRM_Admin_Form_Job', 'formRule')); + $this->addFormRule(['CRM_Admin_Form_Job', 'formRule']); } /** @@ -117,13 +117,13 @@ public function buildQuickForm($check = FALSE) { */ public static function formRule($fields) { - $errors = array(); + $errors = []; require_once 'api/api.php'; /** @var \Civi\API\Kernel $apiKernel */ $apiKernel = \Civi::service('civi_api_kernel'); - $apiRequest = \Civi\API\Request::create($fields['api_entity'], $fields['api_action'], array('version' => 3), NULL); + $apiRequest = \Civi\API\Request::create($fields['api_entity'], $fields['api_action'], ['version' => 3], NULL); try { $apiKernel->resolve($apiRequest); } @@ -142,7 +142,7 @@ public static function formRule($fields) { * @return array */ public function setDefaultValues() { - $defaults = array(); + $defaults = []; if (!$this->_id) { $defaults['is_active'] = $defaults['is_default'] = 1; @@ -180,7 +180,7 @@ public function setDefaultValues() { */ public function postProcess() { - CRM_Utils_System::flushCache('CRM_Core_DAO_Job'); + CRM_Utils_System::flushCache(); if ($this->_action & CRM_Core_Action::DELETE) { CRM_Core_BAO_Job::del($this->_id); @@ -217,13 +217,13 @@ public function postProcess() { The result will land on the same day of the month except for days 29-31 when the target month contains fewer days than the previous month. For example, if a job is scheduled to run on August 31st, the following invocation will occur on October 1st, and then the 1st of every month thereafter. To avoid this issue, please schedule Monthly and Quarterly jobs to run within the first 28 days of the month.'), - ts('Warning'), 'info', array('expires' => 0)); + ts('Warning'), 'info', ['expires' => 0]); } } } // ...otherwise, if this isn't a new scheduled job, clear the next scheduled run elseif ($dao->id) { - $job = new CRM_Core_ScheduledJob(array('id' => $dao->id)); + $job = new CRM_Core_ScheduledJob(['id' => $dao->id]); $job->clearScheduledRunDate(); } @@ -233,7 +233,7 @@ public function postProcess() { if ($values['api_action'] == 'update_greeting' && CRM_Utils_Array::value('is_active', $values) == 1) { // pass "wiki" as 6th param to docURL2 if you are linking to a page in wiki.civicrm.org $docLink = CRM_Utils_System::docURL2("Managing Scheduled Jobs", NULL, NULL, NULL, NULL, "wiki"); - $msg = ts('The update greeting job can be very resource intensive and is typically not necessary to run on a regular basis. If you do choose to enable the job, we recommend you do not run it with the force=1 option, which would rebuild greetings on all records. Leaving that option absent, or setting it to force=0, will only rebuild greetings for contacts that do not currently have a value stored. %1', array(1 => $docLink)); + $msg = ts('The update greeting job can be very resource intensive and is typically not necessary to run on a regular basis. If you do choose to enable the job, we recommend you do not run it with the force=1 option, which would rebuild greetings on all records. Leaving that option absent, or setting it to force=0, will only rebuild greetings for contacts that do not currently have a value stored. %1', [1 => $docLink]); CRM_Core_Session::setStatus($msg, ts('Warning: Update Greeting job enabled'), 'alert'); } diff --git a/CRM/Admin/Form/LabelFormats.php b/CRM/Admin/Form/LabelFormats.php index e6f9e4efddbf..6e3ad025df0c 100644 --- a/CRM/Admin/Form/LabelFormats.php +++ b/CRM/Admin/Form/LabelFormats.php @@ -1,7 +1,7 @@ _id = $this->get('id'); $this->_group = CRM_Utils_Request::retrieve('group', 'String', $this, FALSE, 'label_format'); - $this->_values = array(); + $this->_values = []; if (isset($this->_id)) { - $params = array('id' => $this->_id); + $params = ['id' => $this->_id]; CRM_Core_BAO_LabelFormat::retrieve($params, $this->_values, $this->_group); } } @@ -69,7 +71,7 @@ public function buildQuickForm() { return; } - $disabled = array(); + $disabled = []; $required = TRUE; $is_reserved = $this->_id ? CRM_Core_BAO_LabelFormat::getFieldValue('CRM_Core_BAO_LabelFormat', $this->_id, 'is_reserved') : FALSE; if ($is_reserved) { @@ -79,7 +81,7 @@ public function buildQuickForm() { $attributes = CRM_Core_DAO::getAttribute('CRM_Core_BAO_LabelFormat'); $this->add('text', 'label', ts('Name'), $attributes['label'] + $disabled, $required); - $this->add('text', 'description', ts('Description'), array('size' => CRM_Utils_Type::HUGE)); + $this->add('text', 'description', ts('Description'), ['size' => CRM_Utils_Type::HUGE]); $this->add('checkbox', 'is_default', ts('Is this Label Format the default?')); // currently we support only mailing label creation, hence comment below code @@ -97,18 +99,18 @@ public function buildQuickForm() { */ $this->add('select', 'paper_size', ts('Sheet Size'), - array( + [ 0 => ts('- default -'), - ) + CRM_Core_BAO_PaperSize::getList(TRUE), FALSE, - array( + ] + CRM_Core_BAO_PaperSize::getList(TRUE), FALSE, + [ 'onChange' => "selectPaper( this.value );", - ) + $disabled + ] + $disabled ); $this->add('static', 'paper_dimensions', NULL, ts('Sheet Size (w x h)')); $this->add('select', 'orientation', ts('Orientation'), CRM_Core_BAO_LabelFormat::getPageOrientations(), FALSE, - array( + [ 'onChange' => "updatePaperDimensions();", - ) + $disabled + ] + $disabled ); $this->add('select', 'font_name', ts('Font Name'), CRM_Core_BAO_LabelFormat::getFontNames($this->_group)); $this->add('select', 'font_size', ts('Font Size'), CRM_Core_BAO_LabelFormat::getFontSizes()); @@ -116,24 +118,24 @@ public function buildQuickForm() { $this->add('checkbox', 'bold', ts('Bold')); $this->add('checkbox', 'italic', ts('Italic')); $this->add('select', 'metric', ts('Unit of Measure'), CRM_Core_BAO_LabelFormat::getUnits(), FALSE, - array('onChange' => "selectMetric( this.value );") + ['onChange' => "selectMetric( this.value );"] ); - $this->add('text', 'width', ts('Label Width'), array('size' => 8, 'maxlength' => 8) + $disabled, $required); - $this->add('text', 'height', ts('Label Height'), array('size' => 8, 'maxlength' => 8) + $disabled, $required); - $this->add('text', 'NX', ts('Labels Per Row'), array('size' => 3, 'maxlength' => 3) + $disabled, $required); - $this->add('text', 'NY', ts('Labels Per Column'), array('size' => 3, 'maxlength' => 3) + $disabled, $required); - $this->add('text', 'tMargin', ts('Top Margin'), array('size' => 8, 'maxlength' => 8) + $disabled, $required); - $this->add('text', 'lMargin', ts('Left Margin'), array('size' => 8, 'maxlength' => 8) + $disabled, $required); - $this->add('text', 'SpaceX', ts('Horizontal Spacing'), array('size' => 8, 'maxlength' => 8) + $disabled, $required); - $this->add('text', 'SpaceY', ts('Vertical Spacing'), array('size' => 8, 'maxlength' => 8) + $disabled, $required); - $this->add('text', 'lPadding', ts('Left Padding'), array('size' => 8, 'maxlength' => 8), $required); - $this->add('text', 'tPadding', ts('Top Padding'), array('size' => 8, 'maxlength' => 8), $required); - $this->add('text', 'weight', ts('Order'), CRM_Core_DAO::getAttribute('CRM_Core_BAO_LabelFormat', 'weight'), TRUE); - - $this->addRule('label', ts('Name already exists in Database.'), 'objectExists', array( + $this->add('text', 'width', ts('Label Width'), ['size' => 8, 'maxlength' => 8] + $disabled, $required); + $this->add('text', 'height', ts('Label Height'), ['size' => 8, 'maxlength' => 8] + $disabled, $required); + $this->add('text', 'NX', ts('Labels Per Row'), ['size' => 3, 'maxlength' => 3] + $disabled, $required); + $this->add('text', 'NY', ts('Labels Per Column'), ['size' => 3, 'maxlength' => 3] + $disabled, $required); + $this->add('text', 'tMargin', ts('Top Margin'), ['size' => 8, 'maxlength' => 8] + $disabled, $required); + $this->add('text', 'lMargin', ts('Left Margin'), ['size' => 8, 'maxlength' => 8] + $disabled, $required); + $this->add('text', 'SpaceX', ts('Horizontal Spacing'), ['size' => 8, 'maxlength' => 8] + $disabled, $required); + $this->add('text', 'SpaceY', ts('Vertical Spacing'), ['size' => 8, 'maxlength' => 8] + $disabled, $required); + $this->add('text', 'lPadding', ts('Left Padding'), ['size' => 8, 'maxlength' => 8], $required); + $this->add('text', 'tPadding', ts('Top Padding'), ['size' => 8, 'maxlength' => 8], $required); + $this->add('number', 'weight', ts('Order'), CRM_Core_DAO::getAttribute('CRM_Core_BAO_LabelFormat', 'weight'), TRUE); + + $this->addRule('label', ts('Name already exists in Database.'), 'objectExists', [ 'CRM_Core_BAO_LabelFormat', $this->_id, - )); + ]); $this->addRule('NX', ts('Please enter a valid integer.'), 'integer'); $this->addRule('NY', ts('Please enter a valid integer.'), 'integer'); $this->addRule('tMargin', ts('Please enter a valid number.'), 'numeric'); @@ -186,14 +188,14 @@ public function postProcess() { if ($this->_action & CRM_Core_Action::COPY) { // make a copy of the Label Format $labelFormat = CRM_Core_BAO_LabelFormat::getById($this->_id, $this->_group); - $newlabel = ts('Copy of %1', array(1 => $labelFormat['label'])); + $newlabel = ts('Copy of %1', [1 => $labelFormat['label']]); $list = CRM_Core_BAO_LabelFormat::getList(TRUE, $this->_group); $count = 1; while (in_array($newlabel, $list)) { $count++; - $newlabel = ts('Copy %1 of %2', array(1 => $count, 2 => $labelFormat['label'])); + $newlabel = ts('Copy %1 of %2', [1 => $count, 2 => $labelFormat['label']]); } $labelFormat['label'] = $newlabel; @@ -203,7 +205,7 @@ public function postProcess() { $bao = new CRM_Core_BAO_LabelFormat(); $bao->saveLabelFormat($labelFormat, NULL, $this->_group); - CRM_Core_Session::setStatus(ts('%1 has been created.', array(1 => $labelFormat['label'])), ts('Saved'), 'success'); + CRM_Core_Session::setStatus(ts('%1 has been created.', [1 => $labelFormat['label']]), ts('Saved'), 'success'); return; } @@ -240,9 +242,9 @@ public function postProcess() { $bao = new CRM_Core_BAO_LabelFormat(); $bao->saveLabelFormat($values, $this->_id, $values['label_type']); - $status = ts('Your new Label Format titled %1 has been saved.', array(1 => $values['label'])); + $status = ts('Your new Label Format titled %1 has been saved.', [1 => $values['label']]); if ($this->_action & CRM_Core_Action::UPDATE) { - $status = ts('Your Label Format titled %1 has been updated.', array(1 => $values['label'])); + $status = ts('Your Label Format titled %1 has been updated.', [1 => $values['label']]); } CRM_Core_Session::setStatus($status, ts('Saved'), 'success'); } diff --git a/CRM/Admin/Form/LocationType.php b/CRM/Admin/Form/LocationType.php index 06284afc4e0c..65e43aff0b12 100644 --- a/CRM/Admin/Form/LocationType.php +++ b/CRM/Admin/Form/LocationType.php @@ -1,9 +1,9 @@ addRule('name', ts('Name already exists in Database.'), 'objectExists', - array('CRM_Core_DAO_LocationType', $this->_id) + ['CRM_Core_DAO_LocationType', $this->_id] ); $this->addRule('name', ts('Name can only consist of alpha-numeric characters'), @@ -74,10 +74,10 @@ public function buildQuickForm() { if ($this->_action & CRM_Core_Action::UPDATE) { if (CRM_Core_DAO::getFieldValue('CRM_Core_DAO_LocationType', $this->_id, 'is_reserved')) { - $this->freeze(array('name', 'description', 'is_active')); + $this->freeze(['name', 'description', 'is_active']); } if (CRM_Core_DAO::getFieldValue('CRM_Core_DAO_LocationType', $this->_id, 'is_default')) { - $this->freeze(array('is_default')); + $this->freeze(['is_default']); } } } @@ -86,7 +86,7 @@ public function buildQuickForm() { * Process the form submission. */ public function postProcess() { - CRM_Utils_System::flushCache('CRM_Core_DAO_LocationType'); + CRM_Utils_System::flushCache(); if ($this->_action & CRM_Core_Action::DELETE) { CRM_Core_BAO_LocationType::del($this->_id); @@ -120,7 +120,7 @@ public function postProcess() { $locationType->save(); CRM_Core_Session::setStatus(ts("The location type '%1' has been saved.", - array(1 => $locationType->name) + [1 => $locationType->name] ), ts('Saved'), 'success'); } diff --git a/CRM/Admin/Form/MailSettings.php b/CRM/Admin/Form/MailSettings.php index 3d35b35faee0..b87ccd961047 100644 --- a/CRM/Admin/Form/MailSettings.php +++ b/CRM/Admin/Form/MailSettings.php @@ -1,9 +1,9 @@ add('select', 'protocol', ts('Protocol'), - array('' => ts('- select -')) + CRM_Core_PseudoConstant::get('CRM_Core_DAO_MailSettings', 'protocol'), + ['' => ts('- select -')] + CRM_Core_PseudoConstant::get('CRM_Core_DAO_MailSettings', 'protocol'), TRUE ); $this->add('text', 'server', ts('Server'), $attributes['server']); - $this->add('text', 'username', ts('Username'), array('autocomplete' => 'off')); + $this->add('text', 'username', ts('Username'), ['autocomplete' => 'off']); - $this->add('password', 'password', ts('Password'), array('autocomplete' => 'off')); + $this->add('password', 'password', ts('Password'), ['autocomplete' => 'off']); $this->add('text', 'source', ts('Source'), $attributes['source']); $this->add('checkbox', 'is_ssl', ts('Use SSL?')); - $usedfor = array( + $usedfor = [ 1 => ts('Bounce Processing'), 0 => ts('Email-to-Activity Processing'), - ); + ]; $this->add('select', 'is_default', ts('Used For?'), $usedfor); + $this->addField('activity_status', ['placeholder' => FALSE]); } /** * Add local and global form rules. */ public function addRules() { - $this->addFormRule(array('CRM_Admin_Form_MailSettings', 'formRule')); + $this->addFormRule(['CRM_Admin_Form_MailSettings', 'formRule']); + } + + public function getDefaultEntity() { + return 'MailSettings'; + } + + /** + * Add local and global form rules. + */ + public function setDefaultValues() { + $defaults = parent::setDefaultValues(); + + // Set activity status to "Completed" by default. + if ($this->_action != CRM_Core_Action::DELETE && + (!$this->_id || !CRM_Core_DAO::getFieldValue('CRM_Core_BAO_MailSettings', $this->_id, 'activity_status')) + ) { + $defaults['activity_status'] = 'Completed'; + } + + return $defaults; } /** @@ -104,7 +125,7 @@ public function addRules() { * list of errors to be posted back to the form */ public static function formRule($fields) { - $errors = array(); + $errors = []; // Check for default from email address and organization (domain) name. Force them to change it. if ($fields['domain'] == 'EXAMPLE.ORG') { $errors['domain'] = ts('Please enter a valid domain for this mailbox account (the part after @).'); @@ -127,7 +148,7 @@ public function postProcess() { $formValues = $this->controller->exportValues($this->_name); //form fields. - $fields = array( + $fields = [ 'name', 'domain', 'localpart', @@ -140,14 +161,15 @@ public function postProcess() { 'source', 'is_ssl', 'is_default', - ); + 'activity_status', + ]; - $params = array(); + $params = []; foreach ($fields as $f) { - if (in_array($f, array( + if (in_array($f, [ 'is_default', 'is_ssl', - ))) { + ])) { $params[$f] = CRM_Utils_Array::value($f, $formValues, FALSE); } else { diff --git a/CRM/Admin/Form/Mapping.php b/CRM/Admin/Form/Mapping.php index 4c0d84f38043..2dac50e76328 100644 --- a/CRM/Admin/Form/Mapping.php +++ b/CRM/Admin/Form/Mapping.php @@ -1,9 +1,9 @@ add('text', 'name', ts('Name'), CRM_Core_DAO::getAttribute('CRM_Core_DAO_Mapping', 'name'), TRUE ); - $this->addRule('name', ts('Name already exists in Database.'), 'objectExists', array( - 'CRM_Core_DAO_Mapping', - $this->_id, - )); + $this->addRule('name', ts('Name already exists in Database.'), 'objectExists', [ + 'CRM_Core_DAO_Mapping', + $this->_id, + ]); $this->addElement('text', 'description', ts('Description'), CRM_Core_DAO::getAttribute('CRM_Core_DAO_Mapping', 'description') diff --git a/CRM/Admin/Form/MessageTemplates.php b/CRM/Admin/Form/MessageTemplates.php index 964dc953e0fe..06f0259ec921 100644 --- a/CRM/Admin/Form/MessageTemplates.php +++ b/CRM/Admin/Form/MessageTemplates.php @@ -1,9 +1,9 @@ _workflow_id = CRM_Utils_Array::value('workflow_id', $defaults); - $this->assign('workflow_id', $this->_workflow_id); if ($this->_action & CRM_Core_Action::ADD) { $defaults['is_active'] = 1; - //set the context for redirection after form submit or cancel - $session = CRM_Core_Session::singleton(); - $session->replaceUserContext(CRM_Utils_System::url('civicrm/admin/messageTemplates', - 'selectedChild=user&reset=1' - )); } - // FIXME: we need to fix the Cancel button here as we don’t know whether it’s a workflow template in buildQuickForm() if ($this->_action & CRM_Core_Action::UPDATE) { - if ($this->_workflow_id) { - $selectedChild = 'workflow'; - } - else { - $selectedChild = 'user'; - } - $documentInfo = CRM_Core_BAO_File::getEntityFile('civicrm_msg_template', $this->_id, TRUE); if (!empty($documentInfo)) { $defaults['file_type'] = 1; $this->_is_document = TRUE; $this->assign('attachment', $documentInfo); } - - $cancelURL = CRM_Utils_System::url('civicrm/admin/messageTemplates', "selectedChild={$selectedChild}&reset=1"); - $cancelURL = str_replace('&', '&', $cancelURL); - $this->addButtons( - array( - array( - 'type' => 'upload', - 'name' => ts('Save'), - 'isDefault' => TRUE, - ), - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - 'js' => array('onclick' => "location.href='{$cancelURL}'; return false;"), - ), - ) - ); } return $defaults; @@ -121,55 +95,71 @@ public function setDefaultValues() { * Build the form object. */ public function buildQuickForm() { - // For VIEW we only want Done button if ($this->_action & CRM_Core_Action::VIEW) { // currently, the above action is used solely for previewing default workflow templates $cancelURL = CRM_Utils_System::url('civicrm/admin/messageTemplates', 'selectedChild=workflow&reset=1'); $cancelURL = str_replace('&', '&', $cancelURL); - $this->addButtons(array( - array( - 'type' => 'cancel', - 'name' => ts('Done'), - 'js' => array('onclick' => "location.href='{$cancelURL}'; return false;"), - 'isDefault' => TRUE, - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'cancel', + 'name' => ts('Done'), + 'js' => ['onclick' => "location.href='{$cancelURL}'; return false;"], + 'isDefault' => TRUE, + ], + ]); } else { - $this->addButtons(array( - array( - 'type' => 'upload', - 'name' => $this->_action & CRM_Core_Action::DELETE ? ts('Delete') : ts('Save'), - 'isDefault' => TRUE, - ), - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - ), - ) - ); + $this->_workflow_id = CRM_Utils_Array::value('workflow_id', $this->_values); + $this->checkUserPermission($this->_workflow_id); + $this->assign('workflow_id', $this->_workflow_id); + + if ($this->_workflow_id) { + $selectedChild = 'workflow'; + } + else { + $selectedChild = 'user'; + } + + $cancelURL = CRM_Utils_System::url('civicrm/admin/messageTemplates', "selectedChild={$selectedChild}&reset=1"); + $cancelURL = str_replace('&', '&', $cancelURL); + $buttons[] = [ + 'type' => 'upload', + 'name' => $this->_action & CRM_Core_Action::DELETE ? ts('Delete') : ts('Save'), + 'isDefault' => TRUE, + ]; + if (!($this->_action & CRM_Core_Action::DELETE)) { + $buttons[] = [ + 'type' => 'submit', + 'name' => ts('Save and Done'), + 'subName' => 'done', + ]; + } + $buttons[] = [ + 'type' => 'cancel', + 'name' => ts('Cancel'), + 'js' => ['onclick' => "location.href='{$cancelURL}'; return false;"], + ]; + $this->addButtons($buttons); } if ($this->_action & CRM_Core_Action::DELETE) { + $this->assign('msg_title', $this->_values['msg_title']); return; } - $breadCrumb = array( - array( + $breadCrumb = [ + [ 'title' => ts('Message Templates'), - 'url' => CRM_Utils_System::url('civicrm/admin/messageTemplates', - 'action=browse&reset=1' - ), - ), - ); + 'url' => CRM_Utils_System::url('civicrm/admin/messageTemplates', 'action=browse&reset=1'), + ], + ]; CRM_Utils_System::appendBreadCrumb($breadCrumb); $this->applyFilter('__ALL__', 'trim'); $this->add('text', 'msg_title', ts('Message Title'), CRM_Core_DAO::getAttribute('CRM_Core_DAO_MessageTemplate', 'msg_title'), TRUE); - $options = array(ts('Compose On-screen'), ts('Upload Document')); + $options = [ts('Compose On-screen'), ts('Upload Document')]; $element = $this->addRadio('file_type', ts('Source'), $options); if ($this->_id) { $element->freeze(); @@ -201,12 +191,12 @@ public function buildQuickForm() { } else { $this->add('wysiwyg', 'msg_html', ts('HTML Message'), - array( + [ 'cols' => '80', 'rows' => '8', 'onkeyup' => "return verify(this)", - 'class' => 'crm-wysiwyg-fullpage', - ) + 'preset' => 'civimail', + ] ); } @@ -215,14 +205,13 @@ public function buildQuickForm() { ); $this->add('select', 'pdf_format_id', ts('PDF Page Format'), - array( + [ 'null' => ts('- default -'), - ) + CRM_Core_BAO_PdfFormat::getList(TRUE), FALSE + ] + CRM_Core_BAO_PdfFormat::getList(TRUE), FALSE ); $this->add('checkbox', 'is_active', ts('Enabled?')); - - $this->addFormRule(array(__CLASS__, 'formRule'), $this); + $this->addFormRule([__CLASS__, 'formRule'], $this); if ($this->_action & CRM_Core_Action::VIEW) { $this->freeze(); @@ -230,6 +219,26 @@ public function buildQuickForm() { } } + /** + * Restrict users access based on permission + * + * @param int $workflowId + */ + private function checkUserPermission($workflowId) { + if (isset($workflowId)) { + $canView = CRM_Core_Permission::check('edit system workflow message templates'); + } + else { + $canView = CRM_Core_Permission::check('edit user-driven message templates'); + } + + if (!$canView && !CRM_Core_Permission::check('edit message templates')) { + CRM_Core_Session::setStatus(ts('You do not have permission to view requested page.'), ts('Access Denied')); + $url = CRM_Utils_System::url('civicrm/admin/messageTemplates', "reset=1"); + CRM_Utils_System::redirect($url); + } + } + /** * Global form rule. * @@ -243,13 +252,11 @@ public function buildQuickForm() { * array of errors */ public static function formRule($params, $files, $self) { - $errors = array(); - // If user uploads non-document file other than odt/docx if (!empty($files['file_id']['tmp_name']) && array_search($files['file_id']['type'], CRM_Core_SelectValues::documentApplicationType()) == NULL ) { - $error['file_id'] = ts('Invalid document file format'); + $errors['file_id'] = ts('Invalid document file format'); } // If default is not set and no document file is uploaded elseif (empty($files['file_id']['tmp_name']) && !empty($params['file_type']) && !$self->_is_document) { @@ -257,7 +264,7 @@ public static function formRule($params, $files, $self) { $errors['file_id'] = ts('Please upload document'); } - return $errors; + return empty($errors) ? TRUE : $errors; } /** @@ -272,10 +279,8 @@ public function postProcess() { CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/admin/messageTemplates', 'selectedChild=workflow&reset=1')); } else { - $params = array(); - // store the submitted values in an array - $params = $this->controller->exportValues($this->_name); + $params = $this->controller->exportValues(); if ($this->_action & CRM_Core_Action::UPDATE) { $params['id'] = $this->_id; @@ -286,7 +291,7 @@ public function postProcess() { unset($params['msg_text']); CRM_Utils_File::formatFile($params, 'file_id'); } - // delete related file refernces if html/text/pdf template are chosen over document + // delete related file references if html/text/pdf template are chosen over document elseif (!empty($this->_id)) { $entityFileDAO = new CRM_Core_DAO_EntityFile(); $entityFileDAO->entity_id = $this->_id; @@ -300,20 +305,24 @@ public function postProcess() { } } + $this->_workflow_id = CRM_Utils_Array::value('workflow_id', $this->_values); if ($this->_workflow_id) { $params['workflow_id'] = $this->_workflow_id; $params['is_active'] = TRUE; } $messageTemplate = CRM_Core_BAO_MessageTemplate::add($params); - CRM_Core_Session::setStatus(ts('The Message Template \'%1\' has been saved.', array(1 => $messageTemplate->msg_title)), ts('Saved'), 'success'); + CRM_Core_Session::setStatus(ts('The Message Template \'%1\' has been saved.', [1 => $messageTemplate->msg_title]), ts('Saved'), 'success'); + if (isset($this->_submitValues['_qf_MessageTemplates_upload'])) { + // Save button was pressed + CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/admin/messageTemplates/add', "action=update&id={$messageTemplate->id}&reset=1")); + } + // Save and done button was pressed if ($this->_workflow_id) { CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/admin/messageTemplates', 'selectedChild=workflow&reset=1')); } - else { - CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/admin/messageTemplates', 'selectedChild=user&reset=1')); - } + CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/admin/messageTemplates', 'selectedChild=user&reset=1')); } } diff --git a/CRM/Admin/Form/Navigation.php b/CRM/Admin/Form/Navigation.php index 74c36b85ce02..1d7ae860cb18 100644 --- a/CRM/Admin/Form/Navigation.php +++ b/CRM/Admin/Form/Navigation.php @@ -1,9 +1,9 @@ _id)) { - $params = array('id' => $this->_id); + $params = ['id' => $this->_id]; CRM_Core_BAO_Navigation::retrieve($params, $this->_defaults); } @@ -67,19 +68,22 @@ public function buildQuickForm() { ); $this->add('text', 'url', ts('Url'), CRM_Core_DAO::getAttribute('CRM_Core_DAO_Navigation', 'url')); - $permissions = array(); + + $this->add('text', 'icon', ts('Icon'), ['class' => 'crm-icon-picker', 'title' => ts('Choose Icon'), 'allowClear' => TRUE]); + + $permissions = []; foreach (CRM_Core_Permission::basicPermissions(TRUE, TRUE) as $id => $vals) { - $permissions[] = array('id' => $id, 'label' => $vals[0], 'description' => (array) CRM_Utils_Array::value(1, $vals)); + $permissions[] = ['id' => $id, 'text' => $vals[0], 'description' => (array) CRM_Utils_Array::value(1, $vals)]; } - $this->add('text', 'permission', ts('Permission'), - array('placeholder' => ts('Unrestricted'), 'class' => 'huge', 'data-select-params' => json_encode(array('data' => array('results' => $permissions, 'text' => 'label')))) + $this->add('select2', 'permission', ts('Permission'), $permissions, FALSE, + ['placeholder' => ts('Unrestricted'), 'class' => 'huge', 'multiple' => TRUE] ); - $operators = array('AND' => ts('AND'), 'OR' => ts('OR')); + $operators = ['AND' => ts('AND'), 'OR' => ts('OR')]; $this->add('select', 'permission_operator', NULL, $operators); //make separator location configurable - $separator = array(ts('None'), ts('After menu element'), ts('Before menu element')); + $separator = [ts('None'), ts('After menu element'), ts('Before menu element')]; $this->add('select', 'has_separator', ts('Separator'), $separator); $active = $this->add('advcheckbox', 'is_active', ts('Enabled')); @@ -98,7 +102,7 @@ public function buildQuickForm() { $homeMenuId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_Navigation', 'Home', 'id', 'name'); unset($parentMenu[$homeMenuId]); - $this->add('select', 'parent_id', ts('Parent'), array('' => ts('Top level')) + $parentMenu, FALSE, array('class' => 'crm-select2')); + $this->add('select', 'parent_id', ts('Parent'), ['' => ts('Top level')] + $parentMenu, FALSE, ['class' => 'crm-select2']); } } @@ -106,7 +110,7 @@ public function buildQuickForm() { * @return array */ public function setDefaultValues() { - $defaults = $this->_defaults; + $defaults = parent::setDefaultValues(); if (isset($this->_id)) { //Take parent id in object variable to calculate the menu //weight if menu parent id changed @@ -119,6 +123,10 @@ public function setDefaultValues() { // its ok if there is no element called is_active $defaults['is_active'] = ($this->_id) ? $this->_defaults['is_active'] : 1; + if (!empty($defaults['icon'])) { + $defaults['icon'] = trim(str_replace('crm-i', '', $defaults['icon'])); + } + return $defaults; } @@ -134,13 +142,17 @@ public function postProcess() { $params['current_parent_id'] = $this->_currentParentID; } + if (!empty($params['icon'])) { + $params['icon'] = 'crm-i ' . $params['icon']; + } + $navigation = CRM_Core_BAO_Navigation::add($params); // also reset navigation CRM_Core_BAO_Navigation::resetNavigation(); CRM_Core_Session::setStatus(ts('Menu \'%1\' has been saved.', - array(1 => $navigation->label) + [1 => $navigation->label] ), ts('Saved'), 'success'); } diff --git a/CRM/Admin/Form/OptionGroup.php b/CRM/Admin/Form/OptionGroup.php index 9ef3380259ac..8076680a6f81 100644 --- a/CRM/Admin/Form/OptionGroup.php +++ b/CRM/Admin/Form/OptionGroup.php @@ -1,9 +1,9 @@ addRule('name', ts('Name already exists in Database.'), 'objectExists', - array('CRM_Core_DAO_OptionGroup', $this->_id) + ['CRM_Core_DAO_OptionGroup', $this->_id] ); $this->add('text', @@ -78,15 +78,15 @@ public function buildQuickForm() { CRM_Core_DAO::getAttribute('CRM_Core_DAO_OptionGroup', 'description') ); - $this->addSelect('data_type', array('options' => CRM_Utils_Type::dataTypes()), TRUE); + $this->addSelect('data_type', ['options' => CRM_Utils_Type::dataTypes()], empty($this->_values['is_reserved'])); $element = $this->add('checkbox', 'is_active', ts('Enabled?')); if ($this->_action & CRM_Core_Action::UPDATE) { - if (in_array($this->_values['name'], array( + if (in_array($this->_values['name'], [ 'encounter_medium', 'case_type', 'case_status', - ))) { + ])) { static $caseCount = NULL; if (!isset($caseCount)) { $caseCount = CRM_Case_BAO_Case::caseCount(NULL, FALSE); @@ -96,8 +96,12 @@ public function buildQuickForm() { $element->freeze(); } } + + $this->add('checkbox', 'is_reserved', ts('Reserved?')); + $this->freeze('is_reserved'); + if (!empty($this->_values['is_reserved'])) { - $this->freeze(array('name', 'is_active')); + $this->freeze(['name', 'is_active', 'data_type']); } } @@ -110,23 +114,26 @@ public function buildQuickForm() { public function postProcess() { CRM_Utils_System::flushCache(); - $params = $this->exportValues(); if ($this->_action & CRM_Core_Action::DELETE) { CRM_Core_BAO_OptionGroup::del($this->_id); CRM_Core_Session::setStatus(ts('Selected option group has been deleted.'), ts('Record Deleted'), 'success'); } else { - - $params = $ids = array(); // store the submitted values in an array $params = $this->exportValues(); - if ($this->_action & CRM_Core_Action::UPDATE) { - $ids['optionGroup'] = $this->_id; + if ($this->_action & CRM_Core_Action::ADD) { + // If we are adding option group via UI it should not be marked reserved. + if (!isset($params['is_reserved'])) { + $params['is_reserved'] = 0; + } + } + elseif ($this->_action & CRM_Core_Action::UPDATE) { + $params['id'] = $this->_id; } - $optionGroup = CRM_Core_BAO_OptionGroup::add($params, $ids); - CRM_Core_Session::setStatus(ts('The Option Group \'%1\' has been saved.', array(1 => $optionGroup->name)), ts('Saved'), 'success'); + $optionGroup = CRM_Core_BAO_OptionGroup::add($params); + CRM_Core_Session::setStatus(ts('The Option Group \'%1\' has been saved.', [1 => $optionGroup->name]), ts('Saved'), 'success'); } } diff --git a/CRM/Admin/Form/Options.php b/CRM/Admin/Form/Options.php index 5a595e47d4e6..3e8dd6a65aa6 100644 --- a/CRM/Admin/Form/Options.php +++ b/CRM/Admin/Form/Options.php @@ -1,9 +1,9 @@ _gLabel = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionGroup', $this->_gid, 'title'); + $this->_domainSpecific = in_array($this->_gName, CRM_Core_OptionGroup::$_domainIDGroups); $url = "civicrm/admin/options/{$this->_gName}"; $params = "reset=1"; if (($this->_action & CRM_Core_Action::DELETE) && - in_array($this->_gName, array('email_greeting', 'postal_greeting', 'addressee')) + in_array($this->_gName, ['email_greeting', 'postal_greeting', 'addressee']) ) { // Don't allow delete if the option value belongs to addressee, postal or email greetings and is in use. $findValue = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionValue', $this->_id, 'value'); - $queryParam = array(1 => array($findValue, 'Integer')); + $queryParam = [1 => [$findValue, 'Integer']]; $columnName = $this->_gName . "_id"; $sql = "SELECT count(id) FROM civicrm_contact WHERE " . $columnName . " = %1"; $isInUse = CRM_Core_DAO::singleValueQuery($sql, $queryParam); if ($isInUse) { $scriptURL = "" . ts('Learn more about a script that can automatically update contact addressee and greeting options.') . ""; - CRM_Core_Session::setStatus(ts('The selected %1 option has not been deleted because it is currently in use. Please update these contacts to use a different format before deleting this option. %2', array( - 1 => $this->_gLabel, - 2 => $scriptURL, - )), ts('Sorry'), 'error'); + CRM_Core_Session::setStatus(ts('The selected %1 option has not been deleted because it is currently in use. Please update these contacts to use a different format before deleting this option. %2', [ + 1 => $this->_gLabel, + 2 => $scriptURL, + ]), ts('Sorry'), 'error'); $redirect = CRM_Utils_System::url($url, $params); CRM_Utils_System::redirect($redirect); } @@ -115,24 +122,27 @@ public function setDefaultValues() { $defaults = parent::setDefaultValues(); // Default weight & value - $fieldValues = array('option_group_id' => $this->_gid); - foreach (array('weight', 'value') as $field) { + $fieldValues = ['option_group_id' => $this->_gid]; + foreach (['weight', 'value'] as $field) { if (empty($defaults[$field])) { $defaults[$field] = CRM_Utils_Weight::getDefaultWeight('CRM_Core_DAO_OptionValue', $fieldValues, $field); } } - //setDefault of contact types for email greeting, postal greeting, addressee, CRM-4575 - if (in_array($this->_gName, array( + // setDefault of contact types for email greeting, postal greeting, addressee, CRM-4575 + if (in_array($this->_gName, [ 'email_greeting', 'postal_greeting', 'addressee', - ))) { + ])) { $defaults['contactOptions'] = (CRM_Utils_Array::value('filter', $defaults)) ? $defaults['filter'] : NULL; } // CRM-11516 if ($this->_gName == 'payment_instrument' && $this->_id) { - $defaults['financial_account_id'] = CRM_Financial_BAO_FinancialTypeAccount::getFinancialAccount($this->_id, 'civicrm_option_value', 'financial_account_id'); + $defaults['financial_account_id'] = CRM_Contribute_PseudoConstant::getRelationalFinancialAccount($this->_id, NULL, 'civicrm_option_value'); + } + if (empty($this->_id) || !CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionValue', $this->_id, 'color')) { + $defaults['color'] = '#ffffff'; } return $defaults; } @@ -142,7 +152,7 @@ public function setDefaultValues() { */ public function buildQuickForm() { parent::buildQuickForm(); - $this->setPageTitle(ts('%1 Option', array(1 => $this->_gLabel))); + $this->setPageTitle(ts('%1 Option', [1 => $this->_gLabel])); if ($this->_action & CRM_Core_Action::DELETE) { return; @@ -169,26 +179,35 @@ public function buildQuickForm() { CRM_Core_DAO::getAttribute('CRM_Core_DAO_OptionValue', 'value'), TRUE ); + $this->addRule('value', + ts('This Value already exists in the database for this option group. Please select a different Value.'), + 'optionExists', + ['CRM_Core_DAO_OptionValue', $this->_id, $this->_gid, 'value', $this->_domainSpecific] + ); + } + else { + $this->add('text', 'icon', ts('Icon'), ['class' => 'crm-icon-picker', 'title' => ts('Choose Icon'), 'allowClear' => TRUE]); + } + + if (in_array($this->_gName, ['activity_status', 'case_status'])) { + $this->add('color', 'color', ts('Color')); } - if (!in_array($this->_gName, array( - 'email_greeting', - 'postal_greeting', - 'addressee', - )) && !$isReserved + if (!in_array($this->_gName, ['email_greeting', 'postal_greeting', 'addressee']) + && !$isReserved ) { $this->addRule('label', - ts('This Label already exists in the database for this option group. Please select a different Value.'), + ts('This Label already exists in the database for this option group. Please select a different Label.'), 'optionExists', - array('CRM_Core_DAO_OptionValue', $this->_id, $this->_gid, 'label') + ['CRM_Core_DAO_OptionValue', $this->_id, $this->_gid, 'label', $this->_domainSpecific] ); } if ($this->_gName == 'case_status') { - $classes = array( + $classes = [ 'Opened' => ts('Opened'), 'Closed' => ts('Closed'), - ); + ]; $grouping = $this->add('select', 'grouping', @@ -205,22 +224,27 @@ public function buildQuickForm() { $financialAccount = CRM_Contribute_PseudoConstant::financialAccount(NULL, key($accountType)); $this->add('select', 'financial_account_id', ts('Financial Account'), - array('' => ts('- select -')) + $financialAccount, + ['' => ts('- select -')] + $financialAccount, TRUE ); } - $required = FALSE; - if ($this->_gName == 'custom_search') { - $required = TRUE; + if ($this->_gName == 'activity_status') { + $this->add('select', + 'filter', + ts('Status Type'), + [ + CRM_Activity_BAO_Activity::INCOMPLETE => ts('Incomplete'), + CRM_Activity_BAO_Activity::COMPLETED => ts('Completed'), + CRM_Activity_BAO_Activity::CANCELLED => ts('Cancelled'), + ] + ); } - elseif ($this->_gName == 'redaction_rule' || $this->_gName == 'engagement_index') { - if ($this->_gName == 'redaction_rule') { - $this->add('checkbox', - 'filter', - ts('Regular Expression?') - ); - } + if ($this->_gName == 'redaction_rule') { + $this->add('checkbox', + 'filter', + ts('Regular Expression?') + ); } if ($this->_gName == 'participant_listing') { $this->add('text', @@ -233,8 +257,8 @@ public function buildQuickForm() { // Hard-coding attributes here since description is still stored as varchar and not text in the schema. dgg $this->add('wysiwyg', 'description', ts('Description'), - array('rows' => 4, 'cols' => 80), - $required + ['rows' => 4, 'cols' => 80], + $this->_gName == 'custom_search' ); } @@ -246,7 +270,7 @@ public function buildQuickForm() { ); } - $this->add('text', + $this->add('number', 'weight', ts('Order'), CRM_Core_DAO::getAttribute('CRM_Core_DAO_OptionValue', 'weight'), @@ -261,7 +285,7 @@ public function buildQuickForm() { (($this->_action & CRM_Core_Action::ADD) || !$isReserved) ) { $caseID = CRM_Core_Component::getComponentID('CiviCase'); - $components = array('' => ts('Contacts AND Cases'), $caseID => ts('Cases Only')); + $components = ['' => ts('Contacts AND Cases'), $caseID => ts('Cases Only')]; $this->add('select', 'component_id', ts('Component'), @@ -275,8 +299,8 @@ public function buildQuickForm() { $enabled->freeze(); } - //fix for CRM-3552, CRM-4575 - $showIsDefaultGroups = array( + // fix for CRM-3552, CRM-4575 + $showIsDefaultGroups = [ 'email_greeting', 'postal_greeting', 'addressee', @@ -288,33 +312,30 @@ public function buildQuickForm() { 'communication_style', 'soft_credit_type', 'website_type', - ); + ]; if (in_array($this->_gName, $showIsDefaultGroups)) { $this->assign('showDefault', TRUE); $this->add('checkbox', 'is_default', ts('Default Option?')); } - //get contact type for which user want to create a new greeting/addressee type, CRM-4575 - if (in_array($this->_gName, array( - 'email_greeting', - 'postal_greeting', - 'addressee', - )) && !$isReserved + // get contact type for which user want to create a new greeting/addressee type, CRM-4575 + if (in_array($this->_gName, ['email_greeting', 'postal_greeting', 'addressee']) + && !$isReserved ) { - $values = array( + $values = [ 1 => ts('Individual'), 2 => ts('Household'), 3 => ts('Organization'), 4 => ts('Multiple Contact Merge'), - ); - $this->add('select', 'contactOptions', ts('Contact Type'), array('' => '-select-') + $values, TRUE); + ]; + $this->add('select', 'contactOptions', ts('Contact Type'), ['' => '-select-'] + $values, TRUE); $this->assign('showContactFilter', TRUE); } if ($this->_gName == 'participant_status') { // For Participant Status options, expose the 'filter' field to track which statuses are "Counted", and the Visibility field - $element = $this->add('checkbox', 'filter', ts('Counted?')); + $this->add('checkbox', 'filter', ts('Counted?')); $this->add('select', 'visibility_id', ts('Visibility'), CRM_Core_PseudoConstant::visibility()); } if ($this->_gName == 'participant_role') { @@ -322,7 +343,7 @@ public function buildQuickForm() { $this->add('checkbox', 'filter', ts('Counted?')); } - $this->addFormRule(array('CRM_Admin_Form_Options', 'formRule'), $this); + $this->addFormRule(['CRM_Admin_Form_Options', 'formRule'], $this); } /** @@ -337,18 +358,16 @@ public function buildQuickForm() { * * @return array * array of errors / empty array. + * @throws \CRM_Core_Exception */ public static function formRule($fields, $files, $self) { - $errors = array(); + $errors = []; if ($self->_gName == 'case_status' && empty($fields['grouping'])) { $errors['grouping'] = ts('Status class is a required field'); } - if (in_array($self->_gName, array( - 'email_greeting', - 'postal_greeting', - 'addressee', - )) && empty($self->_defaultValues['is_reserved']) + if (in_array($self->_gName, ['email_greeting', 'postal_greeting', 'addressee']) + && empty($self->_defaultValues['is_reserved']) ) { $label = $fields['label']; $condition = " AND v.label = '{$label}' "; @@ -379,7 +398,7 @@ public static function formRule($fields, $files, $self) { $dataType = self::getOptionGroupDataType($self->_gName); if ($dataType && $self->_gName !== 'activity_type') { $validate = CRM_Utils_Type::validate($fields['value'], $dataType, FALSE); - if (!$validate) { + if ($validate === FALSE) { CRM_Core_Session::setStatus( ts('Data Type of the value field for this option value does not match ' . $dataType), ts('Value field Data Type mismatch')); @@ -407,39 +426,38 @@ public static function getOptionGroupDataType($optionGroupName) { */ public function postProcess() { if ($this->_action & CRM_Core_Action::DELETE) { - $fieldValues = array('option_group_id' => $this->_gid); - $wt = CRM_Utils_Weight::delWeight('CRM_Core_DAO_OptionValue', $this->_id, $fieldValues); + $fieldValues = ['option_group_id' => $this->_gid]; + CRM_Utils_Weight::delWeight('CRM_Core_DAO_OptionValue', $this->_id, $fieldValues); if (CRM_Core_BAO_OptionValue::del($this->_id)) { if ($this->_gName == 'phone_type') { CRM_Core_BAO_Phone::setOptionToNull(CRM_Utils_Array::value('value', $this->_defaultValues)); } - CRM_Core_Session::setStatus(ts('Selected %1 type has been deleted.', array(1 => $this->_gLabel)), ts('Record Deleted'), 'success'); + CRM_Core_Session::setStatus(ts('Selected %1 type has been deleted.', [1 => $this->_gLabel]), ts('Record Deleted'), 'success'); } else { - CRM_Core_Session::setStatus(ts('Selected %1 type has not been deleted.', array(1 => $this->_gLabel)), ts('Sorry'), 'error'); + CRM_Core_Session::setStatus(ts('Selected %1 type has not been deleted.', [1 => $this->_gLabel]), ts('Sorry'), 'error'); CRM_Utils_Weight::correctDuplicateWeights('CRM_Core_DAO_OptionValue', $fieldValues); } } else { - $params = $ids = array(); $params = $this->exportValues(); // allow multiple defaults within group. - $allowMultiDefaults = array('email_greeting', 'postal_greeting', 'addressee', 'from_email_address'); + $allowMultiDefaults = ['email_greeting', 'postal_greeting', 'addressee', 'from_email_address']; if (in_array($this->_gName, $allowMultiDefaults)) { if ($this->_gName == 'from_email_address') { - $params['reset_default_for'] = array('domain_id' => CRM_Core_Config::domainID()); + $params['reset_default_for'] = ['domain_id' => CRM_Core_Config::domainID()]; } elseif ($filter = CRM_Utils_Array::value('contactOptions', $params)) { $params['filter'] = $filter; - $params['reset_default_for'] = array('filter' => "0, " . $params['filter']); + $params['reset_default_for'] = ['filter' => "0, " . $params['filter']]; } - //make sure we should has to have space, CRM-6977 + //make sure we only have a single space, CRM-6977 and dev/mail/15 if ($this->_gName == 'from_email_address') { - $params['label'] = str_replace('"<', '" <', $params['label']); + $params['label'] = $this->sanitizeFromEmailAddress($params['label']); } } @@ -453,26 +471,24 @@ public function postProcess() { } } - $groupParams = array('name' => ($this->_gName)); - $optionValue = CRM_Core_OptionValue::addOptionValue($params, $groupParams, $this->_action, $this->_id); - - // CRM-11516 - if (!empty($params['financial_account_id'])) { - $relationTypeId = key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Asset Account is' ")); - $params = array( - 'entity_table' => 'civicrm_option_value', - 'entity_id' => $optionValue->id, - 'account_relationship' => $relationTypeId, - 'financial_account_id' => $params['financial_account_id'], - ); - CRM_Financial_BAO_FinancialTypeAccount::add($params); + if (isset($params['color']) && strtolower($params['color']) == '#ffffff') { + $params['color'] = 'null'; } - CRM_Core_Session::setStatus(ts('The %1 \'%2\' has been saved.', array( - 1 => $this->_gLabel, - 2 => $optionValue->label, - )), ts('Saved'), 'success'); + $optionValue = CRM_Core_OptionValue::addOptionValue($params, $this->_gName, $this->_action, $this->_id); + + CRM_Core_Session::setStatus(ts('The %1 \'%2\' has been saved.', [ + 1 => $this->_gLabel, + 2 => $optionValue->label, + ]), ts('Saved'), 'success'); + + $this->ajaxResponse['optionValue'] = $optionValue->toArray(); } } + public function sanitizeFromEmailAddress($email) { + preg_match("/^\"(.*)\" *<([^@>]*@[^@>]*)>$/", $email, $parts); + return "\"{$parts[1]}\" <$parts[2]>"; + } + } diff --git a/CRM/Admin/Form/ParticipantStatusType.php b/CRM/Admin/Form/ParticipantStatusType.php index 0a2987d34199..fcaac917b1af 100644 --- a/CRM/Admin/Form/ParticipantStatusType.php +++ b/CRM/Admin/Form/ParticipantStatusType.php @@ -1,9 +1,9 @@ add('text', 'label', ts('Label'), $attributes['label'], TRUE); - $this->addSelect('class', array('required' => TRUE)); + $this->addSelect('class', ['required' => TRUE]); $this->add('checkbox', 'is_active', ts('Active?')); $this->add('checkbox', 'is_counted', ts('Counted?')); - $this->add('text', 'weight', ts('Order'), $attributes['weight'], TRUE); + $this->add('number', 'weight', ts('Order'), $attributes['weight'], TRUE); - $this->addSelect('visibility_id', array('label' => ts('Visibility'), 'required' => TRUE)); + $this->addSelect('visibility_id', ['label' => ts('Visibility'), 'required' => TRUE]); $this->assign('id', $this->_id); } @@ -83,7 +83,7 @@ public function setDefaultValues() { } $this->_isReserved = CRM_Utils_Array::value('is_reserved', $defaults); if ($this->_isReserved) { - $this->freeze(array('name', 'class', 'is_active')); + $this->freeze(['name', 'class', 'is_active']); } return $defaults; } @@ -101,7 +101,7 @@ public function postProcess() { $formValues = $this->controller->exportValues($this->_name); - $params = array( + $params = [ 'name' => CRM_Utils_Array::value('name', $formValues), 'label' => CRM_Utils_Array::value('label', $formValues), 'class' => CRM_Utils_Array::value('class', $formValues), @@ -109,7 +109,7 @@ public function postProcess() { 'is_counted' => CRM_Utils_Array::value('is_counted', $formValues, FALSE), 'weight' => CRM_Utils_Array::value('weight', $formValues), 'visibility_id' => CRM_Utils_Array::value('visibility_id', $formValues), - ); + ]; // make sure a malicious POST does not change these on reserved statuses if ($this->_isReserved) { diff --git a/CRM/Admin/Form/PaymentProcessor.php b/CRM/Admin/Form/PaymentProcessor.php index 267103f77acf..d8b32939550b 100644 --- a/CRM/Admin/Form/PaymentProcessor.php +++ b/CRM/Admin/Form/PaymentProcessor.php @@ -1,9 +1,9 @@ _id) { - $this->_ppType = CRM_Utils_Request::retrieve('pp', 'String', $this, FALSE, NULL); - if (!$this->_ppType) { - $this->_ppType = CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_PaymentProcessor', + $this->_paymentProcessorType = CRM_Utils_Request::retrieve('pp', 'String', $this, FALSE, NULL); + if (!$this->_paymentProcessorType) { + $this->_paymentProcessorType = CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_PaymentProcessor', $this->_id, 'payment_processor_type_id' ); } - $this->set('pp', $this->_ppType); + $this->set('pp', $this->_paymentProcessorType); } else { - $this->_ppType = CRM_Utils_Request::retrieve('pp', 'String', $this, TRUE, NULL); + $this->_paymentProcessorType = CRM_Utils_Request::retrieve('pp', 'String', $this, TRUE, NULL); } - $this->assign('ppType', $this->_ppType); + $this->assign('ppType', $this->_paymentProcessorType); $ppTypeName = CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_PaymentProcessorType', - $this->_ppType, + $this->_paymentProcessorType, 'name' ); $this->assign('ppTypeName', $ppTypeName); - $this->_ppDAO = new CRM_Financial_DAO_PaymentProcessorType(); - $this->_ppDAO->id = $this->_ppType; + $this->_paymentProcessorDAO = new CRM_Financial_DAO_PaymentProcessorType(); + $this->_paymentProcessorDAO->id = $this->_paymentProcessorType; - if (!$this->_ppDAO->find(TRUE)) { - CRM_Core_Error::fatal(ts('Could not find payment processor meta information')); - } + $this->_paymentProcessorDAO->find(TRUE); if ($this->_id) { $refreshURL = CRM_Utils_System::url('civicrm/admin/paymentProcessor', @@ -101,60 +107,61 @@ public function preProcess() { $refreshURL .= "&civicrmDestination=$destination"; } + $this->refreshURL = $refreshURL; $this->assign('refreshURL', $refreshURL); - $this->assign('is_recur', $this->_ppDAO->is_recur); + $this->assign('is_recur', $this->_paymentProcessorDAO->is_recur); - $this->_fields = array( - array( + $this->_fields = [ + [ 'name' => 'user_name', - 'label' => $this->_ppDAO->user_name_label, - ), - array( + 'label' => $this->_paymentProcessorDAO->user_name_label, + ], + [ 'name' => 'password', - 'label' => $this->_ppDAO->password_label, - ), - array( + 'label' => $this->_paymentProcessorDAO->password_label, + ], + [ 'name' => 'signature', - 'label' => $this->_ppDAO->signature_label, - ), - array( + 'label' => $this->_paymentProcessorDAO->signature_label, + ], + [ 'name' => 'subject', - 'label' => $this->_ppDAO->subject_label, - ), - array( + 'label' => $this->_paymentProcessorDAO->subject_label, + ], + [ 'name' => 'url_site', 'label' => ts('Site URL'), 'rule' => 'url', 'msg' => ts('Enter a valid URL'), - ), - ); + ], + ]; - if ($this->_ppDAO->is_recur) { - $this->_fields[] = array( + if ($this->_paymentProcessorDAO->is_recur) { + $this->_fields[] = [ 'name' => 'url_recur', 'label' => ts('Recurring Payments URL'), 'rule' => 'url', 'msg' => ts('Enter a valid URL'), - ); + ]; } - if (!empty($this->_ppDAO->url_button_default)) { - $this->_fields[] = array( + if (!empty($this->_paymentProcessorDAO->url_button_default)) { + $this->_fields[] = [ 'name' => 'url_button', 'label' => ts('Button URL'), 'rule' => 'url', 'msg' => ts('Enter a valid URL'), - ); + ]; } - if (!empty($this->_ppDAO->url_api_default)) { - $this->_fields[] = array( + if (!empty($this->_paymentProcessorDAO->url_api_default)) { + $this->_fields[] = [ 'name' => 'url_api', 'label' => ts('API URL'), 'rule' => 'url', 'msg' => ts('Enter a valid URL'), - ); + ]; } } @@ -176,18 +183,23 @@ public function buildQuickForm($check = FALSE) { $attributes['name'], TRUE ); - $this->addRule('name', ts('Name already exists in Database.'), 'objectExists', array( - 'CRM_Financial_DAO_PaymentProcessor', - $this->_id, - )); + $this->addRule('name', ts('Name already exists in Database.'), 'objectExists', [ + 'CRM_Financial_DAO_PaymentProcessor', + $this->_id, + 'name', + CRM_Core_Config::domainID(), + ]); $this->add('text', 'description', ts('Description'), $attributes['description'] ); - $types = CRM_Core_PseudoConstant::paymentProcessorType(); - $this->add('select', 'payment_processor_type_id', ts('Payment Processor Type'), $types, TRUE, - array('onchange' => "reload(true)") + $this->add('select', + 'payment_processor_type_id', + ts('Payment Processor Type'), + CRM_Financial_BAO_PaymentProcessor::buildOptions('payment_processor_type_id'), + TRUE, + ['onchange' => "reload(true)"] ); // Financial Account of account type asset CRM-11515 @@ -197,30 +209,35 @@ public function buildQuickForm($check = FALSE) { $this->assign('financialAccount', $fcount); } $this->add('select', 'financial_account_id', ts('Financial Account'), - array('' => ts('- select -')) + $financialAccount, + ['' => ts('- select -')] + $financialAccount, TRUE ); $this->addSelect('payment_instrument_id', - array( + [ 'entity' => 'contribution', 'label' => ts('Payment Method'), 'placeholder' => NULL, - ) + ] ); // is this processor active ? $this->add('checkbox', 'is_active', ts('Is this Payment Processor active?')); $this->add('checkbox', 'is_default', ts('Is this Payment Processor the default?')); - + $creditCardTypes = CRM_Contribute_PseudoConstant::creditCard(); + $this->addCheckBox('accept_credit_cards', ts('Accepted Credit Card Type(s)'), + $creditCardTypes, NULL, NULL, NULL, NULL, '   '); foreach ($this->_fields as $field) { if (empty($field['label'])) { continue; } - $this->add('text', $field['name'], - $field['label'], $attributes[$field['name']] - ); - $this->add('text', "test_{$field['name']}", + $this->addField($field['name'], ['label' => $field['label']]); + + $fieldSpec = civicrm_api3($this->getDefaultEntity(), 'getfield', [ + 'name' => $field['name'], + 'action' => 'create', + ]); + $this->add($fieldSpec['values']['html']['type'], "test_{$field['name']}", $field['label'], $attributes[$field['name']] ); if (!empty($field['rule'])) { @@ -229,7 +246,7 @@ public function buildQuickForm($check = FALSE) { } } - $this->addFormRule(array('CRM_Admin_Form_PaymentProcessor', 'formRule')); + $this->addFormRule(['CRM_Admin_Form_PaymentProcessor', 'formRule']); } /** @@ -242,7 +259,7 @@ public static function formRule($fields) { // make sure that at least one of live or test is present // and we have at least name and url_site // would be good to make this processor specific - $errors = array(); + $errors = []; if (!(self::checkSection($fields, $errors) || self::checkSection($fields, $errors, 'test') @@ -266,7 +283,7 @@ public static function formRule($fields) { * @return bool */ public static function checkSection(&$fields, &$errors, $section = NULL) { - $names = array('user_name'); + $names = ['user_name']; $present = FALSE; $allPresent = TRUE; @@ -294,23 +311,24 @@ public static function checkSection(&$fields, &$errors, $section = NULL) { * @return array */ public function setDefaultValues() { - $defaults = array(); + $defaults = []; if (!$this->_id) { $defaults['is_active'] = $defaults['is_default'] = 1; - $defaults['url_site'] = $this->_ppDAO->url_site_default; - $defaults['url_api'] = $this->_ppDAO->url_api_default; - $defaults['url_recur'] = $this->_ppDAO->url_recur_default; - $defaults['url_button'] = $this->_ppDAO->url_button_default; - $defaults['test_url_site'] = $this->_ppDAO->url_site_test_default; - $defaults['test_url_api'] = $this->_ppDAO->url_api_test_default; - $defaults['test_url_recur'] = $this->_ppDAO->url_recur_test_default; - $defaults['test_url_button'] = $this->_ppDAO->url_button_test_default; - $defaults['payment_instrument_id'] = $this->_ppDAO->payment_instrument_id; + $defaults['url_site'] = $this->_paymentProcessorDAO->url_site_default; + $defaults['url_api'] = $this->_paymentProcessorDAO->url_api_default; + $defaults['url_recur'] = $this->_paymentProcessorDAO->url_recur_default; + $defaults['url_button'] = $this->_paymentProcessorDAO->url_button_default; + $defaults['test_url_site'] = $this->_paymentProcessorDAO->url_site_test_default; + $defaults['test_url_api'] = $this->_paymentProcessorDAO->url_api_test_default; + $defaults['test_url_recur'] = $this->_paymentProcessorDAO->url_recur_test_default; + $defaults['test_url_button'] = $this->_paymentProcessorDAO->url_button_test_default; + $defaults['payment_instrument_id'] = $this->_paymentProcessorDAO->payment_instrument_id; // When user changes payment processor type, it is passed in via $this->_ppType so update defaults array. - if ($this->_ppType) { - $defaults['payment_processor_type_id'] = $this->_ppType; + if ($this->_paymentProcessorType) { + $defaults['payment_processor_type_id'] = $this->_paymentProcessorType; } + $defaults['financial_account_id'] = CRM_Financial_BAO_PaymentProcessor::getDefaultFinancialAccountID(); return $defaults; } $domainID = CRM_Core_Config::domainID(); @@ -323,11 +341,28 @@ public function setDefaultValues() { } CRM_Core_DAO::storeValues($dao, $defaults); - // When user changes payment processor type, it is passed in via $this->_ppType so update defaults array. - if ($this->_ppType) { - $defaults['payment_processor_type_id'] = $this->_ppType; + // If payment processor ID does not exist, $paymentProcessorName will be FALSE + $paymentProcessorName = CRM_Core_PseudoConstant::getName('CRM_Financial_BAO_PaymentProcessor', 'payment_processor_type_id', $this->_paymentProcessorType); + if ($this->_paymentProcessorType && $paymentProcessorName) { + // When user changes payment processor type, it is passed in via $this->_ppType so update defaults array. + $defaults['payment_processor_type_id'] = $this->_paymentProcessorType; + } + else { + CRM_Core_Session::setStatus('Payment Processor Type (ID=' . $this->_paymentProcessorType . ') not found. Did you disable the payment processor extension?', 'Missing Payment Processor', 'alert'); } + $cards = json_decode(CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_PaymentProcessor', + $this->_id, + 'accepted_credit_cards' + ), TRUE); + $acceptedCards = []; + if (!empty($cards)) { + foreach ($cards as $card => $val) { + $acceptedCards[$card] = 1; + } + } + $defaults['accept_credit_cards'] = $acceptedCards; + unset($defaults['accepted_credit_cards']); // now get testID $testDAO = new CRM_Financial_DAO_PaymentProcessor(); $testDAO->name = $dao->name; @@ -341,7 +376,7 @@ public function setDefaultValues() { $defaults[$testName] = $testDAO->{$field['name']}; } } - $defaults['financial_account_id'] = CRM_Financial_BAO_FinancialTypeAccount::getFinancialAccount($dao->id, 'civicrm_payment_processor', 'financial_account_id'); + $defaults['financial_account_id'] = CRM_Contribute_PseudoConstant::getRelationalFinancialAccount($dao->id, NULL, 'civicrm_payment_processor'); return $defaults; } @@ -365,9 +400,26 @@ public function postProcess() { CRM_Core_DAO::executeQuery($query); } + if ($this->_paymentProcessorType !== $values['payment_processor_type_id']) { + // If we changed the payment processor type, need to update the object as well + $this->_paymentProcessorType = $values['payment_processor_type_id']; + $this->_paymentProcessorDAO = new CRM_Financial_DAO_PaymentProcessorType(); + $this->_paymentProcessorDAO->id = $values['payment_processor_type_id']; + $this->_paymentProcessorDAO->find(TRUE); + } $this->updatePaymentProcessor($values, $domainID, FALSE); $this->updatePaymentProcessor($values, $domainID, TRUE); - CRM_Core_Session::setStatus(ts('Payment processor %1 has been saved.', array(1 => "{$values['name']}")), ts('Saved'), 'success'); + + $processor = civicrm_api3('payment_processor', 'getsingle', ['name' => $values['name'], 'is_test' => 0]); + $errors = Civi\Payment\System::singleton()->checkProcessorConfig($processor); + if ($errors) { + CRM_Core_Session::setStatus($errors, 'Payment processor configuration invalid', 'error'); + Civi::log()->error('Payment processor configuration invalid: ' . $errors); + CRM_Core_Session::singleton()->pushUserContext($this->refreshURL); + } + else { + CRM_Core_Session::setStatus(ts('Payment processor %1 has been saved.', [1 => "{$values['name']}"]), ts('Saved'), 'success'); + } } /** @@ -379,23 +431,38 @@ public function postProcess() { */ public function updatePaymentProcessor(&$values, $domainID, $test) { if ($test) { - foreach (array('user_name', 'password', 'signature', 'url_site', 'url_recur', 'url_api', 'url_button', 'subject') as $field) { + foreach (['user_name', 'password', 'signature', 'url_site', 'url_recur', 'url_api', 'url_button', 'subject'] as $field) { $values[$field] = empty($values["test_{$field}"]) ? CRM_Utils_Array::value($field, $values) : $values["test_{$field}"]; } } - $params = array_merge(array( + if (!empty($values['accept_credit_cards'])) { + $creditCards = []; + $accptedCards = array_keys($values['accept_credit_cards']); + $creditCardTypes = CRM_Contribute_PseudoConstant::creditCard(); + foreach ($creditCardTypes as $type => $val) { + if (in_array($type, $accptedCards)) { + $creditCards[$type] = $creditCardTypes[$type]; + } + } + $creditCards = json_encode($creditCards); + } + else { + $creditCards = "NULL"; + } + $params = array_merge([ 'id' => $test ? $this->_testID : $this->_id, 'domain_id' => $domainID, 'is_test' => $test, 'is_active' => 0, 'is_default' => 0, - 'is_recur' => $this->_ppDAO->is_recur, - 'billing_mode' => $this->_ppDAO->billing_mode, - 'class_name' => $this->_ppDAO->class_name, - 'payment_type' => $this->_ppDAO->payment_type, - 'payment_instrument_id' => $this->_ppDAO->payment_instrument_id, + 'is_recur' => $this->_paymentProcessorDAO->is_recur, + 'billing_mode' => $this->_paymentProcessorDAO->billing_mode, + 'class_name' => $this->_paymentProcessorDAO->class_name, + 'payment_type' => $this->_paymentProcessorDAO->payment_type, + 'payment_instrument_id' => $this->_paymentProcessorDAO->payment_instrument_id, 'financial_account_id' => $values['financial_account_id'], - ), $values); + 'accepted_credit_cards' => $creditCards, + ], $values); civicrm_api3('PaymentProcessor', 'create', $params); } diff --git a/CRM/Admin/Form/PaymentProcessorType.php b/CRM/Admin/Form/PaymentProcessorType.php index c7434f213bd8..29c2e5fde6cc 100644 --- a/CRM/Admin/Form/PaymentProcessorType.php +++ b/CRM/Admin/Form/PaymentProcessorType.php @@ -1,9 +1,9 @@ _fields = array( - array( + $this->_fields = [ + [ 'name' => 'name', 'label' => ts('Name'), 'required' => TRUE, - ), - array( + ], + [ 'name' => 'title', 'label' => ts('Title'), 'required' => TRUE, - ), - array( + ], + [ 'name' => 'billing_mode', 'label' => ts('Billing Mode'), 'required' => TRUE, 'rule' => 'positiveInteger', 'msg' => ts('Enter a positive integer'), - ), - array( + ], + [ 'name' => 'description', 'label' => ts('Description'), - ), - array( + ], + [ 'name' => 'user_name_label', 'label' => ts('User Name Label'), - ), - array( + ], + [ 'name' => 'password_label', 'label' => ts('Password Label'), - ), - array( + ], + [ 'name' => 'signature_label', 'label' => ts('Signature Label'), - ), - array( + ], + [ 'name' => 'subject_label', 'label' => ts('Subject Label'), - ), - array( + ], + [ 'name' => 'class_name', 'label' => ts('PHP class name'), 'required' => TRUE, - ), - array( + ], + [ 'name' => 'url_site_default', 'label' => ts('Live Site URL'), 'required' => TRUE, 'rule' => 'url', 'msg' => ts('Enter a valid URL'), - ), - array( + ], + [ 'name' => 'url_api_default', 'label' => ts('Live API URL'), 'required' => FALSE, 'rule' => 'url', 'msg' => ts('Enter a valid URL'), - ), - array( + ], + [ 'name' => 'url_recur_default', 'label' => ts('Live Recurring Payments URL'), 'required' => TRUE, 'rule' => 'url', 'msg' => ts('Enter a valid URL'), - ), - array( + ], + [ 'name' => 'url_button_default', 'label' => ts('Live Button URL'), 'rule' => 'url', 'msg' => ts('Enter a valid URL'), - ), - array( + ], + [ 'name' => 'url_site_test_default', 'label' => ts('Test Site URL'), 'required' => TRUE, 'rule' => 'url', 'msg' => ts('Enter a valid URL'), - ), - array( + ], + [ 'name' => 'url_api_test_default', 'label' => ts('Test API URL'), 'required' => FALSE, 'rule' => 'url', 'msg' => ts('Enter a valid URL'), - ), - array( + ], + [ 'name' => 'url_recur_test_default', 'label' => ts('Test Recurring Payments URL'), 'required' => TRUE, 'rule' => 'url', 'msg' => ts('Enter a valid URL'), - ), - array( + ], + [ 'name' => 'url_button_test_default', 'label' => ts('Test Button URL'), 'rule' => 'url', 'msg' => ts('Enter a valid URL'), - ), - ); + ], + ]; } /** @@ -176,7 +176,7 @@ public function buildQuickForm($check = FALSE) { * @return array */ public function setDefaultValues() { - $defaults = array(); + $defaults = []; if (!$this->_id) { $defaults['is_active'] = $defaults['is_default'] = 1; @@ -203,7 +203,7 @@ public function setDefaultValues() { * Process the form submission. */ public function postProcess() { - CRM_Utils_System::flushCache('CRM_Financial_DAO_PaymentProcessorType'); + CRM_Utils_System::flushCache(); if ($this->_action & CRM_Core_Action::DELETE) { CRM_Financial_BAO_PaymentProcessorType::del($this->_id); diff --git a/CRM/Admin/Form/PdfFormats.php b/CRM/Admin/Form/PdfFormats.php index 73e39e5191dc..ea8e7c40c91c 100644 --- a/CRM/Admin/Form/PdfFormats.php +++ b/CRM/Admin/Form/PdfFormats.php @@ -1,7 +1,7 @@ add('text', 'name', ts('Name'), $attributes['name'], TRUE); - $this->add('text', 'description', ts('Description'), array('size' => CRM_Utils_Type::HUGE)); + $this->add('text', 'description', ts('Description'), ['size' => CRM_Utils_Type::HUGE]); $this->add('checkbox', 'is_default', ts('Is this PDF Page Format the default?')); $this->add('select', 'paper_size', ts('Paper Size'), - array( + [ 0 => ts('- default -'), - ) + CRM_Core_BAO_PaperSize::getList(TRUE), FALSE, - array('onChange' => "selectPaper( this.value );") + ] + CRM_Core_BAO_PaperSize::getList(TRUE), FALSE, + ['onChange' => "selectPaper( this.value );"] ); $this->add('static', 'paper_dimensions', NULL, ts('Width x Height')); $this->add('select', 'orientation', ts('Orientation'), CRM_Core_BAO_PdfFormat::getPageOrientations(), FALSE, - array('onChange' => "updatePaperDimensions();") + ['onChange' => "updatePaperDimensions();"] ); $this->add('select', 'metric', ts('Unit of Measure'), CRM_Core_BAO_PdfFormat::getUnits(), FALSE, - array('onChange' => "selectMetric( this.value );") + ['onChange' => "selectMetric( this.value );"] ); - $this->add('text', 'margin_left', ts('Left Margin'), array('size' => 8, 'maxlength' => 8), TRUE); - $this->add('text', 'margin_right', ts('Right Margin'), array('size' => 8, 'maxlength' => 8), TRUE); - $this->add('text', 'margin_top', ts('Top Margin'), array('size' => 8, 'maxlength' => 8), TRUE); - $this->add('text', 'margin_bottom', ts('Bottom Margin'), array('size' => 8, 'maxlength' => 8), TRUE); - $this->add('text', 'weight', ts('Order'), CRM_Core_DAO::getAttribute('CRM_Core_BAO_PdfFormat', 'weight'), TRUE); + $this->add('text', 'margin_left', ts('Left Margin'), ['size' => 8, 'maxlength' => 8], TRUE); + $this->add('text', 'margin_right', ts('Right Margin'), ['size' => 8, 'maxlength' => 8], TRUE); + $this->add('text', 'margin_top', ts('Top Margin'), ['size' => 8, 'maxlength' => 8], TRUE); + $this->add('text', 'margin_bottom', ts('Bottom Margin'), ['size' => 8, 'maxlength' => 8], TRUE); + $this->add('number', 'weight', ts('Order'), CRM_Core_DAO::getAttribute('CRM_Core_BAO_PdfFormat', 'weight'), TRUE); - $this->addRule('name', ts('Name already exists in Database.'), 'objectExists', array( - 'CRM_Core_BAO_PdfFormat', - $this->_id, - )); + $this->addRule('name', ts('Name already exists in Database.'), 'objectExists', [ + 'CRM_Core_BAO_PdfFormat', + $this->_id, + ]); $this->addRule('margin_left', ts('Margin must be numeric'), 'numeric'); $this->addRule('margin_right', ts('Margin must be numeric'), 'numeric'); $this->addRule('margin_top', ts('Margin must be numeric'), 'numeric'); @@ -119,9 +120,9 @@ public function postProcess() { $bao = new CRM_Core_BAO_PdfFormat(); $bao->savePdfFormat($values, $this->_id); - $status = ts('Your new PDF Page Format titled %1 has been saved.', array(1 => $values['name']), ts('Saved'), 'success'); + $status = ts('Your new PDF Page Format titled %1 has been saved.', [1 => $values['name']], ts('Saved'), 'success'); if ($this->_action & CRM_Core_Action::UPDATE) { - $status = ts('Your PDF Page Format titled %1 has been updated.', array(1 => $values['name']), ts('Saved'), 'success'); + $status = ts('Your PDF Page Format titled %1 has been updated.', [1 => $values['name']], ts('Saved'), 'success'); } CRM_Core_Session::setStatus($status); } diff --git a/CRM/Admin/Form/Persistent.php b/CRM/Admin/Form/Persistent.php index 13eecb8bcb8e..e62d3313bc0b 100644 --- a/CRM/Admin/Form/Persistent.php +++ b/CRM/Admin/Form/Persistent.php @@ -1,9 +1,9 @@ _indexID && ($this->_action & (CRM_Core_Action::UPDATE))) { - $params = array('id' => $this->_indexID); + $params = ['id' => $this->_indexID]; CRM_Core_BAO_Persistent::retrieve($params, $defaults); if (CRM_Utils_Array::value('is_config', $defaults) == 1) { $defaults['data'] = implode(',', $defaults['data']); @@ -71,23 +71,22 @@ public function setDefaultValues() { public function buildQuickForm() { $this->add('text', 'context', ts('Context:'), NULL, TRUE); $this->add('text', 'name', ts('Name:'), NULL, TRUE); - $this->add('textarea', 'data', ts('Data:'), array('rows' => 4, 'cols' => 50), TRUE); - $this->addButtons(array( - array( - 'type' => 'submit', - 'name' => ts('Save'), - 'isDefault' => TRUE, - ), - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - ), - ) - ); + $this->add('textarea', 'data', ts('Data:'), ['rows' => 4, 'cols' => 50], TRUE); + $this->addButtons([ + [ + 'type' => 'submit', + 'name' => ts('Save'), + 'isDefault' => TRUE, + ], + [ + 'type' => 'cancel', + 'name' => ts('Cancel'), + ], + ]); } public function postProcess() { - $params = $ids = array(); + $params = $ids = []; $params = $this->controller->exportValues($this->_name); $params['is_config'] = $this->_config; diff --git a/CRM/Admin/Form/Preferences.php b/CRM/Admin/Form/Preferences.php index 8d3e6013b45e..55578d404a53 100644 --- a/CRM/Admin/Form/Preferences.php +++ b/CRM/Admin/Form/Preferences.php @@ -1,9 +1,9 @@ _config->contact_id = $this->_contactID; } + $this->addFieldsDefinedInSettingsMetadata(); $settings = Civi::settings(); + // @todo replace this by defining all in settings. foreach ($this->_varNames as $groupName => $settingNames) { + CRM_Core_Error::deprecatedFunctionWarning('deprecated use of preferences form. This will be removed from core soon'); foreach ($settingNames as $settingName => $options) { $this->_config->$settingName = $settings->get($settingName); } @@ -98,23 +104,28 @@ public function preProcess() { * @return array */ public function setDefaultValues() { - $defaults = array(); + $this->_defaults = []; + $this->setDefaultsForMetadataDefinedFields(); foreach ($this->_varNames as $groupName => $settings) { + CRM_Core_Error::deprecatedFunctionWarning('deprecated use of preferences form. This will be removed from core soon'); foreach ($settings as $settingName => $settingDetails) { - $defaults[$settingName] = isset($this->_config->$settingName) ? $this->_config->$settingName : CRM_Utils_Array::value('default', $settingDetails, NULL); + $this->_defaults[$settingName] = isset($this->_config->$settingName) ? $this->_config->$settingName : CRM_Utils_Array::value('default', $settingDetails, NULL); } } - return $defaults; + return $this->_defaults; } /** + * @todo deprecate in favour of setting using metadata. + * * @param $defaults */ public function cbsDefaultValues(&$defaults) { foreach ($this->_varNames as $groupName => $groupValues) { + CRM_Core_Error::deprecatedFunctionWarning('deprecated use of preferences form. This will be removed from core soon'); foreach ($groupValues as $settingName => $fieldValue) { if ($fieldValue['html_type'] == 'checkboxes') { if (isset($this->_config->$settingName) && @@ -124,7 +135,7 @@ public function cbsDefaultValues(&$defaults) { substr($this->_config->$settingName, 1, -1) ); if (!empty($value)) { - $defaults[$settingName] = array(); + $defaults[$settingName] = []; foreach ($value as $n => $v) { $defaults[$settingName][$v] = 1; } @@ -142,10 +153,11 @@ public function buildQuickForm() { parent::buildQuickForm(); if (!empty($this->_varNames)) { + CRM_Core_Error::deprecatedFunctionWarning('deprecated use of preferences form. This will be removed from core soon'); foreach ($this->_varNames as $groupName => $groupValues) { $formName = CRM_Utils_String::titleToVar($groupName); $this->assign('formName', $formName); - $fields = array(); + $fields = []; foreach ($groupValues as $fieldName => $fieldValue) { $fields[$fieldName] = $fieldValue; @@ -154,10 +166,10 @@ public function buildQuickForm() { $this->addElement('text', $fieldName, $fieldValue['title'], - array( + [ 'maxlength' => 64, 'size' => 32, - ) + ] ); break; @@ -175,12 +187,12 @@ public function buildQuickForm() { break; case 'YesNo': - $this->addRadio($fieldName, $fieldValue['title'], array(0 => 'No', 1 => 'Yes'), NULL, '  '); + $this->addRadio($fieldName, $fieldValue['title'], [0 => 'No', 1 => 'Yes'], NULL, '  '); break; case 'checkboxes': $options = array_flip(CRM_Core_OptionGroup::values($fieldName, FALSE, FALSE, TRUE)); - $newOptions = array(); + $newOptions = []; foreach ($options as $key => $val) { $newOptions[$key] = $val; } @@ -188,7 +200,7 @@ public function buildQuickForm() { $fieldValue['title'], $newOptions, NULL, NULL, NULL, NULL, - array('  ', '  ', '
') + ['  ', '  ', '
'] ); break; @@ -196,7 +208,8 @@ public function buildQuickForm() { $this->addElement('select', $fieldName, $fieldValue['title'], - $fieldValue['option_values'] + $fieldValue['option_values'], + CRM_Utils_Array::value('attributes', $fieldValue) ); break; @@ -205,7 +218,7 @@ public function buildQuickForm() { break; case 'entity_reference': - $this->addEntityRef($fieldName, $fieldValue['title'], CRM_Utils_Array::value('options', $fieldValue, array())); + $this->addEntityRef($fieldName, $fieldValue['title'], CRM_Utils_Array::value('options', $fieldValue, [])); } } @@ -214,18 +227,17 @@ public function buildQuickForm() { } } - $this->addButtons(array( - array( - 'type' => 'next', - 'name' => ts('Save'), - 'isDefault' => TRUE, - ), - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'next', + 'name' => ts('Save'), + 'isDefault' => TRUE, + ], + [ + 'type' => 'cancel', + 'name' => ts('Cancel'), + ], + ]); if ($this->_action == CRM_Core_Action::VIEW) { $this->freeze(); @@ -250,6 +262,14 @@ public function postProcess() { * Process the form submission. */ public function postProcessCommon() { + try { + $this->saveMetadataDefinedSettings($this->_params); + $this->filterParamsSetByMetadata($this->_params); + } + catch (CiviCRM_API3_Exception $e) { + CRM_Core_Session::setStatus($e->getMessage(), ts('Save Failed'), 'error'); + } + foreach ($this->_varNames as $groupName => $groupValues) { foreach ($groupValues as $settingName => $fieldValue) { switch ($fieldValue['html_type']) { @@ -282,7 +302,7 @@ public function postProcessCommon() { $value = CRM_Utils_Array::value($settingName, $this->_params); if ($value) { $value = trim($value); - $value = str_replace(array("\r\n", "\r"), "\n", $value); + $value = str_replace(["\r\n", "\r"], "\n", $value); } $this->_config->$settingName = $value; break; diff --git a/CRM/Admin/Form/Preferences/Address.php b/CRM/Admin/Form/Preferences/Address.php index 3443291ff573..059b5bc6ff5f 100644 --- a/CRM/Admin/Form/Preferences/Address.php +++ b/CRM/Admin/Form/Preferences/Address.php @@ -1,9 +1,9 @@ '- select -', - ) + CRM_Core_SelectValues::addressProvider(); - - $this->_varNames = array( - CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME => array( - 'address_options' => array( - 'html_type' => 'checkboxes', - 'title' => ts('Address Fields'), - 'weight' => 1, - ), - 'address_format' => array( - 'html_type' => 'textarea', - 'title' => ts('Display Format'), - 'description' => NULL, - 'weight' => 2, - ), - 'mailing_format' => array( - 'html_type' => 'textarea', - 'title' => ts('Mailing Label Format'), - 'description' => NULL, - 'weight' => 3, - ), - 'hideCountryMailingLabels' => array( - 'html_type' => 'YesNo', - 'title' => 'Hide Country in Mailing Labels when same as domain country', - 'weight' => 4, - ), - ), - CRM_Core_BAO_Setting::ADDRESS_STANDARDIZATION_PREFERENCES_NAME => array( - 'address_standardization_provider' => array( - 'html_type' => 'select', - 'title' => ts('Provider'), - 'option_values' => $addrProviders, - 'weight' => 5, - ), - 'address_standardization_userid' => array( - 'html_type' => 'text', - 'title' => ts('User ID'), - 'description' => NULL, - 'weight' => 6, - ), - 'address_standardization_url' => array( - 'html_type' => 'text', - 'title' => ts('Web Service URL'), - 'description' => NULL, - 'weight' => 7, - ), - ), - ); - - parent::preProcess(); - } - - /** - * @return array - */ - public function setDefaultValues() { - $defaults = array(); - $defaults['address_standardization_provider'] = $this->_config->address_standardization_provider; - $defaults['address_standardization_userid'] = $this->_config->address_standardization_userid; - $defaults['address_standardization_url'] = $this->_config->address_standardization_url; - - $this->addressSequence = isset($newSequence) ? $newSequence : ""; - - $defaults['address_format'] = $this->_config->address_format; - $defaults['mailing_format'] = $this->_config->mailing_format; - $defaults['hideCountryMailingLabels'] = $this->_config->hideCountryMailingLabels; - parent::cbsDefaultValues($defaults); - - return $defaults; - } + protected $_settings = [ + 'address_options' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, + 'address_format' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, + 'mailing_format' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, + 'hideCountryMailingLabels' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, + 'address_standardization_provider' => CRM_Core_BAO_Setting::ADDRESS_STANDARDIZATION_PREFERENCES_NAME, + 'address_standardization_userid' => CRM_Core_BAO_Setting::ADDRESS_STANDARDIZATION_PREFERENCES_NAME, + 'address_standardization_url' => CRM_Core_BAO_Setting::ADDRESS_STANDARDIZATION_PREFERENCES_NAME, + ]; /** * Build the form object. @@ -120,7 +52,7 @@ public function setDefaultValues() { public function buildQuickForm() { $this->applyFilter('__ALL__', 'trim'); - $this->addFormRule(array('CRM_Admin_Form_Preferences_Address', 'formRule')); + $this->addFormRule(['CRM_Admin_Form_Preferences_Address', 'formRule']); //get the tokens for Mailing Label field $tokens = CRM_Core_SelectValues::contactTokens(); @@ -142,13 +74,9 @@ public static function formRule($fields) { // make sure that there is a value for all of them // if any of them are set if ($p || $u || $w) { - if (!CRM_Utils_System::checkPHPVersion(5, FALSE)) { - $errors['_qf_default'] = ts('Address Standardization features require PHP version 5 or greater.'); - return $errors; - } if (!($p && $u && $w)) { - $errors['_qf_default'] = ts('You must provide values for all three Address Standarization fields.'); + $errors['_qf_default'] = ts('You must provide values for all three Address Standardization fields.'); return $errors; } } @@ -165,27 +93,29 @@ public function postProcess() { } $this->_params = $this->controller->exportValues($this->_name); + $addressOptions = CRM_Core_OptionGroup::values('address_options', TRUE); // check if county option has been set - $options = CRM_Core_OptionGroup::values('address_options', FALSE, FALSE, TRUE); - foreach ($options as $key => $title) { - if ($title == ts('County')) { - // check if the $key is present in $this->_params - if (isset($this->_params['address_options']) && - !empty($this->_params['address_options'][$key]) - ) { - // print a status message to the user if county table seems small - $countyCount = CRM_Core_DAO::singleValueQuery("SELECT count(*) FROM civicrm_county"); - if ($countyCount < 10) { - CRM_Core_Session::setStatus(ts('You have enabled the County option. Please ensure you populate the county table in your CiviCRM Database. You can find extensions to populate counties in the CiviCRM Extensions Directory.', array(1 => 'href="' . CRM_Utils_System::url('civicrm/admin/extensions', array('reset' => 1), TRUE, 'extensions-addnew') . '"')), - ts('Populate counties'), - "info" - ); - } - } + if (CRM_Utils_Array::value($addressOptions['County'], $this->_params['address_options'])) { + $countyCount = CRM_Core_DAO::singleValueQuery("SELECT count(*) FROM civicrm_county"); + if ($countyCount < 10) { + CRM_Core_Session::setStatus(ts('You have enabled the County option. Please ensure you populate the county table in your CiviCRM Database. You can find extensions to populate counties in the CiviCRM Extensions Directory.', [1 => 'href="' . CRM_Utils_System::url('civicrm/admin/extensions', ['reset' => 1], TRUE, 'extensions-addnew') . '"']), + ts('Populate counties'), + "info" + ); } } + // check that locale supports address parsing + if ( + CRM_Utils_Array::value($addressOptions['Street Address Parsing'], $this->_params['address_options']) && + !CRM_Core_BAO_Address::isSupportedParsingLocale() + ) { + $config = CRM_Core_Config::singleton(); + $locale = $config->lcMessages; + CRM_Core_Session::setStatus(ts('Default locale (%1) does not support street parsing. en_US locale will be used instead.', [1 => $locale]), ts('Unsupported Locale'), 'alert'); + } + $this->postProcessCommon(); } diff --git a/CRM/Admin/Form/Preferences/Campaign.php b/CRM/Admin/Form/Preferences/Campaign.php index d37327444922..3e372da4c94f 100644 --- a/CRM/Admin/Form/Preferences/Campaign.php +++ b/CRM/Admin/Form/Preferences/Campaign.php @@ -1,9 +1,9 @@ _varNames = array( - CRM_Core_BAO_Setting::CAMPAIGN_PREFERENCES_NAME => array( - 'tag_unconfirmed' => array( - 'html_type' => 'text', - 'title' => ts('Tag for Unconfirmed Petition Signers'), - 'weight' => 1, - 'description' => ts('If set, new contacts that are created when signing a petition are assigned a tag of this name.'), - ), - 'petition_contacts' => array( - 'html_type' => 'text', - 'title' => ts('Petition Signers Group'), - 'weight' => 2, - 'description' => ts('All contacts that have signed a CiviCampaign petition will be added to this group. The group will be created if it does not exist (it is required for email verification).'), - ), - ), - ); - parent::preProcess(); - } + protected $_settings = [ + 'tag_unconfirmed' => CRM_Core_BAO_Setting::CAMPAIGN_PREFERENCES_NAME, + 'petition_contacts' => CRM_Core_BAO_Setting::CAMPAIGN_PREFERENCES_NAME, + ]; } diff --git a/CRM/Admin/Form/Preferences/Contribute.php b/CRM/Admin/Form/Preferences/Contribute.php index caa0c55365c8..7421754c7229 100644 --- a/CRM/Admin/Form/Preferences/Contribute.php +++ b/CRM/Admin/Form/Preferences/Contribute.php @@ -1,9 +1,9 @@ CRM_Core_BAO_Setting::CONTRIBUTE_PREFERENCES_NAME, + 'update_contribution_on_membership_type_change' => CRM_Core_BAO_Setting::CONTRIBUTE_PREFERENCES_NAME, 'acl_financial_type' => CRM_Core_BAO_Setting::CONTRIBUTE_PREFERENCES_NAME, + 'always_post_to_accounts_receivable' => CRM_Core_BAO_Setting::CONTRIBUTE_PREFERENCES_NAME, 'deferred_revenue_enabled' => CRM_Core_BAO_Setting::CONTRIBUTE_PREFERENCES_NAME, 'default_invoice_page' => CRM_Core_BAO_Setting::CONTRIBUTE_PREFERENCES_NAME, - 'financial_account_bal_enable' => CRM_Core_BAO_Setting::CONTRIBUTE_PREFERENCES_NAME, - 'fiscalYearStart' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME, - 'prior_financial_period' => CRM_Core_BAO_Setting::CONTRIBUTE_PREFERENCES_NAME, 'invoicing' => CRM_Core_BAO_Setting::CONTRIBUTE_PREFERENCES_NAME, - ); + ]; /** - * Process the form submission. + * Our standards for settings are to have a setting per value with defined metadata. + * + * Unfortunately the 'contribution_invoice_settings' has been added in non-compliance. + * We use this array to hack-handle. + * + * I think the best way forwards would be to covert to multiple individual settings. + * + * @var array */ - public function preProcess() { - $config = CRM_Core_Config::singleton(); - CRM_Utils_System::setTitle(ts('CiviContribute Component Settings')); - $this->_varNames = array( - CRM_Core_BAO_Setting::CONTRIBUTE_PREFERENCES_NAME => array( - 'invoice_prefix' => array( - 'html_type' => 'text', - 'title' => ts('Invoice Prefix'), - 'weight' => 1, - 'description' => ts('Enter prefix to be display on PDF for invoice'), - ), - 'credit_notes_prefix' => array( - 'html_type' => 'text', - 'title' => ts('Credit Notes Prefix'), - 'weight' => 2, - 'description' => ts('Enter prefix to be display on PDF for credit notes.'), - ), - 'due_date' => array( - 'html_type' => 'text', - 'title' => ts('Due Date'), - 'weight' => 3, - ), - 'due_date_period' => array( - 'html_type' => 'select', - 'title' => ts('For transmission'), - 'weight' => 4, - 'description' => ts('Select the interval for due date.'), - 'option_values' => array( - 'select' => ts('- select -'), - 'days' => ts('Days'), - 'months' => ts('Months'), - 'years' => ts('Years'), - ), - ), - 'notes' => array( - 'html_type' => 'wysiwyg', - 'title' => ts('Notes or Standard Terms'), - 'weight' => 5, - 'description' => ts('Enter note or message to be displayed on PDF invoice or credit notes '), - 'attributes' => array('rows' => 2, 'cols' => 40), - ), - 'is_email_pdf' => array( - 'html_type' => 'checkbox', - 'title' => ts('Automatically email invoice when user purchases online'), - 'weight' => 6, - ), - 'tax_term' => array( - 'html_type' => 'text', - 'title' => ts('Tax Term'), - 'weight' => 7, - ), - 'tax_display_settings' => array( - 'html_type' => 'select', - 'title' => ts('Tax Display Settings'), - 'weight' => 8, - 'option_values' => array( - 'Do_not_show' => ts('Do not show breakdown, only show total -i.e ' . - $config->defaultCurrencySymbol . '120.00'), - 'Inclusive' => ts('Show [tax term] inclusive price - i.e. ' . - $config->defaultCurrencySymbol . - '120.00 (includes [tax term] of ' . - $config->defaultCurrencySymbol . '20.00)'), - 'Exclusive' => ts('Show [tax term] exclusive price - i.e. ' . - $config->defaultCurrencySymbol . '100.00 + ' . - $config->defaultCurrencySymbol . '20.00 [tax term]'), - ), - ), - ), - ); - parent::preProcess(); - } + protected $invoiceSettings = []; /** * Build the form object. */ public function buildQuickForm() { - $htmlFields = array(); - foreach ($this->_settings as $setting => $group) { - $settingMetaData = civicrm_api3('setting', 'getfields', array('name' => $setting)); - $props = $settingMetaData['values'][$setting]; - if (isset($props['quick_form_type'])) { - $add = 'add' . $props['quick_form_type']; - if ($add == 'addElement') { - if (in_array($props['html_type'], array('checkbox', 'textarea'))) { - $this->add($props['html_type'], - $setting, - $props['title'] - ); - } - else { - if ($props['html_type'] == 'select') { - $functionName = CRM_Utils_Array::value('name', CRM_Utils_Array::value('pseudoconstant', $props)); - if ($functionName) { - $props['option_values'] = array('' => ts('- select -')) + CRM_Contribute_PseudoConstant::$functionName(); - } - } - $this->$add( - $props['html_type'], - $setting, - ts($props['title']), - CRM_Utils_Array::value($props['html_type'] == 'select' ? 'option_values' : 'html_attributes', $props, array()), - $props['html_type'] == 'select' ? CRM_Utils_Array::value('html_attributes', $props) : NULL - ); - } - } - elseif ($add == 'addMonthDay') { - $this->add('date', $setting, ts($props['title']), CRM_Core_SelectValues::date(NULL, 'M d')); - } - elseif ($add == 'addDate') { - $this->addDate($setting, ts($props['title']), FALSE, array('formatType' => $props['type'])); - } - else { - $this->$add($setting, ts($props['title'])); - } - } - $htmlFields[$setting] = ts($props['description']); - } - $this->assign('htmlFields', $htmlFields); parent::buildQuickForm(); - $this->addFormRule(array('CRM_Admin_Form_Preferences_Contribute', 'formRule'), $this); - } + $config = CRM_Core_Config::singleton(); + $this->invoiceSettings = [ + 'invoice_prefix' => [ + 'html_type' => 'text', + 'title' => ts('Invoice Prefix'), + 'weight' => 1, + 'description' => ts('Enter prefix to be display on PDF for invoice'), + ], + 'credit_notes_prefix' => [ + 'html_type' => 'text', + 'title' => ts('Credit Notes Prefix'), + 'weight' => 2, + 'description' => ts('Enter prefix to be display on PDF for credit notes.'), + ], + 'due_date' => [ + 'html_type' => 'text', + 'title' => ts('Due Date'), + 'weight' => 3, + ], + 'due_date_period' => [ + 'html_type' => 'select', + 'title' => ts('For transmission'), + 'weight' => 4, + 'description' => ts('Select the interval for due date.'), + 'option_values' => [ + 'select' => ts('- select -'), + 'days' => ts('Days'), + 'months' => ts('Months'), + 'years' => ts('Years'), + ], + ], + 'notes' => [ + 'html_type' => 'wysiwyg', + 'title' => ts('Notes or Standard Terms'), + 'weight' => 5, + 'description' => ts('Enter note or message to be displayed on PDF invoice or credit notes '), + 'attributes' => ['rows' => 2, 'cols' => 40], + ], + 'is_email_pdf' => [ + 'html_type' => 'checkbox', + 'title' => ts('Automatically email invoice when user purchases online'), + 'weight' => 6, + 'description' => ts('Should a pdf invoice be emailed automatically?'), + ], + 'tax_term' => [ + 'html_type' => 'text', + 'title' => ts('Tax Term'), + 'weight' => 7, + ], + 'tax_display_settings' => [ + 'html_type' => 'select', + 'title' => ts('Tax Display Settings'), + 'weight' => 8, + 'option_values' => [ + 'Do_not_show' => ts('Do not show breakdown, only show total -i.e ' . + $config->defaultCurrencySymbol . '120.00'), + 'Inclusive' => ts('Show [tax term] inclusive price - i.e. ' . + $config->defaultCurrencySymbol . + '120.00 (includes [tax term] of ' . + $config->defaultCurrencySymbol . '20.00)'), + 'Exclusive' => ts('Show [tax term] exclusive price - i.e. ' . + $config->defaultCurrencySymbol . '100.00 + ' . + $config->defaultCurrencySymbol . '20.00 [tax term]'), + ], + ], + ]; - /** - * Global validation rules for the form. - * - * @param array $values - * posted values of the form - * @param $files - * @param $self - * - * @return array - * list of errors to be posted back to the form - */ - public static function formRule($values, $files, $self) { - $errors = array(); - if (CRM_Utils_Array::value('deferred_revenue_enabled', $values)) { - $errorMessage = CRM_Financial_BAO_FinancialAccount::validateTogglingDeferredRevenue(); - if ($errorMessage) { - // Since the error msg is too long and - // takes the whole space to display inline - // therefore setting blank text to highlight the field - // setting actual error msg to _qf_default to show in pop-up screen - $errors['deferred_revenue_enabled'] = ' '; - $errors['_qf_default'] = $errorMessage; + // @todo this is a faux metadata approach - we should be honest & add them correctly or find a way to make this + // compatible with our settings standards. + foreach ($this->invoiceSettings as $fieldName => $fieldValue) { + switch ($fieldValue['html_type']) { + case 'text': + $this->addElement('text', + $fieldName, + $fieldValue['title'], + [ + 'maxlength' => 64, + 'size' => 32, + ] + ); + break; + + case 'checkbox': + $this->add($fieldValue['html_type'], + $fieldName, + $fieldValue['title'] + ); + break; + + case 'select': + $this->addElement('select', + $fieldName, + $fieldValue['title'], + $fieldValue['option_values'], + CRM_Utils_Array::value('attributes', $fieldValue) + ); + break; + + case 'wysiwyg': + $this->add('wysiwyg', $fieldName, $fieldValue['title'], $fieldValue['attributes']); + break; } } - return $errors; + + $this->assign('htmlFields', $this->invoiceSettings); } /** @@ -204,21 +175,8 @@ public static function formRule($values, $files, $self) { * default values are retrieved from the database */ public function setDefaultValues() { - $defaults = Civi::settings()->get('contribution_invoice_settings'); - //CRM-16691: Changes made related to settings of 'CVV'. - foreach (array('cvv_backoffice_required') as $setting) { - $settingMetaData = civicrm_api3('setting', 'getfields', array('name' => $setting)); - $defaults[$setting] = civicrm_api3('setting', 'getvalue', - array( - 'name' => $setting, - 'group' => CRM_Core_BAO_Setting::CONTRIBUTE_PREFERENCES_NAME, - 'default_value' => CRM_Utils_Array::value('default', $settingMetaData['values'][$setting]), - ) - ); - } - $defaults['fiscalYearStart'] = Civi::settings()->get('fiscalYearStart'); - $period = CRM_Contribute_BAO_Contribution::checkContributeSettings('prior_financial_period'); - $this->assign('priorFinancialPeriod', $period); + $defaults = parent::setDefaultValues(); + $defaults = array_merge($defaults, Civi::settings()->get('contribution_invoice_settings')); return $defaults; } @@ -228,38 +186,14 @@ public function setDefaultValues() { public function postProcess() { // store the submitted values in an array $params = $this->controller->exportValues($this->_name); - unset($params['qfKey']); - unset($params['entryURL']); - Civi::settings()->set('contribution_invoice_settings', $params); - Civi::settings()->set('fiscalYearStart', $params['fiscalYearStart']); - - // to set default value for 'Invoices / Credit Notes' checkbox on display preferences - $values = CRM_Core_BAO_Setting::getItem("CiviCRM Preferences"); - $optionValues = CRM_Core_OptionGroup::values('user_dashboard_options', FALSE, FALSE, FALSE, NULL, 'name'); - $setKey = array_search('Invoices / Credit Notes', $optionValues); - - if (isset($params['invoicing'])) { - $value = array($setKey => $optionValues[$setKey]); - $setInvoice = CRM_Core_DAO::VALUE_SEPARATOR . - implode(CRM_Core_DAO::VALUE_SEPARATOR, array_keys($value)) . - CRM_Core_DAO::VALUE_SEPARATOR; - Civi::settings()->set('user_dashboard_options', $values['user_dashboard_options'] . $setInvoice); - } - else { - $setting = explode(CRM_Core_DAO::VALUE_SEPARATOR, substr($values['user_dashboard_options'], 1, -1)); - $invoiceKey = array_search($setKey, $setting); - if ($invoiceKey !== FALSE) { - unset($setting[$invoiceKey]); - } - $settingName = CRM_Core_DAO::VALUE_SEPARATOR . - implode(CRM_Core_DAO::VALUE_SEPARATOR, array_values($setting)) . - CRM_Core_DAO::VALUE_SEPARATOR; - Civi::settings()->set('user_dashboard_options', $settingName); - } - //CRM-16691: Changes made related to settings of 'CVV'. - $settings = array_intersect_key($params, array('cvv_backoffice_required' => 1)); - $result = civicrm_api3('setting', 'create', $settings); - CRM_Core_Session::setStatus(ts('Your changes have been saved.'), ts('Changes Saved'), "success"); + $invoiceParams = array_intersect_key($params, $this->invoiceSettings); + // This is a hack - invoicing is it's own setting but it is being used from invoice params + // too. This means that saving from api will not have the desired core effect. + // but we should fix that elsewhere - ie. stop abusing the settings + // and fix the code repetition associated with invoicing + $invoiceParams['invoicing'] = CRM_Utils_Array::value('invoicing', $params, 0); + Civi::settings()->set('contribution_invoice_settings', $invoiceParams); + parent::postProcess(); } } diff --git a/CRM/Admin/Form/Preferences/Display.php b/CRM/Admin/Form/Preferences/Display.php index 423f08c7b2f0..9ba36825cdf9 100644 --- a/CRM/Admin/Form/Preferences/Display.php +++ b/CRM/Admin/Form/Preferences/Display.php @@ -1,9 +1,9 @@ _varNames = array( - CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME => array( - 'contact_view_options' => array( - 'html_type' => 'checkboxes', - 'title' => ts('Viewing Contacts'), - 'weight' => 1, - ), - 'contact_smart_group_display' => array( - 'html_type' => 'radio', - 'title' => ts('Viewing Smart Groups'), - 'weight' => 2, - ), - 'contact_edit_options' => array( - 'html_type' => 'checkboxes', - 'title' => ts('Editing Contacts'), - 'weight' => 3, - ), - 'advanced_search_options' => array( - 'html_type' => 'checkboxes', - 'title' => ts('Contact Search'), - 'weight' => 4, - ), - 'activity_assignee_notification' => array( - 'html_type' => 'checkbox', - 'title' => ts('Notify Activity Assignees'), - 'weight' => 5, - ), - 'activity_assignee_notification_ics' => array( - 'html_type' => 'checkbox', - 'title' => ts('Include ICal Invite to Activity Assignees'), - 'weight' => 6, - ), - 'contact_ajax_check_similar' => array( - 'html_type' => 'checkbox', - 'title' => ts('Check for Similar Contacts'), - 'weight' => 7, - ), - 'user_dashboard_options' => array( - 'html_type' => 'checkboxes', - 'title' => ts('Contact Dashboard'), - 'weight' => 8, - ), - 'display_name_format' => array( - 'html_type' => 'textarea', - 'title' => ts('Individual Display Name Format'), - 'weight' => 9, - ), - 'sort_name_format' => array( - 'html_type' => 'textarea', - 'title' => ts('Individual Sort Name Format'), - 'weight' => 10, - ), - 'editor_id' => array( - 'html_type' => NULL, - 'weight' => 11, - ), - 'ajaxPopupsEnabled' => array( - 'html_type' => 'checkbox', - 'title' => ts('Enable Popup Forms'), - 'weight' => 12, - ), - ), - ); - - parent::preProcess(); - } - /** - * @return array - */ - public function setDefaultValues() { - $defaults = parent::setDefaultValues(); - parent::cbsDefaultValues($defaults); - - if ($this->_config->display_name_format) { - $defaults['display_name_format'] = $this->_config->display_name_format; - } - if ($this->_config->sort_name_format) { - $defaults['sort_name_format'] = $this->_config->sort_name_format; - } - - return $defaults; - } + protected $_settings = [ + 'contact_view_options' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, + 'contact_smart_group_display' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, + 'contact_edit_options' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, + 'advanced_search_options' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, + 'user_dashboard_options' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, + 'contact_ajax_check_similar' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, + 'activity_assignee_notification' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, + 'activity_assignee_notification_ics' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, + 'do_not_notify_assignees_for' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, + 'preserve_activity_tab_filter' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, + 'editor_id' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, + 'ajaxPopupsEnabled' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, + 'display_name_format' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, + 'sort_name_format' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, + 'menubar_position' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, + 'menubar_color' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, + ]; /** * Build the form object. */ public function buildQuickForm() { - $wysiwyg_options = CRM_Core_OptionGroup::values('wysiwyg_editor', FALSE, FALSE, FALSE, NULL, 'label', TRUE, FALSE, 'name'); //changes for freezing the invoices/credit notes checkbox if invoicing is uncheck $invoiceSettings = Civi::settings()->get('contribution_invoice_settings'); - $invoicing = CRM_Utils_Array::value('invoicing', $invoiceSettings); - $this->assign('invoicing', $invoicing); - $extra = array(); + $this->assign('invoicing', CRM_Invoicing_Utils::isInvoicingEnabled()); - $this->addElement('select', 'editor_id', ts('WYSIWYG Editor'), $wysiwyg_options, $extra); $this->addElement('submit', 'ckeditor_config', ts('Configure CKEditor')); $editOptions = CRM_Core_OptionGroup::values('contact_edit_options', FALSE, FALSE, FALSE, 'AND v.filter = 0'); @@ -146,7 +75,7 @@ public function buildQuickForm() { $nameFields = CRM_Core_OptionGroup::values('contact_edit_options', FALSE, FALSE, FALSE, 'AND v.filter = 2'); $this->assign('nameFields', $nameFields); - $this->addElement('hidden', 'contact_edit_preferences', NULL, array('id' => 'contact_edit_preferences')); + $this->addElement('hidden', 'contact_edit_preferences', NULL, ['id' => 'contact_edit_preferences']); $optionValues = CRM_Core_OptionGroup::values('user_dashboard_options', FALSE, FALSE, FALSE, NULL, 'name'); $invoicesKey = array_search('Invoices / Credit Notes', $optionValues); @@ -175,8 +104,6 @@ public function postProcess() { CRM_Core_BAO_OptionValue::updateOptionWeights($opGroupId, array_flip($preferenceWeights)); } - $this->_config->editor_id = $this->_params['editor_id']; - $this->postProcessCommon(); // If "Configure CKEditor" button was clicked diff --git a/CRM/Admin/Form/Preferences/Event.php b/CRM/Admin/Form/Preferences/Event.php deleted file mode 100644 index d8023890ef6a..000000000000 --- a/CRM/Admin/Form/Preferences/Event.php +++ /dev/null @@ -1,72 +0,0 @@ -_varNames = array( - CRM_Core_BAO_Setting::EVENT_PREFERENCES_NAME => array( - 'enable_cart' => array( - 'html_type' => 'checkbox', - 'title' => ts('Use Shopping Cart Style Event Registration'), - 'weight' => 1, - 'description' => ts('This feature allows users to register for more than one event at a time. When enabled, users will add event(s) to a "cart" and then pay for them all at once. Enabling this setting will affect online registration for all active events. The code is an alpha state, and you will potentially need to have developer resources to debug and fix sections of the codebase while testing and deploying it. %1', - array(1 => $docLink)), - ), - 'show_events' => array( - 'html_type' => 'select', - 'title' => ts('Dashboard entries'), - 'weight' => 2, - 'description' => ts('Configure how many events should be shown on the dashboard. This overrides the default value of 10 entries.'), - 'option_values' => array('' => ts('- select -')) + $optionValues + array(-1 => ts('show all')), - ), - ), - ); - - parent::preProcess(); - } - -} diff --git a/CRM/Admin/Form/Preferences/Mailing.php b/CRM/Admin/Form/Preferences/Mailing.php index 7592ddaa6887..ab6069fbed44 100644 --- a/CRM/Admin/Form/Preferences/Mailing.php +++ b/CRM/Admin/Form/Preferences/Mailing.php @@ -1,9 +1,9 @@ _varNames = array( - CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME => array( - 'profile_double_optin' => array( - 'html_type' => 'checkbox', - 'title' => ts('Enable Double Opt-in for Profile Group(s) field'), - 'weight' => 1, - 'description' => ts('When CiviMail is enabled, users who "subscribe" to a group from a profile Group(s) checkbox will receive a confirmation email. They must respond (opt-in) before they are added to the group.'), - ), - 'profile_add_to_group_double_optin' => array( - 'html_type' => 'checkbox', - 'title' => ts('Enable Double Opt-in for Profiles which use the "Add to Group" setting'), - 'weight' => 2, - 'description' => ts('When CiviMail is enabled and a profile uses the "Add to Group" setting, users who complete the profile form will receive a confirmation email. They must respond (opt-in) before they are added to the group.'), - ), - 'track_civimail_replies' => array( - 'html_type' => 'checkbox', - 'title' => ts('Track replies using VERP in Reply-To header'), - 'weight' => 3, - 'description' => ts('If checked, mailings will default to tracking replies using VERP-ed Reply-To.'), - ), - 'civimail_workflow' => array( - 'html_type' => 'checkbox', - 'title' => ts('Enable workflow support for CiviMail'), - 'weight' => 4, - 'description' => ts('Drupal-only. Rules module must be enabled (beta feature - use with caution).'), - ), - 'civimail_multiple_bulk_emails' => array( - 'html_type' => 'checkbox', - 'title' => ts('Enable multiple bulk email address for a contact.'), - 'weight' => 5, - 'description' => ts('CiviMail will deliver a copy of the email to each bulk email listed for the contact.'), - ), - 'civimail_server_wide_lock' => array( - 'html_type' => 'checkbox', - 'title' => ts('Enable global server wide lock for CiviMail'), - 'weight' => 6, - 'description' => NULL, - ), - 'include_message_id' => array( - 'html_type' => 'checkbox', - 'title' => ts('Enable CiviMail to generate Message-ID header'), - 'weight' => 7, - 'description' => NULL, - ), - 'write_activity_record' => array( - 'html_type' => 'checkbox', - 'title' => ts('Enable CiviMail to create activities on delivery'), - 'weight' => 8, - 'description' => NULL, - ), - 'disable_mandatory_tokens_check' => array( - 'html_type' => 'checkbox', - 'title' => ts('Disable check for mandatory tokens'), - 'weight' => 9, - 'description' => ts('Don\'t check for presence of mandatory tokens (domain address; unsubscribe/opt-out) before sending mailings. WARNING: Mandatory tokens are a safe-guard which facilitate compliance with the US CAN-SPAM Act. They should only be disabled if your organization adopts other mechanisms for compliance or if your organization is not subject to CAN-SPAM.'), - ), - 'dedupe_email_default' => array( - 'html_type' => 'checkbox', - 'title' => ts('CiviMail dedupes e-mail addresses by default'), - 'weight' => 10, - 'description' => NULL, - ), - 'hash_mailing_url' => array( - 'html_type' => 'checkbox', - 'title' => ts('Hashed Mailing URL\'s'), - 'weight' => 11, - 'description' => 'If enabled, a randomized hash key will be used to reference the mailing URL in the mailing.viewUrl token, instead of the mailing ID', - ), - ), - ); - parent::preProcess(); - } + protected $_settings = [ + 'profile_double_optin' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME, + 'profile_add_to_group_double_optin' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME, + 'track_civimail_replies' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME, + 'civimail_workflow' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME, + 'civimail_multiple_bulk_emails' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME, + 'civimail_server_wide_lock' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME, + 'include_message_id' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME, + 'write_activity_record' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME, + 'disable_mandatory_tokens_check' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME, + 'dedupe_email_default' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME, + 'hash_mailing_url' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME, + 'auto_recipient_rebuild' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME, + ]; public function postProcess() { - // check if mailing tab is enabled, if not prompt user to enable the tab if "write_activity_record" is disabled $params = $this->controller->exportValues($this->_name); if (empty($params['write_activity_record'])) { + // @todo use the setting onToggle & add an action rather than have specific form handling. + // see logging setting for eg. $existingViewOptions = Civi::settings()->get('contact_view_options'); $displayValue = CRM_Core_OptionGroup::getValue('contact_view_options', 'CiviMail', 'name'); diff --git a/CRM/Admin/Form/Preferences/Member.php b/CRM/Admin/Form/Preferences/Member.php index 1c0bfcfe51de..f9b9b8fe4eed 100644 --- a/CRM/Admin/Form/Preferences/Member.php +++ b/CRM/Admin/Form/Preferences/Member.php @@ -1,9 +1,9 @@ _varNames = array( - CRM_Core_BAO_Setting::MEMBER_PREFERENCES_NAME => array( - 'default_renewal_contribution_page' => array( - 'html_type' => 'select', - 'title' => ts('Default online membership renewal page'), - 'option_values' => array('' => ts('- select -')) + CRM_Contribute_PseudoConstant::contributionPage(), - 'weight' => 1, - 'description' => ts('If you select a default online contribution page for self-service membership renewals, a "renew" link pointing to that page will be displayed on the Contact Dashboard for memberships which were entered offline. You will need to ensure that the membership block for the selected online contribution page includes any currently available memberships.'), - ), - ), - ); - parent::preProcess(); - } - - /** - * Build the form object. - */ - public function buildQuickForm() { - parent::buildQuickForm(); - } + protected $_settings = [ + 'default_renewal_contribution_page' => CRM_Core_BAO_Setting::MEMBER_PREFERENCES_NAME, + ]; } diff --git a/CRM/Admin/Form/Preferences/Multisite.php b/CRM/Admin/Form/Preferences/Multisite.php index 4ebdf158e27a..e5616922686f 100644 --- a/CRM/Admin/Form/Preferences/Multisite.php +++ b/CRM/Admin/Form/Preferences/Multisite.php @@ -1,9 +1,9 @@ _varNames = array( - CRM_Core_BAO_Setting::MULTISITE_PREFERENCES_NAME => array( - 'is_enabled' => array( - 'html_type' => 'checkbox', - 'title' => ts('Enable Multi Site Configuration'), - 'weight' => 1, - 'description' => ts('Make CiviCRM aware of multiple domains. You should configure a domain group if enabled') . ' ' . $msDoc, - ), - /** Remove this checkbox until some one knows what this setting does - * 'uniq_email_per_site' => array( - * 'html_type' => 'checkbox', - * 'title' => ts('Ensure multi sites have a unique email per site'), - * 'weight' => 2, - * 'description' => NULL, - * ), - */ - 'domain_group_id' => array( - 'html_type' => 'entity_reference', - 'title' => ts('Domain Group'), - 'weight' => 3, - 'options' => array('entity' => 'group', 'select' => array('minimumInputLength' => 0)), - 'description' => ts('Contacts created on this site are added to this group'), - ), - /** Remove this checkbox until some one knows what this setting does - * 'event_price_set_domain_id' => array( - * 'html_type' => 'text', - * 'title' => ts('Domain for event price sets'), - * 'weight' => 4, - * 'description' => NULL, - * ), - */ - ), - ); - parent::preProcess(); - } + protected $_settings = [ + 'is_enabled' => CRM_Core_BAO_Setting::MULTISITE_PREFERENCES_NAME, + 'domain_group_id' => CRM_Core_BAO_Setting::MULTISITE_PREFERENCES_NAME, + ]; } diff --git a/CRM/Admin/Form/PreferencesDate.php b/CRM/Admin/Form/PreferencesDate.php index 68c6f6c5186e..95940ff6f8f7 100644 --- a/CRM/Admin/Form/PreferencesDate.php +++ b/CRM/Admin/Form/PreferencesDate.php @@ -1,9 +1,9 @@ add('select', 'date_format', ts('Format'), - array('' => ts('- default input format -')) + CRM_Core_SelectValues::getDatePluginInputFormats() + ['' => ts('- default input format -')] + CRM_Core_SelectValues::getDatePluginInputFormats() ); $this->add('select', 'time_format', ts('Time'), - array('' => ts('- none -')) + CRM_Core_SelectValues::getTimeFormats() + ['' => ts('- none -')] + CRM_Core_SelectValues::getTimeFormats() ); } $this->addRule('start', ts('Value must be an integer.'), 'integer'); $this->addRule('end', ts('Value must be an integer.'), 'integer'); // add a form rule - $this->addFormRule(array('CRM_Admin_Form_PreferencesDate', 'formRule')); + $this->addFormRule(['CRM_Admin_Form_PreferencesDate', 'formRule']); } /** @@ -93,7 +93,7 @@ public function buildQuickForm() { * true otherwise */ public static function formRule($fields) { - $errors = array(); + $errors = []; if ($fields['name'] == 'activityDateTime' && !$fields['time_format']) { $errors['time_format'] = ts('Time is required for this format.'); @@ -129,7 +129,7 @@ public function postProcess() { CRM_Core_Resources::singleton()->resetCacheCode(); CRM_Core_Session::setStatus(ts("The date type '%1' has been saved.", - array(1 => $params['name']) + [1 => $params['name']] ), ts('Saved'), 'success'); } diff --git a/CRM/Admin/Form/RelationshipType.php b/CRM/Admin/Form/RelationshipType.php index f932cf3bda89..1b537f90634a 100644 --- a/CRM/Admin/Form/RelationshipType.php +++ b/CRM/Admin/Form/RelationshipType.php @@ -1,9 +1,9 @@ '' to hide) + * - not-auto-addable - this class will not attempt to add the field using addField. + * (this will be automatically set if the field does not have html in it's metadata + * or is not a core field on the form's entity). + * - help (option) add help to the field - e.g ['id' => 'id-source', 'file' => 'CRM/Contact/Form/Contact']] + * - template - use a field specific template to render this field + * - required + * - is_freeze (field should be frozen). + * + * @var array + */ + protected $entityFields = []; + + /** + * Set entity fields to be assigned to the form. + */ + protected function setEntityFields() { + $this->entityFields = [ + 'label_a_b' => [ + 'name' => 'label_a_b', + 'description' => ts("Label for the relationship from Contact A to Contact B. EXAMPLE: Contact A is 'Parent of' Contact B."), + 'required' => TRUE, + ], + 'label_b_a' => [ + 'name' => 'label_b_a', + 'description' => ts("Label for the relationship from Contact B to Contact A. EXAMPLE: Contact B is 'Child of' Contact A. You may leave this blank for relationships where the name is the same in both directions (e.g. Spouse)."), + ], + 'description' => [ + 'name' => 'description', + 'description' => '', + ], + 'contact_types_a' => ['name' => 'contact_types_a', 'not-auto-addable' => TRUE], + 'contact_types_b' => ['name' => 'contact_types_b', 'not-auto-addable' => TRUE], + 'is_active' => ['name' => 'is_active'], + ]; + + self::setEntityFieldsMetadata(); + } + + /** + * Deletion message to be assigned to the form. + * + * @var string + */ + protected $deleteMessage; + + /** + * Explicitly declare the entity api name. + */ + public function getDefaultEntity() { + return 'RelationshipType'; + } + + /** + * Set the delete message. + * + * We do this from the constructor in order to do a translation. + */ + public function setDeleteMessage() { + $this->deleteMessage = ts('WARNING: Deleting this option will result in the loss of all Relationship records of this type.') . ts('This may mean the loss of a substantial amount of data, and the action cannot be undone.') . ts('Do you want to continue?'); + } + /** * Build the form object. */ public function buildQuickForm() { - parent::buildQuickForm(); - $this->setPageTitle(ts('Relationship Type')); + $isReserved = ($this->_id && CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_RelationshipType', $this->_id, 'is_reserved')); + $this->entityFields['is_active']['is_freeze'] = $isReserved; + self::buildQuickEntityForm(); if ($this->_action & CRM_Core_Action::DELETE) { return; } - $this->applyFilter('__ALL__', 'trim'); - - $this->add('text', 'label_a_b', ts('Relationship Label-A to B'), - CRM_Core_DAO::getAttribute('CRM_Contact_DAO_RelationshipType', 'label_a_b'), TRUE - ); $this->addRule('label_a_b', ts('Label already exists in Database.'), - 'objectExists', array('CRM_Contact_DAO_RelationshipType', $this->_id, 'label_a_b') - ); - - $this->add('text', 'label_b_a', ts('Relationship Label-B to A'), - CRM_Core_DAO::getAttribute('CRM_Contact_DAO_RelationshipType', 'label_b_a') + 'objectExists', ['CRM_Contact_DAO_RelationshipType', $this->_id, 'label_a_b'] ); - $this->addRule('label_b_a', ts('Label already exists in Database.'), - 'objectExists', array('CRM_Contact_DAO_RelationshipType', $this->_id, 'label_b_a') - ); - - $this->add('text', 'description', ts('Description'), - CRM_Core_DAO::getAttribute('CRM_Contact_DAO_RelationshipType', 'description') + 'objectExists', ['CRM_Contact_DAO_RelationshipType', $this->_id, 'label_b_a'] ); $contactTypes = CRM_Contact_BAO_ContactType::getSelectElements(FALSE, TRUE, '__'); - - // add select for contact type - $contactTypeA = &$this->add('select', 'contact_types_a', ts('Contact Type A') . ' ', - array( - '' => ts('All Contacts'), - ) + $contactTypes - ); - $contactTypeB = &$this->add('select', 'contact_types_b', ts('Contact Type B') . ' ', - array( - '' => ts('All Contacts'), - ) + $contactTypes - ); - - $isActive = &$this->add('checkbox', 'is_active', ts('Enabled?')); - - //only selected field should be allow for edit, CRM-4888 - if ($this->_id && - CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_RelationshipType', $this->_id, 'is_reserved') - ) { - foreach (array('contactTypeA', 'contactTypeB', 'isActive') as $field) { - $$field->freeze(); + foreach (['contact_types_a' => ts('Contact Type A'), 'contact_types_b' => ts('Contact Type B')] as $name => $label) { + $element = $this->add('select', $name, $label . ' ', + [ + '' => ts('All Contacts'), + ] + $contactTypes + ); + if ($isReserved) { + $element->freeze(); } } @@ -97,8 +141,6 @@ public function buildQuickForm() { $this->freeze(); } - $this->assign('relationship_type_id', $this->_id); - } /** @@ -108,8 +150,8 @@ public function setDefaultValues() { if ($this->_action != CRM_Core_Action::DELETE && isset($this->_id) ) { - $defaults = $params = array(); - $params = array('id' => $this->_id); + $defaults = $params = []; + $params = ['id' => $this->_id]; $baoName = $this->_BAOName; $baoName::retrieve($params, $defaults); $defaults['contact_types_a'] = CRM_Utils_Array::value('contact_type_a', $defaults); @@ -117,7 +159,7 @@ public function setDefaultValues() { $defaults['contact_types_a'] .= '__' . $defaults['contact_sub_type_a']; } - $defaults['contact_types_b'] = $defaults['contact_type_b']; + $defaults['contact_types_b'] = CRM_Utils_Array::value('contact_type_b', $defaults); if (!empty($defaults['contact_sub_type_b'])) { $defaults['contact_types_b'] .= '__' . $defaults['contact_sub_type_b']; } @@ -137,15 +179,12 @@ public function postProcess() { CRM_Core_Session::setStatus(ts('Selected Relationship type has been deleted.'), ts('Record Deleted'), 'success'); } else { - $params = array(); - $ids = array(); - // store the submitted values in an array $params = $this->exportValues(); $params['is_active'] = CRM_Utils_Array::value('is_active', $params, FALSE); if ($this->_action & CRM_Core_Action::UPDATE) { - $ids['relationshipType'] = $this->_id; + $params['id'] = $this->_id; } $cTypeA = CRM_Utils_System::explode('__', @@ -160,10 +199,22 @@ public function postProcess() { $params['contact_type_a'] = $cTypeA[0]; $params['contact_type_b'] = $cTypeB[0]; - $params['contact_sub_type_a'] = $cTypeA[1] ? $cTypeA[1] : 'NULL'; - $params['contact_sub_type_b'] = $cTypeB[1] ? $cTypeB[1] : 'NULL'; + $params['contact_sub_type_a'] = $cTypeA[1] ? $cTypeA[1] : 'null'; + $params['contact_sub_type_b'] = $cTypeB[1] ? $cTypeB[1] : 'null'; + + if (!strlen(trim(CRM_Utils_Array::value('label_b_a', $params)))) { + $params['label_b_a'] = CRM_Utils_Array::value('label_a_b', $params); + } + + if (empty($params['id'])) { + // Set name on created but don't update on update as the machine name is not exposed. + $params['name_b_a'] = CRM_Utils_String::munge($params['label_b_a']); + $params['name_a_b'] = CRM_Utils_String::munge($params['label_a_b']); + } + + $result = civicrm_api3('RelationshipType', 'create', $params); - CRM_Contact_BAO_RelationshipType::add($params, $ids); + $this->ajaxResponse['relationshipType'] = $result['values']; CRM_Core_Session::setStatus(ts('The Relationship Type has been saved.'), ts('Saved'), 'success'); } diff --git a/CRM/Admin/Form/ScheduleReminders.php b/CRM/Admin/Form/ScheduleReminders.php index 9df937354d1e..38e425bfd5f0 100644 --- a/CRM/Admin/Form/ScheduleReminders.php +++ b/CRM/Admin/Form/ScheduleReminders.php @@ -1,9 +1,9 @@ _compId; + } + + /** + * @param mixed $compId + */ + public function setComponentID($compId) { + $this->_compId = $compId; + } + /** * Build the form object. */ @@ -50,56 +67,48 @@ public function buildQuickForm() { parent::buildQuickForm(); $this->_mappingID = $mappingID = NULL; $providersCount = CRM_SMS_BAO_Provider::activeProviderCount(); - $this->_context = CRM_Utils_Request::retrieve('context', 'String', $this); + $this->setContext(); + $isEvent = $this->getContext() == 'event'; - //CRM-16777: Don't provide access to administer schedule reminder page, with user that does not have 'administer CiviCRM' permission - if (empty($this->_context) && !CRM_Core_Permission::check('administer CiviCRM')) { - CRM_Core_Error::fatal(ts('You do not have permission to access this page.')); - } - //CRM-16777: When user have ACLs 'edit' permission for specific event, do not give access to add, delete & updtae - //schedule reminder for other events. - else { - $this->_compId = CRM_Utils_Request::retrieve('compId', 'Integer', $this); - if (!CRM_Event_BAO_Event::checkPermission($this->_compId, CRM_Core_Permission::EDIT)) { - CRM_Core_Error::fatal(ts('You do not have permission to access this page.')); + if ($isEvent) { + $this->setComponentID(CRM_Utils_Request::retrieve('compId', 'Integer', $this)); + if (!CRM_Event_BAO_Event::checkPermission($this->getComponentID(), CRM_Core_Permission::EDIT)) { + throw new CRM_Core_Exception(ts('You do not have permission to access this page.')); } } + elseif (!CRM_Core_Permission::check('administer CiviCRM')) { + throw new CRM_Core_Exception(ts('You do not have permission to access this page.')); + } if ($this->_action & (CRM_Core_Action::DELETE)) { $reminderName = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_ActionSchedule', $this->_id, 'title'); - if ($this->_context == 'event') { - $this->_compId = CRM_Utils_Request::retrieve('compId', 'Integer', $this); - } $this->assign('reminderName', $reminderName); return; } elseif ($this->_action & (CRM_Core_Action::UPDATE)) { $this->_mappingID = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_ActionSchedule', $this->_id, 'mapping_id'); - if ($this->_context == 'event') { - $this->_compId = CRM_Utils_Request::retrieve('compId', 'Integer', $this); - } } - elseif (!empty($this->_context)) { - if ($this->_context == 'event') { - $this->_compId = CRM_Utils_Request::retrieve('compId', 'Integer', $this); - $isTemplate = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_Event', $this->_compId, 'is_template'); - $mapping = CRM_Utils_Array::first(CRM_Core_BAO_ActionSchedule::getMappings(array( - 'id' => $isTemplate ? CRM_Event_ActionMapping::EVENT_TPL_MAPPING_ID : CRM_Event_ActionMapping::EVENT_NAME_MAPPING_ID, - ))); - if ($mapping) { - $this->_mappingID = $mapping->getId(); - } - else { - CRM_Core_Error::fatal('Could not find mapping for event scheduled reminders.'); - } + if ($isEvent) { + $isTemplate = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_Event', $this->getComponentID(), 'is_template'); + $mapping = CRM_Utils_Array::first(CRM_Core_BAO_ActionSchedule::getMappings([ + 'id' => $isTemplate ? CRM_Event_ActionMapping::EVENT_TPL_MAPPING_ID : CRM_Event_ActionMapping::EVENT_NAME_MAPPING_ID, + ])); + if ($mapping) { + $this->_mappingID = $mapping->getId(); + } + else { + throw new CRM_Core_Exception('Could not find mapping for event scheduled reminders.'); } } - if (!empty($_POST) && !empty($_POST['entity']) && empty($this->_context)) { + if (!empty($_POST) && !empty($_POST['entity']) && empty($this->getContext())) { $mappingID = $_POST['entity'][0]; } elseif ($this->_mappingID) { $mappingID = $this->_mappingID; + if ($isEvent) { + $this->add('hidden', 'mappingID', $mappingID); + } } $this->add( @@ -120,21 +129,21 @@ public function buildQuickForm() { array_combine(array_keys($entityRecipientLabels), array_keys($entityRecipientLabels)) )); - if (empty($this->_context)) { + if (!$this->getContext()) { $sel = &$this->add( 'hierselect', 'entity', ts('Entity'), - array( + [ 'name' => 'entity[0]', 'style' => 'vertical-align: top;', - ) + ] ); - $sel->setOptions(array( + $sel->setOptions([ CRM_Utils_Array::collectMethod('getLabel', $mappings), CRM_Core_BAO_ActionSchedule::getAllEntityValueLabels(), CRM_Core_BAO_ActionSchedule::getAllEntityStatusLabels(), - )); + ]); if (is_a($sel->_elements[1], 'HTML_QuickForm_select')) { // make second selector a multi-select - @@ -152,31 +161,30 @@ public function buildQuickForm() { // Dig deeper - this code is sublimely stupid. $allEntityStatusLabels = CRM_Core_BAO_ActionSchedule::getAllEntityStatusLabels(); $options = $allEntityStatusLabels[$this->_mappingID][0]; - $attributes = array('multiple' => 'multiple', 'class' => 'crm-select2 huge', 'placeholder' => $options[0]); + $attributes = ['multiple' => 'multiple', 'class' => 'crm-select2 huge', 'placeholder' => $options[0]]; unset($options[0]); $this->add('select', 'entity', ts('Recipient(s)'), $options, TRUE, $attributes); - $this->assign('context', $this->_context); + $this->assign('context', $this->getContext()); } //get the frequency units. $this->_freqUnits = CRM_Core_SelectValues::getRecurringFrequencyUnits(); - $numericOptions = CRM_Core_SelectValues::getNumericOptions(0, 30); - //reminder_interval - $this->add('select', 'start_action_offset', ts('When'), $numericOptions); - $isActive = ts('Send email'); + $this->add('number', 'start_action_offset', ts('When'), ['class' => 'six', 'min' => 0]); + $this->addRule('start_action_offset', ts('Value should be a positive number'), 'positiveInteger'); + + $isActive = ts('Scheduled Reminder Active'); $recordActivity = ts('Record activity for automated email'); if ($providersCount) { $this->assign('sms', $providersCount); - $isActive = ts('Send email or SMS'); $recordActivity = ts('Record activity for automated email or SMS'); $options = CRM_Core_OptionGroup::values('msg_mode'); $this->add('select', 'mode', ts('Send as'), $options); $providers = CRM_SMS_BAO_Provider::getProviders(NULL, NULL, TRUE, 'is_default desc'); - $providerSelect = array(); + $providerSelect = []; foreach ($providers as $provider) { $providerSelect[$provider['id']] = $provider['title']; } @@ -184,18 +192,18 @@ public function buildQuickForm() { } foreach ($this->_freqUnits as $val => $label) { - $freqUnitsDisplay[$val] = ts('%1(s)', array(1 => $label)); + $freqUnitsDisplay[$val] = ts('%1(s)', [1 => $label]); } - $this->addDate('absolute_date', ts('Start Date'), FALSE, array('formatType' => 'mailing')); + $this->add('datepicker', 'absolute_date', ts('Start Date'), [], FALSE, ['time' => FALSE]); //reminder_frequency $this->add('select', 'start_action_unit', ts('Frequency'), $freqUnitsDisplay, TRUE); - $condition = array( + $condition = [ 'before' => ts('before'), 'after' => ts('after'), - ); + ]; //reminder_action $this->add('select', 'start_action_condition', ts('Action Condition'), $condition); @@ -204,40 +212,44 @@ public function buildQuickForm() { $this->addElement('checkbox', 'record_activity', $recordActivity); $this->addElement('checkbox', 'is_repeat', ts('Repeat'), - NULL, array('onchange' => "return showHideByValue('is_repeat',true,'repeatFields','table-row','radio',false);") + NULL, ['onchange' => "return showHideByValue('is_repeat',true,'repeatFields','table-row','radio',false);"] ); $this->add('select', 'repetition_frequency_unit', ts('every'), $freqUnitsDisplay); - $this->add('select', 'repetition_frequency_interval', ts('every'), $numericOptions); + $this->add('number', 'repetition_frequency_interval', ts('every'), ['class' => 'six', 'min' => 0]); + $this->addRule('repetition_frequency_interval', ts('Value should be a positive number'), 'positiveInteger'); + $this->add('select', 'end_frequency_unit', ts('until'), $freqUnitsDisplay); - $this->add('select', 'end_frequency_interval', ts('until'), $numericOptions); + $this->add('number', 'end_frequency_interval', ts('until'), ['class' => 'six', 'min' => 0]); + $this->addRule('end_frequency_interval', ts('Value should be a positive number'), 'positiveInteger'); + $this->add('select', 'end_action', ts('Repetition Condition'), $condition, TRUE); $this->add('select', 'end_date', ts('Date Field'), $selectedMapping->getDateFields(), TRUE); $this->add('text', 'from_name', ts('From Name')); $this->add('text', 'from_email', ts('From Email')); - $recipientListingOptions = array(); + $recipientListingOptions = []; if ($mappingID) { - $mapping = CRM_Utils_Array::first(CRM_Core_BAO_ActionSchedule::getMappings(array( + $mapping = CRM_Utils_Array::first(CRM_Core_BAO_ActionSchedule::getMappings([ 'id' => $mappingID, - ))); + ])); } - $limitOptions = array('' => '-neither-', 1 => ts('Limit to'), 0 => ts('Also include')); + $limitOptions = ['' => '-neither-', 1 => ts('Limit to'), 0 => ts('Also include')]; - $recipientLabels = array('activity' => ts('Recipients'), 'other' => ts('Limit or Add Recipients')); + $recipientLabels = ['activity' => ts('Recipients'), 'other' => ts('Limit or Add Recipients')]; $this->assign('recipientLabels', $recipientLabels); - $this->add('select', 'limit_to', ts('Limit Options'), $limitOptions, FALSE, array('onChange' => "showHideByValue('limit_to','','recipient', 'select','select',true);")); + $this->add('select', 'limit_to', ts('Limit Options'), $limitOptions, FALSE, ['onChange' => "showHideByValue('limit_to','','recipient', 'select','select',true);"]); $this->add('select', 'recipient', $recipientLabels['other'], $entityRecipientLabels, - FALSE, array('onchange' => "showHideByValue('recipient','manual','recipientManual','table-row','select',false); showHideByValue('recipient','group','recipientGroup','table-row','select',false);") + FALSE, ['onchange' => "showHideByValue('recipient','manual','recipientManual','table-row','select',false); showHideByValue('recipient','group','recipientGroup','table-row','select',false);"] ); if (!empty($this->_submitValues['recipient_listing'])) { - if (!empty($this->_context)) { + if ($this->getContext()) { $recipientListingOptions = CRM_Core_BAO_ActionSchedule::getRecipientListing($this->_mappingID, $this->_submitValues['recipient']); } else { @@ -249,12 +261,12 @@ public function buildQuickForm() { } $this->add('select', 'recipient_listing', ts('Recipient Roles'), $recipientListingOptions, FALSE, - array('multiple' => TRUE, 'class' => 'crm-select2 huge', 'placeholder' => TRUE)); + ['multiple' => TRUE, 'class' => 'crm-select2 huge', 'placeholder' => TRUE]); - $this->addEntityRef('recipient_manual_id', ts('Manual Recipients'), array('multiple' => TRUE, 'create' => TRUE)); + $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, array('class' => 'crm-select2 huge') + CRM_Core_PseudoConstant::nestedGroup('Mailing'), FALSE, ['class' => 'crm-select2 huge'] ); // multilingual only options @@ -264,14 +276,14 @@ public function buildQuickForm() { $smarty->assign('multilingual', $multilingual); $languages = CRM_Core_I18n::languages(TRUE); - $languageFilter = $languages + array(CRM_Core_I18n::NONE => ts('Contacts with no preferred language')); + $languageFilter = $languages + [CRM_Core_I18n::NONE => ts('Contacts with no preferred language')]; $element = $this->add('select', 'filter_contact_language', ts('Recipients language'), $languageFilter, FALSE, - array('multiple' => TRUE, 'class' => 'crm-select2', 'placeholder' => TRUE)); + ['multiple' => TRUE, 'class' => 'crm-select2', 'placeholder' => TRUE]); - $communicationLanguage = array( + $communicationLanguage = [ '' => ts('System default language'), CRM_Core_I18n::AUTO => ts('Follow recipient preferred language'), - ); + ]; $communicationLanguage = $communicationLanguage + $languages; $this->add('select', 'communication_language', ts('Communication language'), $communicationLanguage); } @@ -284,7 +296,7 @@ public function buildQuickForm() { $this->add('checkbox', 'is_active', $isActive); - $this->addFormRule(array('CRM_Admin_Form_ScheduleReminders', 'formRule'), $this); + $this->addFormRule(['CRM_Admin_Form_ScheduleReminders', 'formRule'], $this); $this->setPageTitle(ts('Scheduled Reminder')); } @@ -301,52 +313,73 @@ public function buildQuickForm() { * True if no errors, else array of errors */ public static function formRule($fields, $files, $self) { - $errors = array(); + $errors = []; if ((array_key_exists(1, $fields['entity']) && $fields['entity'][1][0] === 0) || (array_key_exists(2, $fields['entity']) && $fields['entity'][2][0] == 0) ) { $errors['entity'] = ts('Please select appropriate value'); } + $mode = CRM_Utils_Array::value('mode', $fields, FALSE); if (!empty($fields['is_active']) && - CRM_Utils_System::isNull($fields['subject']) + CRM_Utils_System::isNull($fields['subject']) && (!$mode || $mode != 'SMS') ) { $errors['subject'] = ts('Subject is a required field.'); } if (!empty($fields['is_active']) && - CRM_Utils_System::isNull(trim(strip_tags($fields['html_message']))) + CRM_Utils_System::isNull(trim(strip_tags($fields['html_message']))) && (!$mode || $mode != 'SMS') ) { $errors['html_message'] = ts('The HTML message is a required field.'); } - if (empty($self->_context) && CRM_Utils_System::isNull(CRM_Utils_Array::value(1, $fields['entity']))) { + if (!empty($mode) && ($mode == 'SMS' || $mode == 'User_Preference') && !empty($fields['is_active']) && + CRM_Utils_System::isNull(trim(strip_tags($fields['sms_text_message']))) + ) { + $errors['sms_text_message'] = ts('The SMS message is a required field.'); + } + + if (empty($self->getContext()) && CRM_Utils_System::isNull(CRM_Utils_Array::value(1, $fields['entity']))) { $errors['entity'] = ts('Please select entity value'); } if (!CRM_Utils_System::isNull($fields['absolute_date'])) { - if (CRM_Utils_Date::format(CRM_Utils_Date::processDate($fields['absolute_date'], NULL)) < CRM_Utils_Date::format(date('Ymd'))) { + if ($fields['absolute_date'] < date('Y-m-d')) { $errors['absolute_date'] = ts('Absolute date cannot be earlier than the current time.'); } } - - $recipientKind = array( - 'participant_role' => array( + else { + if (CRM_Utils_System::isNull($fields['start_action_offset'])) { + $errors['start_action_offset'] = ts('Start Action Offset must be filled in or Absolute Date set'); + } + } + if (!CRM_Utils_Rule::email($fields['from_email']) && (!$mode || $mode != 'SMS')) { + $errors['from_email'] = ts('Please enter a valid email address.'); + } + $recipientKind = [ + 'participant_role' => [ 'name' => 'participant role', 'target_id' => 'recipient_listing', - ), - 'manual' => array( + ], + 'manual' => [ 'name' => 'recipient', 'target_id' => 'recipient_manual_id', - ), - ); + ], + ]; if ($fields['limit_to'] != '' && array_key_exists($fields['recipient'], $recipientKind) && empty($fields[$recipientKind[$fields['recipient']]['target_id']])) { - $errors[$recipientKind[$fields['recipient']]['target_id']] = ts('If "Also include" or "Limit to" are selected, you must specify at least one %1', array(1 => $recipientKind[$fields['recipient']]['name'])); + $errors[$recipientKind[$fields['recipient']]['target_id']] = ts('If "Also include" or "Limit to" are selected, you must specify at least one %1', [1 => $recipientKind[$fields['recipient']]['name']]); + } + + //CRM-21523 + if (!empty($fields['is_repeat']) && + (empty($fields['repetition_frequency_interval']) || ($fields['end_frequency_interval'] == NULL)) + ) { + $errors['is_repeat'] = ts('If you are enabling repetition you must indicate the frequency and ending term.'); } - $actionSchedule = $self->parseActionSchedule($fields); - if ($actionSchedule->mapping_id) { - $mapping = CRM_Core_BAO_ActionSchedule::getMapping($actionSchedule->mapping_id); - CRM_Utils_Array::extend($errors, $mapping->validateSchedule($actionSchedule)); + $self->_actionSchedule = $self->parseActionSchedule($fields); + if ($self->_actionSchedule->mapping_id) { + $mapping = CRM_Core_BAO_ActionSchedule::getMapping($self->_actionSchedule->mapping_id); + CRM_Utils_Array::extend($errors, $mapping->validateSchedule($self->_actionSchedule)); } if (!empty($errors)) { @@ -369,7 +402,7 @@ public function setDefaultValues() { $defaults = $this->_values; $entityValue = explode(CRM_Core_DAO::VALUE_SEPARATOR, CRM_Utils_Array::value('entity_value', $defaults)); $entityStatus = explode(CRM_Core_DAO::VALUE_SEPARATOR, CRM_Utils_Array::value('entity_status', $defaults)); - if (empty($this->_context)) { + if (empty($this->getContext())) { $defaults['entity'][0] = CRM_Utils_Array::value('mapping_id', $defaults); $defaults['entity'][1] = $entityValue; $defaults['entity'][2] = $entityStatus; @@ -377,10 +410,6 @@ public function setDefaultValues() { else { $defaults['entity'] = $entityStatus; } - if ($absoluteDate = CRM_Utils_Array::value('absolute_date', $defaults)) { - list($date, $time) = CRM_Utils_Date::setDateDefaults($absoluteDate); - $defaults['absolute_date'] = $date; - } if ($recipientListing = CRM_Utils_Array::value('recipient_listing', $defaults)) { $defaults['recipient_listing'] = explode(CRM_Core_DAO::VALUE_SEPARATOR, @@ -415,9 +444,9 @@ public function postProcess() { // delete reminder CRM_Core_BAO_ActionSchedule::del($this->_id); CRM_Core_Session::setStatus(ts('Selected Reminder has been deleted.'), ts('Record Deleted'), 'success'); - if ($this->_context == 'event' && $this->_compId) { + if ($this->getContext() == 'event' && $this->getComponentID()) { $url = CRM_Utils_System::url('civicrm/event/manage/reminder', - "reset=1&action=browse&id={$this->_compId}&component={$this->_context}&setTab=1" + "reset=1&action=browse&id=" . $this->getComponentID() . "&component=" . $this->getContext() . "&setTab=1" ); $session = CRM_Core_Session::singleton(); $session->pushUserContext($url); @@ -425,25 +454,30 @@ public function postProcess() { return; } $values = $this->controller->exportValues($this->getName()); - $bao = $this->parseActionSchedule($values)->save(); + if (empty($this->_actionSchedule)) { + $bao = $this->parseActionSchedule($values)->save(); + } + else { + $bao = $this->_actionSchedule->save(); + } // we need to set this on the form so that hooks can identify the created entity $this->set('id', $bao->id); $bao->free(); $status = ts("Your new Reminder titled %1 has been saved.", - array(1 => "{$values['title']}") + [1 => "{$values['title']}"] ); if ($this->_action) { if ($this->_action & CRM_Core_Action::UPDATE) { $status = ts("Your Reminder titled %1 has been updated.", - array(1 => "{$values['title']}") + [1 => "{$values['title']}"] ); } - if ($this->_context == 'event' && $this->_compId) { - $url = CRM_Utils_System::url('civicrm/event/manage/reminder', "reset=1&action=browse&id={$this->_compId}&component={$this->_context}&setTab=1"); + if ($this->getContext() == 'event' && $this->getComponentID()) { + $url = CRM_Utils_System::url('civicrm/event/manage/reminder', "reset=1&action=browse&id=" . $this->getComponentID() . "&component=" . $this->getContext() . "&setTab=1"); $session = CRM_Core_Session::singleton(); $session->pushUserContext($url); } @@ -457,9 +491,9 @@ public function postProcess() { * @return CRM_Core_DAO_ActionSchedule */ public function parseActionSchedule($values) { - $params = array(); + $params = []; - $keys = array( + $keys = [ 'title', 'subject', 'absolute_date', @@ -470,14 +504,14 @@ public function parseActionSchedule($values) { 'sms_provider_id', 'from_name', 'from_email', - ); + ]; foreach ($keys as $key) { $params[$key] = CRM_Utils_Array::value($key, $values); } $params['is_repeat'] = CRM_Utils_Array::value('is_repeat', $values, 0); - $moreKeys = array( + $moreKeys = [ 'start_action_offset', 'start_action_unit', 'start_action_condition', @@ -488,20 +522,17 @@ public function parseActionSchedule($values) { 'end_frequency_interval', 'end_action', 'end_date', - ); + ]; - if ($absoluteDate = CRM_Utils_Array::value('absolute_date', $params)) { - $params['absolute_date'] = CRM_Utils_Date::processDate($absoluteDate); - $params['is_repeat'] = 0; - foreach ($moreKeys as $mkey) { - $params[$mkey] = 'null'; - } - } - else { + if (!CRM_Utils_Array::value('absolute_date', $params)) { $params['absolute_date'] = 'null'; - foreach ($moreKeys as $mkey) { - $params[$mkey] = CRM_Utils_Array::value($mkey, $values); + } + foreach ($moreKeys as $mkey) { + if ($params['absolute_date'] != 'null' && CRM_Utils_String::startsWith($mkey, 'start_action')) { + $params[$mkey] = 'null'; + continue; } + $params[$mkey] = CRM_Utils_Array::value($mkey, $values); } $params['body_text'] = CRM_Utils_Array::value('text_message', $values); @@ -528,9 +559,9 @@ public function parseActionSchedule($values) { $params['group_id'] = $params['recipient_manual'] = $params['recipient_listing'] = 'null'; } - if (!empty($this->_mappingID) && !empty($this->_compId)) { + if (!empty($this->_mappingID) && !empty($this->getComponentID())) { $params['mapping_id'] = $this->_mappingID; - $params['entity_value'] = $this->_compId; + $params['entity_value'] = $this->getComponentID(); $params['entity_status'] = implode(CRM_Core_DAO::VALUE_SEPARATOR, $values['entity']); } else { @@ -539,8 +570,8 @@ public function parseActionSchedule($values) { $params['limit_to'] = 1; } - $entity_value = CRM_Utils_Array::value(1, $values['entity'], array()); - $entity_status = CRM_Utils_Array::value(2, $values['entity'], array()); + $entity_value = CRM_Utils_Array::value(1, $values['entity'], []); + $entity_status = CRM_Utils_Array::value(2, $values['entity'], []); $params['entity_value'] = implode(CRM_Core_DAO::VALUE_SEPARATOR, $entity_value); $params['entity_status'] = implode(CRM_Core_DAO::VALUE_SEPARATOR, $entity_status); } @@ -557,7 +588,7 @@ public function parseActionSchedule($values) { } // multilingual options - $params['filter_contact_language'] = CRM_Utils_Array::value('filter_contact_language', $values, array()); + $params['filter_contact_language'] = CRM_Utils_Array::value('filter_contact_language', $values, []); $params['filter_contact_language'] = implode(CRM_Core_DAO::VALUE_SEPARATOR, $params['filter_contact_language']); $params['communication_language'] = CRM_Utils_Array::value('communication_language', $values, NULL); @@ -569,7 +600,7 @@ public function parseActionSchedule($values) { $params['name'] = CRM_Utils_String::munge($params['title'], '_', 64); } - $modePrefixes = array('Mail' => NULL, 'SMS' => 'SMS'); + $modePrefixes = ['Mail' => NULL, 'SMS' => 'SMS']; if ($params['mode'] == 'Email' || empty($params['sms_provider_id'])) { unset($modePrefixes['SMS']); @@ -580,17 +611,17 @@ public function parseActionSchedule($values) { //TODO: handle postprocessing of SMS and/or Email info based on $modePrefixes - $composeFields = array( + $composeFields = [ 'template', 'saveTemplate', 'updateTemplate', 'saveTemplateName', - ); + ]; $msgTemplate = NULL; //mail template is composed foreach ($modePrefixes as $prefix) { - $composeParams = array(); + $composeParams = []; foreach ($composeFields as $key) { $key = $prefix . $key; if (!empty($values[$key])) { @@ -599,19 +630,19 @@ public function parseActionSchedule($values) { } if (!empty($composeParams[$prefix . 'updateTemplate'])) { - $templateParams = array('is_active' => TRUE); + $templateParams = ['is_active' => TRUE]; if ($prefix == 'SMS') { - $templateParams += array( + $templateParams += [ 'msg_text' => $params['sms_body_text'], 'is_sms' => TRUE, - ); + ]; } else { - $templateParams += array( + $templateParams += [ 'msg_text' => $params['body_text'], 'msg_html' => $params['body_html'], 'msg_subject' => $params['subject'], - ); + ]; } $templateParams['id'] = $values[$prefix . 'template']; @@ -619,19 +650,19 @@ public function parseActionSchedule($values) { } if (!empty($composeParams[$prefix . 'saveTemplate'])) { - $templateParams = array('is_active' => TRUE); + $templateParams = ['is_active' => TRUE]; if ($prefix == 'SMS') { - $templateParams += array( + $templateParams += [ 'msg_text' => $params['sms_body_text'], 'is_sms' => TRUE, - ); + ]; } else { - $templateParams += array( + $templateParams += [ 'msg_text' => $params['body_text'], 'msg_html' => $params['body_html'], 'msg_subject' => $params['subject'], - ); + ]; } $templateParams['msg_title'] = $composeParams[$prefix . 'saveTemplateName']; diff --git a/CRM/Admin/Form/Setting.php b/CRM/Admin/Form/Setting.php index 7a6b91b36f09..e0be4241a38e 100644 --- a/CRM/Admin/Form/Setting.php +++ b/CRM/Admin/Form/Setting.php @@ -1,9 +1,9 @@ _defaults) { - $this->_defaults = array(); - $formArray = array('Component', 'Localization'); + $this->_defaults = []; + $formArray = ['Component', 'Localization']; $formMode = FALSE; if (in_array($this->_name, $formArray)) { $formMode = TRUE; } - CRM_Core_BAO_ConfigSetting::retrieve($this->_defaults); + $this->setDefaultsForMetadataDefinedFields(); - // we can handle all the ones defined in the metadata here. Others to be converted - foreach ($this->_settings as $setting => $group) { - $this->_defaults[$setting] = civicrm_api('setting', 'getvalue', array( - 'version' => 3, - 'name' => $setting, - 'group' => $group, - ) - ); - } - - $this->_defaults['contact_autocomplete_options'] = self::getAutocompleteContactSearch(); - $this->_defaults['contact_reference_options'] = self::getAutocompleteContactReference(); + // @todo these should be retrievable from the above function. $this->_defaults['enableSSL'] = Civi::settings()->get('enableSSL'); $this->_defaults['verifySSL'] = Civi::settings()->get('verifySSL'); + $this->_defaults['environment'] = CRM_Core_Config::environment(); $this->_defaults['enableComponents'] = Civi::settings()->get('enable_components'); } @@ -78,88 +72,24 @@ public function setDefaultValues() { * Build the form object. */ public function buildQuickForm() { - $session = CRM_Core_Session::singleton(); - $session->pushUserContext(CRM_Utils_System::url('civicrm/admin', 'reset=1')); - $args = func_get_args(); - $check = reset($args); - $this->addButtons(array( - array( - 'type' => 'next', - 'name' => ts('Save'), - 'isDefault' => TRUE, - ), - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - ), - ) - ); - - $descriptions = array(); - foreach ($this->_settings as $setting => $group) { - $settingMetaData = civicrm_api('setting', 'getfields', array('version' => 3, 'name' => $setting)); - $props = $settingMetaData['values'][$setting]; - if (isset($props['quick_form_type'])) { - if (isset($props['pseudoconstant'])) { - $options = civicrm_api3('Setting', 'getoptions', array( - 'field' => $setting, - )); - } - else { - $options = NULL; - } - - $add = 'add' . $props['quick_form_type']; - if ($add == 'addElement') { - $this->$add( - $props['html_type'], - $setting, - ts($props['title']), - ($options !== NULL) ? $options['values'] : CRM_Utils_Array::value('html_attributes', $props, array()), - ($options !== NULL) ? CRM_Utils_Array::value('html_attributes', $props, array()) : NULL - ); - } - elseif ($add == 'addSelect') { - $this->addElement('select', $setting, ts($props['title']), $options['values'], CRM_Utils_Array::value('html_attributes', $props)); - } - elseif ($add == 'addCheckBox') { - $this->addCheckBox($setting, ts($props['title']), $options['values'], NULL, CRM_Utils_Array::value('html_attributes', $props), NULL, NULL, array('  ')); - } - elseif ($add == 'addChainSelect') { - $this->addChainSelect($setting, array( - 'label' => ts($props['title']), - )); - } - elseif ($add == 'addMonthDay') { - $this->add('date', $setting, ts($props['title']), CRM_Core_SelectValues::date(NULL, 'M d')); - } - else { - $this->$add($setting, ts($props['title'])); - } - // Migrate to using an array as easier in smart... - $descriptions[$setting] = ts($props['description']); - $this->assign("{$setting}_description", ts($props['description'])); - if ($setting == 'max_attachments') { - //temp hack @todo fix to get from metadata - $this->addRule('max_attachments', ts('Value should be a positive number'), 'positiveInteger'); - } - if ($setting == 'maxFileSize') { - //temp hack - $this->addRule('maxFileSize', ts('Value should be a positive number'), 'positiveInteger'); - } - - } + CRM_Core_Session::singleton()->pushUserContext(CRM_Utils_System::url('civicrm/admin', 'reset=1')); + $this->addButtons([ + [ + 'type' => 'next', + 'name' => ts('Save'), + 'isDefault' => TRUE, + ], + [ + 'type' => 'cancel', + 'name' => ts('Cancel'), + ], + ]); + + $this->addFieldsDefinedInSettingsMetadata(); + + if ($this->includesReadOnlyFields) { + CRM_Core_Session::setStatus(ts("Some fields are loaded as 'readonly' as they have been set (overridden) in civicrm.settings.php."), '', 'info', ['expires' => 0]); } - $this->assign('setting_descriptions', $descriptions); - } - - /** - * Get default entity. - * - * @return string - */ - public function getDefaultEntity() { - return 'Setting'; } /** @@ -178,59 +108,42 @@ public function postProcess() { * @todo Document what I do. * * @param array $params + * @throws \CRM_Core_Exception */ public function commonProcess(&$params) { - // save autocomplete search options - if (!empty($params['contact_autocomplete_options'])) { - Civi::settings()->set('contact_autocomplete_options', - CRM_Utils_Array::implodePadded(array_keys($params['contact_autocomplete_options']))); - unset($params['contact_autocomplete_options']); - } - - // save autocomplete contact reference options - if (!empty($params['contact_reference_options'])) { - Civi::settings()->set('contact_reference_options', - CRM_Utils_Array::implodePadded(array_keys($params['contact_reference_options']))); - unset($params['contact_reference_options']); - } - // save components to be enabled if (array_key_exists('enableComponents', $params)) { - civicrm_api3('setting', 'create', array( + civicrm_api3('setting', 'create', [ 'enable_components' => $params['enableComponents'], - )); + ]); unset($params['enableComponents']); } - // verify ssl peer option - if (isset($params['verifySSL'])) { - Civi::settings()->set('verifySSL', $params['verifySSL']); - unset($params['verifySSL']); - } - - // force secure URLs - if (isset($params['enableSSL'])) { - Civi::settings()->set('enableSSL', $params['enableSSL']); - unset($params['enableSSL']); + foreach (['verifySSL', 'enableSSL'] as $name) { + if (isset($params[$name])) { + Civi::settings()->set($name, $params[$name]); + unset($params[$name]); + } } - $settings = array_intersect_key($params, $this->_settings); - $result = civicrm_api('setting', 'create', $settings + array('version' => 3)); - foreach ($settings as $setting => $settingGroup) { - //@todo array_diff this - unset($params[$setting]); + try { + $settings = $this->getSettingsToSetByMetadata($params); + $this->saveMetadataDefinedSettings($params); } - if (!empty($result['error_message'])) { - CRM_Core_Session::setStatus($result['error_message'], ts('Save Failed'), 'error'); + catch (CiviCRM_API3_Exception $e) { + CRM_Core_Session::setStatus($e->getMessage(), ts('Save Failed'), 'error'); } - //CRM_Core_BAO_ConfigSetting::create($params); + $this->filterParamsSetByMetadata($params); + $params = CRM_Core_BAO_ConfigSetting::filterSkipVars($params); if (!empty($params)) { - CRM_Core_Error::fatal('Unrecognized setting. This may be a config field which has not been properly migrated to a setting. (' . implode(', ', array_keys($params)) . ')'); + throw new CRM_Core_Exception('Unrecognized setting. This may be a config field which has not been properly migrated to a setting. (' . implode(', ', array_keys($params)) . ')'); } CRM_Core_Config::clearDBCache(); + // This doesn't make a lot of sense to me, but it maintains pre-existing behavior. + Civi::cache('session')->clear(); CRM_Utils_System::flushCache(); CRM_Core_Resources::singleton()->resetCacheCode(); @@ -249,51 +162,4 @@ public function rebuildMenu() { @unlink($configFile); } - /** - * Ugh, this shouldn't exist. - * - * Get the selected values of "contact_reference_options" formatted for checkboxes. - * - * @return array - */ - public static function getAutocompleteContactReference() { - $cRlist = array_flip(CRM_Core_OptionGroup::values('contact_reference_options', - FALSE, FALSE, TRUE, NULL, 'name' - )); - $cRlistEnabled = CRM_Core_BAO_Setting::valueOptions(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, - 'contact_reference_options' - ); - $cRSearchFields = array(); - if (!empty($cRlist) && !empty($cRlistEnabled)) { - $cRSearchFields = array_combine($cRlist, $cRlistEnabled); - } - return array( - '1' => 1, - ) + $cRSearchFields; - } - - /** - * Ugh, this shouldn't exist. - * - * Get the selected values of "contact_autocomplete_options" formatted for checkboxes. - * - * @return array - */ - public static function getAutocompleteContactSearch() { - $list = array_flip(CRM_Core_OptionGroup::values('contact_autocomplete_options', - FALSE, FALSE, TRUE, NULL, 'name' - )); - $listEnabled = CRM_Core_BAO_Setting::valueOptions(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, - 'contact_autocomplete_options' - ); - $autoSearchFields = array(); - if (!empty($list) && !empty($listEnabled)) { - $autoSearchFields = array_combine($list, $listEnabled); - } - //Set defaults for autocomplete and contact reference options - return array( - '1' => 1, - ) + $autoSearchFields; - } - } diff --git a/CRM/Admin/Form/Setting/Case.php b/CRM/Admin/Form/Setting/Case.php new file mode 100644 index 000000000000..242c93adcdcd --- /dev/null +++ b/CRM/Admin/Form/Setting/Case.php @@ -0,0 +1,54 @@ + CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, + 'civicaseAllowMultipleClients' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, + 'civicaseNaturalActivityTypeSort' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, + 'civicaseActivityRevisions' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, + ]; + + /** + * Build the form object. + */ + public function buildQuickForm() { + CRM_Utils_System::setTitle(ts('Settings - CiviCase')); + parent::buildQuickForm(); + } + +} diff --git a/CRM/Admin/Form/Setting/Component.php b/CRM/Admin/Form/Setting/Component.php index 61df21517b41..f64cf081e761 100644 --- a/CRM/Admin/Form/Setting/Component.php +++ b/CRM/Admin/Form/Setting/Component.php @@ -1,9 +1,9 @@ _getComponentSelectValues(); $include = &$this->addElement('advmultiselect', 'enableComponents', ts('Components') . ' ', $components, - array( + [ 'size' => 5, 'style' => 'width:150px', 'class' => 'advmultiselect', - ) + ] ); - $include->setButtonAttributes('add', array('value' => ts('Enable >>'))); - $include->setButtonAttributes('remove', array('value' => ts('<< Disable'))); + $include->setButtonAttributes('add', ['value' => ts('Enable >>')]); + $include->setButtonAttributes('remove', ['value' => ts('<< Disable')]); - $this->addFormRule(array('CRM_Admin_Form_Setting_Component', 'formRule'), $this); + $this->addFormRule(['CRM_Admin_Form_Setting_Component', 'formRule'], $this); parent::buildQuickForm(); } @@ -74,7 +74,7 @@ public function buildQuickForm() { * true if no errors, else array of errors */ public static function formRule($fields, $files, $options) { - $errors = array(); + $errors = []; if (array_key_exists('enableComponents', $fields) && is_array($fields['enableComponents'])) { if (in_array('CiviPledge', $fields['enableComponents']) && @@ -96,7 +96,7 @@ public static function formRule($fields, $files, $options) { * @return array */ private function _getComponentSelectValues() { - $ret = array(); + $ret = []; $this->_components = CRM_Core_Component::getComponents(); foreach ($this->_components as $name => $object) { $ret[$name] = $object->info['translatedName']; @@ -115,7 +115,8 @@ public function postProcess() { } /** - * @param $dsn + * Load case sample data. + * * @param string $fileName * @param bool $lineMode */ diff --git a/CRM/Admin/Form/Setting/Date.php b/CRM/Admin/Form/Setting/Date.php index c99335a79cab..cd8d5976e60b 100644 --- a/CRM/Admin/Form/Setting/Date.php +++ b/CRM/Admin/Form/Setting/Date.php @@ -1,9 +1,9 @@ CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME, 'dateformatFull' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME, 'dateformatPartial' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME, 'dateformatYear' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME, 'dateformatTime' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME, 'dateformatFinancialBatch' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME, + 'dateformatshortdate' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME, 'weekBegins' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME, 'dateInputFormat' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME, 'timeInputFormat' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME, 'fiscalYearStart' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME, - ); + ]; /** * Build the form object. diff --git a/CRM/Admin/Form/Setting/Debugging.php b/CRM/Admin/Form/Setting/Debugging.php index 7c08c679b25c..133ed57a9dc6 100644 --- a/CRM/Admin/Form/Setting/Debugging.php +++ b/CRM/Admin/Form/Setting/Debugging.php @@ -1,9 +1,9 @@ CRM_Core_BAO_Setting::DEVELOPER_PREFERENCES_NAME, 'backtrace' => CRM_Core_BAO_Setting::DEVELOPER_PREFERENCES_NAME, 'fatalErrorHandler' => CRM_Core_BAO_Setting::DEVELOPER_PREFERENCES_NAME, - ); + 'assetCache' => CRM_Core_BAO_Setting::DEVELOPER_PREFERENCES_NAME, + 'environment' => CRM_Core_BAO_Setting::DEVELOPER_PREFERENCES_NAME, + ]; /** * Build the form object. @@ -52,6 +54,11 @@ public function buildQuickForm() { } parent::buildQuickForm(); + if (Civi::settings()->getMandatory('environment') !== NULL) { + $element = $this->getElement('environment'); + $element->freeze(); + CRM_Core_Session::setStatus(ts('The environment settings have been disabled because it has been overridden in the settings file.'), ts('Environment settings'), 'info'); + } } } diff --git a/CRM/Admin/Form/Setting/Localization.php b/CRM/Admin/Form/Setting/Localization.php index 5ad770b38ba9..2940f4136fa6 100644 --- a/CRM/Admin/Form/Setting/Localization.php +++ b/CRM/Admin/Form/Setting/Localization.php @@ -1,9 +1,9 @@ CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME, 'countryLimit' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME, 'customTranslateFunction' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME, @@ -52,7 +52,15 @@ class CRM_Admin_Form_Setting_Localization extends CRM_Admin_Form_Setting { 'moneyformat' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME, 'moneyvalueformat' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME, 'provinceLimit' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME, - ); + 'uiLanguages' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME, + ]; + + public function preProcess() { + if (!CRM_Core_I18n::isMultiLingual()) { + CRM_Core_Resources::singleton() + ->addScriptFile('civicrm', 'templates/CRM/Admin/Form/Setting/Localization.js', 1, 'html-header'); + } + } /** * Build the form object. @@ -65,20 +73,17 @@ public function buildQuickForm() { $warningTitle = json_encode(ts("Warning")); $defaultLocaleOptions = CRM_Admin_Form_Setting_Localization::getDefaultLocaleOptions(); - $domain = new CRM_Core_DAO_Domain(); - $domain->find(TRUE); - - if ($domain->locales) { + if (CRM_Core_I18n::isMultiLingual()) { // add language limiter and language adder $this->addCheckBox('languageLimit', ts('Available Languages'), array_flip($defaultLocaleOptions), NULL, NULL, NULL, NULL, '   '); - $this->addElement('select', 'addLanguage', ts('Add Language'), array_merge(array('' => ts('- select -')), array_diff(CRM_Core_I18n::languages(), $defaultLocaleOptions))); + $this->addElement('select', 'addLanguage', ts('Add Language'), array_merge(['' => ts('- select -')], array_diff(CRM_Core_I18n::languages(), $defaultLocaleOptions))); // add the ability to return to single language $warning = ts('This will make your CiviCRM installation a single-language one again. THIS WILL DELETE ALL DATA RELATED TO LANGUAGES OTHER THAN THE DEFAULT ONE SELECTED ABOVE (and only that language will be preserved).'); $this->assign('warning', $warning); $warning = json_encode($warning); $this->addElement('checkbox', 'makeSinglelingual', ts('Return to Single Language'), - NULL, array('onChange' => "if (this.checked) CRM.alert($warning, $warningTitle)") + NULL, ['onChange' => "if (this.checked) CRM.alert($warning, $warningTitle)"] ); } else { @@ -88,10 +93,10 @@ public function buildQuickForm() { $validTriggerPermission = CRM_Core_DAO::checkTriggerViewPermission(TRUE); if ($validTriggerPermission && - !$config->logging + !\Civi::settings()->get('logging') ) { $this->addElement('checkbox', 'makeMultilingual', ts('Enable Multiple Languages'), - NULL, array('onChange' => "if (this.checked) CRM.alert($warning, $warningTitle)") + NULL, ['onChange' => "if (this.checked) CRM.alert($warning, $warningTitle)"] ); } } @@ -100,17 +105,17 @@ public function buildQuickForm() { $includeCurrency = &$this->addElement('advmultiselect', 'currencyLimit', ts('Available Currencies') . ' ', self::getCurrencySymbols(), - array( + [ 'size' => 5, 'style' => 'width:150px', 'class' => 'advmultiselect', - ) + ] ); - $includeCurrency->setButtonAttributes('add', array('value' => ts('Add >>'))); - $includeCurrency->setButtonAttributes('remove', array('value' => ts('<< Remove'))); + $includeCurrency->setButtonAttributes('add', ['value' => ts('Add >>')]); + $includeCurrency->setButtonAttributes('remove', ['value' => ts('<< Remove')]); - $this->addFormRule(array('CRM_Admin_Form_Setting_Localization', 'formRule')); + $this->addFormRule(['CRM_Admin_Form_Setting_Localization', 'formRule']); parent::buildQuickForm(); } @@ -121,7 +126,7 @@ public function buildQuickForm() { * @return array|bool */ public static function formRule($fields) { - $errors = array(); + $errors = []; if (CRM_Utils_Array::value('monetaryThousandSeparator', $fields) == CRM_Utils_Array::value('monetaryDecimalPoint', $fields) ) { @@ -158,6 +163,11 @@ public static function formRule($fields) { return empty($errors) ? TRUE : $errors; } + /** + * Set the default values for the form. + * + * @return array + */ public function setDefaultValues() { parent::setDefaultValues(); @@ -186,40 +196,16 @@ public function postProcess() { // we do this only to initialize monetary decimal point and thousand separator $config = CRM_Core_Config::singleton(); - // save enabled currencies and defaul currency in option group 'currencies_enabled' + // save enabled currencies and default currency in option group 'currencies_enabled' // CRM-1496 if (empty($values['currencyLimit'])) { - $values['currencyLimit'] = array($values['defaultCurrency']); + $values['currencyLimit'] = [$values['defaultCurrency']]; } - elseif (!in_array($values['defaultCurrency'], - $values['currencyLimit'] - ) - ) { + elseif (!in_array($values['defaultCurrency'], $values['currencyLimit'])) { $values['currencyLimit'][] = $values['defaultCurrency']; } - // sort so that when we display drop down, weights have right value - sort($values['currencyLimit']); - - // get labels for all the currencies - $options = array(); - - $currencySymbols = self::getCurrencySymbols(); - for ($i = 0; $i < count($values['currencyLimit']); $i++) { - $options[] = array( - 'label' => $currencySymbols[$values['currencyLimit'][$i]], - 'value' => $values['currencyLimit'][$i], - 'weight' => $i + 1, - 'is_active' => 1, - 'is_default' => $values['currencyLimit'][$i] == $values['defaultCurrency'], - ); - } - - $dontCare = NULL; - CRM_Core_OptionGroup::createAssoc('currencies_enabled', - $options, - $dontCare - ); + self::updateEnabledCurrencies($values['currencyLimit'], $values['defaultCurrency']); // unset currencyLimit so we dont store there unset($values['currencyLimit']); @@ -245,6 +231,11 @@ public function postProcess() { $values['languageLimit'][$values['addLanguage']] = 1; } + // current language should be in the ui list + if (!in_array($values['lcMessages'], $values['uiLanguages'])) { + $values['uiLanguages'][] = $values['lcMessages']; + } + // if we manipulated the language list, return to the localization admin screen $return = (bool) (CRM_Utils_Array::value('makeMultilingual', $values) or CRM_Utils_Array::value('addLanguage', $values)); @@ -264,19 +255,51 @@ public function postProcess() { } } + /** + * Replace available currencies by the ones provided + * + * @param $currencies array of currencies ['USD', 'CAD'] + * @param $default default currency + */ + public static function updateEnabledCurrencies($currencies, $default) { + + // sort so that when we display drop down, weights have right value + sort($currencies); + + // get labels for all the currencies + $options = []; + + $currencySymbols = CRM_Admin_Form_Setting_Localization::getCurrencySymbols(); + for ($i = 0; $i < count($currencies); $i++) { + $options[] = [ + 'label' => $currencySymbols[$currencies[$i]], + 'value' => $currencies[$i], + 'weight' => $i + 1, + 'is_active' => 1, + 'is_default' => $currencies[$i] == $default, + ]; + } + + $dontCare = NULL; + CRM_Core_OptionGroup::createAssoc('currencies_enabled', $options, $dontCare); + + } + /** * @return array */ public static function getAvailableCountries() { $i18n = CRM_Core_I18n::singleton(); - $country = array(); + $country = []; CRM_Core_PseudoConstant::populate($country, 'CRM_Core_DAO_Country', TRUE, 'name', 'is_active'); - $i18n->localizeArray($country, array('context' => 'country')); + $i18n->localizeArray($country, ['context' => 'country']); asort($country); return $country; } /** + * Get the default locale options. + * * @return array */ public static function getDefaultLocaleOptions() { @@ -285,7 +308,7 @@ public static function getDefaultLocaleOptions() { $locales = CRM_Core_I18n::languages(); if ($domain->locales) { // for multi-lingual sites, populate default language drop-down with available languages - $defaultLocaleOptions = array(); + $defaultLocaleOptions = []; foreach ($locales as $loc => $lang) { if (substr_count($domain->locales, $loc)) { $defaultLocaleOptions[$loc] = $lang; @@ -305,11 +328,11 @@ public static function getDefaultLocaleOptions() { * Array('USD' => 'USD ($)'). */ public static function getCurrencySymbols() { - $symbols = CRM_Core_PseudoConstant::get('CRM_Contribute_DAO_Contribution', 'currency', array( + $symbols = CRM_Core_PseudoConstant::get('CRM_Contribute_DAO_Contribution', 'currency', [ 'labelColumn' => 'symbol', 'orderColumn' => TRUE, - )); - $_currencySymbols = array(); + ]); + $_currencySymbols = []; foreach ($symbols as $key => $value) { $_currencySymbols[$key] = "$key"; if ($value) { @@ -319,6 +342,14 @@ public static function getCurrencySymbols() { return $_currencySymbols; } + /** + * Update session and uf_match table when the locale is updated. + * + * @param string $oldLocale + * @param string $newLocale + * @param array $metadata + * @param int $domainID + */ public static function onChangeLcMessages($oldLocale, $newLocale, $metadata, $domainID) { if ($oldLocale == $newLocale) { return; @@ -336,15 +367,35 @@ public static function onChangeLcMessages($oldLocale, $newLocale, $metadata, $do } } + public static function onChangeDefaultCurrency($oldCurrency, $newCurrency, $metadata) { + if ($oldCurrency == $newCurrency) { + return; + } + + // ensure that default currency is always in the list of enabled currencies + $currencies = array_keys(CRM_Core_OptionGroup::values('currencies_enabled')); + if (!in_array($newCurrency, $currencies)) { + if (empty($currencies)) { + $currencies = [$values['defaultCurrency']]; + } + else { + $currencies[] = $newCurrency; + } + + CRM_Admin_Form_Setting_Localization::updateEnabledCurrencies($currencies, $newCurrency); + } + + } + /** * @return array */ public static function getDefaultLanguageOptions() { - return array( + return [ '*default*' => ts('Use default site language'), 'undefined' => ts('Leave undefined'), 'current_site_language' => ts('Use language in use at the time'), - ); + ]; } } diff --git a/CRM/Admin/Form/Setting/Mail.php b/CRM/Admin/Form/Setting/Mail.php index 3d04558b035d..bd75e95a8381 100644 --- a/CRM/Admin/Form/Setting/Mail.php +++ b/CRM/Admin/Form/Setting/Mail.php @@ -1,9 +1,9 @@ CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME, + protected $_settings = [ 'mailerBatchLimit' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME, + 'mailThrottleTime' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME, 'mailerJobSize' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME, 'mailerJobsMax' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME, - 'mailThrottleTime' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME, 'verpSeparator' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME, - ); + 'replyTo' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME, + ]; /** * Build the form object. */ public function buildQuickForm() { CRM_Utils_System::setTitle(ts('Settings - CiviMail')); - $check = TRUE; - - // redirect to Administer Section After hitting either Save or Cancel button. - $session = CRM_Core_Session::singleton(); - $session->pushUserContext(CRM_Utils_System::url('civicrm/admin', 'reset=1')); - - $this->addFormRule(array('CRM_Admin_Form_Setting_Mail', 'formRule')); + $this->addFormRule(['CRM_Admin_Form_Setting_Mail', 'formRule']); - parent::buildQuickForm($check); + parent::buildQuickForm(); } /** @@ -67,7 +61,7 @@ public function buildQuickForm() { * @return array|bool */ public static function formRule($fields) { - $errors = array(); + $errors = []; if (CRM_Utils_Array::value('mailerJobSize', $fields) > 0) { if (CRM_Utils_Array::value('mailerJobSize', $fields) < 1000) { diff --git a/CRM/Admin/Form/Setting/Mapping.php b/CRM/Admin/Form/Setting/Mapping.php index 8069ac283bc5..2d71f739df02 100644 --- a/CRM/Admin/Form/Setting/Mapping.php +++ b/CRM/Admin/Form/Setting/Mapping.php @@ -1,9 +1,9 @@ CRM_Core_BAO_Setting::MAP_PREFERENCES_NAME, 'mapProvider' => CRM_Core_BAO_Setting::MAP_PREFERENCES_NAME, 'geoAPIKey' => CRM_Core_BAO_Setting::MAP_PREFERENCES_NAME, 'geoProvider' => CRM_Core_BAO_Setting::MAP_PREFERENCES_NAME, - ); + ]; /** * Build the form object. @@ -61,7 +61,7 @@ public function buildQuickForm() { * true if no errors, else array of errors */ public static function formRule($fields) { - $errors = array(); + $errors = []; if (!CRM_Utils_System::checkPHPVersion(5, FALSE)) { $errors['_qf_default'] = ts('Mapping features require PHP version 5 or greater'); @@ -84,7 +84,7 @@ public static function formRule($fields) { * All local rules are added near the element */ public function addRules() { - $this->addFormRule(array('CRM_Admin_Form_Setting_Mapping', 'formRule')); + $this->addFormRule(['CRM_Admin_Form_Setting_Mapping', 'formRule']); } } diff --git a/CRM/Admin/Form/Setting/Miscellaneous.php b/CRM/Admin/Form/Setting/Miscellaneous.php index 6de75f826950..8b92a4b411f5 100644 --- a/CRM/Admin/Form/Setting/Miscellaneous.php +++ b/CRM/Admin/Form/Setting/Miscellaneous.php @@ -1,9 +1,9 @@ CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'contact_undelete' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'empoweredBy' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'logging' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'maxFileSize' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'doNotAttachPDFReceipt' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, + 'recordGeneratedLetters' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'secondDegRelPermissions' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'checksum_timeout' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'recaptchaOptions' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'recaptchaPublicKey' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'recaptchaPrivateKey' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, + 'forceRecaptcha' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'wkhtmltopdfPath' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'recentItemsMaxCount' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'recentItemsProviders' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'dedupe_default_limit' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'remote_profile_submissions' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, - ); + 'allow_alert_autodismissal' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, + 'prevNextBackend' => CRM_Core_BAO_Setting::SEARCH_PREFERENCES_NAME, + ]; public $_uploadMaxSize; @@ -66,7 +70,7 @@ public function preProcess() { CRM_Utils_Number::formatUnitSize(ini_get('post_max_size'), TRUE); // This is a temp hack for the fact we really don't need to hard-code each setting in the tpl but // we haven't worked through NOT doing that. These settings have been un-hardcoded. - $this->assign('pure_config_settings', array( + $this->assign('pure_config_settings', [ 'empoweredBy', 'max_attachments', 'maxFileSize', @@ -74,7 +78,8 @@ public function preProcess() { 'recentItemsMaxCount', 'recentItemsProviders', 'dedupe_default_limit', - )); + 'prevNextBackend', + ]); } /** @@ -85,7 +90,7 @@ public function buildQuickForm() { $this->assign('validTriggerPermission', CRM_Core_DAO::checkTriggerViewPermission(FALSE)); - $this->addFormRule(array('CRM_Admin_Form_Setting_Miscellaneous', 'formRule'), $this); + $this->addFormRule(['CRM_Admin_Form_Setting_Miscellaneous', 'formRule'], $this); parent::buildQuickForm(); $this->addRule('checksum_timeout', ts('Value should be a positive number'), 'positiveInteger'); @@ -105,7 +110,7 @@ public function buildQuickForm() { * true if no errors, else array of errors */ public static function formRule($fields, $files, $options) { - $errors = array(); + $errors = []; // validate max file size if ($fields['maxFileSize'] > $options->_uploadMaxSize) { @@ -114,7 +119,7 @@ public static function formRule($fields, $files, $options) { // validate recent items stack size if ($fields['recentItemsMaxCount'] && ($fields['recentItemsMaxCount'] < 1 || $fields['recentItemsMaxCount'] > CRM_Utils_Recent::MAX_ITEMS)) { - $errors['recentItemsMaxCount'] = ts("Illegal stack size. Use values between 1 and %1.", array(1 => CRM_Utils_Recent::MAX_ITEMS)); + $errors['recentItemsMaxCount'] = ts("Illegal stack size. Use values between 1 and %1.", [1 => CRM_Utils_Recent::MAX_ITEMS]); } if (!empty($fields['wkhtmltopdfPath'])) { diff --git a/CRM/Admin/Form/Setting/Path.php b/CRM/Admin/Form/Setting/Path.php index edf3ecc784ab..b83c52706748 100644 --- a/CRM/Admin/Form/Setting/Path.php +++ b/CRM/Admin/Form/Setting/Path.php @@ -1,9 +1,9 @@ CRM_Core_BAO_Setting::DIRECTORY_PREFERENCES_NAME, 'imageUploadDir' => CRM_Core_BAO_Setting::DIRECTORY_PREFERENCES_NAME, 'customFileUploadDir' => CRM_Core_BAO_Setting::DIRECTORY_PREFERENCES_NAME, 'customTemplateDir' => CRM_Core_BAO_Setting::DIRECTORY_PREFERENCES_NAME, 'customPHPPathDir' => CRM_Core_BAO_Setting::DIRECTORY_PREFERENCES_NAME, 'extensionsDir' => CRM_Core_BAO_Setting::DIRECTORY_PREFERENCES_NAME, - ); + ]; /** * Build the form object. @@ -52,19 +52,19 @@ public function buildQuickForm() { CRM_Utils_System::setTitle(ts('Settings - Upload Directories')); parent::buildQuickForm(); - $directories = array( + $directories = [ 'uploadDir' => ts('Temporary Files'), 'imageUploadDir' => ts('Images'), 'customFileUploadDir' => ts('Custom Files'), 'customTemplateDir' => ts('Custom Templates'), 'customPHPPathDir' => ts('Custom PHP Path Directory'), 'extensionsDir' => ts('CiviCRM Extensions Directory'), - ); + ]; foreach ($directories as $name => $title) { //$this->add('text', $name, $title); $this->addRule($name, ts("'%1' directory does not exist", - array(1 => $title) + [1 => $title] ), 'settingPath' ); diff --git a/CRM/Admin/Form/Setting/Search.php b/CRM/Admin/Form/Setting/Search.php index ae4e40c675c4..95cab96824fe 100644 --- a/CRM/Admin/Form/Setting/Search.php +++ b/CRM/Admin/Form/Setting/Search.php @@ -1,9 +1,9 @@ CRM_Core_BAO_Setting::SEARCH_PREFERENCES_NAME, - 'contact_autocomplete_options' => CRM_Core_BAO_Setting::SEARCH_PREFERENCES_NAME, + protected $_settings = [ + 'contact_reference_options' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, + 'contact_autocomplete_options' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'search_autocomplete_count' => CRM_Core_BAO_Setting::SEARCH_PREFERENCES_NAME, 'enable_innodb_fts' => CRM_Core_BAO_Setting::SEARCH_PREFERENCES_NAME, 'includeWildCardInName' => CRM_Core_BAO_Setting::SEARCH_PREFERENCES_NAME, @@ -49,7 +49,9 @@ class CRM_Admin_Form_Setting_Search extends CRM_Admin_Form_Setting { 'includeOrderByClause' => CRM_Core_BAO_Setting::SEARCH_PREFERENCES_NAME, 'smartGroupCacheTimeout' => CRM_Core_BAO_Setting::SEARCH_PREFERENCES_NAME, 'defaultSearchProfileID' => CRM_Core_BAO_Setting::SEARCH_PREFERENCES_NAME, - ); + 'searchPrimaryDetailsOnly' => CRM_Core_BAO_Setting::SEARCH_PREFERENCES_NAME, + 'quicksearch_options' => CRM_Core_BAO_Setting::SEARCH_PREFERENCES_NAME, + ]; /** * Build the form object. @@ -68,40 +70,36 @@ public function buildQuickForm() { // Autocomplete for Contact Reference (custom fields) $element = $this->getElement('contact_reference_options'); $element->_elements[0]->_flagFrozen = TRUE; + + // Freeze first element of quicksearch options + $element = $this->getElement('quicksearch_options'); + $element->_elements[0]->_flagFrozen = TRUE; } /** * @return array */ public static function getContactAutocompleteOptions() { - return array( - ts('Contact Name') => 1, - ) + array_flip(CRM_Core_OptionGroup::values('contact_autocomplete_options', - FALSE, FALSE, TRUE - )); + return [1 => ts('Contact Name')] + CRM_Core_OptionGroup::values('contact_autocomplete_options', FALSE, FALSE, TRUE); } /** * @return array */ public static function getAvailableProfiles() { - return array('' => ts('- none -')) + CRM_Core_BAO_UFGroup::getProfiles(array( + return ['' => ts('- none -')] + CRM_Core_BAO_UFGroup::getProfiles([ 'Contact', 'Individual', 'Organization', 'Household', - )); + ]); } /** * @return array */ public static function getContactReferenceOptions() { - return array( - ts('Contact Name') => 1, - ) + array_flip(CRM_Core_OptionGroup::values('contact_reference_options', - FALSE, FALSE, TRUE - )); + return [1 => ts('Contact Name')] + CRM_Core_OptionGroup::values('contact_reference_options', FALSE, FALSE, TRUE); } } diff --git a/CRM/Admin/Form/Setting/Smtp.php b/CRM/Admin/Form/Setting/Smtp.php index 8d1d06141b71..e70260e63f77 100644 --- a/CRM/Admin/Form/Setting/Smtp.php +++ b/CRM/Admin/Form/Setting/Smtp.php @@ -1,9 +1,9 @@ ts('mail()'), CRM_Mailing_Config::OUTBOUND_OPTION_SMTP => ts('SMTP'), CRM_Mailing_Config::OUTBOUND_OPTION_SENDMAIL => ts('Sendmail'), CRM_Mailing_Config::OUTBOUND_OPTION_DISABLED => ts('Disable Outbound Email'), CRM_Mailing_Config::OUTBOUND_OPTION_REDIRECT_TO_DB => ts('Redirect to Database'), - ); + ]; $this->addRadio('outBound_option', ts('Select Mailer'), $outBoundOption); CRM_Utils_System::setTitle(ts('Settings - Outbound Mail')); + $this->add('checkbox', 'allow_mail_from_logged_in_contact', ts('Allow Mail to be sent from logged in contact\'s email address')); $this->add('text', 'sendmail_path', ts('Sendmail Path')); $this->add('text', 'sendmail_args', ts('Sendmail Argument')); $this->add('text', 'smtpServer', ts('SMTP Server')); @@ -62,10 +63,10 @@ public function buildQuickForm() { $this->_testButtonName = $this->getButtonName('refresh', 'test'); - $this->addFormRule(array('CRM_Admin_Form_Setting_Smtp', 'formRule')); + $this->addFormRule(['CRM_Admin_Form_Setting_Smtp', 'formRule']); parent::buildQuickForm(); $buttons = $this->getElement('buttons')->getElements(); - $buttons[] = $this->createElement('submit', $this->_testButtonName, ts('Save & Send Test Email'), array('crm-icon' => 'fa-envelope-o')); + $buttons[] = $this->createElement('submit', $this->_testButtonName, ts('Save & Send Test Email'), ['crm-icon' => 'fa-envelope-o']); $this->getElement('buttons')->setElements($buttons); } @@ -79,6 +80,9 @@ public function postProcess() { $formValues = $this->controller->exportValues($this->_name); + Civi::settings()->set('allow_mail_from_logged_in_contact', (!empty($formValues['allow_mail_from_logged_in_contact']))); + unset($formValues['allow_mail_from_logged_in_contact']); + $buttonName = $this->controller->getButtonName(); // check if test button if ($buttonName == $this->_testButtonName) { @@ -98,7 +102,7 @@ public function postProcess() { if (!$domainEmailAddress || $domainEmailAddress == 'info@EXAMPLE.ORG') { $fixUrl = CRM_Utils_System::url("civicrm/admin/domain", 'action=update&reset=1'); - CRM_Core_Error::fatal(ts('The site administrator needs to enter a valid \'FROM Email Address\' in Administer CiviCRM » Communications » FROM Email Addresses. The email address used may need to be a valid mail account with your email service provider.', array(1 => $fixUrl))); + CRM_Core_Error::fatal(ts('The site administrator needs to enter a valid \'FROM Email Address\' in Administer CiviCRM » Communications » FROM Email Addresses. The email address used may need to be a valid mail account with your email service provider.', [1 => $fixUrl])); } if (!$toEmail) { @@ -111,12 +115,11 @@ public function postProcess() { $to = '"' . $toDisplayName . '"' . "<$toEmail>"; $from = '"' . $domainEmailName . '" <' . $domainEmailAddress . '>'; - $testMailStatusMsg = ts('Sending test email. FROM: %1 TO: %2.
', array( - 1 => $domainEmailAddress, - 2 => $toEmail, - )); + $testMailStatusMsg = ts('Sending test email') . ':
' + . ts('From: %1', [1 => $domainEmailAddress]) . '
' + . ts('To: %1', [1 => $toEmail]) . '
'; - $params = array(); + $params = []; if ($formValues['outBound_option'] == CRM_Mailing_Config::OUTBOUND_OPTION_SMTP) { $subject = "Test for SMTP settings"; $message = "SMTP settings are correct."; @@ -155,23 +158,29 @@ public function postProcess() { $mailerName = 'mail'; } - $headers = array( + $headers = [ 'From' => $from, 'To' => $to, 'Subject' => $subject, - ); + ]; $mailer = Mail::factory($mailerName, $params); $errorScope = CRM_Core_TemporaryErrorScope::ignoreException(); $result = $mailer->send($toEmail, $headers, $message); unset($errorScope); - if (!is_a($result, 'PEAR_Error')) { - CRM_Core_Session::setStatus($testMailStatusMsg . ts('Your %1 settings are correct. A test email has been sent to your email address.', array(1 => strtoupper($mailerName))), ts("Mail Sent"), "success"); + if (defined('CIVICRM_MAIL_LOG') && defined('CIVICRM_MAIL_LOG_AND_SEND')) { + $testMailStatusMsg .= '
' . ts('You have defined CIVICRM_MAIL_LOG_AND_SEND - mail will be logged.') . '

'; + } + if (defined('CIVICRM_MAIL_LOG') && !defined('CIVICRM_MAIL_LOG_AND_SEND')) { + CRM_Core_Session::setStatus($testMailStatusMsg . ts('You have defined CIVICRM_MAIL_LOG - no mail will be sent. Your %1 settings have not been tested.', [1 => strtoupper($mailerName)]), ts("Mail not sent"), "warning"); + } + elseif (!is_a($result, 'PEAR_Error')) { + CRM_Core_Session::setStatus($testMailStatusMsg . ts('Your %1 settings are correct. A test email has been sent to your email address.', [1 => strtoupper($mailerName)]), ts("Mail Sent"), "success"); } else { $message = CRM_Utils_Mail::errorMessage($mailer, $result); - CRM_Core_Session::setStatus($testMailStatusMsg . ts('Oops. Your %1 settings are incorrect. No test mail has been sent.', array(1 => strtoupper($mailerName))) . $message, ts("Mail Not Sent"), "error"); + CRM_Core_Session::setStatus($testMailStatusMsg . ts('Oops. Your %1 settings are incorrect. No test mail has been sent.', [1 => strtoupper($mailerName)]) . $message, ts("Mail Not Sent"), "error"); } } } @@ -233,7 +242,7 @@ public static function formRule($fields) { */ public function setDefaultValues() { if (!$this->_defaults) { - $this->_defaults = array(); + $this->_defaults = []; $mailingBackend = Civi::settings()->get('mailing_backend'); if (!empty($mailingBackend)) { @@ -256,6 +265,7 @@ public function setDefaultValues() { } } } + $this->_defaults['allow_mail_from_logged_in_contact'] = Civi::settings()->get('allow_mail_from_logged_in_contact'); return $this->_defaults; } diff --git a/CRM/Admin/Form/Setting/UF.php b/CRM/Admin/Form/Setting/UF.php index 029d77eb92e7..efb8e9a44fbb 100644 --- a/CRM/Admin/Form/Setting/UF.php +++ b/CRM/Admin/Form/Setting/UF.php @@ -1,9 +1,9 @@ _uf = $config->userFramework; + $this->_settings['syncCMSEmail'] = CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME; if ($this->_uf == 'WordPress') { $this->_settings['wpBasePage'] = CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME; } CRM_Utils_System::setTitle( - ts('Settings - %1 Integration', array(1 => $this->_uf)) + ts('Settings - %1 Integration', [1 => $this->_uf]) ); if ($config->userSystem->is_drupal) { @@ -81,7 +82,11 @@ function_exists('module_exists') && $dsnArray = DB::parseDSN($config->dsn); $tableNames = CRM_Core_DAO::getTableNames(); $tablePrefixes = '$databases[\'default\'][\'default\'][\'prefix\']= array('; - $tablePrefixes .= "\n 'default' => '$drupal_prefix',"; // add default prefix: the drupal database prefix + if ($config->userFramework === 'Backdrop') { + $tablePrefixes = '$database_prefix = array('; + } + // add default prefix: the drupal database prefix + $tablePrefixes .= "\n 'default' => '$drupal_prefix',"; $prefix = ""; if ($config->dsn != $config->userFrameworkDSN) { $prefix = "`{$dsnArray['database']}`."; diff --git a/CRM/Admin/Form/Setting/UpdateConfigBackend.php b/CRM/Admin/Form/Setting/UpdateConfigBackend.php index 23a99a6b3e25..d1a258738c8b 100644 --- a/CRM/Admin/Form/Setting/UpdateConfigBackend.php +++ b/CRM/Admin/Form/Setting/UpdateConfigBackend.php @@ -1,9 +1,9 @@ addElement( 'submit', $this->getButtonName('next', 'cleanup'), 'Cleanup Caches', - array('class' => 'crm-form-submit', 'id' => 'cleanup-cache') + ['class' => 'crm-form-submit', 'id' => 'cleanup-cache'] ); $this->addElement( 'submit', $this->getButtonName('next', 'resetpaths'), 'Reset Paths', - array('class' => 'crm-form-submit', 'id' => 'resetpaths') + ['class' => 'crm-form-submit', 'id' => 'resetpaths'] ); //parent::buildQuickForm(); @@ -65,6 +65,7 @@ public function postProcess() { // clear all caches CRM_Core_Config::clearDBCache(); + Civi::cache('session')->clear(); CRM_Utils_System::flushCache(); parent::rebuildMenu(); diff --git a/CRM/Admin/Form/Setting/Url.php b/CRM/Admin/Form/Setting/Url.php index 470e48c38ed5..9d6a62245553 100644 --- a/CRM/Admin/Form/Setting/Url.php +++ b/CRM/Admin/Form/Setting/Url.php @@ -1,9 +1,9 @@ CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'userFrameworkResourceURL' => CRM_Core_BAO_Setting::URL_PREFERENCES_NAME, 'imageUploadURL' => CRM_Core_BAO_Setting::URL_PREFERENCES_NAME, 'customCSSURL' => CRM_Core_BAO_Setting::URL_PREFERENCES_NAME, 'extensionsURL' => CRM_Core_BAO_Setting::URL_PREFERENCES_NAME, - ); + ]; /** * Build the form object. */ public function buildQuickForm() { CRM_Utils_System::setTitle(ts('Settings - Resource URLs')); - $settingFields = civicrm_api('setting', 'getfields', array( + $settingFields = civicrm_api('setting', 'getfields', [ 'version' => 3, - )); + ]); $this->addYesNo('enableSSL', ts('Force Secure URLs (SSL)')); $this->addYesNo('verifySSL', ts('Verify SSL Certs')); // FIXME: verifySSL should use $_settings instead of manually adding fields $this->assign('verifySSL_description', $settingFields['values']['verifySSL']['description']); - $this->addFormRule(array('CRM_Admin_Form_Setting_Url', 'formRule')); + $this->addFormRule(['CRM_Admin_Form_Setting_Url', 'formRule']); parent::buildQuickForm(); } @@ -78,9 +78,9 @@ public static function formRule($fields) { ) ); if (!CRM_Utils_System::checkURL($url, TRUE)) { - $errors = array( + $errors = [ 'enableSSL' => ts('You need to set up a secure server before you can use the Force Secure URLs option'), - ); + ]; return $errors; } } diff --git a/CRM/Admin/Form/SettingTrait.php b/CRM/Admin/Form/SettingTrait.php new file mode 100644 index 000000000000..f22470803fe6 --- /dev/null +++ b/CRM/Admin/Form/SettingTrait.php @@ -0,0 +1,343 @@ +_settings. + * + * @return array + */ + protected function getSettingsMetaData() { + if (empty($this->settingsMetadata)) { + $allSettingMetaData = civicrm_api3('setting', 'getfields', []); + $this->settingsMetadata = array_intersect_key($allSettingMetaData['values'], $this->_settings); + // This array_merge re-orders to the key order of $this->_settings. + $this->settingsMetadata = array_merge($this->_settings, $this->settingsMetadata); + } + return $this->settingsMetadata; + } + + /** + * Get the settings which can be stored based on metadata. + * + * @param array $params + * @return array + */ + protected function getSettingsToSetByMetadata($params) { + $setValues = array_intersect_key($params, $this->_settings); + // Checkboxes will be unset rather than empty so we need to add them back in. + // Handle quickform hateability just once, right here right now. + $unsetValues = array_diff_key($this->_settings, $params); + foreach ($unsetValues as $key => $unsetValue) { + if ($this->getQuickFormType($this->getSettingMetadata($key)) === 'CheckBox') { + $setValues[$key] = [$key => 0]; + } + } + return $setValues; + } + + /** + * @param $params + */ + protected function filterParamsSetByMetadata(&$params) { + foreach ($this->getSettingsToSetByMetadata($params) as $setting => $settingGroup) { + //@todo array_diff this + unset($params[$setting]); + } + } + + /** + * Get the metadata for a particular field. + * + * @param $setting + * @return mixed + */ + protected function getSettingMetadata($setting) { + return $this->getSettingsMetaData()[$setting]; + } + + /** + * Get the metadata for a particular field for a particular item. + * + * e.g get 'serialize' key, if exists, for a field. + * + * @param $setting + * @param $item + * @return mixed + */ + protected function getSettingMetadataItem($setting, $item) { + return CRM_Utils_Array::value($item, $this->getSettingsMetaData()[$setting]); + } + + /** + * @return string + */ + protected function getSettingPageFilter() { + if (!isset($this->_filter)) { + // Get the last URL component without modifying the urlPath property. + $urlPath = array_values($this->urlPath); + $this->_filter = end($urlPath); + } + return $this->_filter; + } + + /** + * Returns a re-keyed copy of the settings, ordered by weight. + * + * @return array + */ + protected function getSettingsOrderedByWeight() { + $settingMetaData = $this->getSettingsMetaData(); + $filter = $this->getSettingPageFilter(); + + usort($settingMetaData, function ($a, $b) use ($filter) { + // Handle cases in which a comparison is impossible. Such will be considered ties. + if ( + // A comparison can't be made unless both setting weights are declared. + !isset($a['settings_pages'][$filter]['weight'], $b['settings_pages'][$filter]['weight']) + // A pair of settings might actually have the same weight. + || $a['settings_pages'][$filter]['weight'] === $b['settings_pages'][$filter]['weight'] + ) { + return 0; + } + + return $a['settings_pages'][$filter]['weight'] > $b['settings_pages'][$filter]['weight'] ? 1 : -1; + }); + + return $settingMetaData; + } + + /** + * Add fields in the metadata to the template. + */ + protected function addFieldsDefinedInSettingsMetadata() { + $settingMetaData = $this->getSettingsMetaData(); + $descriptions = []; + foreach ($settingMetaData as $setting => $props) { + $quickFormType = $this->getQuickFormType($props); + if (isset($quickFormType)) { + $options = CRM_Utils_Array::value('options', $props); + if (isset($props['pseudoconstant'])) { + $options = civicrm_api3('Setting', 'getoptions', [ + 'field' => $setting, + ])['values']; + if ($props['html_type'] === 'Select' && isset($props['is_required']) && $props['is_required'] === FALSE && !isset($options[''])) { + // If the spec specifies the field is not required add a null option. + // Why not if empty($props['is_required']) - basically this has been added to the spec & might not be set to TRUE + // when it is true. + $options = ['' => ts('None')] + $options; + } + } + if ($props['type'] === 'Boolean') { + $options = [$props['title'] => $props['name']]; + } + + //Load input as readonly whose values are overridden in civicrm.settings.php. + if (Civi::settings()->getMandatory($setting)) { + $props['html_attributes']['readonly'] = TRUE; + $this->includesReadOnlyFields = TRUE; + } + + $add = 'add' . $quickFormType; + if ($add == 'addElement') { + $this->$add( + $props['html_type'], + $setting, + ts($props['title']), + ($options !== NULL) ? $options : CRM_Utils_Array::value('html_attributes', $props, []), + ($options !== NULL) ? CRM_Utils_Array::value('html_attributes', $props, []) : NULL + ); + } + elseif ($add == 'addSelect') { + $this->addElement('select', $setting, ts($props['title']), $options, CRM_Utils_Array::value('html_attributes', $props)); + } + elseif ($add == 'addCheckBox') { + $this->addCheckBox($setting, '', $options, NULL, CRM_Utils_Array::value('html_attributes', $props), NULL, NULL, ['  ']); + } + elseif ($add == 'addCheckBoxes') { + $options = array_flip($options); + $newOptions = []; + foreach ($options as $key => $val) { + $newOptions[$key] = $val; + } + $this->addCheckBox($setting, + $props['title'], + $newOptions, + NULL, NULL, NULL, NULL, + ['  ', '  ', '
'] + ); + } + elseif ($add == 'addChainSelect') { + $this->addChainSelect($setting, [ + 'label' => ts($props['title']), + ]); + } + elseif ($add == 'addMonthDay') { + $this->add('date', $setting, ts($props['title']), CRM_Core_SelectValues::date(NULL, 'M d')); + } + elseif ($add === 'addEntityRef') { + $this->$add($setting, ts($props['title']), $props['entity_reference_options']); + } + elseif ($add === 'addYesNo' && ($props['type'] === 'Boolean')) { + $this->addRadio($setting, ts($props['title']), [1 => 'Yes', 0 => 'No'], NULL, '  '); + } + elseif ($add === 'add') { + $this->add($props['html_type'], $setting, ts($props['title']), $options); + } + else { + $this->$add($setting, ts($props['title']), $options); + } + // Migrate to using an array as easier in smart... + $description = CRM_Utils_Array::value('description', $props); + $descriptions[$setting] = $description; + $this->assign("{$setting}_description", $description); + if ($setting == 'max_attachments') { + //temp hack @todo fix to get from metadata + $this->addRule('max_attachments', ts('Value should be a positive number'), 'positiveInteger'); + } + if ($setting == 'maxFileSize') { + //temp hack + $this->addRule('maxFileSize', ts('Value should be a positive number'), 'positiveInteger'); + } + + } + } + // setting_description should be deprecated - see Mail.tpl for metadata based tpl. + $this->assign('setting_descriptions', $descriptions); + $this->assign('settings_fields', $settingMetaData); + $this->assign('fields', $this->getSettingsOrderedByWeight()); + } + + /** + * Get the quickform type for the given html type. + * + * @param array $spec + * + * @return string + */ + protected function getQuickFormType($spec) { + if (isset($spec['quick_form_type']) && + !($spec['quick_form_type'] === 'Element' && !empty($spec['html_type']))) { + // This is kinda transitional + return $spec['quick_form_type']; + } + + // The spec for settings has been updated for consistency - we provide deprecation notices for sites that have + // not made this change. + $htmlType = $spec['html_type']; + if ($htmlType !== strtolower($htmlType)) { + CRM_Core_Error::deprecatedFunctionWarning(ts('Settings fields html_type should be lower case - see https://docs.civicrm.org/dev/en/latest/framework/setting/ - this needs to be fixed for ' . $spec['name'])); + $htmlType = strtolower($spec['html_type']); + } + $mapping = [ + 'checkboxes' => 'CheckBoxes', + 'checkbox' => 'CheckBox', + 'radio' => 'Radio', + 'select' => 'Select', + 'textarea' => 'Element', + 'text' => 'Element', + 'entity_reference' => 'EntityRef', + 'advmultiselect' => 'Element', + ]; + $mapping += array_fill_keys(CRM_Core_Form::$html5Types, ''); + return $mapping[$htmlType]; + } + + /** + * Get the defaults for all fields defined in the metadata. + * + * All others are pending conversion. + */ + protected function setDefaultsForMetadataDefinedFields() { + CRM_Core_BAO_ConfigSetting::retrieve($this->_defaults); + foreach (array_keys($this->_settings) as $setting) { + $this->_defaults[$setting] = civicrm_api3('setting', 'getvalue', ['name' => $setting]); + $spec = $this->getSettingsMetadata()[$setting]; + if (!empty($spec['serialize'])) { + $this->_defaults[$setting] = CRM_Core_DAO::unSerializeField($this->_defaults[$setting], $spec['serialize']); + } + if ($this->getQuickFormType($spec) === 'CheckBoxes') { + $this->_defaults[$setting] = array_fill_keys($this->_defaults[$setting], 1); + } + if ($this->getQuickFormType($spec) === 'CheckBox') { + $this->_defaults[$setting] = [$setting => $this->_defaults[$setting]]; + } + } + } + + /** + * @param $params + * + */ + protected function saveMetadataDefinedSettings($params) { + $settings = $this->getSettingsToSetByMetadata($params); + foreach ($settings as $setting => $settingValue) { + if ($this->getQuickFormType($this->getSettingMetadata($setting)) === 'CheckBoxes') { + $settings[$setting] = array_keys($settingValue); + } + if ($this->getQuickFormType($this->getSettingMetadata($setting)) === 'CheckBox') { + // This will be an array with one value. + $settings[$setting] = (int) reset($settings[$setting]); + } + } + civicrm_api3('setting', 'create', $settings); + } + +} diff --git a/CRM/Admin/Form/Tag.php b/CRM/Admin/Form/Tag.php deleted file mode 100644 index 98bc99e023e3..000000000000 --- a/CRM/Admin/Form/Tag.php +++ /dev/null @@ -1,175 +0,0 @@ -setPageTitle($this->_isTagSet ? ts('Tag Set') : ts('Tag')); - - if ($this->_action == CRM_Core_Action::DELETE) { - if ($this->_id && $tag = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_Tag', $this->_id, 'name', 'parent_id')) { - $url = CRM_Utils_System::url('civicrm/admin/tag', "reset=1"); - CRM_Core_Error::statusBounce(ts("This tag cannot be deleted. You must delete all its child tags ('%1', etc) prior to deleting this tag.", array(1 => $tag)), $url); - } - if ($this->_values['is_reserved'] == 1 && !CRM_Core_Permission::check('administer reserved tags')) { - CRM_Core_Error::statusBounce(ts("You do not have sufficient permission to delete this reserved tag.")); - } - } - else { - $this->_isTagSet = CRM_Utils_Request::retrieve('tagset', 'Positive', $this); - - if (!$this->_isTagSet && - $this->_id && - CRM_Core_DAO::getFieldValue('CRM_Core_DAO_Tag', $this->_id, 'is_tagset') - ) { - $this->_isTagSet = TRUE; - } - - $allTag = array('' => ts('- select -')) + CRM_Core_BAO_Tag::getTagsNotInTagset(); - - if ($this->_id) { - unset($allTag[$this->_id]); - } - - if (!$this->_isTagSet) { - $this->add('select', 'parent_id', ts('Parent Tag'), $allTag, FALSE, array('class' => 'crm-select2')); - - // Tagsets are not selectable by definition so only include the selectable field if NOT a tagset. - $selectable = $this->add('checkbox', 'is_selectable', ts('Selectable?')); - // Selectable should be checked by default when creating a new tag - if ($this->_action == CRM_Core_Action::ADD) { - $selectable->setValue(1); - } - - } - - $this->assign('isTagSet', $this->_isTagSet); - - $this->applyFilter('__ALL__', 'trim'); - - $this->add('text', 'name', ts('Name'), - CRM_Core_DAO::getAttribute('CRM_Core_DAO_Tag', 'name'), TRUE - ); - $this->addRule('name', ts('Name already exists in Database.'), 'objectExists', array( - 'CRM_Core_DAO_Tag', - $this->_id, - )); - - $this->add('text', 'description', ts('Description'), - CRM_Core_DAO::getAttribute('CRM_Core_DAO_Tag', 'description') - ); - - $isReserved = $this->add('checkbox', 'is_reserved', ts('Reserved?')); - - $usedFor = $this->addSelect('used_for', array('multiple' => TRUE, 'option_url' => NULL)); - - if ($this->_id && - CRM_Core_DAO::getFieldValue('CRM_Core_DAO_Tag', $this->_id, 'parent_id') - ) { - $usedFor->freeze(); - } - - $adminTagset = TRUE; - if (!CRM_Core_Permission::check('administer Tagsets')) { - $adminTagset = FALSE; - } - $this->assign('adminTagset', $adminTagset); - - $adminReservedTags = TRUE; - if (!CRM_Core_Permission::check('administer reserved tags')) { - $isReserved->freeze(); - $adminReservedTags = FALSE; - } - $this->assign('adminReservedTags', $adminReservedTags); - - } - parent::buildQuickForm(); - } - - /** - * Process the form submission. - */ - public function postProcess() { - $params = $ids = array(); - - // store the submitted values in an array - $params = $this->exportValues(); - - $ids['tag'] = $this->_id; - if ($this->_action == CRM_Core_Action::ADD || - $this->_action == CRM_Core_Action::UPDATE - ) { - $params['used_for'] = implode(",", $params['used_for']); - } - - $params['is_tagset'] = 0; - if ($this->_isTagSet) { - $params['is_tagset'] = 1; - } - - if (!isset($params['is_reserved'])) { - $params['is_reserved'] = 0; - } - - if (!isset($params['is_selectable'])) { - $params['is_selectable'] = 0; - } - - if ($this->_action == CRM_Core_Action::DELETE) { - if ($this->_id > 0) { - $tag = civicrm_api3('tag', 'getsingle', array('id' => $this->_id)); - CRM_Core_BAO_Tag::del($this->_id); - CRM_Core_Session::setStatus(ts('The tag \'%1\' has been deleted.', array(1 => $tag['name'])), ts('Deleted'), 'success'); - } - } - else { - $tag = CRM_Core_BAO_Tag::add($params, $ids); - CRM_Core_Session::setStatus(ts('The tag \'%1\' has been saved.', array(1 => $tag->name)), ts('Saved'), 'success'); - } - } - -} diff --git a/CRM/Admin/Form/WordReplacements.php b/CRM/Admin/Form/WordReplacements.php index 0a5fabdb0c5f..7213107cf547 100644 --- a/CRM/Admin/Form/WordReplacements.php +++ b/CRM/Admin/Form/WordReplacements.php @@ -1,9 +1,9 @@ _defaults; } - $this->_defaults = array(); + $this->_defaults = []; $config = CRM_Core_Config::singleton(); $values = CRM_Core_BAO_WordReplacement::getLocaleCustomStrings($config->lcMessages); $i = 1; - $enableDisable = array( + $enableDisable = [ 1 => 'enabled', 0 => 'disabled', - ); + ]; - $cardMatch = array('wildcardMatch', 'exactMatch'); + $cardMatch = ['wildcardMatch', 'exactMatch']; foreach ($enableDisable as $key => $val) { foreach ($cardMatch as $kc => $vc) { @@ -112,9 +112,9 @@ public function buildQuickForm() { } $soInstances = range(1, $this->_numStrings, 1); - $stringOverrideInstances = array(); + $stringOverrideInstances = []; if ($this->_soInstance) { - $soInstances = array($this->_soInstance); + $soInstances = [$this->_soInstance]; } elseif (!empty($_POST['old'])) { $soInstances = $stringOverrideInstances = array_keys($_POST['old']); @@ -127,8 +127,8 @@ public function buildQuickForm() { } foreach ($soInstances as $instance) { $this->addElement('checkbox', "enabled[$instance]"); - $this->add('textarea', "old[$instance]", NULL, array('rows' => 1, 'cols' => 40)); - $this->add('textarea', "new[$instance]", NULL, array('rows' => 1, 'cols' => 40)); + $this->add('text', "old[$instance]", NULL); + $this->add('text', "new[$instance]", NULL); $this->addElement('checkbox', "cb[$instance]"); } $this->assign('numStrings', $this->_numStrings); @@ -138,19 +138,18 @@ public function buildQuickForm() { $this->assign('stringOverrideInstances', empty($stringOverrideInstances) ? FALSE : $stringOverrideInstances); - $this->addButtons(array( - array( - 'type' => 'next', - 'name' => ts('Save'), - 'isDefault' => TRUE, - ), - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - ), - ) - ); - $this->addFormRule(array('CRM_Admin_Form_WordReplacements', 'formRule'), $this); + $this->addButtons([ + [ + 'type' => 'next', + 'name' => ts('Save'), + 'isDefault' => TRUE, + ], + [ + 'type' => 'cancel', + 'name' => ts('Cancel'), + ], + ]); + $this->addFormRule(['CRM_Admin_Form_WordReplacements', 'formRule'], $this); } /** @@ -163,7 +162,7 @@ public function buildQuickForm() { * list of errors to be posted back to the form */ public static function formRule($values) { - $errors = array(); + $errors = []; $oldValues = CRM_Utils_Array::value('old', $values); $newValues = CRM_Utils_Array::value('new', $values); @@ -195,38 +194,39 @@ public function postProcess() { $params = $this->controller->exportValues($this->_name); $this->_numStrings = count($params['old']); - $enabled['exactMatch'] = $enabled['wildcardMatch'] = $disabled['exactMatch'] = $disabled['wildcardMatch'] = array(); + $enabled['exactMatch'] = $enabled['wildcardMatch'] = $disabled['exactMatch'] = $disabled['wildcardMatch'] = []; for ($i = 1; $i <= $this->_numStrings; $i++) { if (!empty($params['new'][$i]) && !empty($params['old'][$i])) { if (isset($params['enabled']) && !empty($params['enabled'][$i])) { if (!empty($params['cb']) && !empty($params['cb'][$i])) { - $enabled['exactMatch'] += array($params['old'][$i] => $params['new'][$i]); + $enabled['exactMatch'] += [$params['old'][$i] => $params['new'][$i]]; } else { - $enabled['wildcardMatch'] += array($params['old'][$i] => $params['new'][$i]); + $enabled['wildcardMatch'] += [$params['old'][$i] => $params['new'][$i]]; } } else { if (isset($params['cb']) && is_array($params['cb']) && array_key_exists($i, $params['cb'])) { - $disabled['exactMatch'] += array($params['old'][$i] => $params['new'][$i]); + $disabled['exactMatch'] += [$params['old'][$i] => $params['new'][$i]]; } else { - $disabled['wildcardMatch'] += array($params['old'][$i] => $params['new'][$i]); + $disabled['wildcardMatch'] += [$params['old'][$i] => $params['new'][$i]]; } } } } - $overrides = array( + $overrides = [ 'enabled' => $enabled, 'disabled' => $disabled, - ); + ]; $config = CRM_Core_Config::singleton(); CRM_Core_BAO_WordReplacement::setLocaleCustomStrings($config->lcMessages, $overrides); // This controller was originally written to CRUD $config->locale_custom_strings, - // but that's no longer the canonical store. Sync changes to canonical store. + // but that's no longer the canonical store. Sync changes to canonical store + // (civicrm_word_replacement table in the database). // This is inefficient - at some point, we should rewrite this UI. CRM_Core_BAO_WordReplacement::rebuildWordReplacementTable(); diff --git a/CRM/Admin/Page/AJAX.php b/CRM/Admin/Page/AJAX.php index 139321abe4f5..f782de942853 100644 --- a/CRM/Admin/Page/AJAX.php +++ b/CRM/Admin/Page/AJAX.php @@ -1,9 +1,9 @@ get('userID'); - if ($contactID) { - CRM_Core_Page_AJAX::setJsHeaders(); - $smarty = CRM_Core_Smarty::singleton(); - $smarty->assign('includeEmail', civicrm_api3('setting', 'getvalue', array('name' => 'includeEmailInName', 'group' => 'Search Preferences'))); - print $smarty->fetchWith('CRM/common/navigation.js.tpl', array( - 'navigation' => CRM_Core_BAO_Navigation::createNavigation($contactID), - )); + public static function navMenu() { + if (CRM_Core_Session::getLoggedInContactID()) { + + $menu = CRM_Core_BAO_Navigation::buildNavigationTree(); + CRM_Core_BAO_Navigation::buildHomeMenu($menu); + CRM_Utils_Hook::navigationMenu($menu); + CRM_Core_BAO_Navigation::fixNavigationMenu($menu); + CRM_Core_BAO_Navigation::orderByWeight($menu); + CRM_Core_BAO_Navigation::filterByPermission($menu); + self::formatMenuItems($menu); + + $output = [ + 'menu' => $menu, + 'search' => CRM_Utils_Array::makeNonAssociative(self::getSearchOptions()), + ]; + // Encourage browsers to cache for a long time - 1 year + $ttl = 60 * 60 * 24 * 364; + CRM_Utils_System::setHttpHeader('Expires', gmdate('D, d M Y H:i:s \G\M\T', time() + $ttl)); + CRM_Utils_System::setHttpHeader('Cache-Control', "max-age=$ttl, public"); + CRM_Utils_System::setHttpHeader('Content-Type', 'application/json'); + print (json_encode($output)); } CRM_Utils_System::civiExit(); } /** - * Return menu tree as json data for editing. + * @param array $menu */ - public static function getNavigationList() { - echo CRM_Core_BAO_Navigation::buildNavigation(TRUE, FALSE); - CRM_Utils_System::civiExit(); + public static function formatMenuItems(&$menu) { + foreach ($menu as $key => &$item) { + $props = $item['attributes']; + unset($item['attributes']); + if (!empty($props['separator'])) { + $item['separator'] = ($props['separator'] == 1 ? 'bottom' : 'top'); + } + if (!empty($props['icon'])) { + $item['icon'] = $props['icon']; + } + if (!empty($props['attr'])) { + $item['attr'] = $props['attr']; + } + if (!empty($props['url'])) { + $item['url'] = CRM_Utils_System::evalUrl(CRM_Core_BAO_Navigation::makeFullyFormedUrl($props['url'])); + } + if (!empty($props['label'])) { + $item['label'] = ts($props['label'], ['context' => 'menu']); + } + $item['name'] = !empty($props['name']) ? $props['name'] : CRM_Utils_String::munge(CRM_Utils_Array::value('label', $props)); + if (!empty($item['child'])) { + self::formatMenuItems($item['child']); + } + } + $menu = array_values($menu); + } + + public static function getSearchOptions() { + $searchOptions = array_merge(['sort_name'], Civi::settings()->get('quicksearch_options')); + $labels = CRM_Core_SelectValues::quicksearchOptions(); + $result = []; + foreach ($searchOptions as $key) { + $label = $labels[$key]; + if (strpos($key, 'custom_') === 0) { + $key = 'custom_' . CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomField', substr($key, 7), 'id', 'name'); + $label = array_slice(explode(': ', $label, 2), -1); + } + $result[$key] = $label; + } + return $result; } /** @@ -76,16 +123,16 @@ public static function getStatusMsg() { require_once 'api/v3/utils.php'; $recordID = CRM_Utils_Type::escape($_GET['id'], 'Integer'); $entity = CRM_Utils_Type::escape($_GET['entity'], 'String'); - $ret = array(); + $ret = []; if ($recordID && $entity && $recordBAO = _civicrm_api3_get_BAO($entity)) { switch ($recordBAO) { case 'CRM_Core_BAO_UFGroup': $method = 'getUFJoinRecord'; - $result = array($recordBAO, $method); - $ufJoin = call_user_func_array(($result), array($recordID, TRUE)); + $result = [$recordBAO, $method]; + $ufJoin = call_user_func_array(($result), [$recordID, TRUE]); if (!empty($ufJoin)) { - $ret['content'] = ts('This profile is currently used for %1.', array(1 => implode(', ', $ufJoin))) . '

' . ts('If you disable the profile - it will be removed from these forms and/or modules. Do you want to continue?'); + $ret['content'] = ts('This profile is currently used for %1.', [1 => implode(', ', $ufJoin)]) . '

' . ts('If you disable the profile - it will be removed from these forms and/or modules. Do you want to continue?'); } else { $ret['content'] = ts('Are you sure you want to disable this profile?'); @@ -99,12 +146,12 @@ public static function getStatusMsg() { if (!CRM_Utils_System::isNull($usedBy)) { $template = CRM_Core_Smarty::singleton(); $template->assign('usedBy', $usedBy); - $comps = array( + $comps = [ 'Event' => 'civicrm_event', 'Contribution' => 'civicrm_contribution_page', 'EventTemplate' => 'civicrm_event_template', - ); - $contexts = array(); + ]; + $contexts = []; foreach ($comps as $name => $table) { if (array_key_exists($table, $usedBy)) { $contexts[] = $name; @@ -114,12 +161,12 @@ public static function getStatusMsg() { $ret['illegal'] = TRUE; $table = $template->fetch('CRM/Price/Page/table.tpl'); - $ret['content'] = ts('Unable to disable the \'%1\' price set - it is currently in use by one or more active events, contribution pages or contributions.', array( - 1 => $priceSet, - )) . "
$table"; + $ret['content'] = ts('Unable to disable the \'%1\' price set - it is currently in use by one or more active events, contribution pages or contributions.', [ + 1 => $priceSet, + ]) . "
$table"; } else { - $ret['content'] = ts('Are you sure you want to disable \'%1\' Price Set?', array(1 => $priceSet)); + $ret['content'] = ts('Are you sure you want to disable \'%1\' Price Set?', [1 => $priceSet]); } break; @@ -131,7 +178,7 @@ public static function getStatusMsg() { $ret['content'] = ts('Are you sure you want to disable this CiviCRM Profile field?'); break; - case 'CRM_Contribute_BAO_ManagePremiums': + case 'CRM_Contribute_BAO_Product': $ret['content'] = ts('Are you sure you want to disable this premium? This action will remove the premium from any contribution pages that currently offer it. However it will not delete the premium record - so you can re-enable it and add it back to your contribution page(s) at a later time.'); break; @@ -173,7 +220,7 @@ public static function getStatusMsg() { $ret['content'] = ts('Are you sure you want to disable this Participant Status?') . '

' . ts('Users will no longer be able to select this value when adding or editing Participant Status.'); break; - case 'CRM_Mailing_BAO_Component': + case 'CRM_Mailing_BAO_MailingComponent': $ret['content'] = ts('Are you sure you want to disable this component?'); break; @@ -211,6 +258,7 @@ public static function getStatusMsg() { case 'CRM_Contact_BAO_Group': $ret['content'] = ts('Are you sure you want to disable this Group?'); + $ret['content'] .= '

' . ts('WARNING - Disabling this group will disable all the child groups associated if any.') . ''; break; case 'CRM_Core_BAO_OptionGroup': @@ -223,7 +271,7 @@ public static function getStatusMsg() { case 'CRM_Core_BAO_OptionValue': $label = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionValue', $recordID, 'label'); - $ret['content'] = ts('Are you sure you want to disable the \'%1\' option ?', array(1 => $label)); + $ret['content'] = ts('Are you sure you want to disable the \'%1\' option ?', [1 => $label]); $ret['content'] .= '

' . ts('WARNING - Disabling an option which has been assigned to existing records will result in that option being cleared when the record is edited.'); break; @@ -242,89 +290,41 @@ public static function getStatusMsg() { } } else { - $ret = array('status' => 'error', 'content' => 'Error: Unknown entity type.', 'illegal' => TRUE); + $ret = ['status' => 'error', 'content' => 'Error: Unknown entity type.', 'illegal' => TRUE]; } CRM_Core_Page_AJAX::returnJsonResponse($ret); } - public static function mergeTagList() { - $name = CRM_Utils_Type::escape($_GET['term'], 'String'); - $fromId = CRM_Utils_Type::escape($_GET['fromId'], 'Integer'); - $limit = Civi::settings()->get('search_autocomplete_count'); - - // build used-for clause to be used in main query - $usedForTagA = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_Tag', $fromId, 'used_for'); - $usedForClause = array(); - if ($usedForTagA) { - $usedForTagA = explode(",", $usedForTagA); - foreach ($usedForTagA as $key => $value) { - $usedForClause[] = "t1.used_for LIKE '%{$value}%'"; - } - } - $usedForClause = !empty($usedForClause) ? implode(' OR ', $usedForClause) : '1'; - sort($usedForTagA); - - // query to list mergable tags - $query = " -SELECT t1.name, t1.id, t1.used_for, t2.name as parent -FROM civicrm_tag t1 -LEFT JOIN civicrm_tag t2 ON t1.parent_id = t2.id -WHERE t1.id <> {$fromId} AND - t1.name LIKE '%{$name}%' AND - ({$usedForClause}) -LIMIT $limit"; - $dao = CRM_Core_DAO::executeQuery($query); - $result = array(); - - while ($dao->fetch()) { - $row = array( - 'id' => $dao->id, - 'text' => ($dao->parent ? "{$dao->parent} :: " : '') . $dao->name, - ); - // Add warning about used_for types - if (!empty($dao->used_for)) { - $usedForTagB = explode(',', $dao->used_for); - sort($usedForTagB); - $usedForDiff = array_diff($usedForTagA, $usedForTagB); - if (!empty($usedForDiff)) { - $row['warning'] = TRUE; - } - } - $result[] = $row; - } - CRM_Utils_JSON::output($result); - } - /** * Get a list of mappings. * * This appears to be only used by scheduled reminders. */ - static public function mappingList() { + public static function mappingList() { if (empty($_GET['mappingID'])) { - CRM_Utils_JSON::output(array('status' => 'error', 'error_msg' => 'required params missing.')); + CRM_Utils_JSON::output(['status' => 'error', 'error_msg' => 'required params missing.']); } $mapping = CRM_Core_BAO_ActionSchedule::getMapping($_GET['mappingID']); - $dateFieldLabels = $mapping ? $mapping->getDateFields() : array(); + $dateFieldLabels = $mapping ? $mapping->getDateFields() : []; // The UX here is quirky -- for "Activity" types, there's a simple drop "Recipients" // dropdown which is always displayed. For other types, the "Recipients" drop down is // conditional upon the weird isLimit ('Limit To / Also Include / Neither') dropdown. $noThanksJustKidding = !$_GET['isLimit']; if ($mapping instanceof CRM_Activity_ActionMapping || !$noThanksJustKidding) { - $entityRecipientLabels = $mapping ? ($mapping->getRecipientTypes() + CRM_Core_BAO_ActionSchedule::getAdditionalRecipients()) : array(); + $entityRecipientLabels = $mapping ? ($mapping->getRecipientTypes() + CRM_Core_BAO_ActionSchedule::getAdditionalRecipients()) : []; } else { $entityRecipientLabels = CRM_Core_BAO_ActionSchedule::getAdditionalRecipients(); } $recipientMapping = array_combine(array_keys($entityRecipientLabels), array_keys($entityRecipientLabels)); - $output = array( - 'sel4' => CRM_Utils_Array::toKeyValueRows($dateFieldLabels), - 'sel5' => CRM_Utils_Array::toKeyValueRows($entityRecipientLabels), + $output = [ + 'sel4' => CRM_Utils_Array::makeNonAssociative($dateFieldLabels), + 'sel5' => CRM_Utils_Array::makeNonAssociative($entityRecipientLabels), 'recipientMapping' => $recipientMapping, - ); + ]; CRM_Utils_JSON::output($output); } @@ -335,39 +335,108 @@ static public function mappingList() { * Ex: GET /civicrm/ajax/recipientListing?mappingID=contribpage&recipientType= */ public static function recipientListing() { - $mappingID = filter_input(INPUT_GET, 'mappingID', FILTER_VALIDATE_REGEXP, array( - 'options' => array( + $mappingID = filter_input(INPUT_GET, 'mappingID', FILTER_VALIDATE_REGEXP, [ + 'options' => [ 'regexp' => '/^[a-zA-Z0-9_\-]+$/', - ), - )); - $recipientType = filter_input(INPUT_GET, 'recipientType', FILTER_VALIDATE_REGEXP, array( - 'options' => array( + ], + ]); + $recipientType = filter_input(INPUT_GET, 'recipientType', FILTER_VALIDATE_REGEXP, [ + 'options' => [ 'regexp' => '/^[a-zA-Z0-9_\-]+$/', - ), - )); + ], + ]); - CRM_Utils_JSON::output(array( - 'recipients' => CRM_Utils_Array::toKeyValueRows(CRM_Core_BAO_ActionSchedule::getRecipientListing($mappingID, $recipientType)), - )); + CRM_Utils_JSON::output([ + 'recipients' => CRM_Utils_Array::makeNonAssociative(CRM_Core_BAO_ActionSchedule::getRecipientListing($mappingID, $recipientType)), + ]); } - public static function mergeTags() { - $tagAId = CRM_Utils_Type::escape($_POST['fromId'], 'Integer'); - $tagBId = CRM_Utils_Type::escape($_POST['toId'], 'Integer'); + /** + * Outputs one branch in the tag tree + * + * Used by jstree to incrementally load tags + */ + public static function getTagTree() { + $parent = CRM_Utils_Type::escape(CRM_Utils_Array::value('parent_id', $_GET, 0), 'Integer'); + $substring = CRM_Utils_Type::escape(CRM_Utils_Array::value('str', $_GET), 'String'); + $result = []; + + $whereClauses = ['is_tagset <> 1']; + $orderColumn = 'name'; - $result = CRM_Core_BAO_EntityTag::mergeTags($tagAId, $tagBId); + // fetch all child tags in Array('parent_tag' => array('child_tag_1', 'child_tag_2', ...)) format + $childTagIDs = CRM_Core_BAO_Tag::getChildTags($substring); + $parentIDs = array_keys($childTagIDs); - if (!empty($result['tagB_used_for'])) { - $usedFor = CRM_Core_OptionGroup::values('tag_used_for'); - foreach ($result['tagB_used_for'] as & $val) { - $val = $usedFor[$val]; + if ($parent) { + $whereClauses[] = "parent_id = $parent"; + } + elseif ($substring) { + $whereClauses['substring'] = " name LIKE '%$substring%' "; + if (!empty($parentIDs)) { + $whereClauses['substring'] = sprintf(" %s OR id IN (%s) ", $whereClauses['substring'], implode(',', $parentIDs)); } - $result['tagB_used_for'] = implode(', ', $result['tagB_used_for']); + $orderColumn = 'id'; } + else { + $whereClauses[] = "parent_id IS NULL"; + } + + $dao = CRM_Utils_SQL_Select::from('civicrm_tag') + ->where($whereClauses) + ->groupBy('id') + ->orderBy($orderColumn) + ->execute(); - $result['message'] = ts('"%1" has been merged with "%2". All records previously tagged "%1" are now tagged "%2".', - array(1 => $result['tagA'], 2 => $result['tagB']) - ); + while ($dao->fetch()) { + if (!empty($substring)) { + $result[] = $dao->id; + if (!empty($childTagIDs[$dao->id])) { + $result = array_merge($result, $childTagIDs[$dao->id]); + } + } + else { + $hasChildTags = empty($childTagIDs[$dao->id]) ? FALSE : TRUE; + $usedFor = (array) explode(',', $dao->used_for); + $tag = [ + 'id' => $dao->id, + 'text' => $dao->name, + 'a_attr' => [ + 'class' => 'crm-tag-item', + ], + 'children' => $hasChildTags, + 'data' => [ + 'description' => (string) $dao->description, + 'is_selectable' => (bool) $dao->is_selectable, + 'is_reserved' => (bool) $dao->is_reserved, + 'used_for' => $usedFor, + 'color' => $dao->color ? $dao->color : '#ffffff', + 'usages' => civicrm_api3('EntityTag', 'getcount', [ + 'entity_table' => ['IN' => $usedFor], + 'tag_id' => $dao->id, + ]), + ], + ]; + if ($dao->description || $dao->is_reserved) { + $tag['li_attr']['title'] = ((string) $dao->description) . ($dao->is_reserved ? ' (*' . ts('Reserved') . ')' : ''); + } + if ($dao->is_reserved) { + $tag['li_attr']['class'] = 'is-reserved'; + } + if ($dao->color) { + $tag['a_attr']['style'] = "background-color: {$dao->color}; color: " . CRM_Utils_Color::getContrast($dao->color); + } + $result[] = $tag; + } + } + + if ($substring) { + $result = array_values(array_unique($result)); + } + + if (!empty($_REQUEST['is_unit_test'])) { + return $result; + } CRM_Utils_JSON::output($result); } diff --git a/CRM/Admin/Page/APIExplorer.php b/CRM/Admin/Page/APIExplorer.php index 90358db15f80..a1ade427f6a9 100644 --- a/CRM/Admin/Page/APIExplorer.php +++ b/CRM/Admin/Page/APIExplorer.php @@ -1,9 +1,9 @@ $rawPath) { + $pathParts = explode(DIRECTORY_SEPARATOR, $rawPath); + foreach ($pathParts as $partId => $part) { + if (empty($part)) { + unset($pathParts[$partId]); + } + } + $newRawPath = implode(DIRECTORY_SEPARATOR, $pathParts); + if ($newRawPath != $rawPath) { + $paths[$id] = DIRECTORY_SEPARATOR . $newRawPath; + } + } + $paths = array_unique($paths); + return $paths; + } + /** * Run page. * @@ -46,17 +69,25 @@ public function run() { ->addScriptFile('civicrm', 'templates/CRM/Admin/Page/APIExplorer.js') ->addScriptFile('civicrm', 'bower_components/google-code-prettify/bin/prettify.min.js', 99) ->addStyleFile('civicrm', 'bower_components/google-code-prettify/bin/prettify.min.css', 99) - ->addVars('explorer', array('max_joins' => \Civi\API\SelectQuery::MAX_JOINS)); + ->addVars('explorer', ['max_joins' => \Civi\API\Api3SelectQuery::MAX_JOINS]); $this->assign('operators', CRM_Core_DAO::acceptedSQLOperators()); // List example directories - $examples = array(); - foreach (scandir(\Civi::paths()->getPath('[civicrm.root]/api/v3/examples')) as $item) { - if ($item && strpos($item, '.') === FALSE) { - $examples[] = $item; + // use get_include_path to ensure that extensions are captured. + $examples = []; + $paths = self::uniquePaths(); + foreach ($paths as $path) { + $dir = \CRM_Utils_File::addTrailingSlash($path) . 'api' . DIRECTORY_SEPARATOR . 'v3' . DIRECTORY_SEPARATOR . 'examples'; + if (is_dir($dir)) { + foreach (scandir($dir) as $item) { + if ($item && strpos($item, '.') === FALSE && array_search($item, $examples) === FALSE) { + $examples[] = $item; + } + } } } + sort($examples); $this->assign('examples', $examples); return parent::run(); @@ -77,21 +108,32 @@ public function userContext() { */ public static function getExampleFile() { if (!empty($_GET['entity']) && strpos($_GET['entity'], '.') === FALSE) { - $examples = array(); - foreach (scandir(\Civi::paths()->getPath("[civicrm.root]/api/v3/examples/{$_GET['entity']}")) as $item) { - $item = str_replace('.php', '', $item); - if ($item && strpos($item, '.') === FALSE) { - $examples[] = array('key' => $item, 'value' => $item); + $examples = []; + $paths = self::uniquePaths(); + foreach ($paths as $path) { + $dir = \CRM_Utils_File::addTrailingSlash($path) . 'api' . DIRECTORY_SEPARATOR . 'v3' . DIRECTORY_SEPARATOR . 'examples' . DIRECTORY_SEPARATOR . $_GET['entity']; + if (is_dir($dir)) { + foreach (scandir($dir) as $item) { + $item = str_replace('.php', '', $item); + if ($item && strpos($item, '.') === FALSE) { + $examples[] = ['key' => $item, 'value' => $item]; + } + } } } CRM_Utils_JSON::output($examples); } if (!empty($_GET['file']) && strpos($_GET['file'], '.') === FALSE) { - $fileName = \Civi::paths()->getPath("[civicrm.root]/api/v3/examples/{$_GET['file']}.php"); - if (file_exists($fileName)) { - echo file_get_contents($fileName); + $paths = self::uniquePaths(); + $fileFound = FALSE; + foreach ($paths as $path) { + $fileName = \CRM_Utils_File::addTrailingSlash($path) . 'api' . DIRECTORY_SEPARATOR . 'v3' . DIRECTORY_SEPARATOR . 'examples' . DIRECTORY_SEPARATOR . $_GET['file'] . '.php'; + if (!$fileFound && file_exists($fileName)) { + $fileFound = TRUE; + echo file_get_contents($fileName); + } } - else { + if (!$fileFound) { echo "Not found."; } CRM_Utils_System::civiExit(); @@ -109,11 +151,11 @@ public static function getDoc() { if (!empty($entity) && in_array($entity, $entities['values']) && strpos($entity, '.') === FALSE) { $action = CRM_Utils_Array::value('action', $_GET); $doc = self::getDocblock($entity, $action); - $result = array( + $result = [ 'doc' => $doc ? self::formatDocBlock($doc[0]) : 'Not found.', 'code' => $doc ? $doc[1] : NULL, 'file' => $doc ? $doc[2] : NULL, - ); + ]; if (!$action) { $actions = civicrm_api3($entity, 'getactions'); $result['actions'] = CRM_Utils_Array::makeNonAssociative(array_combine($actions['values'], $actions['values'])); @@ -141,11 +183,11 @@ private static function getDocBlock($entity, $action) { // Api does not exist return FALSE; } - $docblock = $code = array(); + $docblock = $code = []; // Fetch docblock for the api file if (!$action) { if (preg_match('#/\*\*\n.*?\n \*/\n#s', $contents, $docblock)) { - return array($docblock[0], NULL, $file); + return [$docblock[0], NULL, $file]; } } // Fetch block for a specific action @@ -172,7 +214,7 @@ private static function getDocBlock($entity, $action) { if (preg_match('#(/\*\*(\n \*.*)*\n \*/\n)function[ ]+' . $fnName . '#i', $contents, $docblock)) { // Fetch the code in a separate regex to preserve sanity preg_match("#^function[ ]+$fnName.*?^}#ism", $contents, $code); - return array($docblock[1], $code[0], $file); + return [$docblock[1], $code[0], $file]; } } } @@ -184,15 +226,20 @@ private static function getDocBlock($entity, $action) { * @param string $text * @return string */ - private static function formatDocBlock($text) { + public static function formatDocBlock($text) { + // Normalize #leading spaces. + $lines = explode("\n", $text); + $lines = preg_replace('/^ +\*/', ' *', $lines); + $text = implode("\n", $lines); + // Get rid of comment stars - $text = str_replace(array("\n * ", "\n *\n", "\n */\n", "/**\n"), array("\n", "\n\n", '', ''), $text); + $text = str_replace(["\n * ", "\n *\n", "\n */\n", "/**\n"], ["\n", "\n\n", '', ''], $text); // Format for html $text = htmlspecialchars($text); // Extract code blocks - save for later to skip html conversion - $code = array(); + $code = []; preg_match_all('#@code(.*?)@endcode#is', $text, $code); $text = preg_replace('#@code.*?@endcode#is', '
', $text);
 
diff --git a/CRM/Admin/Page/Access.php b/CRM/Admin/Page/Access.php
index 62d84f0980a8..9d016ae5a19f 100644
--- a/CRM/Admin/Page/Access.php
+++ b/CRM/Admin/Page/Access.php
@@ -1,9 +1,9 @@
 assign('registerSite', htmlspecialchars('https://civicrm.org/register-your-site?src=iam&sid=' . CRM_Utils_System::getSiteID()));
 
-    $groups = array(
+    $groups = [
       'Customize Data and Screens' => ts('Customize Data and Screens'),
       'Communications' => ts('Communications'),
       'Localization' => ts('Localization'),
       'Users and Permissions' => ts('Users and Permissions'),
       'System Settings' => ts('System Settings'),
-    );
+    ];
 
     $config = CRM_Core_Config::singleton();
     if (in_array('CiviContribute', $config->enableComponents)) {
@@ -98,7 +99,7 @@ public function run() {
         $adminPanel[$groupId]['title'] = $title;
       }
       else {
-        $adminPanel[$groupId] = array();
+        $adminPanel[$groupId] = [];
         $adminPanel[$groupId]['show'] = '';
         $adminPanel[$groupId]['hide'] = '';
         $adminPanel[$groupId]['title'] = $title;
diff --git a/CRM/Admin/Page/CKEditorConfig.php b/CRM/Admin/Page/CKEditorConfig.php
index ebeb35a4196b..22f65399139c 100644
--- a/CRM/Admin/Page/CKEditorConfig.php
+++ b/CRM/Admin/Page/CKEditorConfig.php
@@ -1,9 +1,9 @@
  'moono',
-    'extraPlugins' => '',
-  );
+  public $blackList = [
+    'on',
+    'skin',
+    'extraPlugins',
+    'toolbarGroups',
+    'removeButtons',
+    'filebrowserBrowseUrl',
+    'filebrowserImageBrowseUrl',
+    'filebrowserFlashBrowseUrl',
+    'filebrowserUploadUrl',
+    'filebrowserImageUploadUrl',
+    'filebrowserFlashUploadUrl',
+  ];
+
+  public $preset;
 
   /**
    * Run page.
@@ -58,14 +69,19 @@ class CRM_Admin_Page_CKEditorConfig extends CRM_Core_Page {
    * @return string
    */
   public function run() {
+    $this->preset = CRM_Utils_Array::value('preset', $_REQUEST, 'default');
+
     // If the form was submitted, take appropriate action.
     if (!empty($_POST['revert'])) {
-      self::deleteConfigFile();
+      self::deleteConfigFile($this->preset);
+      self::setConfigDefault();
     }
     elseif (!empty($_POST['config'])) {
       $this->save($_POST);
     }
 
+    $settings = $this->getConfigSettings();
+
     CRM_Core_Resources::singleton()
       ->addScriptFile('civicrm', 'bower_components/ckeditor/ckeditor.js', 0, 'page-header')
       ->addScriptFile('civicrm', 'bower_components/ckeditor/samples/toolbarconfigurator/js/fulltoolbareditor.js', 1)
@@ -74,20 +90,28 @@ public function run() {
       ->addScriptFile('civicrm', 'js/wysiwyg/admin.ckeditor-configurator.js', 10)
       ->addStyleFile('civicrm', 'bower_components/ckeditor/samples/toolbarconfigurator/css/fontello.css')
       ->addStyleFile('civicrm', 'bower_components/ckeditor/samples/css/samples.css')
-      ->addVars('ckConfig', array(
+      ->addVars('ckConfig', [
         'plugins' => array_values($this->getCKPlugins()),
-      ));
+        'blacklist' => $this->blackList,
+        'settings' => $settings,
+      ]);
 
+    $configUrl = self::getConfigUrl($this->preset) ?: self::getConfigUrl('default');
+
+    $this->assign('preset', $this->preset);
+    $this->assign('presets', CRM_Core_OptionGroup::values('wysiwyg_presets', FALSE, FALSE, FALSE, NULL, 'label', TRUE, FALSE, 'name'));
     $this->assign('skins', $this->getCKSkins());
-    $this->assign('skin', $this->getConfigSetting('skin'));
-    $this->assign('extraPlugins', $this->getConfigSetting('extraPlugins'));
-    $this->assign('configUrl', self::getConfigUrl());
-    $this->assign('revertConfirm', htmlspecialchars(ts('Are you sure you want to revert all changes?', array('escape' => 'js'))));
+    $this->assign('skin', CRM_Utils_Array::value('skin', $settings));
+    $this->assign('extraPlugins', CRM_Utils_Array::value('extraPlugins', $settings));
+    $this->assign('configUrl', $configUrl);
+    $this->assign('revertConfirm', htmlspecialchars(ts('Are you sure you want to revert all changes?', ['escape' => 'js'])));
 
-    CRM_Utils_System::appendBreadCrumb(array(array(
-      'url' => CRM_Utils_System::url('civicrm/admin/setting/preferences/display', 'reset=1'),
-      'title' => ts('Display Preferences'),
-    )));
+    CRM_Utils_System::appendBreadCrumb([
+      [
+        'url' => CRM_Utils_System::url('civicrm/admin/setting/preferences/display', 'reset=1'),
+        'title' => ts('Display Preferences'),
+      ],
+    ]);
 
     return parent::run();
   }
@@ -98,24 +122,30 @@ public function run() {
    * @param array $params
    */
   public function save($params) {
-    $config = "/**\n"
-      . " * CKEditor config file auto-generated by CiviCRM.\n"
-      . " *\n"
-      . " * Note: This file will be overwritten if settings are modified at:\n"
-      . " * @link " . CRM_Utils_System::url(CRM_Utils_System::currentPath(), NULL, TRUE, NULL, FALSE) . "\n"
-      . " */\n\n"
+    $config = self::fileHeader()
       // Standardize line-endings
       . preg_replace('~\R~u', "\n", $params['config']);
 
-    // Use defaultSettings as a whitelist so we don't just insert any old junk into the file
-    foreach ($this->defaultSettings as $key => $default) {
-      if (isset($params[$key]) && strlen($params[$key])) {
+    // Use all params starting with config_
+    foreach ($params as $key => $val) {
+      $val = trim($val);
+      if (strpos($key, 'config_') === 0 && strlen($val)) {
+        if ($val != 'true' && $val != 'false' && $val != 'null' && $val[0] != '{' && $val[0] != '[' && !is_numeric($val)) {
+          $val = json_encode($val, JSON_UNESCAPED_SLASHES);
+        }
+        elseif ($val[0] == '{' || $val[0] == '[') {
+          if (!is_array(json_decode($val, TRUE))) {
+            // Invalid JSON. Do not save.
+            continue;
+          }
+        }
         $pos = strrpos($config, '};');
-        $setting = "\n\tconfig.$key = '{$params[$key]}';\n";
+        $key = preg_replace('/^config_/', 'config.', $key);
+        $setting = "\n\t{$key} = {$val};\n";
         $config = substr_replace($config, $setting, $pos, 0);
       }
     }
-    self::saveConfigFile($config);
+    self::saveConfigFile($this->preset, $config);
     if (!empty($params['save'])) {
       CRM_Core_Session::setStatus(ts("You may need to clear your browser's cache to see the changes in CiviCRM."), ts('CKEditor Saved'), 'success');
     }
@@ -127,7 +157,7 @@ public function save($params) {
    * @return array
    */
   private function getCKPlugins() {
-    $plugins = array();
+    $plugins = [];
     $pluginDir = Civi::paths()->getPath('[civicrm.root]/bower_components/ckeditor/plugins');
 
     foreach (glob($pluginDir . '/*', GLOB_ONLYDIR) as $dir) {
@@ -135,11 +165,11 @@ private function getCKPlugins() {
       $name = substr($dir, strrpos($dir, '/') + 1);
       $dir = CRM_Utils_file::addTrailingSlash($dir, '/');
       if (is_file($dir . 'plugin.js')) {
-        $plugins[$name] = array(
+        $plugins[$name] = [
           'id' => $name,
           'text' => ucfirst($name),
           'icon' => NULL,
-        );
+        ];
         if (is_dir($dir . "icons")) {
           if (is_file($dir . "icons/$name.png")) {
             $plugins[$name]['icon'] = "bower_components/ckeditor/plugins/$name/icons/$name.png";
@@ -162,7 +192,7 @@ private function getCKPlugins() {
    * @return array
    */
   private function getCKSkins() {
-    $skins = array();
+    $skins = [];
     $skinDir = Civi::paths()->getPath('[civicrm.root]/bower_components/ckeditor/skins');
     foreach (glob($skinDir . '/*', GLOB_ONLYDIR) as $dir) {
       $dir = rtrim(str_replace('\\', '/', $dir), '/');
@@ -172,60 +202,92 @@ private function getCKSkins() {
   }
 
   /**
-   * @param $setting
-   * @return string
+   * @return array
    */
-  private function getConfigSetting($setting) {
-    $value = CRM_Utils_Array::value($setting, $this->defaultSettings, '');
-    $file = self::getConfigFile();
+  private function getConfigSettings() {
+    $matches = $result = [];
+    $file = self::getConfigFile($this->preset) ?: self::getConfigFile('default');
+    $result['skin'] = 'moono';
     if ($file) {
       $contents = file_get_contents($file);
-      $matches = array();
-      preg_match("/\sconfig\.$setting\s?=\s?'([^']*)'/", $contents, $matches);
-      if ($matches) {
-        $value = $matches[1];
+      preg_match_all("/\sconfig\.(\w+)\s?=\s?([^;]*);/", $contents, $matches);
+      foreach ($matches[1] as $i => $match) {
+        $result[$match] = trim($matches[2][$i], ' "\'');
       }
     }
-    return $value;
+    return $result;
   }
 
   /**
-   * @return null|string
+   * @param string $preset
+   *   Omit to get an array of all presets
+   * @return array|null|string
    */
-  public static function getConfigUrl() {
-    if (self::getConfigFile()) {
-      return Civi::paths()->getUrl(self::CONFIG_FILENAME, 'absolute');
+  public static function getConfigUrl($preset = NULL) {
+    $items = [];
+    $presets = CRM_Core_OptionGroup::values('wysiwyg_presets', FALSE, FALSE, FALSE, NULL, 'name');
+    foreach ($presets as $key => $name) {
+      if (self::getConfigFile($name)) {
+        $items[$name] = Civi::paths()->getUrl(self::CONFIG_FILEPATH . $name . '.js', 'absolute');
+      }
     }
-    return NULL;
+    return $preset ? CRM_Utils_Array::value($preset, $items) : $items;
   }
 
   /**
-   * @param bool $checkIfFileExists
-   *   If false, this fn will return fileName even if it doesn't exist
+   * @param string $preset
    *
    * @return null|string
    */
-  public static function getConfigFile($checkIfFileExists = TRUE) {
-    $fileName = Civi::paths()->getPath(self::CONFIG_FILENAME);
-    return !$checkIfFileExists || is_file($fileName) ? $fileName : NULL;
+  public static function getConfigFile($preset = 'default') {
+    $fileName = Civi::paths()->getPath(self::CONFIG_FILEPATH . $preset . '.js');
+    return is_file($fileName) ? $fileName : NULL;
   }
 
   /**
+   * @param string $preset
    * @param string $contents
    */
-  public static function saveConfigFile($contents) {
-    $file = self::getConfigFile(FALSE);
+  public static function saveConfigFile($preset, $contents) {
+    $file = Civi::paths()->getPath(self::CONFIG_FILEPATH . $preset . '.js');
     file_put_contents($file, $contents);
   }
 
   /**
    * Delete config file.
    */
-  public static function deleteConfigFile() {
-    $file = self::getConfigFile();
+  public static function deleteConfigFile($preset) {
+    $file = self::getConfigFile($preset);
     if ($file) {
       unlink($file);
     }
   }
 
+  /**
+   * Create default config file if it doesn't exist
+   */
+  public static function setConfigDefault() {
+    if (!self::getConfigFile()) {
+      $config = self::fileHeader() . "CKEDITOR.editorConfig = function( config ) {\n\tconfig.allowedContent = true;\n};\n";
+      // Make sure directories exist
+      if (!is_dir(Civi::paths()->getPath('[civicrm.files]/persist'))) {
+        mkdir(Civi::paths()->getPath('[civicrm.files]/persist'));
+      }
+      $newFileName = Civi::paths()->getPath(self::CONFIG_FILEPATH . 'default.js');
+      file_put_contents($newFileName, $config);
+    }
+  }
+
+  /**
+   * @return string
+   */
+  public static function fileHeader() {
+    return "/**\n"
+    . " * CKEditor config file auto-generated by CiviCRM (" . date('Y-m-d H:i:s') . ").\n"
+    . " *\n"
+    . " * Note: This file will be overwritten if settings are modified at:\n"
+    . " * @link " . CRM_Utils_System::url('civicrm/admin/ckeditor', NULL, TRUE, NULL, FALSE) . "\n"
+    . " */\n";
+  }
+
 }
diff --git a/CRM/Admin/Page/ConfigTaskList.php b/CRM/Admin/Page/ConfigTaskList.php
index 76957494b3f3..7d43f650e485 100644
--- a/CRM/Admin/Page/ConfigTaskList.php
+++ b/CRM/Admin/Page/ConfigTaskList.php
@@ -1,9 +1,9 @@
  array(
+      self::$_links = [
+        CRM_Core_Action::UPDATE => [
           'name' => ts('Edit'),
           'url' => 'civicrm/admin/options/subtype',
           'qs' => 'action=update&id=%%id%%&reset=1',
           'title' => ts('Edit Contact Type'),
-        ),
-        CRM_Core_Action::DISABLE => array(
+        ],
+        CRM_Core_Action::DISABLE => [
           'name' => ts('Disable'),
           'ref' => 'crm-enable-disable',
           'title' => ts('Disable Contact Type'),
-        ),
-        CRM_Core_Action::ENABLE => array(
+        ],
+        CRM_Core_Action::ENABLE => [
           'name' => ts('Enable'),
           'ref' => 'crm-enable-disable',
           'title' => ts('Enable Contact Type'),
-        ),
-        CRM_Core_Action::DELETE => array(
+        ],
+        CRM_Core_Action::DELETE => [
           'name' => ts('Delete'),
           'url' => 'civicrm/admin/options/subtype',
           'qs' => 'action=delete&id=%%id%%',
           'title' => ts('Delete Contact Type'),
-        ),
-      );
+        ],
+      ];
     }
     return self::$_links;
   }
@@ -124,7 +124,7 @@ public function browse() {
         }
       }
       $rows[$key]['action'] = CRM_Core_Action::formLink(self::links(), $mask,
-        array('id' => $value['id']),
+        ['id' => $value['id']],
         ts('more'),
         FALSE,
         'contactType.manage.action',
diff --git a/CRM/Admin/Page/EventTemplate.php b/CRM/Admin/Page/EventTemplate.php
index 5d2b45baf4ba..049b4d65667c 100644
--- a/CRM/Admin/Page/EventTemplate.php
+++ b/CRM/Admin/Page/EventTemplate.php
@@ -1,9 +1,9 @@
  array(
+      self::$_links = [
+        CRM_Core_Action::UPDATE => [
           'name' => ts('Edit'),
           'url' => 'civicrm/event/manage/settings',
           'qs' => 'action=update&id=%%id%%&reset=1',
           'title' => ts('Edit Event Template'),
-        ),
-        CRM_Core_Action::DELETE => array(
+        ],
+        CRM_Core_Action::DELETE => [
           'name' => ts('Delete'),
           'url' => 'civicrm/event/manage',
           'qs' => 'action=delete&id=%%id%%',
           'title' => ts('Delete Event Template'),
-        ),
-      );
+        ],
+      ];
     }
 
     return self::$_links;
@@ -86,7 +86,7 @@ public function &links() {
    */
   public function browse() {
     //get all event templates.
-    $allEventTemplates = array();
+    $allEventTemplates = [];
 
     $eventTemplate = new CRM_Event_DAO_Event();
 
@@ -120,7 +120,7 @@ public function browse() {
 
       //add action links.
       $allEventTemplates[$eventTemplate->id]['action'] = CRM_Core_Action::formLink(self::links(), $action,
-        array('id' => $eventTemplate->id),
+        ['id' => $eventTemplate->id],
         ts('more'),
         FALSE,
         'eventTemplate.manage.action',
diff --git a/CRM/Admin/Page/Extensions.php b/CRM/Admin/Page/Extensions.php
index b595b5916272..e190d36f636e 100644
--- a/CRM/Admin/Page/Extensions.php
+++ b/CRM/Admin/Page/Extensions.php
@@ -1,9 +1,9 @@
  array(
+      self::$_links = [
+        CRM_Core_Action::ADD => [
           'name' => ts('Install'),
           'url' => 'civicrm/admin/extensions',
           'qs' => 'action=add&id=%%id%%&key=%%key%%',
           'title' => ts('Install'),
-        ),
-        CRM_Core_Action::ENABLE => array(
+        ],
+        CRM_Core_Action::ENABLE => [
           'name' => ts('Enable'),
           'url' => 'civicrm/admin/extensions',
           'qs' => 'action=enable&id=%%id%%&key=%%key%%',
           'ref' => 'enable-action',
           'title' => ts('Enable'),
-        ),
-        CRM_Core_Action::DISABLE => array(
+        ],
+        CRM_Core_Action::DISABLE => [
           'name' => ts('Disable'),
           'url' => 'civicrm/admin/extensions',
           'qs' => 'action=disable&id=%%id%%&key=%%key%%',
           'title' => ts('Disable'),
-        ),
-        CRM_Core_Action::DELETE => array(
+        ],
+        CRM_Core_Action::DELETE => [
           'name' => ts('Uninstall'),
           'url' => 'civicrm/admin/extensions',
           'qs' => 'action=delete&id=%%id%%&key=%%key%%',
           'title' => ts('Uninstall Extension'),
-        ),
-        CRM_Core_Action::UPDATE => array(
+        ],
+        CRM_Core_Action::UPDATE => [
           'name' => ts('Download'),
           'url' => 'civicrm/admin/extensions',
           'qs' => 'action=update&id=%%id%%&key=%%key%%',
           'title' => ts('Download Extension'),
-        ),
-      );
+        ],
+      ];
     }
     return self::$_links;
   }
@@ -125,8 +125,6 @@ public function run() {
    * Browse all options.
    */
   public function browse() {
-    $mapper = CRM_Extension_System::singleton()->getMapper();
-    $manager = CRM_Extension_System::singleton()->getManager();
 
     // build announcements at the top of the page
     $this->assign('extAddNewEnabled', CRM_Extension_System::singleton()->getBrowser()->isEnabled());
@@ -145,8 +143,25 @@ public function browse() {
     // TODO: Debate whether to immediately detect changes in underlying source tree
     // $manager->refresh();
 
-    // build list of local extensions
-    $localExtensionRows = array(); // array($pseudo_id => extended_CRM_Extension_Info)
+    $localExtensionRows = $this->formatLocalExtensionRows();
+    $this->assign('localExtensionRows', $localExtensionRows);
+
+    $remoteExtensionRows = $this->formatRemoteExtensionRows($localExtensionRows);
+    $this->assign('remoteExtensionRows', $remoteExtensionRows);
+  }
+
+  /**
+   * Get the list of local extensions and format them as a table with
+   * status and action data.
+   *
+   * @return array
+   */
+  public function formatLocalExtensionRows() {
+    $mapper = CRM_Extension_System::singleton()->getMapper();
+    $manager = CRM_Extension_System::singleton()->getManager();
+
+    // array($pseudo_id => extended_CRM_Extension_Info)
+    $localExtensionRows = [];
     $keys = array_keys($manager->getStatuses());
     sort($keys);
     foreach ($keys as $key) {
@@ -154,7 +169,7 @@ public function browse() {
         $obj = $mapper->keyToInfo($key);
       }
       catch (CRM_Extension_Exception $ex) {
-        CRM_Core_Session::setStatus(ts('Failed to read extension (%1). Please refresh the extension list.', array(1 => $key)));
+        CRM_Core_Session::setStatus(ts('Failed to read extension (%1). Please refresh the extension list.', [1 => $key]));
         continue;
       }
 
@@ -188,10 +203,10 @@ public function browse() {
       // then $action += CRM_Core_Action::UPDATE
       $row['action'] = CRM_Core_Action::formLink(self::links(),
         $action,
-        array(
+        [
           'id' => $row['id'],
           'key' => $obj->key,
-        ),
+        ],
         ts('more'),
         FALSE,
         'extension.local.action',
@@ -203,28 +218,42 @@ public function browse() {
 
       $localExtensionRows[$row['id']] = $row;
     }
-    $this->assign('localExtensionRows', $localExtensionRows);
+    return $localExtensionRows;
+  }
 
+  /**
+   * Get the list of remote extensions and format them as a table with
+   * status and action data.
+   *
+   * @param array $localExtensionRows
+   * @return array
+   */
+  public function formatRemoteExtensionRows($localExtensionRows) {
     try {
       $remoteExtensions = CRM_Extension_System::singleton()->getBrowser()->getExtensions();
     }
     catch (CRM_Extension_Exception $e) {
-      $remoteExtensions = array();
+      $remoteExtensions = [];
       CRM_Core_Session::setStatus($e->getMessage(), ts('Extension download error'), 'error');
     }
 
     // build list of available downloads
-    $remoteExtensionRows = array();
+    $remoteExtensionRows = [];
+    $compat = CRM_Extension_System::getCompatibilityInfo();
+
     foreach ($remoteExtensions as $info) {
+      if (!empty($compat[$info->key]['obsolete'])) {
+        continue;
+      }
       $row = (array) $info;
       $row['id'] = $info->key;
       $action = CRM_Core_Action::UPDATE;
       $row['action'] = CRM_Core_Action::formLink(self::links(),
         $action,
-        array(
+        [
           'id' => $row['id'],
           'key' => $row['key'],
-        ),
+        ],
         ts('more'),
         FALSE,
         'extension.remote.action',
@@ -232,13 +261,16 @@ public function browse() {
         $row['id']
       );
       if (isset($localExtensionRows[$info->key])) {
-        if (version_compare($localExtensionRows[$info->key]['version'], $info->version, '<')) {
-          $row['is_upgradeable'] = TRUE;
+        if (array_key_exists('version', $localExtensionRows[$info->key])) {
+          if (version_compare($localExtensionRows[$info->key]['version'], $info->version, '<')) {
+            $row['is_upgradeable'] = TRUE;
+          }
         }
       }
       $remoteExtensionRows[$row['id']] = $row;
     }
-    $this->assign('remoteExtensionRows', $remoteExtensionRows);
+
+    return $remoteExtensionRows;
   }
 
   /**
@@ -296,43 +328,7 @@ public function userContextParams($mode = NULL) {
    * @return array
    */
   public static function createExtendedInfo(CRM_Extension_Info $obj) {
-    $mapper = CRM_Extension_System::singleton()->getMapper();
-    $manager = CRM_Extension_System::singleton()->getManager();
-
-    $extensionRow = (array) $obj;
-    try {
-      $extensionRow['path'] = $mapper->keyToBasePath($obj->key);
-    }
-    catch (CRM_Extension_Exception $e) {
-      $extensionRow['path'] = '';
-    }
-    $extensionRow['status'] = $manager->getStatus($obj->key);
-
-    switch ($extensionRow['status']) {
-      case CRM_Extension_Manager::STATUS_UNINSTALLED:
-        $extensionRow['statusLabel'] = ''; // ts('Uninstalled');
-        break;
-
-      case CRM_Extension_Manager::STATUS_DISABLED:
-        $extensionRow['statusLabel'] = ts('Disabled');
-        break;
-
-      case CRM_Extension_Manager::STATUS_INSTALLED:
-        $extensionRow['statusLabel'] = ts('Enabled'); // ts('Installed');
-        break;
-
-      case CRM_Extension_Manager::STATUS_DISABLED_MISSING:
-        $extensionRow['statusLabel'] = ts('Disabled (Missing)');
-        break;
-
-      case CRM_Extension_Manager::STATUS_INSTALLED_MISSING:
-        $extensionRow['statusLabel'] = ts('Enabled (Missing)'); // ts('Installed');
-        break;
-
-      default:
-        $extensionRow['statusLabel'] = '(' . $extensionRow['status'] . ')';
-    }
-    return $extensionRow;
+    return CRM_Extension_System::createExtendedInfo($obj);
   }
 
 }
diff --git a/CRM/Admin/Page/ExtensionsUpgrade.php b/CRM/Admin/Page/ExtensionsUpgrade.php
index 1c7d0d861c41..3a183826fc25 100644
--- a/CRM/Admin/Page/ExtensionsUpgrade.php
+++ b/CRM/Admin/Page/ExtensionsUpgrade.php
@@ -10,18 +10,22 @@ class CRM_Admin_Page_ExtensionsUpgrade extends CRM_Core_Page {
   const END_URL = 'civicrm/admin/extensions';
   const END_PARAMS = 'reset=1';
 
+  /**
+   * Run Page.
+   */
   public function run() {
     $queue = CRM_Extension_Upgrades::createQueue();
-    $runner = new CRM_Queue_Runner(array(
+    $runner = new CRM_Queue_Runner([
       'title' => ts('Database Upgrades'),
       'queue' => $queue,
       'errorMode' => CRM_Queue_Runner::ERROR_ABORT,
-      'onEnd' => array('CRM_Admin_Page_ExtensionsUpgrade', 'onEnd'),
-      'onEndUrl' => CRM_Utils_System::url(self::END_URL, self::END_PARAMS),
-    ));
+      'onEnd' => ['CRM_Admin_Page_ExtensionsUpgrade', 'onEnd'],
+      'onEndUrl' => !empty($_GET['destination']) ? $_GET['destination'] : CRM_Utils_System::url(self::END_URL, self::END_PARAMS),
+    ]);
 
     CRM_Core_Error::debug_log_message('CRM_Admin_Page_ExtensionsUpgrade: Start upgrades');
-    $runner->runAllViaWeb(); // does not return
+    // does not return
+    $runner->runAllViaWeb();
   }
 
   /**
diff --git a/CRM/Admin/Page/Job.php b/CRM/Admin/Page/Job.php
index cd29853bc075..9fdc42cc44be 100644
--- a/CRM/Admin/Page/Job.php
+++ b/CRM/Admin/Page/Job.php
@@ -1,9 +1,9 @@
  'action=delete&id=%%id%%',
           'title' => ts('Delete Scheduled Job'),
         ),
+        CRM_Core_Action::COPY => array(
+          'name' => ts('Copy'),
+          'url' => 'civicrm/admin/job',
+          'qs' => 'action=copy&id=%%id%%',
+          'title' => ts('Copy Scheduled Job'),
+        ),
       );
     }
     return self::$_links;
@@ -128,11 +134,25 @@ public function run() {
       $this, FALSE, 0
     );
 
+    // FIXME: Why are we comparing an integer with a string here?
     if ($this->_action == 'export') {
       $session = CRM_Core_Session::singleton();
       $session->pushUserContext(CRM_Utils_System::url('civicrm/admin/job', 'reset=1'));
     }
 
+    if (($this->_action & CRM_Core_Action::COPY) && (!empty($this->_id))) {
+      try {
+        $jobResult = civicrm_api3('Job', 'clone', array('id' => $this->_id));
+        if ($jobResult['count'] > 0) {
+          CRM_Core_Session::setStatus($jobResult['values'][$jobResult['id']]['name'], ts('Job copied successfully'), 'success');
+        }
+        CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/admin/job', 'reset=1'));
+      }
+      catch (Exception $e) {
+        CRM_Core_Session::setStatus(ts('Failed to copy job'), 'Error');
+      }
+    }
+
     return parent::run();
   }
 
@@ -142,6 +162,10 @@ public function run() {
    * @param null $action
    */
   public function browse($action = NULL) {
+    // check if non-prod mode is enabled.
+    if (CRM_Core_Config::environment() != 'Production') {
+      CRM_Core_Session::setStatus(ts('Execution of scheduled jobs has been turned off by default since this is a non-production environment. You can override this for particular jobs by adding runInNonProductionEnvironment=TRUE as a parameter.'), ts("Non-production Environment"), "warning", array('expires' => 0));
+    }
 
     // using Export action for Execute. Doh.
     if ($this->_action & CRM_Core_Action::EXPORT) {
diff --git a/CRM/Admin/Page/JobLog.php b/CRM/Admin/Page/JobLog.php
index 869fcf364be5..b71ba3ab4d41 100644
--- a/CRM/Admin/Page/JobLog.php
+++ b/CRM/Admin/Page/JobLog.php
@@ -1,9 +1,9 @@
  array(
+      self::$_links = [
+        CRM_Core_Action::UPDATE => [
           'name' => ts('Edit'),
           'url' => 'civicrm/admin/labelFormats',
           'qs' => 'action=update&id=%%id%%&group=%%group%%&reset=1',
           'title' => ts('Edit Label Format'),
-        ),
-        CRM_Core_Action::COPY => array(
+        ],
+        CRM_Core_Action::COPY => [
           'name' => ts('Copy'),
           'url' => 'civicrm/admin/labelFormats',
           'qs' => 'action=copy&id=%%id%%&group=%%group%%&reset=1',
           'title' => ts('Copy Label Format'),
-        ),
-        CRM_Core_Action::DELETE => array(
+        ],
+        CRM_Core_Action::DELETE => [
           'name' => ts('Delete'),
           'url' => 'civicrm/admin/labelFormats',
           'qs' => 'action=delete&id=%%id%%&group=%%group%%&reset=1',
           'title' => ts('Delete Label Format'),
-        ),
-      );
+        ],
+      ];
     }
 
     return self::$_links;
@@ -141,7 +141,7 @@ public function browse($action = NULL) {
 
       $format['groupName'] = ts('Mailing Label');
       $format['action'] = CRM_Core_Action::formLink(self::links(), $action,
-        array('id' => $format['id'], 'group' => 'label_format'),
+        ['id' => $format['id'], 'group' => 'label_format'],
         ts('more'),
         FALSE,
         'labelFormat.manage.action',
diff --git a/CRM/Admin/Page/LocationType.php b/CRM/Admin/Page/LocationType.php
index d6699ad50c55..f22c00800a7b 100644
--- a/CRM/Admin/Page/LocationType.php
+++ b/CRM/Admin/Page/LocationType.php
@@ -1,9 +1,9 @@
  array(
+      self::$_links = [
+        CRM_Core_Action::UPDATE => [
           'name' => ts('Edit'),
           'url' => 'civicrm/admin/locationType',
           'qs' => 'action=update&id=%%id%%&reset=1',
           'title' => ts('Edit Location Type'),
-        ),
-        CRM_Core_Action::DISABLE => array(
+        ],
+        CRM_Core_Action::DISABLE => [
           'name' => ts('Disable'),
           'ref' => 'crm-enable-disable',
           'title' => ts('Disable Location Type'),
-        ),
-        CRM_Core_Action::ENABLE => array(
+        ],
+        CRM_Core_Action::ENABLE => [
           'name' => ts('Enable'),
           'ref' => 'crm-enable-disable',
           'title' => ts('Enable Location Type'),
-        ),
-        CRM_Core_Action::DELETE => array(
+        ],
+        CRM_Core_Action::DELETE => [
           'name' => ts('Delete'),
           'url' => 'civicrm/admin/locationType',
           'qs' => 'action=delete&id=%%id%%',
           'title' => ts('Delete Location Type'),
-        ),
-      );
+        ],
+      ];
     }
     return self::$_links;
   }
diff --git a/CRM/Admin/Page/MailSettings.php b/CRM/Admin/Page/MailSettings.php
index e4256c8e474f..5a95c855e04d 100644
--- a/CRM/Admin/Page/MailSettings.php
+++ b/CRM/Admin/Page/MailSettings.php
@@ -1,9 +1,9 @@
  array(
+      self::$_links = [
+        CRM_Core_Action::UPDATE => [
           'name' => ts('Edit'),
           'url' => 'civicrm/admin/mailSettings',
           'qs' => 'action=update&id=%%id%%&reset=1',
           'title' => ts('Edit Mail Settings'),
-        ),
-        CRM_Core_Action::DELETE => array(
+        ],
+        CRM_Core_Action::DELETE => [
           'name' => ts('Delete'),
           'url' => 'civicrm/admin/mailSettings',
           'qs' => 'action=delete&id=%%id%%',
           'title' => ts('Delete Mail Settings'),
-        ),
-      );
+        ],
+      ];
     }
 
     return self::$_links;
@@ -88,7 +88,7 @@ public function &links() {
    */
   public function browse() {
     //get all mail settings.
-    $allMailSettings = array();
+    $allMailSettings = [];
     $mailSetting = new CRM_Core_DAO_MailSettings();
 
     $allProtocols = CRM_Core_PseudoConstant::get('CRM_Core_DAO_MailSettings', 'protocol');
@@ -113,7 +113,7 @@ public function browse() {
 
       //add action links.
       $allMailSettings[$mailSetting->id]['action'] = CRM_Core_Action::formLink(self::links(), $action,
-        array('id' => $mailSetting->id),
+        ['id' => $mailSetting->id],
         ts('more'),
         FALSE,
         'mailSetting.manage.action',
diff --git a/CRM/Admin/Page/Mapping.php b/CRM/Admin/Page/Mapping.php
index 93eb274be3bd..0da215d2cc2c 100644
--- a/CRM/Admin/Page/Mapping.php
+++ b/CRM/Admin/Page/Mapping.php
@@ -1,9 +1,9 @@
  array(
+      self::$_links = [
+        CRM_Core_Action::UPDATE => [
           'name' => ts('Edit'),
           'url' => 'civicrm/admin/mapping',
           'qs' => 'action=update&id=%%id%%&reset=1',
           'title' => ts('Edit Mapping'),
-        ),
-        CRM_Core_Action::DELETE => array(
+        ],
+        CRM_Core_Action::DELETE => [
           'name' => ts('Delete'),
           'url' => 'civicrm/admin/mapping',
           'qs' => 'action=delete&id=%%id%%',
           'title' => ts('Delete Mapping'),
-        ),
-      );
+        ],
+      ];
     }
     return self::$_links;
   }
diff --git a/CRM/Admin/Page/MessageTemplates.php b/CRM/Admin/Page/MessageTemplates.php
index f3ac667bb688..b81a56a0a02e 100644
--- a/CRM/Admin/Page/MessageTemplates.php
+++ b/CRM/Admin/Page/MessageTemplates.php
@@ -1,9 +1,9 @@
  'js')) . '\n\n' . ts('We recommend that you save a copy of the your customized Text and HTML message content to a text file before reverting so you can combine your changes with the system default messages as needed.', array('escape' => 'js'));
-      self::$_links = array(
-        CRM_Core_Action::UPDATE => array(
+      $confirm = ts('Are you sure you want to revert this template to the default for this workflow? You will lose any customizations you have made.', ['escape' => 'js']) . '\n\n' . ts('We recommend that you save a copy of the your customized Text and HTML message content to a text file before reverting so you can combine your changes with the system default messages as needed.', ['escape' => 'js']);
+      self::$_links = [
+        CRM_Core_Action::UPDATE => [
           'name' => ts('Edit'),
           'url' => 'civicrm/admin/messageTemplates/add',
           'qs' => 'action=update&id=%%id%%&reset=1',
           'title' => ts('Edit this message template'),
-        ),
-        CRM_Core_Action::DISABLE => array(
+        ],
+        CRM_Core_Action::DISABLE => [
           'name' => ts('Disable'),
           'ref' => 'crm-enable-disable',
           'title' => ts('Disable this message template'),
-        ),
-        CRM_Core_Action::ENABLE => array(
+        ],
+        CRM_Core_Action::ENABLE => [
           'name' => ts('Enable'),
           'ref' => 'crm-enable-disable',
           'title' => ts('Enable this message template'),
-        ),
-        CRM_Core_Action::DELETE => array(
+        ],
+        CRM_Core_Action::DELETE => [
           'name' => ts('Delete'),
           'url' => 'civicrm/admin/messageTemplates',
           'qs' => 'action=delete&id=%%id%%',
           'title' => ts('Delete this message template'),
-        ),
-        CRM_Core_Action::REVERT => array(
+        ],
+        CRM_Core_Action::REVERT => [
           'name' => ts('Revert to Default'),
           'extra' => "onclick = 'return confirm(\"$confirm\");'",
           'url' => 'civicrm/admin/messageTemplates',
           'qs' => 'action=revert&id=%%id%%&selectedChild=workflow',
           'title' => ts('Revert this workflow message template to the system default'),
-        ),
-        CRM_Core_Action::VIEW => array(
+        ],
+        CRM_Core_Action::VIEW => [
           'name' => ts('View Default'),
           'url' => 'civicrm/admin/messageTemplates',
           'qs' => 'action=view&id=%%orig_id%%&reset=1',
           'title' => ts('View the system default for this workflow message template'),
-        ),
-      );
+        ],
+      ];
     }
     return self::$_links;
   }
@@ -161,10 +167,11 @@ public function action(&$object, $action, &$values, &$links, $permission, $force
       }
 
       // rebuild the action links HTML, as we need to handle %%orig_id%% for revertible templates
-      $values['action'] = CRM_Core_Action::formLink($links, $action, array(
+      $values['action'] = CRM_Core_Action::formLink($links, $action,
+        [
           'id' => $object->id,
           'orig_id' => CRM_Utils_Array::value($object->id, $this->_revertible),
-        ),
+        ],
         ts('more'),
         FALSE,
         'messageTemplate.manage.action',
@@ -187,21 +194,13 @@ public function action(&$object, $action, &$values, &$links, $permission, $force
    * @throws Exception
    */
   public function run($args = NULL, $pageArgs = NULL, $sort = NULL) {
+    $id = $this->getIdAndAction();
     // handle the revert action and offload the rest to parent
-    if (CRM_Utils_Request::retrieve('action', 'String', $this) & CRM_Core_Action::REVERT) {
-
-      $id = CRM_Utils_Request::retrieve('id', 'Positive', $this);
-      if (!$this->checkPermission($id, NULL)) {
-        CRM_Core_Error::fatal(ts('You do not have permission to revert this template.'));
-      }
-
+    if ($this->_action & CRM_Core_Action::REVERT) {
       $this->_revertedId = $id;
-
       CRM_Core_BAO_MessageTemplate::revert($id);
     }
 
-    $this->assign('selectedChild', CRM_Utils_Request::retrieve('selectedChild', 'String', $this));
-
     return parent::run($args, $pageArgs, $sort);
   }
 
@@ -263,13 +262,13 @@ public function browse() {
     $messageTemplate = new CRM_Core_BAO_MessageTemplate();
     $messageTemplate->orderBy('msg_title' . ' asc');
 
-    $userTemplates = array();
-    $workflowTemplates = array();
+    $userTemplates = [];
+    $workflowTemplates = [];
 
     // find all objects
     $messageTemplate->find();
     while ($messageTemplate->fetch()) {
-      $values[$messageTemplate->id] = array();
+      $values[$messageTemplate->id] = [];
       CRM_Core_DAO::storeValues($messageTemplate, $values[$messageTemplate->id]);
       // populate action links
       $this->action($messageTemplate, $action, $values[$messageTemplate->id], $links, CRM_Core_Permission::EDIT);
@@ -282,12 +281,15 @@ public function browse() {
       }
     }
 
-    $rows = array(
+    $rows = [
       'userTemplates' => $userTemplates,
       'workflowTemplates' => $workflowTemplates,
-    );
+    ];
 
     $this->assign('rows', $rows);
+    $this->assign('canEditSystemTemplates', CRM_Core_Permission::check('edit system workflow message templates'));
+    $this->assign('canEditMessageTemplates', CRM_Core_Permission::check('edit message templates'));
+    $this->assign('canEditUserDrivenMessageTemplates', CRM_Core_Permission::check('edit user-driven message templates'));
   }
 
 }
diff --git a/CRM/Admin/Page/Navigation.php b/CRM/Admin/Page/Navigation.php
index 18f5915a4172..30234dacfca8 100644
--- a/CRM/Admin/Page/Navigation.php
+++ b/CRM/Admin/Page/Navigation.php
@@ -1,9 +1,9 @@
 addScriptFile('civicrm', 'packages/jquery/plugins/jstree/jquery.jstree.js', 0, 'html-header', FALSE)
-      ->addStyleFile('civicrm', 'packages/jquery/plugins/jstree/themes/default/style.css', 0, 'html-header');
+      ->addScriptFile('civicrm', 'bower_components/jstree/dist/jstree.min.js', 0, 'html-header')
+      ->addStyleFile('civicrm', 'bower_components/jstree/dist/themes/default/style.min.css');
   }
 
 }
diff --git a/CRM/Admin/Page/Options.php b/CRM/Admin/Page/Options.php
index f2d10334f324..7ba567c047e8 100644
--- a/CRM/Admin/Page/Options.php
+++ b/CRM/Admin/Page/Options.php
@@ -1,9 +1,9 @@
 urlPath[3])) {
       self::$_gName = $this->urlPath[3];
+      self::$_isLocked = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionGroup', self::$_gName, 'is_locked', 'name');
     }
     // If an id arg is passed instead of a group name in the path
     elseif (!self::$_gName && !empty($_GET['gid'])) {
@@ -151,6 +152,7 @@ public function preProcess() {
       $this->assign('showCounted', TRUE);
     }
     $this->assign('isLocked', self::$_isLocked);
+    $this->assign('allowLoggedIn', Civi::settings()->get('allow_mail_from_logged_in_contact'));
     $config = CRM_Core_Config::singleton();
     if (self::$_gName == 'activity_type') {
       $this->assign('showComponent', TRUE);
@@ -245,7 +247,7 @@ public function browse() {
     // retrieve financial account name for the payment method page
     if ($gName = "payment_instrument") {
       foreach ($optionValue as $key => $option) {
-        $optionValue[$key]['financial_account'] = CRM_Financial_BAO_FinancialTypeAccount::getFinancialAccount($key, 'civicrm_option_value');
+        $optionValue[$key]['financial_account'] = CRM_Contribute_PseudoConstant::getRelationalFinancialAccount($key, NULL, 'civicrm_option_value', 'financial_account_id.name');
       }
     }
     $this->assign('rows', $optionValue);
diff --git a/CRM/Admin/Page/ParticipantStatusType.php b/CRM/Admin/Page/ParticipantStatusType.php
index fc46bb01c35c..7f0202c3a4a1 100644
--- a/CRM/Admin/Page/ParticipantStatusType.php
+++ b/CRM/Admin/Page/ParticipantStatusType.php
@@ -1,9 +1,9 @@
  array(
+      $links = [
+        CRM_Core_Action::UPDATE => [
           'name' => ts('Edit'),
           'url' => 'civicrm/admin/participant_status',
           'qs' => 'action=update&id=%%id%%&reset=1',
           'title' => ts('Edit Status'),
-        ),
-        CRM_Core_Action::DELETE => array(
+        ],
+        CRM_Core_Action::DELETE => [
           'name' => ts('Delete'),
           'url' => 'civicrm/admin/participant_status',
           'qs' => 'action=delete&id=%%id%%',
           'title' => ts('Delete Status'),
-        ),
-        CRM_Core_Action::DISABLE => array(
+        ],
+        CRM_Core_Action::DISABLE => [
           'name' => ts('Disable'),
           'ref' => 'crm-enable-disable',
           'title' => ts('Disable Status'),
-        ),
-        CRM_Core_Action::ENABLE => array(
+        ],
+        CRM_Core_Action::ENABLE => [
           'name' => ts('Enable'),
           'ref' => 'crm-enable-disable',
           'title' => ts('Enable Status'),
-        ),
-      );
+        ],
+      ];
     }
     return $links;
   }
 
   public function browse() {
-    $statusTypes = array();
+    $statusTypes = [];
 
     $dao = new CRM_Event_DAO_ParticipantStatusType();
     $dao->orderBy('weight');
@@ -87,13 +87,13 @@ public function browse() {
     $visibilities = CRM_Core_PseudoConstant::visibility();
 
     // these statuses are reserved, but disabled by default - so should be disablable after being enabled
-    $disablable = array(
+    $disablable = [
       'On waitlist',
       'Awaiting approval',
       'Pending from waitlist',
       'Pending from approval',
       'Rejected',
-    );
+    ];
 
     while ($dao->fetch()) {
       CRM_Core_DAO::storeValues($dao, $statusTypes[$dao->id]);
@@ -108,7 +108,7 @@ public function browse() {
       $statusTypes[$dao->id]['action'] = CRM_Core_Action::formLink(
         self::links(),
         $action,
-        array('id' => $dao->id),
+        ['id' => $dao->id],
         ts('more'),
         FALSE,
         'participantStatusType.manage.action',
diff --git a/CRM/Admin/Page/PaymentProcessor.php b/CRM/Admin/Page/PaymentProcessor.php
index af23ee7896a9..3ed9836bd18b 100644
--- a/CRM/Admin/Page/PaymentProcessor.php
+++ b/CRM/Admin/Page/PaymentProcessor.php
@@ -1,9 +1,9 @@
  'name',
-        'flip' => 1,
-      ));
+      'labelColumn' => 'name',
+      'flip' => 1,
+    ));
     $this->assign('defaultPaymentProcessorType', $paymentProcessorTypes['PayPal']);
     $breadCrumb = array(
       array(
@@ -134,8 +134,9 @@ public function browse($action = NULL) {
     while ($dao->fetch()) {
       $paymentProcessor[$dao->id] = array();
       CRM_Core_DAO::storeValues($dao, $paymentProcessor[$dao->id]);
-      $paymentProcessor[$dao->id]['payment_processor_type'] = CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_PaymentProcessorType',
-        $paymentProcessor[$dao->id]['payment_processor_type_id']);
+      $paymentProcessor[$dao->id]['payment_processor_type'] = CRM_Core_PseudoConstant::getLabel(
+        'CRM_Financial_DAO_PaymentProcessor', 'payment_processor_type_id', $dao->payment_processor_type_id
+      );
 
       // form all action links
       $action = array_sum(array_keys($this->links()));
@@ -156,7 +157,14 @@ public function browse($action = NULL) {
         'PaymentProcessor',
         $dao->id
       );
-      $paymentProcessor[$dao->id]['financialAccount'] = CRM_Financial_BAO_FinancialTypeAccount::getFinancialAccount($dao->id, 'civicrm_payment_processor');
+      $paymentProcessor[$dao->id]['financialAccount'] = CRM_Contribute_PseudoConstant::getRelationalFinancialAccount($dao->id, NULL, 'civicrm_payment_processor', 'financial_account_id.name');
+
+      try {
+        $paymentProcessor[$dao->id]['test_id'] = CRM_Financial_BAO_PaymentProcessor::getTestProcessorId($dao->id);
+      }
+      catch (CiviCRM_API3_Exception $e) {
+        CRM_Core_Session::setStatus(ts('No test processor entry exists for %1. Not having a test entry for each processor could cause problems', [$dao->name]));
+      }
     }
 
     $this->assign('rows', $paymentProcessor);
diff --git a/CRM/Admin/Page/PaymentProcessorType.php b/CRM/Admin/Page/PaymentProcessorType.php
index 758d557b7054..ab366bd54f89 100644
--- a/CRM/Admin/Page/PaymentProcessorType.php
+++ b/CRM/Admin/Page/PaymentProcessorType.php
@@ -1,9 +1,9 @@
  array(
+      self::$_links = [
+        CRM_Core_Action::UPDATE => [
           'name' => ts('Edit'),
           'url' => 'civicrm/admin/paymentProcessorType',
           'qs' => 'action=update&id=%%id%%&reset=1',
           'title' => ts('Edit Payment ProcessorType'),
-        ),
-        CRM_Core_Action::DISABLE => array(
+        ],
+        CRM_Core_Action::DISABLE => [
           'name' => ts('Disable'),
           'ref' => 'crm-enable-disable',
           'title' => ts('Disable Payment ProcessorType'),
-        ),
-        CRM_Core_Action::ENABLE => array(
+        ],
+        CRM_Core_Action::ENABLE => [
           'name' => ts('Enable'),
           'ref' => 'crm-enable-disable',
           'title' => ts('Enable Payment ProcessorType'),
-        ),
-        CRM_Core_Action::DELETE => array(
+        ],
+        CRM_Core_Action::DELETE => [
           'name' => ts('Delete'),
           'url' => 'civicrm/admin/paymentProcessorType',
           'qs' => 'action=delete&id=%%id%%',
           'title' => ts('Delete Payment ProcessorType'),
-        ),
-      );
+        ],
+      ];
     }
     return self::$_links;
   }
diff --git a/CRM/Admin/Page/PdfFormats.php b/CRM/Admin/Page/PdfFormats.php
index b76513170ea9..24abb2921207 100644
--- a/CRM/Admin/Page/PdfFormats.php
+++ b/CRM/Admin/Page/PdfFormats.php
@@ -1,7 +1,7 @@
  array(
+      self::$_links = [
+        CRM_Core_Action::UPDATE => [
           'name' => ts('Edit'),
           'url' => 'civicrm/admin/pdfFormats',
           'qs' => 'action=update&id=%%id%%&reset=1',
           'title' => ts('Edit PDF Page Format'),
-        ),
-        CRM_Core_Action::DELETE => array(
+        ],
+        CRM_Core_Action::DELETE => [
           'name' => ts('Delete'),
           'url' => 'civicrm/admin/pdfFormats',
           'qs' => 'action=delete&id=%%id%%',
           'title' => ts('Delete PDF Page Format'),
-        ),
-      );
+        ],
+      ];
     }
 
     return self::$_links;
@@ -131,7 +131,7 @@ public function browse($action = NULL) {
       $format['action'] = CRM_Core_Action::formLink(
         self::links(),
         $action,
-        array('id' => $format['id']),
+        ['id' => $format['id']],
         ts('more'),
         FALSE,
         'pdfFormat.manage.action',
diff --git a/CRM/Admin/Page/Persistent.php b/CRM/Admin/Page/Persistent.php
index 158a89fd3d65..4524da544492 100644
--- a/CRM/Admin/Page/Persistent.php
+++ b/CRM/Admin/Page/Persistent.php
@@ -1,9 +1,9 @@
  array(
+      self::$_stringActionLinks = [
+        CRM_Core_Action::UPDATE => [
           'name' => ts('Edit'),
           'url' => 'civicrm/admin/tplstrings/add',
           'qs' => 'reset=1&action=update&id=%%id%%',
           'title' => ts('Configure'),
-        ),
-      );
+        ],
+      ];
     }
     return self::$_stringActionLinks;
   }
@@ -73,14 +73,14 @@ public function &customizeActionLinks() {
     // check if variable _actionsLinks is populated
     if (!isset(self::$_customizeActionLinks)) {
 
-      self::$_customizeActionLinks = array(
-        CRM_Core_Action::UPDATE => array(
+      self::$_customizeActionLinks = [
+        CRM_Core_Action::UPDATE => [
           'name' => ts('Edit'),
           'url' => 'civicrm/admin/tplstrings/add',
           'qs' => 'reset=1&action=update&id=%%id%%&config=1',
           'title' => ts('Configure'),
-        ),
-      );
+        ],
+      ];
     }
     return self::$_customizeActionLinks;
   }
@@ -107,14 +107,14 @@ public function browse() {
 
     $daoResult = new CRM_Core_DAO_Persistent();
     $daoResult->find();
-    $schoolValues = array();
+    $schoolValues = [];
     while ($daoResult->fetch()) {
-      $values[$daoResult->id] = array();
+      $values[$daoResult->id] = [];
       CRM_Core_DAO::storeValues($daoResult, $values[$daoResult->id]);
       if ($daoResult->is_config == 1) {
         $values[$daoResult->id]['action'] = CRM_Core_Action::formLink(self::customizeActionLinks(),
           NULL,
-          array('id' => $daoResult->id),
+          ['id' => $daoResult->id],
           ts('more'),
           FALSE,
           'persistent.config.actions',
@@ -127,7 +127,7 @@ public function browse() {
       if ($daoResult->is_config == 0) {
         $values[$daoResult->id]['action'] = CRM_Core_Action::formLink(self::stringActionLinks(),
           NULL,
-          array('id' => $daoResult->id),
+          ['id' => $daoResult->id],
           ts('more'),
           FALSE,
           'persistent.row.actions',
@@ -137,10 +137,10 @@ public function browse() {
         $configStrings[$daoResult->id] = $values[$daoResult->id];
       }
     }
-    $rows = array(
+    $rows = [
       'configTemplates' => $configStrings,
       'customizeTemplates' => $configCustomization,
-    );
+    ];
     $this->assign('rows', $rows);
   }
 
diff --git a/CRM/Admin/Page/PreferencesDate.php b/CRM/Admin/Page/PreferencesDate.php
index 5fcf4fd53ca2..053d7efb28c0 100644
--- a/CRM/Admin/Page/PreferencesDate.php
+++ b/CRM/Admin/Page/PreferencesDate.php
@@ -1,9 +1,9 @@
  array(
+      self::$_links = [
+        CRM_Core_Action::UPDATE => [
           'name' => ts('Edit'),
           'url' => 'civicrm/admin/setting/preferences/date',
           'qs' => 'action=update&id=%%id%%&reset=1',
           'title' => ts('Edit Date Type'),
-        ),
-      );
+        ],
+      ];
     }
     return self::$_links;
   }
diff --git a/CRM/Admin/Page/RelationshipType.php b/CRM/Admin/Page/RelationshipType.php
index f006aa89d415..8657da762576 100644
--- a/CRM/Admin/Page/RelationshipType.php
+++ b/CRM/Admin/Page/RelationshipType.php
@@ -1,9 +1,9 @@
  array(
+      self::$_links = [
+        CRM_Core_Action::VIEW => [
           'name' => ts('View'),
           'url' => 'civicrm/admin/reltype',
           'qs' => 'action=view&id=%%id%%&reset=1',
           'title' => ts('View Relationship Type'),
-        ),
-        CRM_Core_Action::UPDATE => array(
+        ],
+        CRM_Core_Action::UPDATE => [
           'name' => ts('Edit'),
           'url' => 'civicrm/admin/reltype',
           'qs' => 'action=update&id=%%id%%&reset=1',
           'title' => ts('Edit Relationship Type'),
-        ),
-        CRM_Core_Action::DISABLE => array(
+        ],
+        CRM_Core_Action::DISABLE => [
           'name' => ts('Disable'),
           'ref' => 'crm-enable-disable',
           'title' => ts('Disable Relationship Type'),
-        ),
-        CRM_Core_Action::ENABLE => array(
+        ],
+        CRM_Core_Action::ENABLE => [
           'name' => ts('Enable'),
           'ref' => 'crm-enable-disable',
           'title' => ts('Enable Relationship Type'),
-        ),
-        CRM_Core_Action::DELETE => array(
+        ],
+        CRM_Core_Action::DELETE => [
           'name' => ts('Delete'),
           'url' => 'civicrm/admin/reltype',
           'qs' => 'action=delete&id=%%id%%',
           'title' => ts('Delete Reletionship Type'),
-        ),
-      );
+        ],
+      ];
     }
     return self::$_links;
   }
diff --git a/CRM/Admin/Page/ScheduleReminders.php b/CRM/Admin/Page/ScheduleReminders.php
index 6ef704d023ed..c87d5c654ec8 100644
--- a/CRM/Admin/Page/ScheduleReminders.php
+++ b/CRM/Admin/Page/ScheduleReminders.php
@@ -1,9 +1,9 @@
  array(
+      self::$_links = [
+        CRM_Core_Action::UPDATE => [
           'name' => ts('Edit'),
           'url' => 'civicrm/admin/scheduleReminders',
           'qs' => 'action=update&id=%%id%%&reset=1',
           'title' => ts('Edit Schedule Reminders'),
-        ),
-        CRM_Core_Action::ENABLE => array(
+        ],
+        CRM_Core_Action::ENABLE => [
           'name' => ts('Enable'),
           'ref' => 'crm-enable-disable',
           'title' => ts('Enable Label Format'),
-        ),
-        CRM_Core_Action::DISABLE => array(
+        ],
+        CRM_Core_Action::DISABLE => [
           'name' => ts('Disable'),
           'ref' => 'crm-enable-disable',
           'title' => ts('Disable Label Format'),
-        ),
-        CRM_Core_Action::DELETE => array(
+        ],
+        CRM_Core_Action::DELETE => [
           'name' => ts('Delete'),
           'url' => 'civicrm/admin/scheduleReminders',
           'qs' => 'action=delete&id=%%id%%',
           'title' => ts('Delete Schedule Reminders'),
-        ),
-      );
+        ],
+      ];
     }
 
     return self::$_links;
@@ -153,7 +153,7 @@ public function browse($action = NULL) {
         $format['action'] = CRM_Core_Action::formLink(
           self::links(),
           $action,
-          array('id' => $format['id']),
+          ['id' => $format['id']],
           ts('more'),
           FALSE,
           'actionSchedule.manage.action',
diff --git a/CRM/Admin/Page/Setting.php b/CRM/Admin/Page/Setting.php
index d497129ec41a..0f747b58c155 100644
--- a/CRM/Admin/Page/Setting.php
+++ b/CRM/Admin/Page/Setting.php
@@ -1,9 +1,9 @@
  array(
-          'name' => ts('Edit'),
-          'url' => 'civicrm/admin/tag',
-          'qs' => 'action=update&id=%%id%%&reset=1',
-          'title' => ts('Edit Tag'),
-        ),
-        CRM_Core_Action::DELETE => array(
-          'name' => ts('Delete'),
-          'url' => 'civicrm/admin/tag',
-          'qs' => 'action=delete&id=%%id%%',
-          'title' => ts('Delete Tag'),
-        ),
-        CRM_Core_Action::FOLLOWUP => array(
-          'name' => ts('Merge'),
-          'class' => 'merge_tag',
-          'title' => ts('Merge Tag'),
-        ),
-      );
-    }
-    return self::$_links;
-  }
-
-  /**
-   * Get name of edit form.
-   *
-   * @return string
-   *   Classname of edit form.
-   */
-  public function editForm() {
-    return 'CRM_Admin_Form_Tag';
-  }
-
-  /**
-   * Get form name for edit form.
-   *
-   * @return string
-   *   name of this page.
-   */
-  public function editName() {
-    return 'Tag';
-  }
-
-  /**
-   * Get form name for delete form.
-   *
-   * @return string
-   *   name of this page.
-   */
-  public function deleteName() {
-    return 'Tag';
-  }
-
-  /**
-   * Get user context.
-   *
-   * @param null $mode
-   *
-   * @return string
-   *   user context.
-   */
-  public function userContext($mode = NULL) {
-    return 'civicrm/admin/tag';
-  }
-
-  /**
-   * Get name of delete form.
-   *
-   * @return string
-   *   Classname of delete form.
-   */
-  public function deleteForm() {
-    return 'CRM_Admin_Form_Tag';
-  }
-
-  /**
-   * Override function browse()
-   *
-   * @param null $action
-   * @param null $sort
-   */
-  public function browse($action = NULL, $sort = NULL) {
-    $adminTagSet = FALSE;
-    if (CRM_Core_Permission::check('administer Tagsets')) {
-      $adminTagSet = TRUE;
-    }
-    $this->assign('adminTagSet', $adminTagSet);
-
-    $reservedClause = !CRM_Core_Permission::check('administer reserved tags') ? "AND t1.is_reserved != 1" : '';
-    $query = "SELECT t1.name, t1.id
-FROM civicrm_tag t1 LEFT JOIN civicrm_tag t2 ON t1.id = t2.parent_id
-WHERE t2.id IS NULL {$reservedClause}";
-    $tag = CRM_Core_DAO::executeQuery($query);
-
-    $mergeableTags = array();
-    while ($tag->fetch()) {
-      $mergeableTags[$tag->id] = 1;
-    }
-
-    $usedFor = CRM_Core_OptionGroup::values('tag_used_for');
-
-    $query = "SELECT t1.name, t1.id, t2.name as parent, t1.description, t1.used_for, t1.is_tagset,
-                        t1.is_reserved, t1.parent_id, t1.used_for
-                 FROM civicrm_tag t1 LEFT JOIN civicrm_tag t2 ON t1.parent_id = t2.id
-                 GROUP BY t1.parent_id, t1.id";
-
-    $tag = CRM_Core_DAO::executeQuery($query);
-    $values = array();
-
-    $action = CRM_Core_Action::UPDATE + CRM_Core_Action::DELETE;
-    $permission = CRM_Core_Permission::EDIT;
-
-    while ($tag->fetch()) {
-      $values[$tag->id] = (array) $tag;
-
-      $used = array();
-      if ($values[$tag->id]['used_for']) {
-        $usedArray = explode(",", $values[$tag->id]['used_for']);
-        foreach ($usedArray as $key => $value) {
-          $used[$key] = $usedFor[$value];
-        }
-      }
-
-      if (!empty($used)) {
-        $values[$tag->id]['used_for'] = implode(", ", $used);
-      }
-
-      $newAction = $action;
-      if ($values[$tag->id]['is_reserved']) {
-        $newAction = CRM_Core_Action::UPDATE;
-      }
-
-      if ($values[$tag->id]['is_tagset'] && !CRM_Core_Permission::check('administer Tagsets')) {
-        $newAction = 0;
-      }
-
-      if (array_key_exists($tag->id, $mergeableTags)) {
-        $newAction += CRM_Core_Action::FOLLOWUP;
-      }
-
-      // populate action links
-      if ($newAction) {
-        $this->action($tag, $newAction, $values[$tag->id], self::links(), $permission, TRUE);
-      }
-      else {
-        $values[$tag->id]['action'] = '';
-      }
-    }
-
-    $this->assign('rows', $values);
-  }
-
-}
diff --git a/CRM/Badge/BAO/Badge.php b/CRM/Badge/BAO/Badge.php
index 93c7f99af20f..7775cd4ae541 100644
--- a/CRM/Badge/BAO/Badge.php
+++ b/CRM/Badge/BAO/Badge.php
@@ -1,7 +1,7 @@
 pdf->Output(CRM_Utils_String::munge($layoutInfo['title'], '_', 64) . '.pdf', 'D');
-    CRM_Utils_System::civiExit(1);
+    CRM_Utils_System::civiExit();
   }
 
   /**
@@ -88,7 +88,7 @@ public function createLabels(&$participants, &$layoutInfo) {
    *   row with meta data
    */
   public static function formatLabel(&$row, &$layout) {
-    $formattedRow = array('labelFormat' => $layout['label_format_name']);
+    $formattedRow = ['labelFormat' => $layout['label_format_name']];
     $formattedRow['labelTitle'] = $layout['title'];
     $formattedRow['labelId'] = $layout['id'];
 
@@ -103,14 +103,14 @@ public static function formatLabel(&$row, &$layout) {
           }
         }
 
-        $formattedRow['token'][$key] = array(
+        $formattedRow['token'][$key] = [
           'value' => $value,
           'font_name' => $layout['data']['font_name'][$key],
           'font_size' => $layout['data']['font_size'][$key],
           'font_style' => $layout['data']['font_style'][$key],
           'text_alignment' => $layout['data']['text_alignment'][$key],
           'token' => $layout['data']['token'][$key],
-        );
+        ];
       }
     }
 
@@ -147,10 +147,10 @@ public static function formatLabel(&$row, &$layout) {
     }
 
     if (!empty($layout['data']['add_barcode'])) {
-      $formattedRow['barcode'] = array(
+      $formattedRow['barcode'] = [
         'alignment' => $layout['data']['barcode_alignment'],
         'type' => $layout['data']['barcode_type'],
-      );
+      ];
     }
 
     // finally assign all the row values, so that we can use it for barcode etc
@@ -230,13 +230,13 @@ public function labelCreator(&$formattedRow, $cellspacing = 0) {
       }
     }
 
-    $this->pdf->SetLineStyle(array(
+    $this->pdf->SetLineStyle([
       'width' => 0.1,
       'cap' => 'round',
       'join' => 'round',
       'dash' => '2,2',
-      'color' => array(0, 0, 200),
-    ));
+      'color' => [0, 0, 200],
+    ]);
 
     $rowCount = CRM_Badge_Form_Layout::FIELD_ROWCOUNT;
     for ($i = 1; $i <= $rowCount; $i++) {
@@ -304,7 +304,7 @@ public function labelCreator(&$formattedRow, $cellspacing = 0) {
             break;
         }
 
-        $style = array(
+        $style = [
           'position' => '',
           'align' => '',
           'stretch' => FALSE,
@@ -313,13 +313,13 @@ public function labelCreator(&$formattedRow, $cellspacing = 0) {
           'border' => FALSE,
           'hpadding' => 13.5,
           'vpadding' => 'auto',
-          'fgcolor' => array(0, 0, 0),
+          'fgcolor' => [0, 0, 0],
           'bgcolor' => FALSE,
           'text' => FALSE,
           'font' => 'helvetica',
           'fontsize' => 8,
           'stretchtext' => 0,
-        );
+        ];
 
         $this->pdf->write1DBarcode($data['current_value'], 'C128', $xAlign, $y + $this->pdf->height - 10, '70',
           12, 0.4, $style, 'B');
@@ -342,14 +342,14 @@ public function labelCreator(&$formattedRow, $cellspacing = 0) {
             break;
         }
 
-        $style = array(
+        $style = [
           'border' => FALSE,
           'hpadding' => 13.5,
           'vpadding' => 'auto',
-          'fgcolor' => array(0, 0, 0),
+          'fgcolor' => [0, 0, 0],
           'bgcolor' => FALSE,
           'position' => '',
-        );
+        ];
 
         $this->pdf->write2DBarcode($data['current_value'], 'QRCODE,H', $xAlign, $y + $this->pdf->height - 26, 30,
           30, $style, 'B');
@@ -400,7 +400,7 @@ public static function getImageProperties($img, $imgRes = 300, $w = NULL, $h = N
     $f = $imgRes / 25.4;
     $w = !empty($w) ? $w : $imgsize[0] / $f;
     $h = !empty($h) ? $h : $imgsize[1] / $f;
-    return array($w, $h);
+    return [$w, $h];
   }
 
   /**
@@ -415,7 +415,7 @@ public static function buildBadges(&$params, &$form) {
     $layoutInfo = CRM_Badge_BAO_Layout::buildLayout($params);
 
     // split/get actual field names from token and individual contact image URLs
-    $returnProperties = array();
+    $returnProperties = [];
     if (!empty($layoutInfo['data']['token'])) {
       foreach ($layoutInfo['data']['token'] as $index => $value) {
         $element = '';
@@ -445,7 +445,7 @@ public static function buildBadges(&$params, &$form) {
     }
 
     // add additional required fields for query execution
-    $additionalFields = array('participant_register_date', 'participant_id', 'event_id', 'contact_id', 'image_URL');
+    $additionalFields = ['participant_register_date', 'participant_id', 'event_id', 'contact_id', 'image_URL'];
     foreach ($additionalFields as $field) {
       $returnProperties[$field] = 1;
     }
@@ -479,10 +479,10 @@ public static function buildBadges(&$params, &$form) {
     $queryString = "$select $from $where $having $sortOrder";
 
     $dao = CRM_Core_DAO::executeQuery($queryString);
-    $rows = array();
+    $rows = [];
     while ($dao->fetch()) {
       $query->convertToPseudoNames($dao);
-      $rows[$dao->participant_id] = array();
+      $rows[$dao->participant_id] = [];
       foreach ($returnProperties as $key => $dontCare) {
         $value = isset($dao->$key) ? $dao->$key : NULL;
         // Format custom fields
diff --git a/CRM/Badge/BAO/Layout.php b/CRM/Badge/BAO/Layout.php
index c077725b4edd..8ad8ca1dad18 100644
--- a/CRM/Badge/BAO/Layout.php
+++ b/CRM/Badge/BAO/Layout.php
@@ -1,9 +1,9 @@
 find();
 
-    $labels = array();
+    $labels = [];
     while ($printLabel->fetch()) {
       $labels[$printLabel->id] = $printLabel->title;
     }
@@ -159,10 +158,11 @@ public static function getList() {
    *   array formatted array
    */
   public static function buildLayout(&$params) {
-    $layoutParams = array('id' => $params['badge_id']);
+    $layoutParams = ['id' => $params['badge_id']];
     CRM_Badge_BAO_Layout::retrieve($layoutParams, $layoutInfo);
 
-    $formatProperties = CRM_Core_OptionGroup::getValue('name_badge', $layoutInfo['label_format_name'], 'name');
+    $formatProperties = CRM_Core_PseudoConstant::getKey('CRM_Core_DAO_PrintLabel', 'label_format_name', $layoutInfo['label_format_name']);
+
     $layoutInfo['format'] = json_decode($formatProperties, TRUE);
     $layoutInfo['data'] = CRM_Badge_BAO_Layout::getDecodedData($layoutInfo['data']);
     return $layoutInfo;
@@ -177,7 +177,7 @@ public static function buildLayout(&$params) {
    * @return array
    *   associated array of decoded elements
    */
-  static public function getDecodedData($jsonData) {
+  public static function getDecodedData($jsonData) {
     return json_decode($jsonData, TRUE);
   }
 
diff --git a/CRM/Badge/Form/Layout.php b/CRM/Badge/Form/Layout.php
index 3f0ece80f94b..af93d47ee057 100644
--- a/CRM/Badge/Form/Layout.php
+++ b/CRM/Badge/Form/Layout.php
@@ -1,9 +1,9 @@
 addSetting(
-      array(
+      [
         'kcfinderPath' => $config->userFrameworkResourceURL . 'packages' . DIRECTORY_SEPARATOR,
-      )
+      ]
     );
     $resources->addScriptFile('civicrm', 'templates/CRM/Badge/Form/Layout.js', 1, 'html-header');
 
@@ -60,25 +60,25 @@ public function buildQuickForm() {
     $this->add('text', 'title', ts('Title'), CRM_Core_DAO::getAttribute('CRM_Core_DAO_PrintLabel', 'title'), TRUE);
 
     $labelStyle = CRM_Core_BAO_LabelFormat::getList(TRUE, 'name_badge');
-    $this->add('select', 'label_format_name', ts('Label Format'), array('' => ts('- select -')) + $labelStyle, TRUE);
+    $this->add('select', 'label_format_name', ts('Label Format'), ['' => ts('- select -')] + $labelStyle, TRUE);
 
     $this->add('text', 'description', ts('Description'),
       CRM_Core_DAO::getAttribute('CRM_Core_DAO_PrintLabel', 'title'));
 
     // get the tokens
     $contactTokens = CRM_Core_SelectValues::contactTokens();
-    $eventTokens = array(
+    $eventTokens = [
       '{event.event_id}' => ts('Event ID'),
       '{event.title}' => ts('Event Title'),
       '{event.start_date}' => ts('Event Start Date'),
       '{event.end_date}' => ts('Event End Date'),
-    );
+    ];
     $participantTokens = CRM_Core_SelectValues::participantTokens();
 
     $tokens = array_merge($contactTokens, $eventTokens, $participantTokens);
     asort($tokens);
 
-    $tokens = array_merge(array('spacer' => ts('- spacer -')) + $tokens);
+    $tokens = array_merge(['spacer' => ts('- spacer -')] + $tokens);
 
     $fontSizes = CRM_Core_BAO_LabelFormat::getFontSizes();
     $fontStyles = CRM_Core_BAO_LabelFormat::getFontStyles();
@@ -89,7 +89,7 @@ public function buildQuickForm() {
 
     $rowCount = self::FIELD_ROWCOUNT;
     for ($i = 1; $i <= $rowCount; $i++) {
-      $this->add('select', "token[$i]", ts('Token'), array('' => ts('- skip -')) + $tokens);
+      $this->add('select', "token[$i]", ts('Token'), ['' => ts('- skip -')] + $tokens);
       $this->add('select', "font_name[$i]", ts('Font Name'), $fontNames);
       $this->add('select', "font_size[$i]", ts('Font Size'), $fontSizes);
       $this->add('select', "font_style[$i]", ts('Font Style'), $fontStyles);
@@ -103,20 +103,20 @@ public function buildQuickForm() {
     $this->add('select', "barcode_type", ts('Type'), $barcodeTypes);
     $this->add('select', "barcode_alignment", ts('Alignment'), $textAlignment);
 
-    $attributes = array('readonly' => TRUE);
+    $attributes = ['readonly' => TRUE];
     $this->add('text', 'image_1', ts('Image (top left)'),
       $attributes + CRM_Core_DAO::getAttribute('CRM_Core_DAO_PrintLabel', 'title'));
-    $this->add('text', 'width_image_1', ts('Width (mm)'), array('size' => 6));
-    $this->add('text', 'height_image_1', ts('Height (mm)'), array('size' => 6));
+    $this->add('text', 'width_image_1', ts('Width (mm)'), ['size' => 6]);
+    $this->add('text', 'height_image_1', ts('Height (mm)'), ['size' => 6]);
 
     $this->add('text', 'image_2', ts('Image (top right)'),
       $attributes + CRM_Core_DAO::getAttribute('CRM_Core_DAO_PrintLabel', 'title'));
-    $this->add('text', 'width_image_2', ts('Width (mm)'), array('size' => 6));
-    $this->add('text', 'height_image_2', ts('Height (mm)'), array('size' => 6));
+    $this->add('text', 'width_image_2', ts('Width (mm)'), ['size' => 6]);
+    $this->add('text', 'height_image_2', ts('Height (mm)'), ['size' => 6]);
 
     $this->add('checkbox', 'show_participant_image', ts('Use Participant Image?'));
-    $this->add('text', 'width_participant_image', ts('Width (mm)'), array('size' => 6));
-    $this->add('text', 'height_participant_image', ts('Height (mm)'), array('size' => 6));
+    $this->add('text', 'width_participant_image', ts('Width (mm)'), ['size' => 6]);
+    $this->add('text', 'height_participant_image', ts('Height (mm)'), ['size' => 6]);
     $this->add('select', "alignment_participant_image", ts('Image Alignment'), $imageAlignment);
 
     $this->add('checkbox', 'is_default', ts('Default?'));
@@ -130,22 +130,21 @@ public function buildQuickForm() {
     $this->addRule('height_participant_image', ts('Enter valid height'), 'positiveInteger');
     $this->addRule('width_participant_image', ts('Enter valid height'), 'positiveInteger');
 
-    $this->addButtons(array(
-        array(
-          'type' => 'next',
-          'name' => ts('Save'),
-          'isDefault' => TRUE,
-        ),
-        array(
-          'type' => 'refresh',
-          'name' => ts('Save and Preview'),
-        ),
-        array(
-          'type' => 'cancel',
-          'name' => ts('Cancel'),
-        ),
-      )
-    );
+    $this->addButtons([
+      [
+        'type' => 'next',
+        'name' => ts('Save'),
+        'isDefault' => TRUE,
+      ],
+      [
+        'type' => 'refresh',
+        'name' => ts('Save and Preview'),
+      ],
+      [
+        'type' => 'cancel',
+        'name' => ts('Cancel'),
+      ],
+    ]);
   }
 
   /**
@@ -154,7 +153,7 @@ public function buildQuickForm() {
   public function setDefaultValues() {
     if (isset($this->_id)) {
       $defaults = array_merge($this->_values,
-        CRM_Badge_BAO_Layout::getDecodedData($this->_values['data']));
+        CRM_Badge_BAO_Layout::getDecodedData(CRM_Utils_Array::value('data', $this->_values, '[]')));
     }
     else {
       for ($i = 1; $i <= self::FIELD_ROWCOUNT; $i++) {
@@ -201,7 +200,7 @@ public function postProcess() {
     }
     else {
       CRM_Core_Session::setStatus(ts("The badge layout '%1' has been saved.",
-        array(1 => $params['title'])
+        [1 => $params['title']]
       ), ts('Saved'), 'success');
     }
   }
@@ -220,7 +219,7 @@ public function buildPreview(&$params) {
     }
 
     $this->_single = TRUE;
-    $this->_participantIds = array($participantID);
+    $this->_participantIds = [$participantID];
     $this->_componentClause = " civicrm_participant.id = $participantID ";
 
     CRM_Badge_BAO_Badge::buildBadges($params, $this);
diff --git a/CRM/Badge/Page/AJAX.php b/CRM/Badge/Page/AJAX.php
index bb6348e79c5e..4e0af60ad66e 100644
--- a/CRM/Badge/Page/AJAX.php
+++ b/CRM/Badge/Page/AJAX.php
@@ -1,9 +1,9 @@
  $w, 'height' => $h));
+    CRM_Utils_JSON::output(['width' => $w, 'height' => $h]);
   }
 
 }
diff --git a/CRM/Badge/Page/Layout.php b/CRM/Badge/Page/Layout.php
index 93f8b8f43f48..da978c55f711 100644
--- a/CRM/Badge/Page/Layout.php
+++ b/CRM/Badge/Page/Layout.php
@@ -1,9 +1,9 @@
  array(
+      self::$_links = [
+        CRM_Core_Action::UPDATE => [
           'name' => ts('Edit'),
           'url' => 'civicrm/admin/badgelayout',
           'qs' => 'action=update&id=%%id%%&reset=1',
           'title' => ts('Edit Badge Layout'),
-        ),
-        CRM_Core_Action::DISABLE => array(
+        ],
+        CRM_Core_Action::DISABLE => [
           'name' => ts('Disable'),
           'ref' => 'crm-enable-disable',
           'title' => ts('Disable Badge Layout'),
-        ),
-        CRM_Core_Action::ENABLE => array(
+        ],
+        CRM_Core_Action::ENABLE => [
           'name' => ts('Enable'),
           'ref' => 'crm-enable-disable',
           'title' => ts('Enable Badge Layout'),
-        ),
-        CRM_Core_Action::DELETE => array(
+        ],
+        CRM_Core_Action::DELETE => [
           'name' => ts('Delete'),
           'url' => 'civicrm/admin/badgelayout',
           'qs' => 'action=delete&id=%%id%%',
           'title' => ts('Delete Badge Layout'),
-        ),
-      );
+        ],
+      ];
     }
     return self::$_links;
   }
diff --git a/CRM/Batch/BAO/Batch.php b/CRM/Batch/BAO/Batch.php
index 171ad95e37aa..aa87f7076f52 100644
--- a/CRM/Batch/BAO/Batch.php
+++ b/CRM/Batch/BAO/Batch.php
@@ -1,9 +1,9 @@
 copyValues($params);
-    if ($context == 'financialBatch' && !empty($ids['batchID'])) {
-      $batch->id = $ids['batchID'];
-    }
     $batch->save();
 
+    CRM_Utils_Hook::post($op, 'Batch', $batch->id, $batch);
+
     return $batch;
   }
 
@@ -132,32 +132,7 @@ public static function getProfileId($batchTypeId) {
   public static function generateBatchName() {
     $sql = "SELECT max(id) FROM civicrm_batch";
     $batchNo = CRM_Core_DAO::singleValueQuery($sql) + 1;
-    return ts('Batch %1', array(1 => $batchNo)) . ': ' . date('Y-m-d');
-  }
-
-  /**
-   * Create entity batch entry.
-   *
-   * @param array $params
-   * @return array
-   */
-  public static function addBatchEntity(&$params) {
-    $entityBatch = new CRM_Batch_DAO_EntityBatch();
-    $entityBatch->copyValues($params);
-    $entityBatch->save();
-    return $entityBatch;
-  }
-
-  /**
-   * Remove entries from entity batch.
-   * @param array $params
-   * @return CRM_Batch_DAO_EntityBatch
-   */
-  public static function removeBatchEntity($params) {
-    $entityBatch = new CRM_Batch_DAO_EntityBatch();
-    $entityBatch->copyValues($params);
-    $entityBatch->delete();
-    return $entityBatch;
+    return ts('Batch %1', [1 => $batchNo]) . ': ' . date('Y-m-d');
   }
 
   /**
@@ -170,9 +145,11 @@ public static function removeBatchEntity($params) {
    */
   public static function deleteBatch($batchId) {
     // delete entry from batch table
+    CRM_Utils_Hook::pre('delete', 'Batch', $batchId, CRM_Core_DAO::$_nullArray);
     $batch = new CRM_Batch_DAO_Batch();
     $batch->id = $batchId;
     $batch->delete();
+    CRM_Utils_Hook::post('delete', 'Batch', $batch->id, $batch);
     return TRUE;
   }
 
@@ -185,7 +162,7 @@ public static function deleteBatch($batchId) {
    * @return array
    *   associated array of batch list
    */
-  public function getBatchListSelector(&$params) {
+  public static function getBatchListSelector(&$params) {
     // format the params
     $params['offset'] = ($params['page'] - 1) * $params['rp'];
     $params['rowCount'] = $params['rp'];
@@ -195,12 +172,12 @@ public function getBatchListSelector(&$params) {
     $batches = self::getBatchList($params);
 
     // get batch totals for open batches
-    $fetchTotals = array();
-    $batchStatus = CRM_Core_PseudoConstant::get('CRM_Batch_DAO_Batch', 'status_id', array('labelColumn' => 'name'));
-    $batchStatus = array(
+    $fetchTotals = [];
+    $batchStatus = CRM_Core_PseudoConstant::get('CRM_Batch_DAO_Batch', 'status_id', ['labelColumn' => 'name']);
+    $batchStatus = [
       array_search('Open', $batchStatus),
       array_search('Reopened', $batchStatus),
-    );
+    ];
     if ($params['context'] == 'financialBatch') {
       foreach ($batches as $id => $batch) {
         if (in_array($batch['status_id'], $batchStatus)) {
@@ -214,10 +191,10 @@ public function getBatchListSelector(&$params) {
     $params['total'] = self::getBatchCount($params);
 
     // format params and add links
-    $batchList = array();
+    $batchList = [];
 
     foreach ($batches as $id => $value) {
-      $batch = array();
+      $batch = [];
       if ($params['context'] == 'financialBatch') {
         $batch['check'] = $value['check'];
       }
@@ -225,9 +202,10 @@ public function getBatchListSelector(&$params) {
       $batch['total'] = '';
       $batch['payment_instrument'] = $value['payment_instrument'];
       $batch['item_count'] = CRM_Utils_Array::value('item_count', $value);
-      $batch['type'] = $value['batch_type'];
+      $batch['type'] = CRM_Utils_Array::value('batch_type', $value);
       if (!empty($value['total'])) {
-        $batch['total'] = CRM_Utils_Money::format($value['total']);
+        // CRM-21205
+        $batch['total'] = CRM_Utils_Money::format($value['total'], $value['currency']);
       }
 
       // Compare totals with actuals
@@ -252,60 +230,70 @@ public function getBatchListSelector(&$params) {
    * @return array
    */
   public static function getBatchList(&$params) {
-    $whereClause = self::whereClause($params);
+    $apiParams = self::whereClause($params);
 
     if (!empty($params['rowCount']) && is_numeric($params['rowCount'])
       && is_numeric($params['offset']) && $params['rowCount'] > 0
     ) {
-      $limit = " LIMIT {$params['offset']}, {$params['rowCount']} ";
+      $apiParams['options'] = ['offset' => $params['offset'], 'limit' => $params['rowCount']];
     }
-
-    $orderBy = ' ORDER BY batch.id desc';
+    $apiParams['options']['sort'] = 'id DESC';
     if (!empty($params['sort'])) {
-      $orderBy = ' ORDER BY ' . CRM_Utils_Type::escape($params['sort'], 'String');
-    }
-
-    $query = "
-      SELECT batch.*, c.sort_name created_by
-      FROM  civicrm_batch batch
-      INNER JOIN civicrm_contact c ON batch.created_id = c.id
-    WHERE {$whereClause}
-    {$orderBy}
-    {$limit}";
-
-    $object = CRM_Core_DAO::executeQuery($query, $params, TRUE, 'CRM_Batch_DAO_Batch');
+      $apiParams['options']['sort'] = CRM_Utils_Type::escape($params['sort'], 'String');
+    }
+
+    $return = [
+      "id",
+      "name",
+      "title",
+      "description",
+      "created_date",
+      "status_id",
+      "modified_id",
+      "modified_date",
+      "type_id",
+      "mode_id",
+      "total",
+      "item_count",
+      "exported_date",
+      "payment_instrument_id",
+      "created_id.sort_name",
+      "created_id",
+    ];
+    $apiParams['return'] = $return;
+    $batches = civicrm_api3('Batch', 'get', $apiParams);
+    $obj = new CRM_Batch_BAO_Batch();
     if (!empty($params['context'])) {
-      $links = self::links($params['context']);
+      $links = $obj->links($params['context']);
     }
     else {
-      $links = self::links();
+      $links = $obj->links();
     }
 
     $batchTypes = CRM_Core_PseudoConstant::get('CRM_Batch_DAO_Batch', 'type_id');
     $batchStatus = CRM_Core_PseudoConstant::get('CRM_Batch_DAO_Batch', 'status_id');
-    $batchStatusByName = CRM_Core_PseudoConstant::get('CRM_Batch_DAO_Batch', 'status_id', array('labelColumn' => 'name'));
+    $batchStatusByName = CRM_Core_PseudoConstant::get('CRM_Batch_DAO_Batch', 'status_id', ['labelColumn' => 'name']);
     $paymentInstrument = CRM_Contribute_PseudoConstant::paymentInstrument();
 
-    $results = array();
-    while ($object->fetch()) {
-      $values = array();
+    $results = [];
+    foreach ($batches['values'] as $values) {
       $newLinks = $links;
-      CRM_Core_DAO::storeValues($object, $values);
       $action = array_sum(array_keys($newLinks));
 
       if ($values['status_id'] == array_search('Closed', $batchStatusByName) && $params['context'] != 'financialBatch') {
-        $newLinks = array();
+        $newLinks = [];
       }
       elseif ($params['context'] == 'financialBatch') {
         $values['check'] = "";
 
         switch ($batchStatusByName[$values['status_id']]) {
           case 'Open':
+          case 'Reopened':
             CRM_Utils_Array::remove($newLinks, 'reopen', 'download');
             break;
 
@@ -316,23 +304,39 @@ public static function getBatchList(&$params) {
           case 'Exported':
             CRM_Utils_Array::remove($newLinks, 'close', 'edit', 'reopen', 'export');
         }
+        if (!CRM_Batch_BAO_Batch::checkBatchPermission('edit', $values['created_id'])) {
+          CRM_Utils_Array::remove($newLinks, 'edit');
+        }
+        if (!CRM_Batch_BAO_Batch::checkBatchPermission('close', $values['created_id'])) {
+          CRM_Utils_Array::remove($newLinks, 'close', 'export');
+        }
+        if (!CRM_Batch_BAO_Batch::checkBatchPermission('reopen', $values['created_id'])) {
+          CRM_Utils_Array::remove($newLinks, 'reopen');
+        }
+        if (!CRM_Batch_BAO_Batch::checkBatchPermission('export', $values['created_id'])) {
+          CRM_Utils_Array::remove($newLinks, 'export', 'download');
+        }
+        if (!CRM_Batch_BAO_Batch::checkBatchPermission('delete', $values['created_id'])) {
+          CRM_Utils_Array::remove($newLinks, 'delete');
+        }
       }
       if (!empty($values['type_id'])) {
         $values['batch_type'] = $batchTypes[$values['type_id']];
       }
       $values['batch_status'] = $batchStatus[$values['status_id']];
-      $values['created_by'] = $object->created_by;
+      $values['created_by'] = $values['created_id.sort_name'];
       $values['payment_instrument'] = '';
-      if (!empty($object->payment_instrument_id)) {
-        $values['payment_instrument'] = $paymentInstrument[$object->payment_instrument_id];
+      if (!empty($values['payment_instrument_id'])) {
+        $values['payment_instrument'] = $paymentInstrument[$values['payment_instrument_id']];
       }
-      $tokens = array('id' => $object->id, 'status' => $values['status_id']);
+      $tokens = ['id' => $values['id'], 'status' => $values['status_id']];
       if ($values['status_id'] == array_search('Exported', $batchStatusByName)) {
-        $aid = CRM_Core_OptionGroup::getValue('activity_type', 'Export Accounting Batch');
-        $activityParams = array('source_record_id' => $object->id, 'activity_type_id' => $aid);
+        $aid = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Export Accounting Batch');
+        $activityParams = ['source_record_id' => $values['id'], 'activity_type_id' => $aid];
         $exportActivity = CRM_Activity_BAO_Activity::retrieve($activityParams, $val);
         $fid = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_EntityFile', $exportActivity->id, 'file_id', 'entity_id');
-        $tokens = array_merge(array('eid' => $exportActivity->id, 'fid' => $fid), $tokens);
+        $fileHash = CRM_Core_BAO_File::generateFileHash($exportActivity->id, $fid);
+        $tokens = array_merge(['eid' => $exportActivity->id, 'fid' => $fid, 'fcs' => $fileHash], $tokens);
       }
       $values['action'] = CRM_Core_Action::formLink(
         $newLinks,
@@ -342,9 +346,20 @@ public static function getBatchList(&$params) {
         FALSE,
         'batch.selector.row',
         'Batch',
-        $object->id
+        $values['id']
       );
-      $results[$object->id] = $values;
+      // CRM-21205
+      $values['currency'] = CRM_Core_DAO::singleValueQuery("
+        SELECT GROUP_CONCAT(DISTINCT ft.currency)
+        FROM  civicrm_batch batch
+        JOIN civicrm_entity_batch eb
+          ON batch.id = eb.batch_id
+        JOIN civicrm_financial_trxn ft
+          ON eb.entity_id = ft.id
+        WHERE batch.id = %1
+        GROUP BY batch.id
+      ", [1 => [$values['id'], 'Positive']]);
+      $results[$values['id']] = $values;
     }
 
     return $results;
@@ -359,12 +374,8 @@ public static function getBatchList(&$params) {
    * @return null|string
    */
   public static function getBatchCount(&$params) {
-    $args = array();
-    $whereClause = self::whereClause($params, $args);
-    $query = " SELECT COUNT(*) FROM civicrm_batch batch
-      INNER JOIN civicrm_contact c ON batch.created_id = c.id
-      WHERE {$whereClause}";
-    return CRM_Core_DAO::singleValueQuery($query);
+    $apiParams = self::whereClause($params);
+    return civicrm_api3('Batch', 'getCount', $apiParams);
   }
 
   /**
@@ -376,43 +387,56 @@ public static function getBatchCount(&$params) {
    * @return string
    */
   public static function whereClause($params) {
-    $clauses = array();
+    $clauses = [];
     // Exclude data-entry batches
-    $batchStatus = CRM_Core_PseudoConstant::get('CRM_Batch_DAO_Batch', 'status_id', array('labelColumn' => 'name'));
+    $batchStatus = CRM_Core_PseudoConstant::get('CRM_Batch_DAO_Batch', 'status_id', ['labelColumn' => 'name']);
     if (empty($params['status_id'])) {
-      $clauses[] = 'batch.status_id <> ' . array_search('Data Entry', $batchStatus);
+      $clauses['status_id'] = ['NOT IN' => ["Data Entry"]];
+    }
+
+    $return = [
+      "id",
+      "name",
+      "title",
+      "description",
+      "created_date",
+      "status_id",
+      "modified_id",
+      "modified_date",
+      "type_id",
+      "mode_id",
+      "total",
+      "item_count",
+      "exported_date",
+      "payment_instrument_id",
+      "created_id.sort_name",
+      "created_id",
+    ];
+    if (!CRM_Core_Permission::check("view all manual batches")) {
+      if (CRM_Core_Permission::check("view own manual batches")) {
+        $loggedInContactId = CRM_Core_Session::singleton()->get('userID');
+        $params['created_id'] = $loggedInContactId;
+      }
+      else {
+        $params['created_id'] = 0;
+      }
     }
-
-    $fields = array(
-      'title' => 'String',
-      'sort_name' => 'String',
-      'status_id' => 'Integer',
-      'payment_instrument_id' => 'Integer',
-      'item_count' => 'Integer',
-      'total' => 'Float',
-    );
-
-    foreach ($fields as $field => $type) {
-      $table = $field == 'sort_name' ? 'c' : 'batch';
-      if (isset($params[$field])) {
-        $value = CRM_Utils_Type::escape($params[$field], $type, FALSE);
-        if ($value && $type == 'String') {
-          $clauses[] = "$table.$field LIKE '%$value%'";
-        }
-        elseif ($value && $type == 'Float') {
-          $clauses[] = "$table.$field = '$value'";
-        }
-        elseif ($value) {
-          if ($field == 'status_id' && $value == array_search('Open', $batchStatus)) {
-            $clauses[] = "$table.$field IN ($value," . array_search('Reopened', $batchStatus) . ')';
-          }
-          else {
-            $clauses[] = "$table.$field = $value";
-          }
-        }
+    foreach ($return as $field) {
+      if (!isset($params[$field])) {
+        continue;
+      }
+      $value = CRM_Utils_Type::escape($params[$field], 'String', FALSE);
+      if (in_array($field, ['name', 'title', 'description', 'created_id.sort_name'])) {
+        $clauses[$field] = ['LIKE' => "%{$value}%"];
+      }
+      elseif ($field == 'status_id' && $value == array_search('Open', $batchStatus)) {
+        $clauses['status_id'] = ['IN' => ["Open", 'Reopened']];
+      }
+      else {
+        $clauses[$field] = $value;
       }
     }
-    return $clauses ? implode(' AND ', $clauses) : '1';
+    return $clauses;
   }
 
   /**
@@ -425,72 +449,72 @@ public static function whereClause($params) {
    */
   public function links($context = NULL) {
     if ($context == 'financialBatch') {
-      $links = array(
-        'transaction' => array(
+      $links = [
+        'transaction' => [
           'name' => ts('Transactions'),
           'url' => 'civicrm/batchtransaction',
           'qs' => 'reset=1&bid=%%id%%',
           'title' => ts('View/Add Transactions to Batch'),
-        ),
-        'edit' => array(
+        ],
+        'edit' => [
           'name' => ts('Edit'),
           'url' => 'civicrm/financial/batch',
           'qs' => 'reset=1&action=update&id=%%id%%&context=1',
           'title' => ts('Edit Batch'),
-        ),
-        'close' => array(
+        ],
+        'close' => [
           'name' => ts('Close'),
           'title' => ts('Close Batch'),
           'url' => '#',
           'extra' => 'rel="close"',
-        ),
-        'export' => array(
+        ],
+        'export' => [
           'name' => ts('Export'),
           'title' => ts('Export Batch'),
           'url' => '#',
           'extra' => 'rel="export"',
-        ),
-        'reopen' => array(
+        ],
+        'reopen' => [
           'name' => ts('Re-open'),
           'title' => ts('Re-open Batch'),
           'url' => '#',
           'extra' => 'rel="reopen"',
-        ),
-        'delete' => array(
+        ],
+        'delete' => [
           'name' => ts('Delete'),
           'title' => ts('Delete Batch'),
           'url' => '#',
           'extra' => 'rel="delete"',
-        ),
-        'download' => array(
+        ],
+        'download' => [
           'name' => ts('Download'),
           'url' => 'civicrm/file',
-          'qs' => 'reset=1&id=%%fid%%&eid=%%eid%%',
+          'qs' => 'reset=1&id=%%fid%%&eid=%%eid%%&fcs=%%fcs%%',
           'title' => ts('Download Batch'),
-        ),
-      );
+        ],
+      ];
     }
     else {
-      $links = array(
-        CRM_Core_Action::COPY => array(
+      $links = [
+        CRM_Core_Action::COPY => [
           'name' => ts('Enter records'),
           'url' => 'civicrm/batch/entry',
           'qs' => 'id=%%id%%&reset=1',
           'title' => ts('Batch Data Entry'),
-        ),
-        CRM_Core_Action::UPDATE => array(
+        ],
+        CRM_Core_Action::UPDATE => [
           'name' => ts('Edit'),
           'url' => 'civicrm/batch',
           'qs' => 'action=update&id=%%id%%&reset=1',
           'title' => ts('Edit Batch'),
-        ),
-        CRM_Core_Action::DELETE => array(
+        ],
+        CRM_Core_Action::DELETE => [
           'name' => ts('Delete'),
           'url' => 'civicrm/batch',
           'qs' => 'action=delete&id=%%id%%',
           'title' => ts('Delete Batch'),
-        ),
-      );
+        ],
+      ];
     }
     return $links;
   }
@@ -502,14 +526,14 @@ public function links($context = NULL) {
    *   all batches excluding batches with data entry in progress
    */
   public static function getBatches() {
-    $dataEntryStatusId = CRM_Core_OptionGroup::getValue('batch_status', 'Data Entry', 'name');
+    $dataEntryStatusId = CRM_Core_PseudoConstant::getKey('CRM_Batch_BAO_Batch', 'status_id', 'Data Entry');
     $query = "SELECT id, title
       FROM civicrm_batch
       WHERE item_count >= 1
       AND status_id != {$dataEntryStatusId}
       ORDER BY title";
 
-    $batches = array();
+    $batches = [];
     $dao = CRM_Core_DAO::executeQuery($query);
     while ($dao->fetch()) {
       $batches[$dao->id] = $dao->title;
@@ -517,7 +541,6 @@ public static function getBatches() {
     return $batches;
   }
 
-
   /**
    * Calculate sum of all entries in a batch.
    * Used to validate and update item_count and total when closing an accounting batch
@@ -526,7 +549,7 @@ public static function getBatches() {
    * @return array
    */
   public static function batchTotals($batchIds) {
-    $totals = array_fill_keys($batchIds, array('item_count' => 0, 'total' => 0));
+    $totals = array_fill_keys($batchIds, ['item_count' => 0, 'total' => 0]);
     if ($batchIds) {
       $sql = "SELECT eb.batch_id, COUNT(tx.id) AS item_count, SUM(tx.total_amount) AS total
       FROM civicrm_entity_batch eb
@@ -573,8 +596,10 @@ public static function displayTotals($actual, $expected) {
    *   Associated array of batch ids.
    * @param string $exportFormat
    *   Export format.
+   * @param bool $downloadFile
+   *   Download export file?.
    */
-  public static function exportFinancialBatch($batchIds, $exportFormat) {
+  public static function exportFinancialBatch($batchIds, $exportFormat, $downloadFile) {
     if (empty($batchIds)) {
       CRM_Core_Error::fatal(ts('No batches were selected.'));
       return;
@@ -593,17 +618,29 @@ public static function exportFinancialBatch($batchIds, $exportFormat) {
     else {
       CRM_Core_Error::fatal("Could not locate exporter: $exporterClass");
     }
+    $export = [];
+    $exporter->_isDownloadFile = $downloadFile;
     foreach ($batchIds as $batchId) {
+      // export only batches whose status is set to Exported.
+      $result = civicrm_api3('Batch', 'getcount', [
+        'id' => $batchId,
+        'status_id' => "Exported",
+      ]);
+      if (!$result) {
+        continue;
+      }
       $export[$batchId] = $exporter->generateExportQuery($batchId);
     }
-    $exporter->makeExport($export);
+    if ($export) {
+      $exporter->makeExport($export);
+    }
   }
 
   /**
    * @param array $batchIds
    * @param $status
    */
-  public static function closeReOpen($batchIds = array(), $status) {
+  public static function closeReOpen($batchIds = [], $status) {
     $batchStatus = CRM_Core_PseudoConstant::get('CRM_Batch_DAO_Batch', 'status_id');
     $params['status_id'] = CRM_Utils_Array::key($status, $batchStatus);
     $session = CRM_Core_Session::singleton();
@@ -648,20 +685,21 @@ public static function getBatchFinancialItems($entityID, $returnValues, $notPres
     }
 
     $from = "civicrm_financial_trxn
-LEFT JOIN civicrm_entity_financial_trxn ON civicrm_entity_financial_trxn.financial_trxn_id = civicrm_financial_trxn.id
+INNER JOIN civicrm_entity_financial_trxn ON civicrm_entity_financial_trxn.financial_trxn_id = civicrm_financial_trxn.id
+INNER JOIN civicrm_contribution ON (civicrm_contribution.id = civicrm_entity_financial_trxn.entity_id
+  AND civicrm_entity_financial_trxn.entity_table='civicrm_contribution')
 LEFT JOIN civicrm_entity_batch ON civicrm_entity_batch.entity_table = 'civicrm_financial_trxn'
 AND civicrm_entity_batch.entity_id = civicrm_financial_trxn.id
-LEFT JOIN civicrm_contribution ON civicrm_contribution.id = civicrm_entity_financial_trxn.entity_id
 LEFT JOIN civicrm_financial_type ON civicrm_financial_type.id = civicrm_contribution.financial_type_id
 LEFT JOIN civicrm_contact contact_a ON contact_a.id = civicrm_contribution.contact_id
 LEFT JOIN civicrm_contribution_soft ON civicrm_contribution_soft.contribution_id = civicrm_contribution.id
 ";
 
-    $searchFields = array(
+    $searchFields = [
       'sort_name',
       'financial_type_id',
       'contribution_page_id',
-      'payment_instrument_id',
+      'contribution_payment_instrument_id',
       'contribution_trxn_id',
       'contribution_source',
       'contribution_currency_type',
@@ -683,8 +721,10 @@ public static function getBatchFinancialItems($entityID, $returnValues, $notPres
       'contribution_date_low',
       'contribution_check_number',
       'contribution_status_id',
-    );
-    $values = array();
+      'financial_trxn_card_type_id',
+      'financial_trxn_pan_truncation',
+    ];
+    $values = [];
     foreach ($searchFields as $field) {
       if (isset($params[$field])) {
         $values[$field] = $params[$field];
@@ -707,37 +747,42 @@ public static function getBatchFinancialItems($entityID, $returnValues, $notPres
           $values['contribution_date_low'] = $date['from'];
           $values['contribution_date_high'] = $date['to'];
         }
-        $searchParams = CRM_Contact_BAO_Query::convertFormValues($values);
-        $query = new CRM_Contact_BAO_Query($searchParams,
-          CRM_Contribute_BAO_Query::defaultReturnProperties(CRM_Contact_BAO_Query::MODE_CONTRIBUTE,
-            FALSE
-          ), NULL, FALSE, FALSE, CRM_Contact_BAO_Query::MODE_CONTRIBUTE
-        );
-        if ($field == 'contribution_date_high' || $field == 'contribution_date_low') {
-          $query->dateQueryBuilder($params[$field], 'civicrm_contribution', 'contribution_date', 'receive_date', 'Contribution Date');
-        }
       }
     }
+
+    $searchParams = CRM_Contact_BAO_Query::convertFormValues(
+      $values,
+      0,
+      FALSE,
+      NULL,
+      [
+        'financial_type_id',
+        'contribution_soft_credit_type_id',
+        'contribution_status_id',
+        'contribution_page_id',
+        'financial_trxn_card_type_id',
+        'contribution_payment_instrument_id',
+      ]
+    );
+    // @todo the use of defaultReturnProperties means the search will be inefficient
+    // as slow-unneeded properties are included.
+    $query = new CRM_Contact_BAO_Query($searchParams,
+      CRM_Contribute_BAO_Query::defaultReturnProperties(CRM_Contact_BAO_Query::MODE_CONTRIBUTE,
+        FALSE
+      ), NULL, FALSE, FALSE, CRM_Contact_BAO_Query::MODE_CONTRIBUTE
+    );
+
     if (!empty($query->_where[0])) {
       $where = implode(' AND ', $query->_where[0]) .
-        " AND civicrm_entity_batch.batch_id IS NULL
-         AND civicrm_entity_financial_trxn.entity_table = 'civicrm_contribution'";
+        " AND civicrm_entity_batch.batch_id IS NULL ";
       $where = str_replace('civicrm_contribution.payment_instrument_id', 'civicrm_financial_trxn.payment_instrument_id', $where);
-      $searchValue = TRUE;
     }
     else {
-      $searchValue = FALSE;
-    }
-
-    if (!$searchValue) {
       if (!$notPresent) {
-        $where = " ( civicrm_entity_batch.batch_id = {$entityID}
-        AND civicrm_entity_batch.entity_table = 'civicrm_financial_trxn'
-        AND civicrm_entity_financial_trxn.entity_table = 'civicrm_contribution') ";
+        $where = " civicrm_entity_batch.batch_id = {$entityID} ";
       }
       else {
-        $where = " ( civicrm_entity_batch.batch_id IS NULL
-        AND civicrm_entity_financial_trxn.entity_table = 'civicrm_contribution')";
+        $where = " civicrm_entity_batch.batch_id IS NULL ";
       }
     }
 
@@ -768,7 +813,7 @@ public static function getBatchNames($batchIds) {
       FROM civicrm_batch
       WHERE id IN (' . $batchIds . ')';
 
-    $batches = array();
+    $batches = [];
     $dao = CRM_Core_DAO::executeQuery($query);
     while ($dao->fetch()) {
       $batches[$dao->id] = $dao->title;
@@ -789,7 +834,7 @@ public static function getBatchStatuses($batchIds) {
       FROM civicrm_batch
       WHERE id IN (' . $batchIds . ')';
 
-    $batches = array();
+    $batches = [];
     $dao = CRM_Core_DAO::executeQuery($query);
     while ($dao->fetch()) {
       $batches[$dao->id] = $dao->status_id;
@@ -797,4 +842,29 @@ public static function getBatchStatuses($batchIds) {
     return $batches;
   }
 
+  /**
+   * Function to check permission for batch.
+   *
+   * @param string $action
+   * @param int $batchCreatedId
+   *   batch created by contact id
+   *
+   * @return bool
+   */
+  public static function checkBatchPermission($action, $batchCreatedId = NULL) {
+    if (CRM_Core_Permission::check("{$action} all manual batches")) {
+      return TRUE;
+    }
+    if (CRM_Core_Permission::check("{$action} own manual batches")) {
+      $loggedInContactId = CRM_Core_Session::singleton()->get('userID');
+      if ($batchCreatedId == $loggedInContactId) {
+        return TRUE;
+      }
+      elseif (CRM_Utils_System::isNull($batchCreatedId)) {
+        return TRUE;
+      }
+    }
+    return FALSE;
+  }
+
 }
diff --git a/CRM/Batch/BAO/EntityBatch.php b/CRM/Batch/BAO/EntityBatch.php
index 589c1a2fa1bc..426ce282622d 100644
--- a/CRM/Batch/BAO/EntityBatch.php
+++ b/CRM/Batch/BAO/EntityBatch.php
@@ -1,9 +1,9 @@
 copyValues($params);
+    $entityBatch->save();
+    CRM_Utils_Hook::post($op, 'EntityBatch', $entityBatch->id, $entityBatch);
+    return $entityBatch;
+  }
+
+  /**
+   * Remove entries from entity batch.
+   * @param array|int $params
+   * @return CRM_Batch_DAO_EntityBatch
+   */
+  public static function del($params) {
+    if (!is_array($params)) {
+      $params = ['id' => $params];
+    }
+    $entityBatch = new CRM_Batch_DAO_EntityBatch();
+    $entityId = CRM_Utils_Array::value('id', $params);
+    CRM_Utils_Hook::pre('delete', 'EntityBatch', $entityId, $params);
+    $entityBatch->copyValues($params);
+    $entityBatch->delete();
+    CRM_Utils_Hook::post('delete', 'EntityBatch', $entityBatch->id, $entityBatch);
+    return $entityBatch;
+  }
+
 }
diff --git a/CRM/Batch/DAO/Batch.php b/CRM/Batch/DAO/Batch.php
index 9f38f0a558a0..a066888d143f 100644
--- a/CRM/Batch/DAO/Batch.php
+++ b/CRM/Batch/DAO/Batch.php
@@ -1,385 +1,453 @@
 __table = 'civicrm_batch';
     parent::__construct();
   }
+
   /**
-   * Returns foreign keys and entity references
+   * Returns foreign keys and entity references.
    *
    * @return array
    *   [CRM_Core_Reference_Interface]
    */
-  static function getReferenceColumns() {
+  public static function getReferenceColumns() {
     if (!isset(Civi::$statics[__CLASS__]['links'])) {
-      Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__);
-      Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'created_id', 'civicrm_contact', 'id');
-      Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'modified_id', 'civicrm_contact', 'id');
-      Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'saved_search_id', 'civicrm_saved_search', 'id');
+      Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__);
+      Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'created_id', 'civicrm_contact', 'id');
+      Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'modified_id', 'civicrm_contact', 'id');
+      Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'saved_search_id', 'civicrm_saved_search', 'id');
       CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']);
     }
     return Civi::$statics[__CLASS__]['links'];
   }
+
   /**
    * Returns all the column names of this table
    *
    * @return array
    */
-  static function &fields() {
+  public static function &fields() {
     if (!isset(Civi::$statics[__CLASS__]['fields'])) {
-      Civi::$statics[__CLASS__]['fields'] = array(
-        'id' => array(
+      Civi::$statics[__CLASS__]['fields'] = [
+        'id' => [
           'name' => 'id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Batch ID') ,
-          'description' => 'Unique Address ID',
-          'required' => true,
-        ) ,
-        'name' => array(
+          'title' => ts('Batch ID'),
+          'description' => ts('Unique Address ID'),
+          'required' => TRUE,
+          'table_name' => 'civicrm_batch',
+          'entity' => 'Batch',
+          'bao' => 'CRM_Batch_BAO_Batch',
+          'localizable' => 0,
+        ],
+        'name' => [
           'name' => 'name',
           'type' => CRM_Utils_Type::T_STRING,
-          'title' => ts('Batch Name') ,
-          'description' => 'Variable name/programmatic handle for this batch.',
+          'title' => ts('Batch Name'),
+          'description' => ts('Variable name/programmatic handle for this batch.'),
           'maxlength' => 64,
           'size' => CRM_Utils_Type::BIG,
-          'html' => array(
+          'table_name' => 'civicrm_batch',
+          'entity' => 'Batch',
+          'bao' => 'CRM_Batch_BAO_Batch',
+          'localizable' => 0,
+          'html' => [
             'type' => 'Text',
-          ) ,
-        ) ,
-        'title' => array(
+          ],
+        ],
+        'title' => [
           'name' => 'title',
           'type' => CRM_Utils_Type::T_STRING,
-          'title' => ts('Batch Title') ,
-          'description' => 'Friendly Name.',
-          'maxlength' => 64,
-          'size' => CRM_Utils_Type::BIG,
-          'html' => array(
+          'title' => ts('Batch Title'),
+          'description' => ts('Friendly Name.'),
+          'maxlength' => 255,
+          'size' => CRM_Utils_Type::HUGE,
+          'table_name' => 'civicrm_batch',
+          'entity' => 'Batch',
+          'bao' => 'CRM_Batch_BAO_Batch',
+          'localizable' => 1,
+          'html' => [
             'type' => 'Text',
-          ) ,
-        ) ,
-        'description' => array(
+          ],
+        ],
+        'description' => [
           'name' => 'description',
           'type' => CRM_Utils_Type::T_TEXT,
-          'title' => ts('Batch Description') ,
-          'description' => 'Description of this batch set.',
+          'title' => ts('Batch Description'),
+          'description' => ts('Description of this batch set.'),
           'rows' => 4,
           'cols' => 80,
-          'html' => array(
+          'table_name' => 'civicrm_batch',
+          'entity' => 'Batch',
+          'bao' => 'CRM_Batch_BAO_Batch',
+          'localizable' => 1,
+          'html' => [
             'type' => 'TextArea',
-          ) ,
-        ) ,
-        'created_id' => array(
+          ],
+        ],
+        'created_id' => [
           'name' => 'created_id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Batch Created By') ,
-          'description' => 'FK to Contact ID',
+          'title' => ts('Batch Created By'),
+          'description' => ts('FK to Contact ID'),
+          'table_name' => 'civicrm_batch',
+          'entity' => 'Batch',
+          'bao' => 'CRM_Batch_BAO_Batch',
+          'localizable' => 0,
           'FKClassName' => 'CRM_Contact_DAO_Contact',
-        ) ,
-        'created_date' => array(
+        ],
+        'created_date' => [
           'name' => 'created_date',
           'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME,
-          'title' => ts('Batch Created Date') ,
-          'description' => 'When was this item created',
-          'html' => array(
+          'title' => ts('Batch Created Date'),
+          'description' => ts('When was this item created'),
+          'table_name' => 'civicrm_batch',
+          'entity' => 'Batch',
+          'bao' => 'CRM_Batch_BAO_Batch',
+          'localizable' => 0,
+          'html' => [
             'type' => 'Select Date',
-          ) ,
-        ) ,
-        'modified_id' => array(
+          ],
+        ],
+        'modified_id' => [
           'name' => 'modified_id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Batch Modified By') ,
-          'description' => 'FK to Contact ID',
+          'title' => ts('Batch Modified By'),
+          'description' => ts('FK to Contact ID'),
+          'table_name' => 'civicrm_batch',
+          'entity' => 'Batch',
+          'bao' => 'CRM_Batch_BAO_Batch',
+          'localizable' => 0,
           'FKClassName' => 'CRM_Contact_DAO_Contact',
-        ) ,
-        'modified_date' => array(
+        ],
+        'modified_date' => [
           'name' => 'modified_date',
           'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME,
-          'title' => ts('Batch Modified Date') ,
-          'description' => 'When was this item created',
-        ) ,
-        'saved_search_id' => array(
+          'title' => ts('Batch Modified Date'),
+          'description' => ts('When was this item created'),
+          'table_name' => 'civicrm_batch',
+          'entity' => 'Batch',
+          'bao' => 'CRM_Batch_BAO_Batch',
+          'localizable' => 0,
+        ],
+        'saved_search_id' => [
           'name' => 'saved_search_id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Batch Smart Group') ,
-          'description' => 'FK to Saved Search ID',
+          'title' => ts('Batch Smart Group'),
+          'description' => ts('FK to Saved Search ID'),
+          'table_name' => 'civicrm_batch',
+          'entity' => 'Batch',
+          'bao' => 'CRM_Batch_BAO_Batch',
+          'localizable' => 0,
           'FKClassName' => 'CRM_Contact_DAO_SavedSearch',
-          'html' => array(
+          'html' => [
             'type' => 'EntityRef',
-          ) ,
-        ) ,
-        'status_id' => array(
+          ],
+        ],
+        'status_id' => [
           'name' => 'status_id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Batch Status') ,
-          'description' => 'fk to Batch Status options in civicrm_option_values',
-          'required' => true,
-          'html' => array(
+          'title' => ts('Batch Status'),
+          'description' => ts('fk to Batch Status options in civicrm_option_values'),
+          'required' => TRUE,
+          'table_name' => 'civicrm_batch',
+          'entity' => 'Batch',
+          'bao' => 'CRM_Batch_BAO_Batch',
+          'localizable' => 0,
+          'html' => [
             'type' => 'Select',
-          ) ,
-          'pseudoconstant' => array(
+          ],
+          'pseudoconstant' => [
             'optionGroupName' => 'batch_status',
             'optionEditPath' => 'civicrm/admin/options/batch_status',
-          )
-        ) ,
-        'type_id' => array(
+          ]
+        ],
+        'type_id' => [
           'name' => 'type_id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Batch Type') ,
-          'description' => 'fk to Batch Type options in civicrm_option_values',
-          'html' => array(
+          'title' => ts('Batch Type'),
+          'description' => ts('fk to Batch Type options in civicrm_option_values'),
+          'table_name' => 'civicrm_batch',
+          'entity' => 'Batch',
+          'bao' => 'CRM_Batch_BAO_Batch',
+          'localizable' => 0,
+          'html' => [
             'type' => 'Select',
-          ) ,
-          'pseudoconstant' => array(
+          ],
+          'pseudoconstant' => [
             'optionGroupName' => 'batch_type',
             'optionEditPath' => 'civicrm/admin/options/batch_type',
-          )
-        ) ,
-        'mode_id' => array(
+          ]
+        ],
+        'mode_id' => [
           'name' => 'mode_id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Batch Mode') ,
-          'description' => 'fk to Batch mode options in civicrm_option_values',
-          'html' => array(
+          'title' => ts('Batch Mode'),
+          'description' => ts('fk to Batch mode options in civicrm_option_values'),
+          'table_name' => 'civicrm_batch',
+          'entity' => 'Batch',
+          'bao' => 'CRM_Batch_BAO_Batch',
+          'localizable' => 0,
+          'html' => [
             'type' => 'Select',
-          ) ,
-          'pseudoconstant' => array(
+          ],
+          'pseudoconstant' => [
             'optionGroupName' => 'batch_mode',
             'optionEditPath' => 'civicrm/admin/options/batch_mode',
-          )
-        ) ,
-        'total' => array(
+          ]
+        ],
+        'total' => [
           'name' => 'total',
           'type' => CRM_Utils_Type::T_MONEY,
-          'title' => ts('Batch Total') ,
-          'description' => 'Total amount for this batch.',
-          'precision' => array(
+          'title' => ts('Batch Total'),
+          'description' => ts('Total amount for this batch.'),
+          'precision' => [
             20,
             2
-          ) ,
-          'html' => array(
+          ],
+          'table_name' => 'civicrm_batch',
+          'entity' => 'Batch',
+          'bao' => 'CRM_Batch_BAO_Batch',
+          'localizable' => 0,
+          'html' => [
             'type' => 'Text',
-          ) ,
-        ) ,
-        'item_count' => array(
+          ],
+        ],
+        'item_count' => [
           'name' => 'item_count',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Batch Number of Items') ,
-          'description' => 'Number of items in a batch.',
-          'html' => array(
+          'title' => ts('Batch Number of Items'),
+          'description' => ts('Number of items in a batch.'),
+          'table_name' => 'civicrm_batch',
+          'entity' => 'Batch',
+          'bao' => 'CRM_Batch_BAO_Batch',
+          'localizable' => 0,
+          'html' => [
             'type' => 'Text',
-          ) ,
-        ) ,
-        'payment_instrument_id' => array(
+          ],
+        ],
+        'payment_instrument_id' => [
           'name' => 'payment_instrument_id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Batch Payment Method') ,
-          'description' => 'fk to Payment Instrument options in civicrm_option_values',
-          'html' => array(
+          'title' => ts('Batch Payment Method'),
+          'description' => ts('fk to Payment Instrument options in civicrm_option_values'),
+          'table_name' => 'civicrm_batch',
+          'entity' => 'Batch',
+          'bao' => 'CRM_Batch_BAO_Batch',
+          'localizable' => 0,
+          'html' => [
             'type' => 'Select',
-          ) ,
-          'pseudoconstant' => array(
+          ],
+          'pseudoconstant' => [
             'optionGroupName' => 'payment_instrument',
             'optionEditPath' => 'civicrm/admin/options/payment_instrument',
-          )
-        ) ,
-        'exported_date' => array(
+          ]
+        ],
+        'exported_date' => [
           'name' => 'exported_date',
           'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME,
-          'title' => ts('Batch Exported Date') ,
-        ) ,
-        'data' => array(
+          'title' => ts('Batch Exported Date'),
+          'table_name' => 'civicrm_batch',
+          'entity' => 'Batch',
+          'bao' => 'CRM_Batch_BAO_Batch',
+          'localizable' => 0,
+        ],
+        'data' => [
           'name' => 'data',
           'type' => CRM_Utils_Type::T_LONGTEXT,
-          'title' => ts('Batch Data') ,
-          'description' => 'cache entered data',
-        ) ,
-      );
+          'title' => ts('Batch Data'),
+          'description' => ts('cache entered data'),
+          'table_name' => 'civicrm_batch',
+          'entity' => 'Batch',
+          'bao' => 'CRM_Batch_BAO_Batch',
+          'localizable' => 0,
+        ],
+      ];
       CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']);
     }
     return Civi::$statics[__CLASS__]['fields'];
   }
+
   /**
    * Return a mapping from field-name to the corresponding key (as used in fields()).
    *
    * @return array
    *   Array(string $name => string $uniqueName).
    */
-  static function &fieldKeys() {
+  public static function &fieldKeys() {
     if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) {
       Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields()));
     }
     return Civi::$statics[__CLASS__]['fieldKeys'];
   }
+
   /**
    * Returns the names of this table
    *
    * @return string
    */
-  static function getTableName() {
+  public static function getTableName() {
     return CRM_Core_DAO::getLocaleTableName(self::$_tableName);
   }
+
   /**
    * Returns if this table needs to be logged
    *
-   * @return boolean
+   * @return bool
    */
-  function getLog() {
+  public function getLog() {
     return self::$_log;
   }
+
   /**
    * Returns the list of fields that can be imported
    *
@@ -387,10 +455,11 @@ function getLog() {
    *
    * @return array
    */
-  static function &import($prefix = false) {
-    $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'batch', $prefix, array());
+  public static function &import($prefix = FALSE) {
+    $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'batch', $prefix, []);
     return $r;
   }
+
   /**
    * Returns the list of fields that can be exported
    *
@@ -398,8 +467,31 @@ static function &import($prefix = false) {
    *
    * @return array
    */
-  static function &export($prefix = false) {
-    $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'batch', $prefix, array());
+  public static function &export($prefix = FALSE) {
+    $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'batch', $prefix, []);
     return $r;
   }
+
+  /**
+   * Returns the list of indices
+   *
+   * @param bool $localize
+   *
+   * @return array
+   */
+  public static function indices($localize = TRUE) {
+    $indices = [
+      'UI_name' => [
+        'name' => 'UI_name',
+        'field' => [
+          0 => 'name',
+        ],
+        'localizable' => FALSE,
+        'unique' => TRUE,
+        'sig' => 'civicrm_batch::1::name',
+      ],
+    ];
+    return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices;
+  }
+
 }
diff --git a/CRM/Batch/DAO/EntityBatch.php b/CRM/Batch/DAO/EntityBatch.php
index 78411ba7101d..7526bf3d9130 100644
--- a/CRM/Batch/DAO/EntityBatch.php
+++ b/CRM/Batch/DAO/EntityBatch.php
@@ -1,172 +1,181 @@
 __table = 'civicrm_entity_batch';
     parent::__construct();
   }
+
   /**
-   * Returns foreign keys and entity references
+   * Returns foreign keys and entity references.
    *
    * @return array
    *   [CRM_Core_Reference_Interface]
    */
-  static function getReferenceColumns() {
+  public static function getReferenceColumns() {
     if (!isset(Civi::$statics[__CLASS__]['links'])) {
-      Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__);
-      Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'batch_id', 'civicrm_batch', 'id');
-      Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Dynamic(self::getTableName() , 'entity_id', NULL, 'id', 'entity_table');
+      Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__);
+      Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'batch_id', 'civicrm_batch', 'id');
+      Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Dynamic(self::getTableName(), 'entity_id', NULL, 'id', 'entity_table');
       CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']);
     }
     return Civi::$statics[__CLASS__]['links'];
   }
+
   /**
    * Returns all the column names of this table
    *
    * @return array
    */
-  static function &fields() {
+  public static function &fields() {
     if (!isset(Civi::$statics[__CLASS__]['fields'])) {
-      Civi::$statics[__CLASS__]['fields'] = array(
-        'id' => array(
+      Civi::$statics[__CLASS__]['fields'] = [
+        'id' => [
           'name' => 'id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('EntityBatch ID') ,
-          'description' => 'primary key',
-          'required' => true,
-        ) ,
-        'entity_table' => array(
+          'title' => ts('EntityBatch ID'),
+          'description' => ts('primary key'),
+          'required' => TRUE,
+          'table_name' => 'civicrm_entity_batch',
+          'entity' => 'EntityBatch',
+          'bao' => 'CRM_Batch_BAO_EntityBatch',
+          'localizable' => 0,
+        ],
+        'entity_table' => [
           'name' => 'entity_table',
           'type' => CRM_Utils_Type::T_STRING,
-          'title' => ts('EntityBatch Table') ,
-          'description' => 'physical tablename for entity being joined to file, e.g. civicrm_contact',
+          'title' => ts('EntityBatch Table'),
+          'description' => ts('physical tablename for entity being joined to file, e.g. civicrm_contact'),
           'maxlength' => 64,
           'size' => CRM_Utils_Type::BIG,
-        ) ,
-        'entity_id' => array(
+          'table_name' => 'civicrm_entity_batch',
+          'entity' => 'EntityBatch',
+          'bao' => 'CRM_Batch_BAO_EntityBatch',
+          'localizable' => 0,
+        ],
+        'entity_id' => [
           'name' => 'entity_id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Entity ID') ,
-          'description' => 'FK to entity table specified in entity_table column.',
-          'required' => true,
-        ) ,
-        'batch_id' => array(
+          'title' => ts('Entity ID'),
+          'description' => ts('FK to entity table specified in entity_table column.'),
+          'required' => TRUE,
+          'table_name' => 'civicrm_entity_batch',
+          'entity' => 'EntityBatch',
+          'bao' => 'CRM_Batch_BAO_EntityBatch',
+          'localizable' => 0,
+        ],
+        'batch_id' => [
           'name' => 'batch_id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Batch ID') ,
-          'description' => 'FK to civicrm_batch',
-          'required' => true,
+          'title' => ts('Batch ID'),
+          'description' => ts('FK to civicrm_batch'),
+          'required' => TRUE,
+          'table_name' => 'civicrm_entity_batch',
+          'entity' => 'EntityBatch',
+          'bao' => 'CRM_Batch_BAO_EntityBatch',
+          'localizable' => 0,
           'FKClassName' => 'CRM_Batch_DAO_Batch',
-        ) ,
-      );
+          'pseudoconstant' => [
+            'table' => 'civicrm_batch',
+            'keyColumn' => 'id',
+            'labelColumn' => 'title',
+          ]
+        ],
+      ];
       CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']);
     }
     return Civi::$statics[__CLASS__]['fields'];
   }
+
   /**
    * Return a mapping from field-name to the corresponding key (as used in fields()).
    *
    * @return array
    *   Array(string $name => string $uniqueName).
    */
-  static function &fieldKeys() {
+  public static function &fieldKeys() {
     if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) {
       Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields()));
     }
     return Civi::$statics[__CLASS__]['fieldKeys'];
   }
+
   /**
    * Returns the names of this table
    *
    * @return string
    */
-  static function getTableName() {
+  public static function getTableName() {
     return self::$_tableName;
   }
+
   /**
    * Returns if this table needs to be logged
    *
-   * @return boolean
+   * @return bool
    */
-  function getLog() {
+  public function getLog() {
     return self::$_log;
   }
+
   /**
    * Returns the list of fields that can be imported
    *
@@ -174,10 +183,11 @@ function getLog() {
    *
    * @return array
    */
-  static function &import($prefix = false) {
-    $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'entity_batch', $prefix, array());
+  public static function &import($prefix = FALSE) {
+    $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'entity_batch', $prefix, []);
     return $r;
   }
+
   /**
    * Returns the list of fields that can be exported
    *
@@ -185,8 +195,42 @@ static function &import($prefix = false) {
    *
    * @return array
    */
-  static function &export($prefix = false) {
-    $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'entity_batch', $prefix, array());
+  public static function &export($prefix = FALSE) {
+    $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'entity_batch', $prefix, []);
     return $r;
   }
+
+  /**
+   * Returns the list of indices
+   *
+   * @param bool $localize
+   *
+   * @return array
+   */
+  public static function indices($localize = TRUE) {
+    $indices = [
+      'index_entity' => [
+        'name' => 'index_entity',
+        'field' => [
+          0 => 'entity_table',
+          1 => 'entity_id',
+        ],
+        'localizable' => FALSE,
+        'sig' => 'civicrm_entity_batch::0::entity_table::entity_id',
+      ],
+      'UI_batch_entity' => [
+        'name' => 'UI_batch_entity',
+        'field' => [
+          0 => 'batch_id',
+          1 => 'entity_id',
+          2 => 'entity_table',
+        ],
+        'localizable' => FALSE,
+        'unique' => TRUE,
+        'sig' => 'civicrm_entity_batch::1::batch_id::entity_id::entity_table',
+      ],
+    ];
+    return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices;
+  }
+
 }
diff --git a/CRM/Batch/Form/Batch.php b/CRM/Batch/Form/Batch.php
index 04788b78850b..4fb2753c19c5 100644
--- a/CRM/Batch/Form/Batch.php
+++ b/CRM/Batch/Form/Batch.php
@@ -1,9 +1,9 @@
 _action & CRM_Core_Action::ADD) {
       // Set batch name default.
@@ -104,7 +104,7 @@ public function postProcess() {
     }
 
     // always create with data entry status
-    $params['status_id'] = CRM_Core_OptionGroup::getValue('batch_status', 'Data Entry', 'name');
+    $params['status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Batch_BAO_Batch', 'status_id', 'Data Entry');
     $batch = CRM_Batch_BAO_Batch::create($params);
 
     // redirect to batch entry page.
diff --git a/CRM/Batch/Form/Entry.php b/CRM/Batch/Form/Entry.php
index b5a50e798cb2..4d0671fa8e34 100644
--- a/CRM/Batch/Form/Entry.php
+++ b/CRM/Batch/Form/Entry.php
@@ -1,9 +1,9 @@
 _action = CRM_Utils_Request::retrieve('action', 'String', $this, FALSE, 'browse');
 
     if (empty($this->_batchInfo)) {
-      $params = array('id' => $this->_batchId);
+      $params = ['id' => $this->_batchId];
       CRM_Batch_BAO_Batch::retrieve($params, $this->_batchInfo);
 
       $this->assign('batchTotal', !empty($this->_batchInfo['total']) ? $this->_batchInfo['total'] : NULL);
@@ -102,12 +118,21 @@ public function preProcess() {
     }
     CRM_Core_Resources::singleton()
       ->addScriptFile('civicrm', 'templates/CRM/Batch/Form/Entry.js', 1, 'html-header')
-      ->addSetting(array('batch' => array('type_id' => $this->_batchInfo['type_id'])))
-      ->addSetting(array('setting' => array('monetaryThousandSeparator' => CRM_Core_Config::singleton()->monetaryThousandSeparator)))
-      ->addSetting(array('setting' => array('monetaryDecimalPoint' => CRM_Core_Config::singleton()->monetaryDecimalPoint)));
+      ->addSetting(['batch' => ['type_id' => $this->_batchInfo['type_id']]])
+      ->addSetting(['setting' => ['monetaryThousandSeparator' => CRM_Core_Config::singleton()->monetaryThousandSeparator]])
+      ->addSetting(['setting' => ['monetaryDecimalPoint' => CRM_Core_Config::singleton()->monetaryDecimalPoint]]);
 
   }
 
+  /**
+   * Set Batch ID.
+   *
+   * @param int $id
+   */
+  public function setBatchID($id) {
+    $this->_batchId = $id;
+  }
+
   /**
    * Build the form object.
    */
@@ -118,7 +143,7 @@ public function buildQuickForm() {
 
     $this->addElement('hidden', 'batch_id', $this->_batchId);
 
-    $batchTypes = CRM_Core_Pseudoconstant::get('CRM_Batch_DAO_Batch', 'type_id', array('flip' => 1), 'validate');
+    $batchTypes = CRM_Core_Pseudoconstant::get('CRM_Batch_DAO_Batch', 'type_id', ['flip' => 1], 'validate');
     // get the profile information
     if ($this->_batchInfo['type_id'] == $batchTypes['Contribution']) {
       CRM_Utils_System::setTitle(ts('Batch Data Entry for Contributions'));
@@ -126,22 +151,17 @@ public function buildQuickForm() {
     }
     elseif ($this->_batchInfo['type_id'] == $batchTypes['Membership']) {
       CRM_Utils_System::setTitle(ts('Batch Data Entry for Memberships'));
-      $customFields = CRM_Core_BAO_CustomField::getFields('Membership');
     }
     elseif ($this->_batchInfo['type_id'] == $batchTypes['Pledge Payment']) {
       CRM_Utils_System::setTitle(ts('Batch Data Entry for Pledge Payments'));
-      $customFields = CRM_Core_BAO_CustomField::getFields('Contribution');
     }
-    $this->_fields = array();
+    $this->_fields = [];
     $this->_fields = CRM_Core_BAO_UFGroup::getFields($this->_profileId, FALSE, CRM_Core_Action::VIEW);
 
     // remove file type field and then limit fields
     $suppressFields = FALSE;
-    $removehtmlTypes = array('File', 'Autocomplete-Select');
     foreach ($this->_fields as $name => $field) {
-      if ($cfID = CRM_Core_BAO_CustomField::getKeyID($name) &&
-        in_array($this->_fields[$name]['html_type'], $removehtmlTypes)
-      ) {
+      if ($cfID = CRM_Core_BAO_CustomField::getKeyID($name) && $this->_fields[$name]['html_type'] == 'Autocomplete-Select') {
         $suppressFields = TRUE;
         unset($this->_fields[$name]);
       }
@@ -153,7 +173,7 @@ public function buildQuickForm() {
       }
     }
 
-    $this->addFormRule(array('CRM_Batch_Form_Entry', 'formRule'), $this);
+    $this->addFormRule(['CRM_Batch_Form_Entry', 'formRule'], $this);
 
     // add the force save button
     $forceSave = $this->getButtonName('upload', 'force');
@@ -163,54 +183,51 @@ public function buildQuickForm() {
       ts('Ignore Mismatch & Process the Batch?')
     );
 
-    $this->addButtons(array(
-        array(
-          'type' => 'upload',
-          'name' => ts('Validate & Process the Batch'),
-          'isDefault' => TRUE,
-        ),
-        array(
-          'type' => 'cancel',
-          'name' => ts('Save & Continue Later'),
-        ),
-      )
-    );
+    $this->addButtons([
+      [
+        'type' => 'upload',
+        'name' => ts('Validate & Process the Batch'),
+        'isDefault' => TRUE,
+      ],
+      [
+        'type' => 'cancel',
+        'name' => ts('Save & Continue Later'),
+      ],
+    ]);
 
     $this->assign('rowCount', $this->_batchInfo['item_count'] + 1);
 
-    $fileFieldExists = FALSE;
-    $preserveDefaultsArray = array(
+    $preserveDefaultsArray = [
       'first_name',
       'last_name',
       'middle_name',
       'organization_name',
       'household_name',
-    );
+    ];
 
-    $contactTypes = array('Contact', 'Individual', 'Household', 'Organization');
-    $contactReturnProperties = array();
-    $config = CRM_Core_Config::singleton();
+    $contactTypes = ['Contact', 'Individual', 'Household', 'Organization'];
+    $contactReturnProperties = [];
 
     for ($rowNumber = 1; $rowNumber <= $this->_batchInfo['item_count']; $rowNumber++) {
-      $this->addEntityRef("primary_contact_id[{$rowNumber}]", '', array(
-          'create' => TRUE,
-          'placeholder' => ts('- select -'),
-        ));
+      $this->addEntityRef("primary_contact_id[{$rowNumber}]", '', [
+        'create' => TRUE,
+        'placeholder' => ts('- select -'),
+      ]);
 
       // special field specific to membership batch udpate
       if ($this->_batchInfo['type_id'] == 2) {
-        $options = array(
+        $options = [
           1 => ts('Add Membership'),
           2 => ts('Renew Membership'),
-        );
+        ];
         $this->add('select', "member_option[$rowNumber]", '', $options);
       }
       if ($this->_batchInfo['type_id'] == $batchTypes['Pledge Payment']) {
-        $options = array('' => '-select-');
-        $optionTypes = array(
+        $options = ['' => '-select-'];
+        $optionTypes = [
           '1' => ts('Adjust Pledge Payment Schedule?'),
           '2' => ts('Adjust Total Pledge Amount?'),
-        );
+        ];
         $this->add('select', "option_type[$rowNumber]", NULL, $optionTypes);
         if (!empty($this->_batchId) && !empty($this->_batchInfo['data']) && !empty($rowNumber)) {
           $dataValues = json_decode($this->_batchInfo['data'], TRUE);
@@ -218,7 +235,7 @@ public function buildQuickForm() {
             $pledgeIDs = CRM_Pledge_BAO_Pledge::getContactPledges($dataValues['values']['primary_contact_id'][$rowNumber]);
             foreach ($pledgeIDs as $pledgeID) {
               $pledgePayment = CRM_Pledge_BAO_PledgePayment::getOldestPledgePayment($pledgeID);
-              $options += array($pledgeID => CRM_Utils_Date::customFormat($pledgePayment['schedule_date'], '%m/%d/%Y') . ', ' . $pledgePayment['amount'] . ' ' . $pledgePayment['currency']);
+              $options += [$pledgeID => CRM_Utils_Date::customFormat($pledgePayment['schedule_date'], '%m/%d/%Y') . ', ' . $pledgePayment['amount'] . ' ' . $pledgePayment['currency']];
             }
           }
         }
@@ -239,20 +256,30 @@ public function buildQuickForm() {
       }
     }
 
+    // CRM-19477: Display Error for Batch Sizes Exceeding php.ini max_input_vars
+    // Notes: $this->_elementIndex gives an approximate count of the variables being sent
+    // An offset value is set to deal with additional vars that are likely passed.
+    // There may be a more accurate way to do this...
+    // set an offset to account for other vars we are not counting
+    $offset = 50;
+    if ((count($this->_elementIndex) + $offset) > ini_get("max_input_vars")) {
+      CRM_Core_Error::fatal(ts('Batch size is too large. Increase value of php.ini setting "max_input_vars" (current val = ' . ini_get("max_input_vars") . ')'));
+    }
+
     $this->assign('fields', $this->_fields);
     CRM_Core_Resources::singleton()
-      ->addSetting(array(
-        'contact' => array(
+      ->addSetting([
+        'contact' => [
           'return' => implode(',', $contactReturnProperties),
           'fieldmap' => array_flip($contactReturnProperties),
-        ),
-      ));
+        ],
+      ]);
 
     // don't set the status message when form is submitted.
     $buttonName = $this->controller->getButtonName('submit');
 
     if ($suppressFields && $buttonName != '_qf_Entry_next') {
-      CRM_Core_Session::setStatus(ts("File or Autocomplete-Select type field(s) in the selected profile are not supported for Update multiple records."), ts('Some Fields Excluded'), 'info');
+      CRM_Core_Session::setStatus(ts("File type field(s) in the selected profile are not supported for Update multiple records."), ts('Some Fields Excluded'), 'info');
     }
   }
 
@@ -270,24 +297,24 @@ public function buildQuickForm() {
    *   list of errors to be posted back to the form
    */
   public static function formRule($params, $files, $self) {
-    $errors = array();
-    $batchTypes = CRM_Core_Pseudoconstant::get('CRM_Batch_DAO_Batch', 'type_id', array('flip' => 1), 'validate');
-    $fields = array(
+    $errors = [];
+    $batchTypes = CRM_Core_Pseudoconstant::get('CRM_Batch_DAO_Batch', 'type_id', ['flip' => 1], 'validate');
+    $fields = [
       'total_amount' => ts('Amount'),
       'financial_type' => ts('Financial Type'),
       'payment_instrument' => ts('Payment Method'),
-    );
+    ];
 
     //CRM-16480 if contact is selected, validate financial type and amount field.
     foreach ($params['field'] as $key => $value) {
       if (isset($value['trxn_id'])) {
-        if (0 < CRM_Core_DAO::singleValueQuery('SELECT id FROM civicrm_contribution WHERE trxn_id = %1', array(1 => array($value['trxn_id'], 'String')))) {
+        if (0 < CRM_Core_DAO::singleValueQuery('SELECT id FROM civicrm_contribution WHERE trxn_id = %1', [1 => [$value['trxn_id'], 'String']])) {
           $errors["field[$key][trxn_id]"] = ts('Transaction ID must be unique within the database');
         }
       }
       foreach ($fields as $field => $label) {
         if (!empty($params['primary_contact_id'][$key]) && empty($value[$field])) {
-          $errors["field[$key][$field]"] = ts('%1 is a required field.', array(1 => $label));
+          $errors["field[$key][$field]"] = ts('%1 is a required field.', [1 => $label]);
         }
       }
     }
@@ -362,17 +389,14 @@ public function setDefaultValues() {
 
     // for add mode set smart defaults
     if ($this->_action & CRM_Core_Action::ADD) {
-      list($currentDate, $currentTime) = CRM_Utils_Date::setDateDefaults(NULL, 'activityDateTime');
+      $currentDate = date('Y-m-d H-i-s');
 
-      //get all status
-      $allStatus = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name');
-      $completeStatus = array_search('Completed', $allStatus);
-      $specialFields = array(
-        'join_date' => $currentDate,
+      $completeStatus = CRM_Contribute_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed');
+      $specialFields = [
+        'join_date' => date('Y-m-d'),
         'receive_date' => $currentDate,
-        'receive_date_time' => $currentTime,
         'contribution_status_id' => $completeStatus,
-      );
+      ];
 
       for ($rowNumber = 1; $rowNumber <= $this->_batchInfo['item_count']; $rowNumber++) {
         foreach ($specialFields as $key => $value) {
@@ -398,8 +422,8 @@ public function postProcess() {
     $params['actualBatchTotal'] = 0;
 
     // get the profile information
-    $batchTypes = CRM_Core_Pseudoconstant::get('CRM_Batch_DAO_Batch', 'type_id', array('flip' => 1), 'validate');
-    if (in_array($this->_batchInfo['type_id'], array($batchTypes['Pledge Payment'], $batchTypes['Contribution']))) {
+    $batchTypes = CRM_Core_PseudoConstant::get('CRM_Batch_DAO_Batch', 'type_id', ['flip' => 1], 'validate');
+    if (in_array($this->_batchInfo['type_id'], [$batchTypes['Pledge Payment'], $batchTypes['Contribution']])) {
       $this->processContribution($params);
     }
     elseif ($this->_batchInfo['type_id'] == $batchTypes['Membership']) {
@@ -407,12 +431,12 @@ public function postProcess() {
     }
 
     // update batch to close status
-    $paramValues = array(
+    $paramValues = [
       'id' => $this->_batchId,
       // close status
-      'status_id' => CRM_Core_OptionGroup::getValue('batch_status', 'Closed', 'name'),
+      'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Batch_BAO_Batch', 'status_id', 'Closed'),
       'total' => $params['actualBatchTotal'],
-    );
+    ];
 
     CRM_Batch_BAO_Batch::create($paramValues);
 
@@ -431,13 +455,15 @@ public function postProcess() {
    * @return bool
    */
   private function processContribution(&$params) {
-    $dates = array(
-      'receive_date',
-      'receipt_date',
-      'thankyou_date',
-      'cancel_date',
-    );
 
+    foreach ($this->submittableMoneyFields as $moneyField) {
+      foreach ($params['field'] as $index => $fieldValues) {
+        if (isset($fieldValues[$moneyField])) {
+          $params['field'][$index][$moneyField] = CRM_Utils_Rule::cleanMoney($params['field'][$index][$moneyField]);
+        }
+      }
+    }
+    $params['actualBatchTotal'] = CRM_Utils_Rule::cleanMoney($params['actualBatchTotal']);
     // get the price set associated with offline contribution record.
     $priceSetId = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', 'default_contribution_amount', 'id', 'name');
     $this->_priceSet = current(CRM_Price_BAO_PriceSet::getSetDetail($priceSetId));
@@ -488,23 +514,17 @@ private function processContribution(&$params) {
           'Contribution'
         );
 
-        foreach ($dates as $val) {
-          if (!empty($value[$val])) {
-            $value[$val] = CRM_Utils_Date::processDate($value[$val], $value[$val . '_time'], TRUE);
-          }
-        }
-
         if (!empty($value['send_receipt'])) {
           $value['receipt_date'] = date('Y-m-d His');
         }
         // these translations & date handling are required because we are calling BAO directly rather than the api
-        $fieldTranslations = array(
+        $fieldTranslations = [
           'financial_type' => 'financial_type_id',
           'payment_instrument' => 'payment_instrument_id',
           'contribution_source' => 'source',
           'contribution_note' => 'note',
 
-        );
+        ];
         foreach ($fieldTranslations as $formField => $baoField) {
           if (isset($value[$formField])) {
             $value[$baoField] = $value[$formField];
@@ -520,7 +540,7 @@ private function processContribution(&$params) {
         $this->_priceSet['fields'][$priceFieldID]['options'][$priceFieldValueID]['amount'] = $value['total_amount'];
         $value['price_' . $priceFieldID] = 1;
 
-        $lineItem = array();
+        $lineItem = [];
         CRM_Price_BAO_PriceSet::processAmount($this->_priceSet['fields'], $value, $lineItem[$priceSetId]);
 
         // @todo - stop setting amount level in this function & call the CRM_Price_BAO_PriceSet::getAmountLevel
@@ -541,9 +561,10 @@ private function processContribution(&$params) {
           }
         }
         $value['line_item'] = $lineItem;
+        $value['skipCleanMoney'] = TRUE;
         //finally call contribution create for all the magic
-        $contribution = CRM_Contribute_BAO_Contribution::create($value, CRM_Core_DAO::$_nullArray);
-        $batchTypes = CRM_Core_Pseudoconstant::get('CRM_Batch_DAO_Batch', 'type_id', array('flip' => 1), 'validate');
+        $contribution = CRM_Contribute_BAO_Contribution::create($value);
+        $batchTypes = CRM_Core_Pseudoconstant::get('CRM_Batch_DAO_Batch', 'type_id', ['flip' => 1], 'validate');
         if (!empty($this->_batchInfo['type_id']) && ($this->_batchInfo['type_id'] == $batchTypes['Pledge Payment'])) {
           $adjustTotalAmount = FALSE;
           if (isset($params['option_type'][$key])) {
@@ -563,7 +584,7 @@ private function processContribution(&$params) {
             }
             CRM_Core_DAO::setFieldValue('CRM_Pledge_DAO_PledgePayment', $pledgePaymentId, 'contribution_id', $contribution->id);
             CRM_Pledge_BAO_PledgePayment::updatePledgePaymentStatus($pledgeId,
-              array($pledgePaymentId),
+              [$pledgePaymentId],
               $contribution->contribution_status_id,
               NULL,
               $contribution->total_amount,
@@ -583,12 +604,12 @@ private function processContribution(&$params) {
               $options[$value['product_name'][0]]
             );
 
-            $premiumParams = array(
+            $premiumParams = [
               'product_id' => $value['product_name'][0],
               'contribution_id' => $contribution->id,
               'product_option' => $value['product_option'],
               'quantity' => 1,
-            );
+            ];
             CRM_Contribute_BAO_Contribution::addPremium($premiumParams);
           }
         }
@@ -621,26 +642,20 @@ private function processContribution(&$params) {
    * @return bool
    */
   private function processMembership(&$params) {
-    $dateTypes = array(
-      'join_date' => 'joinDate',
-      'membership_start_date' => 'startDate',
-      'membership_end_date' => 'endDate',
-    );
-
-    $dates = array(
-      'join_date',
-      'start_date',
-      'end_date',
-      'reminder_date',
-    );
 
     // get the price set associated with offline membership
     $priceSetId = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', 'default_membership_type_amount', 'id', 'name');
     $this->_priceSet = $priceSets = current(CRM_Price_BAO_PriceSet::getSetDetail($priceSetId));
 
     if (isset($params['field'])) {
-      $customFields = array();
+      // @todo - most of the wrangling in this function is because the api is not being used, especially date stuff.
+      $customFields = [];
       foreach ($params['field'] as $key => $value) {
+        foreach ($value as $fieldKey => $fieldValue) {
+          if (isset($this->_fields[$fieldKey]) && $this->_fields[$fieldKey]['data_type'] === 'Money') {
+            $value[$fieldKey] = CRM_Utils_Rule::cleanMoney($fieldValue);
+          }
+        }
         // if contact is not selected we should skip the row
         if (empty($params['primary_contact_id'][$key])) {
           continue;
@@ -653,28 +668,6 @@ private function processMembership(&$params) {
 
         $membershipTypeId = $value['membership_type_id'] = $value['membership_type'][1];
 
-        foreach ($dateTypes as $dateField => $dateVariable) {
-          $$dateVariable = CRM_Utils_Date::processDate($value[$dateField]);
-          $fDate[$dateField] = CRM_Utils_Array::value($dateField, $value);
-        }
-
-        $calcDates = array();
-        $calcDates[$membershipTypeId] = CRM_Member_BAO_MembershipType::getDatesForMembershipType($membershipTypeId,
-          $joinDate, $startDate, $endDate
-        );
-
-        foreach ($calcDates as $memType => $calcDate) {
-          foreach ($dates as $d) {
-            //first give priority to form values then calDates.
-            $date = CRM_Utils_Array::value($d, $value);
-            if (!$date) {
-              $date = CRM_Utils_Array::value($d, $calcDate);
-            }
-
-            $value[$d] = CRM_Utils_Date::processDate($date);
-          }
-        }
-
         if (!empty($value['send_receipt'])) {
           $value['receipt_date'] = date('Y-m-d His');
         }
@@ -728,12 +721,11 @@ private function processMembership(&$params) {
             $value['soft_credit'][$key]['soft_credit_type_id'] = $params['soft_credit_type'][$key];
           }
           else {
-            $value['soft_credit'][$key]['soft_credit_type_id'] = CRM_Core_OptionGroup::getValue('soft_credit_type', 'Gift', 'name');
+            $value['soft_credit'][$key]['soft_credit_type_id'] = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_ContributionSoft', 'soft_credit_type_id', 'Gift');
           }
         }
-
-        if (!empty($value['receive_date'])) {
-          $value['receive_date'] = CRM_Utils_Date::processDate($value['receive_date'], $value['receive_date_time'], TRUE);
+        if (!empty($value['total_amount'])) {
+          $value['total_amount'] = (float) $value['total_amount'];
         }
 
         $params['actualBatchTotal'] += $value['total_amount'];
@@ -746,12 +738,12 @@ private function processMembership(&$params) {
 
         // make entry in line item for contribution
 
-        $editedFieldParams = array(
+        $editedFieldParams = [
           'price_set_id' => $priceSetId,
           'name' => $value['membership_type'][0],
-        );
+        ];
 
-        $editedResults = array();
+        $editedResults = [];
         CRM_Price_BAO_PriceField::retrieve($editedFieldParams, $editedResults);
 
         if (!empty($editedResults)) {
@@ -759,12 +751,12 @@ private function processMembership(&$params) {
           $this->_priceSet['fields'][$editedResults['id']] = $priceSets['fields'][$editedResults['id']];
           unset($this->_priceSet['fields'][$editedResults['id']]['options']);
           $fid = $editedResults['id'];
-          $editedFieldParams = array(
+          $editedFieldParams = [
             'price_field_id' => $editedResults['id'],
             'membership_type_id' => $value['membership_type_id'],
-          );
+          ];
 
-          $editedResults = array();
+          $editedResults = [];
           CRM_Price_BAO_PriceFieldValue::retrieve($editedFieldParams, $editedResults);
           $this->_priceSet['fields'][$fid]['options'][$editedResults['id']] = $priceSets['fields'][$fid]['options'][$editedResults['id']];
           if (!empty($value['total_amount'])) {
@@ -774,7 +766,7 @@ private function processMembership(&$params) {
           $fieldID = key($this->_priceSet['fields']);
           $value['price_' . $fieldID] = $editedResults['id'];
 
-          $lineItem = array();
+          $lineItem = [];
           CRM_Price_BAO_PriceSet::processAmount($this->_priceSet['fields'],
             $value, $lineItem[$priceSetId]
           );
@@ -794,8 +786,6 @@ private function processMembership(&$params) {
         // end of contribution related section
 
         unset($value['membership_type']);
-        unset($value['membership_start_date']);
-        unset($value['membership_end_date']);
 
         $value['is_renew'] = FALSE;
         if (!empty($params['member_option']) && CRM_Utils_Array::value($key, $params['member_option']) == 2) {
@@ -811,14 +801,13 @@ private function processMembership(&$params) {
               $campaignId = CRM_Utils_Array::value('campaign_id', $this->_values);
             }
           }
-          foreach (array('join_date', 'start_date', 'end_date') as $dateType) {
-            //CRM-18000 - ignore $dateType if its not explicitly passed
-            if (!empty($fDate[$dateType]) || !empty($fDate['membership_' . $dateType])) {
-              $formDates[$dateType] = CRM_Utils_Array::value($dateType, $value);
-            }
-          }
+
+          $formDates = [
+            'end_date' => CRM_Utils_Array::value('membership_end_date', $value),
+            'start_date' => CRM_Utils_Array::value('membership_start_date', $value),
+          ];
           $membershipSource = CRM_Utils_Array::value('source', $value);
-          list($membership) = CRM_Member_BAO_Membership::renewMembership(
+          list($membership) = CRM_Member_BAO_Membership::processMembership(
             $value['contact_id'], $value['membership_type_id'], FALSE,
             //$numTerms should be default to 1.
             NULL, NULL, $value['custom'], 1, NULL, FALSE,
@@ -826,13 +815,51 @@ private function processMembership(&$params) {
           );
 
           // make contribution entry
-          $contrbutionParams = array_merge($value, array('membership_id' => $membership->id));
+          $contrbutionParams = array_merge($value, ['membership_id' => $membership->id]);
+          $contrbutionParams['skipCleanMoney'] = TRUE;
           // @todo - calling this from here is pretty hacky since it is called from membership.create anyway
           // This form should set the correct params & not call this fn directly.
           CRM_Member_BAO_Membership::recordMembershipContribution($contrbutionParams);
         }
         else {
-          $membership = CRM_Member_BAO_Membership::create($value, CRM_Core_DAO::$_nullArray);
+          $dateTypes = [
+            'join_date' => 'joinDate',
+            'membership_start_date' => 'startDate',
+            'membership_end_date' => 'endDate',
+          ];
+
+          $dates = [
+            'join_date',
+            'start_date',
+            'end_date',
+            'reminder_date',
+          ];
+          foreach ($dateTypes as $dateField => $dateVariable) {
+            $$dateVariable = CRM_Utils_Date::processDate($value[$dateField]);
+            $fDate[$dateField] = CRM_Utils_Array::value($dateField, $value);
+          }
+
+          $calcDates = [];
+          $calcDates[$membershipTypeId] = CRM_Member_BAO_MembershipType::getDatesForMembershipType($membershipTypeId,
+            $joinDate, $startDate, $endDate
+          );
+
+          foreach ($calcDates as $memType => $calcDate) {
+            foreach ($dates as $d) {
+              //first give priority to form values then calDates.
+              $date = CRM_Utils_Array::value($d, $value);
+              if (!$date) {
+                $date = CRM_Utils_Array::value($d, $calcDate);
+              }
+
+              $value[$d] = CRM_Utils_Date::processDate($date);
+            }
+          }
+
+          unset($value['membership_start_date']);
+          unset($value['membership_end_date']);
+          $ids = [];
+          $membership = CRM_Member_BAO_Membership::create($value, $ids);
         }
 
         //process premiums
@@ -846,12 +873,12 @@ private function processMembership(&$params) {
               $options[$value['product_name'][0]]
             );
 
-            $premiumParams = array(
+            $premiumParams = [
               'product_id' => $value['product_name'][0],
               'contribution_id' => CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipPayment', $membership->id, 'contribution_id', 'membership_id'),
               'product_option' => $value['product_option'],
               'quantity' => 1,
-            );
+            ];
             CRM_Contribute_BAO_Contribution::addPremium($premiumParams);
           }
         }
diff --git a/CRM/Batch/Form/Search.php b/CRM/Batch/Form/Search.php
index 7adc72b0b755..61b86a5f862b 100644
--- a/CRM/Batch/Form/Search.php
+++ b/CRM/Batch/Form/Search.php
@@ -1,9 +1,9 @@
 addButtons(
-      array(
-        array(
+      [
+        [
           'type' => 'refresh',
           'name' => ts('Search'),
           'isDefault' => TRUE,
-        ),
-      )
+        ],
+      ]
     );
 
     parent::buildQuickForm();
diff --git a/CRM/Batch/Page/AJAX.php b/CRM/Batch/Page/AJAX.php
index c340632fc180..3d840a48cc09 100644
--- a/CRM/Batch/Page/AJAX.php
+++ b/CRM/Batch/Page/AJAX.php
@@ -1,9 +1,9 @@
  $_POST)));
+    CRM_Core_DAO::setFieldValue('CRM_Batch_DAO_Batch', $batchId, 'data', json_encode(['values' => $_POST]));
 
     CRM_Utils_System::civiExit();
   }
@@ -54,21 +54,32 @@ public function batchSave() {
    * @deprecated
    */
   public static function getBatchList() {
-    $sortMapper = array(
-      0 => 'batch.title',
-      1 => 'batch.type_id',
-      2 => '',
-      3 => 'batch.total',
-      4 => 'batch.status_id',
-      5 => '',
-    );
-
+    $context = CRM_Utils_Request::retrieve('context', 'Alphanumeric');
+    if ($context != 'financialBatch') {
+      $sortMapper = [
+        0 => 'title',
+        1 => 'type_id.label',
+        2 => 'item_count',
+        3 => 'total',
+        4 => 'status_id.label',
+        5 => 'created_id.sort_name',
+      ];
+    }
+    else {
+      $sortMapper = [
+        1 => 'title',
+        2 => 'payment_instrument_id.label',
+        3 => 'item_count',
+        4 => 'total',
+        5 => 'status_id.label',
+        6 => 'created_id.sort_name',
+      ];
+    }
     $sEcho = CRM_Utils_Type::escape($_REQUEST['sEcho'], 'Integer');
     $offset = isset($_REQUEST['iDisplayStart']) ? CRM_Utils_Type::escape($_REQUEST['iDisplayStart'], 'Integer') : 0;
     $rowCount = isset($_REQUEST['iDisplayLength']) ? CRM_Utils_Type::escape($_REQUEST['iDisplayLength'], 'Integer') : 25;
     $sort = isset($_REQUEST['iSortCol_0']) ? CRM_Utils_Array::value(CRM_Utils_Type::escape($_REQUEST['iSortCol_0'], 'Integer'), $sortMapper) : NULL;
     $sortOrder = isset($_REQUEST['sSortDir_0']) ? CRM_Utils_Type::escape($_REQUEST['sSortDir_0'], 'String') : 'asc';
-    $context = isset($_REQUEST['context']) ? CRM_Utils_Type::escape($_REQUEST['context'], 'String') : NULL;
 
     $params = $_REQUEST;
     if ($sort && $sortOrder) {
@@ -80,7 +91,7 @@ public static function getBatchList() {
 
     if ($context != 'financialBatch') {
       // data entry status batches
-      $params['status_id'] = CRM_Core_OptionGroup::getValue('batch_status', 'Data Entry', 'name');
+      $params['status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Batch_BAO_Batch', 'status_id', 'Data Entry');
     }
 
     $params['context'] = $context;
@@ -91,7 +102,7 @@ public static function getBatchList() {
     $iFilteredTotal = $iTotal = $params['total'];
 
     if ($context == 'financialBatch') {
-      $selectorElements = array(
+      $selectorElements = [
         'check',
         'batch_name',
         'payment_instrument',
@@ -100,10 +111,10 @@ public static function getBatchList() {
         'status',
         'created_by',
         'links',
-      );
+      ];
     }
     else {
-      $selectorElements = array(
+      $selectorElements = [
         'batch_name',
         'type',
         'item_count',
@@ -111,7 +122,7 @@ public static function getBatchList() {
         'status',
         'created_by',
         'links',
-      );
+      ];
     }
     CRM_Utils_System::setHttpHeader('Content-Type', 'application/json');
     echo CRM_Utils_JSON::encodeDataTableSelector($batches, $sEcho, $iTotal, $iFilteredTotal, $selectorElements);
diff --git a/CRM/Batch/Page/Batch.php b/CRM/Batch/Page/Batch.php
index facf36e92a8b..6267d5d90dff 100644
--- a/CRM/Batch/Page/Batch.php
+++ b/CRM/Batch/Page/Batch.php
@@ -1,9 +1,9 @@
  1);
+    $groupParams['group_type'] = ['2' => 1];
     self::updateCiviGroup($groupParams, $op);
 
     if (CRM_Bridge_OG_Utils::aclEnabled()) {
@@ -55,7 +55,7 @@ public static function nodeapi(&$params, $op) {
       $aclParams = $params;
       $aclParams['name'] = $aclParams['title'] = "{$aclParams['name']}: Administrator";
       $aclParams['source'] = CRM_Bridge_OG_Utils::ogSyncACLName($params['og_id']);
-      $aclParams['group_type'] = array('1');
+      $aclParams['group_type'] = ['1'];
       self::updateCiviGroup($aclParams, $op);
 
       $aclParams['acl_group_id'] = $aclParams['group_id'];
@@ -138,7 +138,7 @@ public static function updateCiviACLRole(&$params, $op) {
     $dao->label = $params['title'];
     $dao->is_active = 1;
 
-    $weightParams = array('option_group_id' => $optionGroupID);
+    $weightParams = ['option_group_id' => $optionGroupID];
     $dao->weight = CRM_Utils_Weight::getDefaultWeight('CRM_Core_DAO_OptionValue',
       $weightParams
     );
@@ -153,10 +153,10 @@ public static function updateCiviACLRole(&$params, $op) {
  WHERE v.option_group_id = %1
    AND v.description     = %2
 ";
-    $queryParams = array(
-      1 => array($optionGroupID, 'Integer'),
-      2 => array($params['source'], 'String'),
-    );
+    $queryParams = [
+      1 => [$optionGroupID, 'Integer'],
+      2 => [$params['source'], 'String'],
+    ];
     $dao->id = CRM_Core_DAO::singleValueQuery($query, $queryParams);
     $dao->save();
     $params['acl_role_id'] = $dao->value;
@@ -228,11 +228,11 @@ public static function og(&$params, $op) {
       NULL, TRUE
     );
 
-    $groupParams = array(
+    $groupParams = [
       'contact_id' => $contactID,
       'group_id' => $groupID,
       'version' => 3,
-    );
+    ];
 
     if ($op == 'add') {
       $groupParams['status'] = $params['is_active'] ? 'Added' : 'Pending';
@@ -251,12 +251,12 @@ public static function og(&$params, $op) {
         NULL, TRUE
       );
 
-      $groupParams = array(
+      $groupParams = [
         'contact_id' => $contactID,
         'group_id' => $groupID,
         'status' => $params['is_admin'] ? 'Added' : 'Removed',
         'version' => 3,
-      );
+      ];
 
       if ($params['is_admin']) {
         civicrm_api('GroupContact', 'Create', $groupParams);
diff --git a/CRM/Bridge/OG/Utils.php b/CRM/Bridge/OG/Utils.php
index a9f0819650cc..154bf833082e 100644
--- a/CRM/Bridge/OG/Utils.php
+++ b/CRM/Bridge/OG/Utils.php
@@ -1,9 +1,9 @@
  array($source, 'String'));
+    $params = [1 => [$source, 'String']];
 
     if ($title) {
       $query .= " OR title = %2";
-      $params[2] = array($title, 'String');
+      $params[2] = [$title, 'String'];
     }
 
     $groupID = CRM_Core_DAO::singleValueQuery($query, $params);
diff --git a/CRM/Campaign/BAO/Campaign.php b/CRM/Campaign/BAO/Campaign.php
index 5453c5d560c3..0d5ebe8cb5ba 100644
--- a/CRM/Campaign/BAO/Campaign.php
+++ b/CRM/Campaign/BAO/Campaign.php
@@ -1,9 +1,9 @@
 entity_id = $entityId;
         $dao->group_type = 'Include';
         $dao->save();
-        $dao->free();
       }
     }
 
@@ -182,14 +181,14 @@ public static function getCampaigns(
   ) {
     static $campaigns;
     $cacheKey = 0;
-    $cacheKeyParams = array(
+    $cacheKeyParams = [
       'includeId',
       'excludeId',
       'onlyActive',
       'onlyCurrent',
       'appendDatesToTitle',
       'forceAll',
-    );
+    ];
     foreach ($cacheKeyParams as $param) {
       $cacheParam = $$param;
       if (!$cacheParam) {
@@ -199,7 +198,7 @@ public static function getCampaigns(
     }
 
     if (!isset($campaigns[$cacheKey])) {
-      $where = array('( camp.title IS NOT NULL )');
+      $where = ['( camp.title IS NOT NULL )'];
       if ($excludeId) {
         $where[] = "( camp.id != $excludeId )";
       }
@@ -229,14 +228,14 @@ public static function getCampaigns(
 Order By  camp.title";
 
       $campaign = CRM_Core_DAO::executeQuery($query);
-      $campaigns[$cacheKey] = array();
+      $campaigns[$cacheKey] = [];
       $config = CRM_Core_Config::singleton();
 
       while ($campaign->fetch()) {
         $title = $campaign->title;
         if ($appendDatesToTitle) {
-          $dates = array();
-          foreach (array('start_date', 'end_date') as $date) {
+          $dates = [];
+          foreach (['start_date', 'end_date'] as $date) {
             if ($campaign->$date) {
               $dates[] = CRM_Utils_Date::customFormat($campaign->$date, $config->dateformatFull);
             }
@@ -278,7 +277,7 @@ public static function getPermissionedCampaigns(
     $doCheckForPermissions = TRUE
   ) {
     $cacheKey = 0;
-    $cachekeyParams = array(
+    $cachekeyParams = [
       'includeId',
       'excludeId',
       'onlyActive',
@@ -287,7 +286,7 @@ public static function getPermissionedCampaigns(
       'doCheckForComponent',
       'doCheckForPermissions',
       'forceAll',
-    );
+    ];
     foreach ($cachekeyParams as $param) {
       $cacheKeyParam = $$param;
       if (!$cacheKeyParam) {
@@ -299,11 +298,11 @@ public static function getPermissionedCampaigns(
     static $validCampaigns;
     if (!isset($validCampaigns[$cacheKey])) {
       $isValid = TRUE;
-      $campaigns = array(
-        'campaigns' => array(),
+      $campaigns = [
+        'campaigns' => [],
         'hasAccessCampaign' => FALSE,
         'isCampaignEnabled' => FALSE,
-      );
+      ];
 
       //do check for component.
       if ($doCheckForComponent) {
@@ -359,18 +358,18 @@ public static function isCampaignEnable() {
    *
    * @return array|int
    */
-  public static function getCampaignSummary($params = array(), $onlyCount = FALSE) {
-    $campaigns = array();
+  public static function getCampaignSummary($params = [], $onlyCount = FALSE) {
+    $campaigns = [];
 
     //build the limit and order clause.
     $limitClause = $orderByClause = $lookupTableJoins = NULL;
     if (!$onlyCount) {
-      $sortParams = array(
+      $sortParams = [
         'sort' => 'start_date',
         'offset' => 0,
         'rowCount' => 10,
         'sortOrder' => 'desc',
-      );
+      ];
       foreach ($sortParams as $name => $default) {
         if (!empty($params[$name])) {
           $sortParams[$name] = $params[$name];
@@ -404,32 +403,32 @@ public static function getCampaignSummary($params = array(), $onlyCount = FALSE)
     }
 
     //build the where clause.
-    $queryParams = $where = array();
+    $queryParams = $where = [];
     if (!empty($params['id'])) {
       $where[] = "( campaign.id = %1 )";
-      $queryParams[1] = array($params['id'], 'Positive');
+      $queryParams[1] = [$params['id'], 'Positive'];
     }
     if (!empty($params['name'])) {
       $where[] = "( campaign.name LIKE %2 )";
-      $queryParams[2] = array('%' . trim($params['name']) . '%', 'String');
+      $queryParams[2] = ['%' . trim($params['name']) . '%', 'String'];
     }
     if (!empty($params['title'])) {
       $where[] = "( campaign.title LIKE %3 )";
-      $queryParams[3] = array('%' . trim($params['title']) . '%', 'String');
+      $queryParams[3] = ['%' . trim($params['title']) . '%', 'String'];
     }
     if (!empty($params['start_date'])) {
       $startDate = CRM_Utils_Date::processDate($params['start_date']);
       $where[] = "( campaign.start_date >= %4 OR campaign.start_date IS NULL )";
-      $queryParams[4] = array($startDate, 'String');
+      $queryParams[4] = [$startDate, 'String'];
     }
     if (!empty($params['end_date'])) {
       $endDate = CRM_Utils_Date::processDate($params['end_date'], '235959');
       $where[] = "( campaign.end_date <= %5 OR campaign.end_date IS NULL )";
-      $queryParams[5] = array($endDate, 'String');
+      $queryParams[5] = [$endDate, 'String'];
     }
     if (!empty($params['description'])) {
       $where[] = "( campaign.description LIKE %6 )";
-      $queryParams[6] = array('%' . trim($params['description']) . '%', 'String');
+      $queryParams[6] = ['%' . trim($params['description']) . '%', 'String'];
     }
     if (!empty($params['campaign_type_id'])) {
       $typeId = $params['campaign_type_id'];
@@ -457,7 +456,7 @@ public static function getCampaignSummary($params = array(), $onlyCount = FALSE)
       $whereClause = ' WHERE ' . implode(" \nAND ", $where);
     }
 
-    $properties = array(
+    $properties = [
       'id',
       'name',
       'title',
@@ -467,7 +466,7 @@ public static function getCampaignSummary($params = array(), $onlyCount = FALSE)
       'is_active',
       'description',
       'campaign_type_id',
-    );
+    ];
 
     $selectClause = '
 SELECT  campaign.id               as id,
@@ -520,11 +519,11 @@ public static function getCampaignCount() {
   public static function getCampaignGroups($campaignId) {
     static $campaignGroups;
     if (!$campaignId) {
-      return array();
+      return [];
     }
 
     if (!isset($campaignGroups[$campaignId])) {
-      $campaignGroups[$campaignId] = array();
+      $campaignGroups[$campaignId] = [];
 
       $query = "
     SELECT  grp.title, grp.id
@@ -534,7 +533,7 @@ public static function getCampaignGroups($campaignId) {
        AND  campgrp.entity_table = 'civicrm_group'
        AND  campgrp.campaign_id = %1";
 
-      $groups = CRM_Core_DAO::executeQuery($query, array(1 => array($campaignId, 'Positive')));
+      $groups = CRM_Core_DAO::executeQuery($query, [1 => [$campaignId, 'Positive']]);
       while ($groups->fetch()) {
         $campaignGroups[$campaignId][$groups->id] = $groups->title;
       }
@@ -551,8 +550,8 @@ public static function getCampaignGroups($campaignId) {
    * @param bool $is_active
    *   Value we want to set the is_active field.
    *
-   * @return CRM_Campaign_DAO_Campaign|null
-   *   DAO object on success, null otherwise
+   * @return bool
+   *   true if we found and updated the object, else false
    */
   public static function setIsActive($id, $is_active) {
     return CRM_Core_DAO::setFieldValue('CRM_Campaign_DAO_Campaign', $id, 'is_active', $is_active);
@@ -591,72 +590,31 @@ public static function addCampaign(&$form, $connectedCampaignId = NULL) {
     }
 
     $campaignDetails = self::getPermissionedCampaigns($connectedCampaignId, NULL, TRUE, TRUE, $appendDates);
-    $fields = array('campaigns', 'hasAccessCampaign', 'isCampaignEnabled');
+    $fields = ['campaigns', 'hasAccessCampaign', 'isCampaignEnabled'];
     foreach ($fields as $fld) {
       $$fld = CRM_Utils_Array::value($fld, $campaignDetails);
     }
 
-    //lets see do we have past campaigns.
-    $hasPastCampaigns = FALSE;
-    $allActiveCampaigns = CRM_Campaign_BAO_Campaign::getCampaigns(NULL, NULL, TRUE, FALSE);
-    if (count($allActiveCampaigns) > count($campaigns)) {
-      $hasPastCampaigns = TRUE;
-    }
-    $hasCampaigns = FALSE;
-    if (!empty($campaigns)) {
-      $hasCampaigns = TRUE;
-    }
-    if ($hasPastCampaigns) {
-      $hasCampaigns = TRUE;
-      $form->add('hidden', 'included_past_campaigns');
-    }
-
     $showAddCampaign = FALSE;
-    $alreadyIncludedPastCampaigns = FALSE;
     if ($connectedCampaignId || ($isCampaignEnabled && $hasAccessCampaign)) {
       $showAddCampaign = TRUE;
-      //lets add past campaigns as options to quick-form element.
-      if ($hasPastCampaigns && $form->getElementValue('included_past_campaigns')) {
-        $campaigns = $allActiveCampaigns;
-        $alreadyIncludedPastCampaigns = TRUE;
-      }
-      $campaign = &$form->add('select',
-        'campaign_id',
-        ts('Campaign'),
-        array('' => ts('- select -')) + $campaigns,
-        FALSE,
-        array('class' => 'crm-select2')
-      );
+      $campaign = $form->addEntityRef('campaign_id', ts('Campaign'), [
+        'entity' => 'Campaign',
+        'create' => TRUE,
+        'select' => ['minimumInputLength' => 0],
+      ]);
       //lets freeze when user does not has access or campaign is disabled.
       if (!$isCampaignEnabled || !$hasAccessCampaign) {
         $campaign->freeze();
       }
     }
 
-    $addCampaignURL = NULL;
-    if (empty($campaigns) && $hasAccessCampaign && $isCampaignEnabled) {
-      $addCampaignURL = CRM_Utils_System::url('civicrm/campaign/add', 'reset=1');
-    }
-
-    $includePastCampaignURL = NULL;
-    if ($hasPastCampaigns && $isCampaignEnabled && $hasAccessCampaign) {
-      $includePastCampaignURL = CRM_Utils_System::url('civicrm/ajax/rest',
-        'className=CRM_Campaign_Page_AJAX&fnName=allActiveCampaigns',
-        FALSE, NULL, FALSE
-      );
-    }
-
     //carry this info to templates.
-    $infoFields = array(
-      'hasCampaigns',
-      'addCampaignURL',
+    $infoFields = [
       'showAddCampaign',
-      'hasPastCampaigns',
       'hasAccessCampaign',
       'isCampaignEnabled',
-      'includePastCampaignURL',
-      'alreadyIncludedPastCampaigns',
-    );
+    ];
     foreach ($infoFields as $fld) {
       $campaignInfo[$fld] = $$fld;
     }
@@ -671,9 +629,9 @@ public static function addCampaign(&$form, $connectedCampaignId = NULL) {
    * @param string $elementName
    */
   public static function addCampaignInComponentSearch(&$form, $elementName = 'campaign_id') {
-    $campaignInfo = array();
+    $campaignInfo = [];
     $campaignDetails = self::getPermissionedCampaigns(NULL, NULL, FALSE, FALSE, FALSE, TRUE);
-    $fields = array('campaigns', 'hasAccessCampaign', 'isCampaignEnabled');
+    $fields = ['campaigns', 'hasAccessCampaign', 'isCampaignEnabled'];
     foreach ($fields as $fld) {
       $$fld = CRM_Utils_Array::value($fld, $campaignDetails);
     }
@@ -682,29 +640,86 @@ public static function addCampaignInComponentSearch(&$form, $elementName = 'camp
       //get the current campaign only.
       $currentCampaigns = self::getCampaigns(NULL, NULL, FALSE);
       $pastCampaigns = array_diff($campaigns, $currentCampaigns);
-      $allCampaigns = array();
+      $allCampaigns = [];
       if (!empty($currentCampaigns)) {
-        $allCampaigns = array('crm_optgroup_current_campaign' => ts('Current Campaigns')) + $currentCampaigns;
+        $allCampaigns = ['crm_optgroup_current_campaign' => ts('Current Campaigns')] + $currentCampaigns;
       }
       if (!empty($pastCampaigns)) {
-        $allCampaigns += array('crm_optgroup_past_campaign' => ts('Past Campaigns')) + $pastCampaigns;
+        $allCampaigns += ['crm_optgroup_past_campaign' => ts('Past Campaigns')] + $pastCampaigns;
       }
 
       $showCampaignInSearch = TRUE;
       $form->add('select', $elementName, ts('Campaigns'), $allCampaigns, FALSE,
-        array('id' => 'campaigns', 'multiple' => 'multiple', 'class' => 'crm-select2')
+        ['id' => 'campaigns', 'multiple' => 'multiple', 'class' => 'crm-select2']
       );
     }
-    $infoFields = array(
+    $infoFields = [
       'elementName',
       'hasAccessCampaign',
       'isCampaignEnabled',
       'showCampaignInSearch',
-    );
+    ];
     foreach ($infoFields as $fld) {
       $campaignInfo[$fld] = $$fld;
     }
     $form->assign('campaignInfo', $campaignInfo);
   }
 
+  /**
+   * @return array
+   */
+  public static function getEntityRefFilters() {
+    return [
+      ['key' => 'campaign_type_id', 'value' => ts('Campaign Type')],
+      ['key' => 'status_id', 'value' => ts('Status')],
+      [
+        'key' => 'start_date',
+        'value' => ts('Start Date'),
+        'options' => [
+          ['key' => '{">":"now"}', 'value' => ts('Upcoming')],
+          [
+            'key' => '{"BETWEEN":["now - 3 month","now"]}',
+            'value' => ts('Past 3 Months'),
+          ],
+          [
+            'key' => '{"BETWEEN":["now - 6 month","now"]}',
+            'value' => ts('Past 6 Months'),
+          ],
+          [
+            'key' => '{"BETWEEN":["now - 1 year","now"]}',
+            'value' => ts('Past Year'),
+          ],
+        ],
+      ],
+      [
+        'key' => 'end_date',
+        'value' => ts('End Date'),
+        'options' => [
+          ['key' => '{">":"now"}', 'value' => ts('In the future')],
+          ['key' => '{"<":"now"}', 'value' => ts('In the past')],
+          ['key' => '{"IS NULL":"1"}', 'value' => ts('Not set')],
+        ],
+      ],
+    ];
+  }
+
+  /**
+   * Links to create new campaigns from entityRef widget
+   *
+   * @return array|bool
+   */
+  public static function getEntityRefCreateLinks() {
+    if (CRM_Core_Permission::check([['administer CiviCampaign', 'manage campaign']])) {
+      return [
+        [
+          'label' => ts('New Campaign'),
+          'url' => CRM_Utils_System::url('civicrm/campaign/add', "reset=1",
+            NULL, NULL, FALSE, FALSE, TRUE),
+          'type' => 'Campaign',
+        ],
+      ];
+    }
+    return FALSE;
+  }
+
 }
diff --git a/CRM/Campaign/BAO/Petition.php b/CRM/Campaign/BAO/Petition.php
index 2703b3bb75a1..a9159061a0e9 100644
--- a/CRM/Campaign/BAO/Petition.php
+++ b/CRM/Campaign/BAO/Petition.php
@@ -1,9 +1,9 @@
  'created_date',
         'offset' => 0,
         'rowCount' => 10,
         'sortOrder' => 'desc',
-      );
+      ];
       foreach ($sortParams as $name => $default) {
         if (!empty($params[$name])) {
           $sortParams[$name] = $params[$name];
@@ -90,22 +91,22 @@ public static function getPetitionSummary($params = array(), $onlyCount = FALSE)
     }
 
     //build the where clause.
-    $queryParams = $where = array();
+    $queryParams = $where = [];
 
     //we only have activity type as a
     //difference between survey and petition.
-    $petitionTypeID = CRM_Core_OptionGroup::getValue('activity_type', 'petition', 'name');
+    $petitionTypeID = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Petition');
     if ($petitionTypeID) {
       $where[] = "( petition.activity_type_id = %1 )";
-      $queryParams[1] = array($petitionTypeID, 'Positive');
+      $queryParams[1] = [$petitionTypeID, 'Positive'];
     }
     if (!empty($params['title'])) {
       $where[] = "( petition.title LIKE %2 )";
-      $queryParams[2] = array('%' . trim($params['title']) . '%', 'String');
+      $queryParams[2] = ['%' . trim($params['title']) . '%', 'String'];
     }
     if (!empty($params['campaign_id'])) {
       $where[] = '( petition.campaign_id = %3 )';
-      $queryParams[3] = array($params['campaign_id'], 'Positive');
+      $queryParams[3] = [$params['campaign_id'], 'Positive'];
     }
     $whereClause = NULL;
     if (!empty($where)) {
@@ -132,8 +133,8 @@ public static function getPetitionSummary($params = array(), $onlyCount = FALSE)
       return (int) CRM_Core_DAO::singleValueQuery($query, $queryParams);
     }
 
-    $petitions = array();
-    $properties = array(
+    $petitions = [];
+    $properties = [
       'id',
       'title',
       'campaign_id',
@@ -141,7 +142,7 @@ public static function getPetitionSummary($params = array(), $onlyCount = FALSE)
       'is_default',
       'result_id',
       'activity_type_id',
-    );
+    ];
 
     $petition = CRM_Core_DAO::executeQuery($query, $queryParams);
     while ($petition->fetch()) {
@@ -159,11 +160,11 @@ public static function getPetitionSummary($params = array(), $onlyCount = FALSE)
    */
   public static function getPetitionCount() {
     $whereClause = 'WHERE ( 1 )';
-    $queryParams = array();
-    $petitionTypeID = CRM_Core_OptionGroup::getValue('activity_type', 'petition', 'name');
+    $queryParams = [];
+    $petitionTypeID = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Petition');
     if ($petitionTypeID) {
       $whereClause = "WHERE ( petition.activity_type_id = %1 )";
-      $queryParams[1] = array($petitionTypeID, 'Positive');
+      $queryParams[1] = [$petitionTypeID, 'Positive'];
     }
     $query = "SELECT COUNT(*) FROM civicrm_survey petition {$whereClause}";
 
@@ -176,7 +177,8 @@ public static function getPetitionCount() {
    * @param array $params
    *   (reference ) an assoc array of name/value pairs.
    *
-   * @return CRM_Campaign_BAO_Petition
+   * @return mixed
+   *   CRM_Campaign_BAO_Petition or NULl or void
    */
   public function createSignature(&$params) {
     if (empty($params)) {
@@ -198,7 +200,7 @@ public function createSignature(&$params) {
       // create activity
       // 1-Schedule, 2-Completed
 
-      $activityParams = array(
+      $activityParams = [
         'source_contact_id' => $params['contactId'],
         'target_contact_id' => $params['contactId'],
         'source_record_id' => $params['sid'],
@@ -207,7 +209,7 @@ public function createSignature(&$params) {
         'activity_date_time' => date("YmdHis"),
         'status_id' => $params['statusId'],
         'activity_campaign_id' => $params['activity_campaign_id'],
-      );
+      ];
 
       //activity creation
       // *** check for activity using source id - if already signed
@@ -240,13 +242,13 @@ public function confirmSignature($activity_id, $contact_id, $petition_id) {
     // change activity status to completed (status_id = 2)
     // I wonder why do we need contact_id when we have activity_id anyway? [chastell]
     $sql = 'UPDATE civicrm_activity SET status_id = 2 WHERE id = %1';
-    $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name');
+    $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate');
     $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts);
-    $params = array(
-      1 => array($activity_id, 'Integer'),
-      2 => array($contact_id, 'Integer'),
-      3 => array($sourceID, 'Integer'),
-    );
+    $params = [
+      1 => [$activity_id, 'Integer'],
+      2 => [$contact_id, 'Integer'],
+      3 => [$sourceID, 'Integer'],
+    ];
     CRM_Core_DAO::executeQuery($sql, $params);
 
     $sql = 'UPDATE civicrm_activity_contact SET contact_id = %2 WHERE activity_id = %1 AND record_type_id = %3';
@@ -259,10 +261,10 @@ public function confirmSignature($activity_id, $contact_id, $petition_id) {
 WHERE       entity_table = 'civicrm_contact'
 AND         entity_id = %1
 AND         tag_id = ( SELECT id FROM civicrm_tag WHERE name = %2 )";
-    $params = array(
-      1 => array($contact_id, 'Integer'),
-      2 => array($tag_name, 'String'),
-    );
+    $params = [
+      1 => [$contact_id, 'Integer'],
+      2 => [$tag_name, 'String'],
+    ];
     CRM_Core_DAO::executeQuery($sql, $params);
     // validate arguments to setcookie are numeric to prevent header manipulation
     if (isset($petition_id) && is_numeric($petition_id)
@@ -293,7 +295,7 @@ public function confirmSignature($activity_id, $contact_id, $petition_id) {
    * @return array
    */
   public static function getPetitionSignatureTotalbyCountry($surveyId) {
-    $countries = array();
+    $countries = [];
     $sql = "
             SELECT count(civicrm_address.country_id) as total,
                 IFNULL(country_id,'') as country_id,IFNULL(iso_code,'') as country_iso, IFNULL(civicrm_country.name,'') as country
@@ -307,18 +309,18 @@ public static function getPetitionSignatureTotalbyCountry($surveyId) {
                 civicrm_survey.id =  %1 AND
                 a.source_record_id =  %1  ";
 
-    $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name');
+    $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate');
     $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts);
-    $params = array(
-      1 => array($surveyId, 'Integer'),
-      2 => array($sourceID, 'Integer'),
-    );
+    $params = [
+      1 => [$surveyId, 'Integer'],
+      2 => [$sourceID, 'Integer'],
+    ];
     $sql .= " GROUP BY civicrm_address.country_id";
-    $fields = array('total', 'country_id', 'country_iso', 'country');
+    $fields = ['total', 'country_id', 'country_iso', 'country'];
 
     $dao = CRM_Core_DAO::executeQuery($sql, $params);
     while ($dao->fetch()) {
-      $row = array();
+      $row = [];
       foreach ($fields as $field) {
         $row[$field] = $dao->$field;
       }
@@ -344,7 +346,7 @@ public static function getPetitionSignatureTotal($surveyId) {
             WHERE
             source_record_id = " . (int) $surveyId . " AND activity_type_id = " . (int) $surveyInfo['activity_type_id'] . " GROUP BY status_id";
 
-    $statusTotal = array();
+    $statusTotal = [];
     $total = 0;
     $dao = CRM_Core_DAO::executeQuery($sql);
     while ($dao->fetch()) {
@@ -355,14 +357,13 @@ public static function getPetitionSignatureTotal($surveyId) {
     return $statusTotal;
   }
 
-
   /**
    * @param int $surveyId
    *
    * @return array
    */
   public static function getSurveyInfo($surveyId = NULL) {
-    $surveyInfo = array();
+    $surveyInfo = [];
 
     $sql = "
             SELECT  activity_type_id,
@@ -399,7 +400,7 @@ public static function getPetitionSignature($surveyId, $status_id = NULL) {
 
     // sql injection protection
     $surveyId = (int) $surveyId;
-    $signature = array();
+    $signature = [];
 
     $sql = "
             SELECT  a.id,
@@ -424,19 +425,19 @@ public static function getPetitionSignature($surveyId, $status_id = NULL) {
             civicrm_survey.id =  %1 AND
             a.source_record_id =  %1 ";
 
-    $params = array(1 => array($surveyId, 'Integer'));
+    $params = [1 => [$surveyId, 'Integer']];
 
     if ($status_id) {
       $sql .= " AND status_id = %2";
-      $params[2] = array($status_id, 'Integer');
+      $params[2] = [$status_id, 'Integer'];
     }
     $sql .= " ORDER BY  a.activity_date_time";
 
-    $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name');
+    $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate');
     $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts);
-    $params[3] = array($sourceID, 'Integer');
+    $params[3] = [$sourceID, 'Integer'];
 
-    $fields = array(
+    $fields = [
       'id',
       'survey_id',
       'contact_id',
@@ -451,11 +452,11 @@ public static function getPetitionSignature($surveyId, $status_id = NULL) {
       'state_province_id',
       'country_iso',
       'country',
-    );
+    ];
 
     $dao = CRM_Core_DAO::executeQuery($sql, $params);
     while ($dao->fetch()) {
-      $row = array();
+      $row = [];
       foreach ($fields as $field) {
         $row[$field] = $dao->$field;
       }
@@ -474,7 +475,7 @@ public static function getPetitionSignature($surveyId, $status_id = NULL) {
    *   array of contact ids
    */
   public function getEntitiesByTag($tag) {
-    $contactIds = array();
+    $contactIds = [];
     $entityTagDAO = new CRM_Core_DAO_EntityTag();
     $entityTagDAO->tag_id = $tag['id'];
     $entityTagDAO->find();
@@ -496,8 +497,8 @@ public function getEntitiesByTag($tag) {
   public static function checkSignature($surveyId, $contactId) {
 
     $surveyInfo = CRM_Campaign_BAO_Petition::getSurveyInfo($surveyId);
-    $signature = array();
-    $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name');
+    $signature = [];
+    $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate');
     $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts);
 
     $sql = "
@@ -514,13 +515,13 @@ public static function checkSignature($surveyId, $contactId) {
             AND a.activity_type_id = %3
             AND ac.contact_id = %4
 ";
-    $params = array(
-      1 => array($surveyInfo['title'], 'String'),
-      2 => array($surveyId, 'Integer'),
-      3 => array($surveyInfo['activity_type_id'], 'Integer'),
-      4 => array($contactId, 'Integer'),
-      5 => array($sourceID, 'Integer'),
-    );
+    $params = [
+      1 => [$surveyInfo['title'], 'String'],
+      2 => [$surveyId, 'Integer'],
+      3 => [$surveyInfo['activity_type_id'], 'Integer'],
+      4 => [$contactId, 'Integer'],
+      5 => [$sourceID, 'Integer'],
+    ];
 
     $dao = CRM_Core_DAO::executeQuery($sql, $params);
     while ($dao->fetch()) {
@@ -573,7 +574,7 @@ public static function sendEmail($params, $sendEmailMode) {
 
     // get petition info
     $petitionParams['id'] = $params['sid'];
-    $petitionInfo = array();
+    $petitionInfo = [];
     CRM_Campaign_BAO_Survey::retrieve($petitionParams, $petitionInfo);
     if (empty($petitionInfo)) {
       CRM_Core_Error::fatal('Petition doesn\'t exist.');
@@ -586,7 +587,7 @@ public static function sendEmail($params, $sendEmailMode) {
 
     $toName = CRM_Contact_BAO_Contact::displayName($params['contactId']);
 
-    $replyTo = "do-not-reply@$emailDomain";
+    $replyTo = CRM_Core_BAO_Domain::getNoReplyEmailAddress();
 
     // set additional general message template params (custom tokens to use in email msg templates)
     // tokens then available in msg template as {$petition.title}, etc
@@ -599,12 +600,12 @@ public static function sendEmail($params, $sendEmailMode) {
 
         // add this contact to the CIVICRM_PETITION_CONTACTS group
         // Cannot pass parameter 1 by reference
-        $p = array($params['contactId']);
+        $p = [$params['contactId']];
         CRM_Contact_BAO_GroupContact::addContactsToGroup($p, $group_id, 'API');
 
         if ($params['email-Primary']) {
           CRM_Core_BAO_MessageTemplate::sendTemplate(
-            array(
+            [
               'groupName' => 'msg_tpl_workflow_petition',
               'valueName' => 'petition_sign',
               'contactId' => $params['contactId'],
@@ -615,7 +616,7 @@ public static function sendEmail($params, $sendEmailMode) {
               'replyTo' => $replyTo,
               'petitionId' => $params['sid'],
               'petitionTitle' => $petitionInfo['title'],
-            )
+            ]
           );
         }
         break;
@@ -635,12 +636,12 @@ public static function sendEmail($params, $sendEmailMode) {
         $localpart = CRM_Core_BAO_MailSettings::defaultLocalpart();
 
         $replyTo = implode($config->verpSeparator,
-            array(
+            [
               $localpart . 'c',
               $se->contact_id,
               $se->id,
               $se->hash,
-            )
+            ]
           ) . "@$emailDomain";
 
         $confirmUrl = CRM_Utils_System::url('civicrm/petition/confirm',
@@ -661,7 +662,7 @@ public static function sendEmail($params, $sendEmailMode) {
 
         if ($params['email-Primary']) {
           CRM_Core_BAO_MessageTemplate::sendTemplate(
-            array(
+            [
               'groupName' => 'msg_tpl_workflow_petition',
               'valueName' => 'petition_confirmation_needed',
               'contactId' => $params['contactId'],
@@ -673,7 +674,7 @@ public static function sendEmail($params, $sendEmailMode) {
               'petitionId' => $params['sid'],
               'petitionTitle' => $petitionInfo['title'],
               'confirmUrl' => $confirmUrl,
-            )
+            ]
           );
         }
         break;
diff --git a/CRM/Campaign/BAO/Query.php b/CRM/Campaign/BAO/Query.php
index a47ed1fc851c..d44e12800994 100644
--- a/CRM/Campaign/BAO/Query.php
+++ b/CRM/Campaign/BAO/Query.php
@@ -1,9 +1,9 @@
 _select) && $query->_mode == CRM_Contact_BAO_Query::MODE_CONTACTS) {
       foreach ($query->_select as $field => $queryString) {
         if (substr($field, -11) == 'campaign_id') {
-          $query->_pseudoConstantsSelect[$field] = array(
+          $query->_pseudoConstantsSelect[$field] = [
             'pseudoField' => 'campaign_id',
             'idCol' => $field,
             'bao' => 'CRM_Activity_BAO_Activity',
-          );
+          ];
         }
       }
     }
@@ -171,7 +171,7 @@ public static function whereClauseSingle(&$values, &$query) {
 
     switch ($name) {
       case 'campaign_survey_id':
-        $query->_qill[$grouping][] = ts('Survey - %1', array(1 => CRM_Core_DAO::getFieldValue('CRM_Campaign_DAO_Survey', $value, 'title')));
+        $query->_qill[$grouping][] = ts('Survey - %1', [1 => CRM_Core_DAO::getFieldValue('CRM_Campaign_DAO_Survey', $value, 'title')]);
 
         $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause('civicrm_activity.source_record_id',
           $op, $value, 'Integer'
@@ -184,21 +184,21 @@ public static function whereClauseSingle(&$values, &$query) {
       case 'survey_status_id':
         $activityStatus = CRM_Core_PseudoConstant::activityStatus();
 
-        $query->_qill[$grouping][] = ts('Survey Status - %1', array(1 => $activityStatus[$value]));
+        $query->_qill[$grouping][] = ts('Survey Status - %1', [1 => $activityStatus[$value]]);
         $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause('civicrm_activity.status_id',
           $op, $value, 'Integer'
         );
         return;
 
       case 'campaign_search_voter_for':
-        if (in_array($value, array('release', 'interview'))) {
+        if (in_array($value, ['release', 'interview'])) {
           $query->_where[$grouping][] = '(civicrm_activity.is_deleted = 0 OR civicrm_activity.is_deleted IS NULL)';
         }
         return;
 
       case 'survey_interviewer_id':
         $surveyInterviewerName = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $value, 'sort_name');
-        $query->_qill[$grouping][] = ts('Survey Interviewer - %1', array(1 => $surveyInterviewerName));
+        $query->_qill[$grouping][] = ts('Survey Interviewer - %1', [1 => $surveyInterviewerName]);
         $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause('civicrm_activity_assignment.contact_id',
           $op, $value, 'Integer'
         );
@@ -221,7 +221,7 @@ public static function from($name, $mode, $side) {
       return $from;
     }
 
-    $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name');
+    $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate');
     $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts);
     $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts);
     $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts);
@@ -269,7 +269,7 @@ public static function defaultReturnProperties(
   ) {
     $properties = NULL;
     if ($mode & CRM_Contact_BAO_Query::MODE_CAMPAIGN) {
-      $properties = array(
+      $properties = [
         'contact_id' => 1,
         'contact_type' => 1,
         'contact_sub_type' => 1,
@@ -292,7 +292,7 @@ public static function defaultReturnProperties(
         'campaign_id' => 1,
         'survey_interviewer_id' => 1,
         'survey_activity_target_contact_id' => 1,
-      );
+      ];
     }
 
     return $properties;
@@ -355,11 +355,11 @@ public static function buildSearchForm(&$form) {
     $separator = CRM_Core_DAO::VALUE_SEPARATOR;
     $contactTypes = CRM_Contact_BAO_ContactType::getSelectElements(FALSE, TRUE, $separator);
     $form->add('select', 'contact_type', ts('Contact Type(s)'), $contactTypes, FALSE,
-      array('id' => 'contact_type', 'multiple' => 'multiple', 'class' => 'crm-select2')
+      ['id' => 'contact_type', 'multiple' => 'multiple', 'class' => 'crm-select2']
     );
     $groups = CRM_Core_PseudoConstant::nestedGroup();
     $form->add('select', 'group', ts('Groups'), $groups, FALSE,
-      array('multiple' => 'multiple', 'class' => 'crm-select2')
+      ['multiple' => 'multiple', 'class' => 'crm-select2']
     );
 
     $showInterviewer = FALSE;
@@ -372,7 +372,7 @@ public static function buildSearchForm(&$form) {
       $className == 'CRM_Campaign_Form_Gotv'
     ) {
 
-      $form->addEntityRef('survey_interviewer_id', ts('Interviewer'), array('class' => 'big'));
+      $form->addEntityRef('survey_interviewer_id', ts('Interviewer'), ['class' => 'big']);
 
       $userId = NULL;
       if (isset($form->_interviewerId) && $form->_interviewerId) {
@@ -383,7 +383,7 @@ public static function buildSearchForm(&$form) {
         $userId = $session->get('userID');
       }
       if ($userId) {
-        $defaults = array();
+        $defaults = [];
         $defaults['survey_interviewer_id'] = $userId;
         $form->setDefaults($defaults);
       }
@@ -395,13 +395,10 @@ public static function buildSearchForm(&$form) {
       FROM  civicrm_custom_field fld
 INNER JOIN  civicrm_custom_group grp on fld.custom_group_id = grp.id
      WHERE  grp.name = %1';
-    $dao = CRM_Core_DAO::executeQuery($query, array(1 => array('Voter_Info', 'String')));
-    $customSearchFields = array();
+    $dao = CRM_Core_DAO::executeQuery($query, [1 => ['Voter_Info', 'String']]);
+    $customSearchFields = [];
     while ($dao->fetch()) {
-      foreach (array(
-                 'ward',
-                 'precinct',
-               ) as $name) {
+      foreach (['ward', 'precinct'] as $name) {
         if (stripos($name, $dao->label) !== FALSE) {
           $fieldId = $dao->id;
           $fieldName = 'custom_' . $dao->id;
@@ -419,7 +416,7 @@ public static function buildSearchForm(&$form) {
       ($className == 'CRM_Campaign_Form_Search')
     ) {
       CRM_Core_Error::statusBounce(ts('Could not find survey for %1 respondents.',
-          array(1 => $form->get('op'))
+          [1 => $form->get('op')]
         ),
         CRM_Utils_System::url('civicrm/survey/add',
           'reset=1&action=add'
@@ -432,7 +429,7 @@ public static function buildSearchForm(&$form) {
     //campaign has some contact groups, don't
     //allow to search the contacts those are not
     //in given campaign groups ( ie not in constituents )
-    $props = array('class' => 'crm-select2');
+    $props = ['class' => 'crm-select2'];
     if ($form->get('searchVoterFor') == 'reserve') {
       $props['onChange'] = "buildCampaignGroups( );return false;";
     }
@@ -447,13 +444,14 @@ public static function buildSearchForm(&$form) {
    *   An array.
    * @return $voterClause as a string
    */
+
   /**
    * @param array $params
    *
    * @return array
    */
-  static public function voterClause($params) {
-    $voterClause = array();
+  public static function voterClause($params) {
+    $voterClause = [];
     $fromClause = $whereClause = NULL;
     if (!is_array($params) || empty($params)) {
       return $voterClause;
@@ -463,7 +461,7 @@ static public function voterClause($params) {
 
     //get the survey activities.
     $activityStatus = CRM_Core_PseudoConstant::activityStatus('name');
-    $status = array('Scheduled');
+    $status = ['Scheduled'];
     if ($searchVoterFor == 'reserve') {
       $status[] = 'Completed';
     }
@@ -494,7 +492,7 @@ static public function voterClause($params) {
           is_array($recontactInterval) &&
           !empty($recontactInterval)
         ) {
-          $voterIds = array();
+          $voterIds = [];
           foreach ($voterActValues as $values) {
             $numOfDays = CRM_Utils_Array::value($values['result'], $recontactInterval);
             if ($numOfDays &&
@@ -554,10 +552,10 @@ static public function voterClause($params) {
         }
       }
     }
-    $voterClause = array(
+    $voterClause = [
       'fromClause' => $fromClause,
       'whereClause' => $whereClause,
-    );
+    ];
 
     return $voterClause;
   }
diff --git a/CRM/Campaign/BAO/Survey.php b/CRM/Campaign/BAO/Survey.php
index 26226a474a68..4333c0111888 100644
--- a/CRM/Campaign/BAO/Survey.php
+++ b/CRM/Campaign/BAO/Survey.php
@@ -1,9 +1,9 @@
 copyValues($params);
     $dao->save();
 
+    if (!empty($params['id'])) {
+      CRM_Utils_Hook::post('edit', 'Survey', $dao->id, $dao);
+    }
+    else {
+      CRM_Utils_Hook::post('create', 'Survey', $dao->id, $dao);
+    }
+
     if (!empty($params['custom']) &&
       is_array($params['custom'])
     ) {
@@ -112,16 +121,16 @@ public static function create(&$params) {
    *
    * @return array|int
    */
-  public static function getSurveySummary($params = array(), $onlyCount = FALSE) {
+  public static function getSurveySummary($params = [], $onlyCount = FALSE) {
     //build the limit and order clause.
     $limitClause = $orderByClause = $lookupTableJoins = NULL;
     if (!$onlyCount) {
-      $sortParams = array(
+      $sortParams = [
         'sort' => 'created_date',
         'offset' => 0,
         'rowCount' => 10,
         'sortOrder' => 'desc',
-      );
+      ];
       foreach ($sortParams as $name => $default) {
         if (!empty($params[$name])) {
           $sortParams[$name] = $params[$name];
@@ -154,23 +163,23 @@ public static function getSurveySummary($params = array(), $onlyCount = FALSE) {
     }
 
     //build the where clause.
-    $queryParams = $where = array();
+    $queryParams = $where = [];
 
     //we only have activity type as a
     //difference between survey and petition.
-    $petitionTypeID = CRM_Core_OptionGroup::getValue('activity_type', 'petition', 'name');
+    $petitionTypeID = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Petition');
     if ($petitionTypeID) {
       $where[] = "( survey.activity_type_id != %1 )";
-      $queryParams[1] = array($petitionTypeID, 'Positive');
+      $queryParams[1] = [$petitionTypeID, 'Positive'];
     }
 
     if (!empty($params['title'])) {
       $where[] = "( survey.title LIKE %2 )";
-      $queryParams[2] = array('%' . trim($params['title']) . '%', 'String');
+      $queryParams[2] = ['%' . trim($params['title']) . '%', 'String'];
     }
     if (!empty($params['campaign_id'])) {
       $where[] = '( survey.campaign_id = %3 )';
-      $queryParams[3] = array($params['campaign_id'], 'Positive');
+      $queryParams[3] = [$params['campaign_id'], 'Positive'];
     }
     if (!empty($params['activity_type_id'])) {
       $typeId = $params['activity_type_id'];
@@ -207,8 +216,8 @@ public static function getSurveySummary($params = array(), $onlyCount = FALSE) {
       return (int) CRM_Core_DAO::singleValueQuery($query, $queryParams);
     }
 
-    $surveys = array();
-    $properties = array(
+    $surveys = [];
+    $properties = [
       'id',
       'title',
       'campaign_id',
@@ -219,7 +228,7 @@ public static function getSurveySummary($params = array(), $onlyCount = FALSE) {
       'release_frequency',
       'max_number_of_contacts',
       'default_number_of_contacts',
-    );
+    ];
 
     $survey = CRM_Core_DAO::executeQuery($query, $queryParams);
     while ($survey->fetch()) {
@@ -254,7 +263,7 @@ public static function getSurveyCount() {
    */
   public static function getSurveys($onlyActive = TRUE, $onlyDefault = FALSE, $forceAll = FALSE, $includePetition = FALSE) {
     $cacheKey = 0;
-    $cacheKeyParams = array('onlyActive', 'onlyDefault', 'forceAll', 'includePetition');
+    $cacheKeyParams = ['onlyActive', 'onlyDefault', 'forceAll', 'includePetition'];
     foreach ($cacheKeyParams as $param) {
       $cacheParam = $$param;
       if (!$cacheParam) {
@@ -269,9 +278,9 @@ public static function getSurveys($onlyActive = TRUE, $onlyDefault = FALSE, $for
       if (!$includePetition) {
         //we only have activity type as a
         //difference between survey and petition.
-        $petitionTypeID = CRM_Core_OptionGroup::getValue('activity_type', 'petition', 'name');
+        $petitionTypeID = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'petition');
 
-        $where = array();
+        $where = [];
         if ($petitionTypeID) {
           $where[] = "( survey.activity_type_id != {$petitionTypeID} )";
         }
@@ -289,7 +298,7 @@ public static function getSurveys($onlyActive = TRUE, $onlyDefault = FALSE, $for
         survey.title as title
   FROM  civicrm_survey as survey
  WHERE  {$whereClause}";
-      $surveys[$cacheKey] = array();
+      $surveys[$cacheKey] = [];
       $survey = CRM_Core_DAO::executeQuery($query);
       while ($survey->fetch()) {
         $surveys[$cacheKey][$survey->id] = $survey->title;
@@ -304,13 +313,15 @@ public static function getSurveys($onlyActive = TRUE, $onlyDefault = FALSE, $for
    *
    * @param string $returnColumn
    * @param bool $includePetitionActivityType
+   *
+   * @return mixed
    */
   public static function getSurveyActivityType($returnColumn = 'label', $includePetitionActivityType = FALSE) {
     static $activityTypes;
     $cacheKey = "{$returnColumn}_{$includePetitionActivityType}";
 
     if (!isset($activityTypes[$cacheKey])) {
-      $activityTypes = array();
+      $activityTypes = [];
       $campaignCompId = CRM_Core_Component::getComponentID('CiviCampaign');
       if ($campaignCompId) {
         $condition = " AND v.component_id={$campaignCompId}";
@@ -340,10 +351,10 @@ public static function getSurveyActivityType($returnColumn = 'label', $includePe
    *
    * @return array
    */
-  public static function getSurveyCustomGroups($surveyTypes = array()) {
-    $customGroups = array();
+  public static function getSurveyCustomGroups($surveyTypes = []) {
+    $customGroups = [];
     if (!is_array($surveyTypes)) {
-      $surveyTypes = array($surveyTypes);
+      $surveyTypes = [$surveyTypes];
     }
 
     if (!empty($surveyTypes)) {
@@ -380,8 +391,8 @@ public static function getSurveyCustomGroups($surveyTypes = array()) {
    * @param bool $is_active
    *   Value we want to set the is_active field.
    *
-   * @return Object
-   *   DAO object on success, null otherwise
+   * @return bool
+   *   true if we found and updated the object, else false
    */
   public static function setIsActive($id, $is_active) {
     return CRM_Core_DAO::setFieldValue('CRM_Campaign_DAO_Survey', $id, 'is_active', $is_active);
@@ -418,8 +429,8 @@ public static function del($id) {
    * @return array
    *   array of contact info.
    */
-  public static function voterDetails($voterIds, $returnProperties = array()) {
-    $voterDetails = array();
+  public static function voterDetails($voterIds, $returnProperties = []) {
+    $voterDetails = [];
     if (!is_array($voterIds) || empty($voterIds)) {
       return $voterDetails;
     }
@@ -428,21 +439,18 @@ public static function voterDetails($voterIds, $returnProperties = array()) {
       $autocompleteContactSearch = CRM_Core_BAO_Setting::valueOptions(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
         'contact_autocomplete_options'
       );
-      $returnProperties = array_fill_keys(array_merge(array(
-          'contact_type',
-          'contact_sub_type',
-          'sort_name',
-        ),
+      $returnProperties = array_fill_keys(array_merge(
+        ['contact_type', 'contact_sub_type', 'sort_name'],
         array_keys($autocompleteContactSearch)
       ), 1);
     }
 
-    $select = $from = array();
+    $select = $from = [];
     foreach ($returnProperties as $property => $ignore) {
-      $value = (in_array($property, array(
+      $value = (in_array($property, [
         'city',
         'street_address',
-      ))) ? 'address' : $property;
+      ])) ? 'address' : $property;
       switch ($property) {
         case 'sort_name':
         case 'contact_type':
@@ -493,7 +501,6 @@ public static function voterDetails($voterIds, $returnProperties = array()) {
         );
         $voterDetails[$contact->contactId]['contact_type'] = $image;
       }
-      $contact->free();
     }
 
     return $voterDetails;
@@ -513,8 +520,8 @@ public static function voterDetails($voterIds, $returnProperties = array()) {
    * @return array
    *   array of survey activity.
    */
-  public static function voterActivityDetails($surveyId, $voterIds, $interviewerId = NULL, $statusIds = array()) {
-    $activityDetails = array();
+  public static function voterActivityDetails($surveyId, $voterIds, $interviewerId = NULL, $statusIds = []) {
+    $activityDetails = [];
     if (!$surveyId ||
       !is_array($voterIds) || empty($voterIds)
     ) {
@@ -527,11 +534,11 @@ public static function voterActivityDetails($surveyId, $voterIds, $interviewerId
     }
 
     $targetContactIds = ' ( ' . implode(',', $voterIds) . ' ) ';
-    $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name');
+    $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate');
     $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts);
     $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts);
 
-    $params[1] = array($surveyId, 'Integer');
+    $params[1] = [$surveyId, 'Integer'];
     $query = "
     SELECT  activity.id, activity.status_id,
             activityTarget.contact_id as voter_id,
@@ -545,18 +552,18 @@ public static function voterActivityDetails($surveyId, $voterIds, $interviewerId
      AND  ( activity.is_deleted IS NULL OR activity.is_deleted = 0 ) ";
     if (!empty($interviewerId)) {
       $query .= "AND activityAssignment.contact_id = %2 ";
-      $params[2] = array($interviewerId, 'Integer');
+      $params[2] = [$interviewerId, 'Integer'];
     }
     $query .= "AND  activityTarget.contact_id IN {$targetContactIds}
             $whereClause";
     $activity = CRM_Core_DAO::executeQuery($query, $params);
     while ($activity->fetch()) {
-      $activityDetails[$activity->voter_id] = array(
+      $activityDetails[$activity->voter_id] = [
         'voter_id' => $activity->voter_id,
         'status_id' => $activity->status_id,
         'activity_id' => $activity->id,
         'interviewer_id' => $activity->interviewer_id,
-      );
+      ];
     }
 
     return $activityDetails;
@@ -581,13 +588,13 @@ public static function getSurveyActivities(
     $voterIds = NULL,
     $onlyCount = FALSE
   ) {
-    $activities = array();
+    $activities = [];
     $surveyActivityCount = 0;
     if (!$surveyId) {
       return ($onlyCount) ? 0 : $activities;
     }
 
-    $where = array();
+    $where = [];
     if (!empty($statusIds)) {
       $where[] = '( activity.status_id IN ( ' . implode(',', array_values($statusIds)) . ' ) )';
     }
@@ -623,7 +630,7 @@ public static function getSurveyActivities(
             contact_a.display_name as voter_name";
     }
 
-    $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name');
+    $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate');
     $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts);
     $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts);
 
@@ -640,10 +647,10 @@ public static function getSurveyActivities(
        AND  ( activity.is_deleted IS NULL OR activity.is_deleted = 0 )
             $whereClause";
 
-    $params = array(
-      1 => array($surveyId, 'Integer'),
-      2 => array($actTypeId, 'Integer'),
-    );
+    $params = [
+      1 => [$surveyId, 'Integer'],
+      2 => [$actTypeId, 'Integer'],
+    ];
 
     if ($onlyCount) {
       $dbCount = CRM_Core_DAO::singleValueQuery($query, $params);
@@ -653,7 +660,7 @@ public static function getSurveyActivities(
     $activity = CRM_Core_DAO::executeQuery($query, $params);
 
     while ($activity->fetch()) {
-      $activities[$activity->id] = array(
+      $activities[$activity->id] = [
         'id' => $activity->id,
         'voter_id' => $activity->voter_id,
         'voter_name' => $activity->voter_name,
@@ -661,7 +668,7 @@ public static function getSurveyActivities(
         'interviewer_id' => $activity->interviewer_id,
         'result' => $activity->result,
         'activity_date_time' => $activity->activity_date_time,
-      );
+      ];
     }
 
     return $activities;
@@ -680,8 +687,8 @@ public static function getSurveyActivities(
    * @return array
    *   Survey related contact ids.
    */
-  public static function getSurveyVoterInfo($surveyId, $interviewerId = NULL, $statusIds = array()) {
-    $voterIds = array();
+  public static function getSurveyVoterInfo($surveyId, $interviewerId = NULL, $statusIds = []) {
+    $voterIds = [];
     if (!$surveyId) {
       return $voterIds;
     }
@@ -694,7 +701,7 @@ public static function getSurveyVoterInfo($surveyId, $interviewerId = NULL, $sta
       $cacheKey = "{$cacheKey}_" . implode('_', $statusIds);
     }
 
-    static $contactIds = array();
+    static $contactIds = [];
     if (!isset($contactIds[$cacheKey])) {
       $activities = self::getSurveyActivities($surveyId, $interviewerId, $statusIds);
       foreach ($activities as $values) {
@@ -714,7 +721,7 @@ public static function getSurveyVoterInfo($surveyId, $interviewerId = NULL, $sta
    *   an array of option groups.
    */
   public static function getResultSets($valueColumnName = 'title') {
-    $resultSets = array();
+    $resultSets = [];
     $valueColumnName = CRM_Utils_Type::escape($valueColumnName, 'String');
 
     $query = "SELECT id, {$valueColumnName} FROM civicrm_option_group WHERE name LIKE 'civicrm_survey_%' AND is_active=1";
@@ -763,7 +770,7 @@ public static function isSurveyActivity($activityId) {
    *   an array of option values
    */
   public static function getResponsesOptions($surveyId) {
-    $responseOptions = array();
+    $responseOptions = [];
     if (!$surveyId) {
       return $responseOptions;
     }
@@ -786,12 +793,12 @@ public static function getResponsesOptions($surveyId) {
    *   $url array of permissioned links
    */
   public static function buildPermissionLinks($surveyId, $enclosedInUL = FALSE, $extraULName = 'more') {
-    $menuLinks = array();
+    $menuLinks = [];
     if (!$surveyId) {
       return $menuLinks;
     }
 
-    static $voterLinks = array();
+    static $voterLinks = [];
     if (empty($voterLinks)) {
       $permissioned = FALSE;
       if (CRM_Core_Permission::check('manage campaign') ||
@@ -801,44 +808,44 @@ public static function buildPermissionLinks($surveyId, $enclosedInUL = FALSE, $e
       }
 
       if ($permissioned || CRM_Core_Permission::check("reserve campaign contacts")) {
-        $voterLinks['reserve'] = array(
+        $voterLinks['reserve'] = [
           'name' => 'reserve',
           'url' => 'civicrm/survey/search',
           'qs' => 'sid=%%id%%&reset=1&op=reserve',
           'title' => ts('Reserve Respondents'),
-        );
+        ];
       }
       if ($permissioned || CRM_Core_Permission::check("interview campaign contacts")) {
-        $voterLinks['release'] = array(
+        $voterLinks['release'] = [
           'name' => 'interview',
           'url' => 'civicrm/survey/search',
           'qs' => 'sid=%%id%%&reset=1&op=interview&force=1',
           'title' => ts('Interview Respondents'),
-        );
+        ];
       }
       if ($permissioned || CRM_Core_Permission::check("release campaign contacts")) {
-        $voterLinks['interview'] = array(
+        $voterLinks['interview'] = [
           'name' => 'release',
           'url' => 'civicrm/survey/search',
           'qs' => 'sid=%%id%%&reset=1&op=release&force=1',
           'title' => ts('Release Respondents'),
-        );
+        ];
       }
     }
 
     if (CRM_Core_Permission::check('access CiviReport')) {
       $reportID = self::getReportID($surveyId);
       if ($reportID) {
-        $voterLinks['report'] = array(
+        $voterLinks['report'] = [
           'name' => 'report',
           'url' => "civicrm/report/instance/{$reportID}",
           'qs' => 'reset=1',
           'title' => ts('View Survey Report'),
-        );
+        ];
       }
     }
 
-    $ids = array('id' => $surveyId);
+    $ids = ['id' => $surveyId];
     foreach ($voterLinks as $link) {
       if (!empty($link['qs']) &&
         !CRM_Utils_System::isNull($link['qs'])
@@ -876,19 +883,19 @@ public static function getSurveyProfileId($surveyId) {
       return NULL;
     }
 
-    static $ufIds = array();
+    static $ufIds = [];
     if (!array_key_exists($surveyId, $ufIds)) {
       //get the profile id.
-      $ufJoinParams = array(
+      $ufJoinParams = [
         'entity_id' => $surveyId,
         'entity_table' => 'civicrm_survey',
         'module' => 'CiviCampaign',
-      );
+      ];
 
       list($first, $second) = CRM_Core_BAO_UFJoin::getUFGroupIds($ufJoinParams);
 
       if ($first) {
-        $ufIds[$surveyId] = array($first);
+        $ufIds[$surveyId] = [$first];
       }
       if ($second) {
         $ufIds[$surveyId][] = array_shift($second);
@@ -903,12 +910,12 @@ public static function getSurveyProfileId($surveyId) {
    *
    * @return mixed
    */
-  public Static function getReportID($surveyId) {
-    static $reportIds = array();
+  public static function getReportID($surveyId) {
+    static $reportIds = [];
 
     if (!array_key_exists($surveyId, $reportIds)) {
       $query = "SELECT MAX(id) as id FROM civicrm_report_instance WHERE name = %1";
-      $reportID = CRM_Core_DAO::singleValueQuery($query, array(1 => array("survey_{$surveyId}", 'String')));
+      $reportID = CRM_Core_DAO::singleValueQuery($query, [1 => ["survey_{$surveyId}", 'String']]);
       $reportIds[$surveyId] = $reportID;
     }
     return $reportIds[$surveyId];
@@ -943,8 +950,8 @@ public static function surveyProfileTypes() {
     static $profileTypes;
 
     if (!isset($profileTypes)) {
-      $profileTypes = array_merge(array('Activity', 'Contact'), CRM_Contact_BAO_ContactType::basicTypes());
-      $profileTypes = array_diff($profileTypes, array('Organization', 'Household'));
+      $profileTypes = array_merge(['Activity', 'Contact'], CRM_Contact_BAO_ContactType::basicTypes());
+      $profileTypes = array_diff($profileTypes, ['Organization', 'Household']);
     }
 
     return $profileTypes;
@@ -964,7 +971,7 @@ public static function surveyProfileTypes() {
    */
   public static function getSurveyResponseFields($surveyId, $surveyTypeId = NULL) {
     if (empty($surveyId)) {
-      return array();
+      return [];
     }
 
     static $responseFields;
@@ -974,7 +981,7 @@ public static function getSurveyResponseFields($surveyId, $surveyTypeId = NULL)
       return $responseFields[$cacheKey];
     }
 
-    $responseFields[$cacheKey] = array();
+    $responseFields[$cacheKey] = [];
 
     $profileId = self::getSurveyProfileId($surveyId);
 
@@ -991,7 +998,7 @@ public static function getSurveyResponseFields($surveyId, $surveyTypeId = NULL)
     );
 
     //don't load these fields in grid.
-    $removeFields = array('File', 'RichTextEditor');
+    $removeFields = ['File', 'RichTextEditor'];
 
     $supportableFieldTypes = self::surveyProfileTypes();
 
@@ -1043,8 +1050,8 @@ public static function getInterviewers() {
       $whereClause = ' WHERE survey.activity_type_id IN ( ' . implode(' , ', array_keys($activityTypes)) . ' )';
     }
 
-    $interviewers = array();
-    $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name');
+    $interviewers = [];
+    $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate');
     $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts);
 
     $query = "
@@ -1093,8 +1100,8 @@ public static function releaseRespondent($params) {
      WHERE  activity.is_deleted = 0
        AND  activity.status_id = %1
        AND  activity.activity_type_id IN ( ' . implode(', ', $surveyActivityTypesIds) . ' )';
-      $activity = CRM_Core_DAO::executeQuery($query, array(1 => array($reserveStatusId, 'Positive')));
-      $releasedIds = array();
+      $activity = CRM_Core_DAO::executeQuery($query, [1 => [$reserveStatusId, 'Positive']]);
+      $releasedIds = [];
       while ($activity->fetch()) {
         if (!$activity->release_frequency) {
           continue;
@@ -1118,10 +1125,10 @@ public static function releaseRespondent($params) {
       }
     }
 
-    $rtnMsg = array(
+    $rtnMsg = [
       'is_error' => 0,
       'messages' => "Number of respondents released = {$releasedCount}",
-    );
+    ];
 
     return $rtnMsg;
   }
@@ -1136,14 +1143,14 @@ public static function releaseRespondent($params) {
    *
    * @return array|bool
    */
-  public static function buildOptions($fieldName, $context = NULL, $props = array()) {
-    $params = array();
+  public static function buildOptions($fieldName, $context = NULL, $props = []) {
+    $params = [];
     // Special logic for fields whose options depend on context or properties
     switch ($fieldName) {
       case 'activity_type_id':
         $campaignCompId = CRM_Core_Component::getComponentID('CiviCampaign');
         if ($campaignCompId) {
-          $params['condition'] = array("component_id={$campaignCompId}");
+          $params['condition'] = ["component_id={$campaignCompId}"];
         }
         break;
     }
diff --git a/CRM/Campaign/Controller/Search.php b/CRM/Campaign/Controller/Search.php
index 12ff9b1c478a..5f3740844066 100644
--- a/CRM/Campaign/Controller/Search.php
+++ b/CRM/Campaign/Controller/Search.php
@@ -1,9 +1,9 @@
 __table = 'civicrm_campaign';
     parent::__construct();
   }
+
   /**
-   * Returns foreign keys and entity references
+   * Returns foreign keys and entity references.
    *
    * @return array
    *   [CRM_Core_Reference_Interface]
    */
-  static function getReferenceColumns() {
+  public static function getReferenceColumns() {
     if (!isset(Civi::$statics[__CLASS__]['links'])) {
-      Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__);
-      Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'parent_id', 'civicrm_campaign', 'id');
-      Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'created_id', 'civicrm_contact', 'id');
-      Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'last_modified_id', 'civicrm_contact', 'id');
+      Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__);
+      Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'parent_id', 'civicrm_campaign', 'id');
+      Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'created_id', 'civicrm_contact', 'id');
+      Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'last_modified_id', 'civicrm_contact', 'id');
       CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']);
     }
     return Civi::$statics[__CLASS__]['links'];
   }
+
   /**
    * Returns all the column names of this table
    *
    * @return array
    */
-  static function &fields() {
+  public static function &fields() {
     if (!isset(Civi::$statics[__CLASS__]['fields'])) {
-      Civi::$statics[__CLASS__]['fields'] = array(
-        'id' => array(
+      Civi::$statics[__CLASS__]['fields'] = [
+        'id' => [
           'name' => 'id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Campaign ID') ,
-          'description' => 'Unique Campaign ID.',
-          'required' => true,
-          'import' => true,
+          'title' => ts('Campaign ID'),
+          'description' => ts('Unique Campaign ID.'),
+          'required' => TRUE,
+          'import' => TRUE,
           'where' => 'civicrm_campaign.id',
           'headerPattern' => '',
           'dataPattern' => '',
-          'export' => true,
-        ) ,
-        'name' => array(
+          'export' => TRUE,
+          'table_name' => 'civicrm_campaign',
+          'entity' => 'Campaign',
+          'bao' => 'CRM_Campaign_BAO_Campaign',
+          'localizable' => 0,
+        ],
+        'name' => [
           'name' => 'name',
           'type' => CRM_Utils_Type::T_STRING,
-          'title' => ts('Campaign Name') ,
-          'description' => 'Name of the Campaign.',
-          'required' => true,
+          'title' => ts('Campaign Name'),
+          'description' => ts('Name of the Campaign.'),
+          'required' => TRUE,
           'maxlength' => 255,
           'size' => CRM_Utils_Type::HUGE,
-          'import' => true,
+          'import' => TRUE,
           'where' => 'civicrm_campaign.name',
           'headerPattern' => '',
           'dataPattern' => '',
-          'export' => true,
-          'html' => array(
+          'export' => TRUE,
+          'table_name' => 'civicrm_campaign',
+          'entity' => 'Campaign',
+          'bao' => 'CRM_Campaign_BAO_Campaign',
+          'localizable' => 0,
+          'html' => [
             'type' => 'Text',
-          ) ,
-        ) ,
-        'title' => array(
+          ],
+        ],
+        'title' => [
           'name' => 'title',
           'type' => CRM_Utils_Type::T_STRING,
-          'title' => ts('Campaign Title') ,
-          'description' => 'Title of the Campaign.',
+          'title' => ts('Campaign Title'),
+          'description' => ts('Title of the Campaign.'),
           'maxlength' => 255,
           'size' => CRM_Utils_Type::HUGE,
-          'import' => true,
+          'import' => TRUE,
           'where' => 'civicrm_campaign.title',
           'headerPattern' => '',
           'dataPattern' => '',
-          'export' => true,
-          'html' => array(
+          'export' => TRUE,
+          'table_name' => 'civicrm_campaign',
+          'entity' => 'Campaign',
+          'bao' => 'CRM_Campaign_BAO_Campaign',
+          'localizable' => 0,
+          'html' => [
             'type' => 'Text',
-          ) ,
-        ) ,
-        'description' => array(
+          ],
+        ],
+        'description' => [
           'name' => 'description',
           'type' => CRM_Utils_Type::T_TEXT,
-          'title' => ts('Campaign Description') ,
-          'description' => 'Full description of Campaign.',
+          'title' => ts('Campaign Description'),
+          'description' => ts('Full description of Campaign.'),
           'rows' => 8,
           'cols' => 60,
-          'html' => array(
+          'table_name' => 'civicrm_campaign',
+          'entity' => 'Campaign',
+          'bao' => 'CRM_Campaign_BAO_Campaign',
+          'localizable' => 0,
+          'html' => [
             'type' => 'TextArea',
-          ) ,
-        ) ,
-        'start_date' => array(
+          ],
+        ],
+        'start_date' => [
           'name' => 'start_date',
           'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME,
-          'title' => ts('Campaign Start Date') ,
-          'description' => 'Date and time that Campaign starts.',
-          'import' => true,
+          'title' => ts('Campaign Start Date'),
+          'description' => ts('Date and time that Campaign starts.'),
+          'import' => TRUE,
           'where' => 'civicrm_campaign.start_date',
           'headerPattern' => '/^start|(s(tart\s)?date)$/i',
           'dataPattern' => '',
-          'export' => true,
-          'html' => array(
+          'export' => TRUE,
+          'table_name' => 'civicrm_campaign',
+          'entity' => 'Campaign',
+          'bao' => 'CRM_Campaign_BAO_Campaign',
+          'localizable' => 0,
+          'html' => [
             'type' => 'Select Date',
-          ) ,
-        ) ,
-        'end_date' => array(
+          ],
+        ],
+        'end_date' => [
           'name' => 'end_date',
           'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME,
-          'title' => ts('Campaign End Date') ,
-          'description' => 'Date and time that Campaign ends.',
-          'import' => true,
+          'title' => ts('Campaign End Date'),
+          'description' => ts('Date and time that Campaign ends.'),
+          'import' => TRUE,
           'where' => 'civicrm_campaign.end_date',
           'headerPattern' => '/^end|(e(nd\s)?date)$/i',
           'dataPattern' => '',
-          'export' => true,
-          'html' => array(
+          'export' => TRUE,
+          'table_name' => 'civicrm_campaign',
+          'entity' => 'Campaign',
+          'bao' => 'CRM_Campaign_BAO_Campaign',
+          'localizable' => 0,
+          'html' => [
             'type' => 'Select Date',
-          ) ,
-        ) ,
-        'campaign_type_id' => array(
+          ],
+        ],
+        'campaign_type_id' => [
           'name' => 'campaign_type_id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Campaign Type') ,
-          'description' => 'Campaign Type ID.Implicit FK to civicrm_option_value where option_group = campaign_type',
-          'import' => true,
+          'title' => ts('Campaign Type'),
+          'description' => ts('Campaign Type ID.Implicit FK to civicrm_option_value where option_group = campaign_type'),
+          'import' => TRUE,
           'where' => 'civicrm_campaign.campaign_type_id',
           'headerPattern' => '',
           'dataPattern' => '',
-          'export' => true,
+          'export' => TRUE,
           'default' => 'NULL',
-          'html' => array(
+          'table_name' => 'civicrm_campaign',
+          'entity' => 'Campaign',
+          'bao' => 'CRM_Campaign_BAO_Campaign',
+          'localizable' => 0,
+          'html' => [
             'type' => 'Select',
-          ) ,
-          'pseudoconstant' => array(
+          ],
+          'pseudoconstant' => [
             'optionGroupName' => 'campaign_type',
             'optionEditPath' => 'civicrm/admin/options/campaign_type',
-          )
-        ) ,
-        'status_id' => array(
+          ]
+        ],
+        'status_id' => [
           'name' => 'status_id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Campaign Status') ,
-          'description' => 'Campaign status ID.Implicit FK to civicrm_option_value where option_group = campaign_status',
-          'import' => true,
+          'title' => ts('Campaign Status'),
+          'description' => ts('Campaign status ID.Implicit FK to civicrm_option_value where option_group = campaign_status'),
+          'import' => TRUE,
           'where' => 'civicrm_campaign.status_id',
           'headerPattern' => '',
           'dataPattern' => '',
-          'export' => true,
+          'export' => TRUE,
           'default' => 'NULL',
-          'html' => array(
+          'table_name' => 'civicrm_campaign',
+          'entity' => 'Campaign',
+          'bao' => 'CRM_Campaign_BAO_Campaign',
+          'localizable' => 0,
+          'html' => [
             'type' => 'Select',
-          ) ,
-          'pseudoconstant' => array(
+          ],
+          'pseudoconstant' => [
             'optionGroupName' => 'campaign_status',
             'optionEditPath' => 'civicrm/admin/options/campaign_status',
-          )
-        ) ,
-        'external_identifier' => array(
+          ]
+        ],
+        'external_identifier' => [
           'name' => 'external_identifier',
           'type' => CRM_Utils_Type::T_STRING,
-          'title' => ts('Campaign External ID') ,
-          'description' => 'Unique trusted external ID (generally from a legacy app/datasource). Particularly useful for deduping operations.',
+          'title' => ts('Campaign External ID'),
+          'description' => ts('Unique trusted external ID (generally from a legacy app/datasource). Particularly useful for deduping operations.'),
           'maxlength' => 32,
           'size' => CRM_Utils_Type::MEDIUM,
-          'import' => true,
+          'import' => TRUE,
           'where' => 'civicrm_campaign.external_identifier',
           'headerPattern' => '/external\s?id/i',
           'dataPattern' => '/^\d{11,}$/',
-          'export' => true,
-          'html' => array(
+          'export' => TRUE,
+          'table_name' => 'civicrm_campaign',
+          'entity' => 'Campaign',
+          'bao' => 'CRM_Campaign_BAO_Campaign',
+          'localizable' => 0,
+          'html' => [
             'type' => 'Text',
-          ) ,
-        ) ,
-        'parent_id' => array(
+          ],
+        ],
+        'parent_id' => [
           'name' => 'parent_id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Parent Campaign') ,
-          'description' => 'Optional parent id for this Campaign.',
-          'import' => true,
+          'title' => ts('Parent Campaign'),
+          'description' => ts('Optional parent id for this Campaign.'),
+          'import' => TRUE,
           'where' => 'civicrm_campaign.parent_id',
           'headerPattern' => '',
           'dataPattern' => '',
-          'export' => true,
+          'export' => TRUE,
           'default' => 'NULL',
+          'table_name' => 'civicrm_campaign',
+          'entity' => 'Campaign',
+          'bao' => 'CRM_Campaign_BAO_Campaign',
+          'localizable' => 0,
           'FKClassName' => 'CRM_Campaign_DAO_Campaign',
-          'html' => array(
+          'html' => [
             'type' => 'EntityRef',
-          ) ,
-        ) ,
-        'is_active' => array(
+          ],
+        ],
+        'is_active' => [
           'name' => 'is_active',
           'type' => CRM_Utils_Type::T_BOOLEAN,
-          'title' => ts('Is Campaign Active?') ,
-          'description' => 'Is this Campaign enabled or disabled/cancelled?',
+          'title' => ts('Is Campaign Active?'),
+          'description' => ts('Is this Campaign enabled or disabled/cancelled?'),
           'default' => '1',
-          'html' => array(
+          'table_name' => 'civicrm_campaign',
+          'entity' => 'Campaign',
+          'bao' => 'CRM_Campaign_BAO_Campaign',
+          'localizable' => 0,
+          'html' => [
             'type' => 'CheckBox',
-          ) ,
-        ) ,
-        'created_id' => array(
+          ],
+        ],
+        'created_id' => [
           'name' => 'created_id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Campaign Created By') ,
-          'description' => 'FK to civicrm_contact, who created this Campaign.',
+          'title' => ts('Campaign Created By'),
+          'description' => ts('FK to civicrm_contact, who created this Campaign.'),
+          'table_name' => 'civicrm_campaign',
+          'entity' => 'Campaign',
+          'bao' => 'CRM_Campaign_BAO_Campaign',
+          'localizable' => 0,
           'FKClassName' => 'CRM_Contact_DAO_Contact',
-        ) ,
-        'created_date' => array(
+        ],
+        'created_date' => [
           'name' => 'created_date',
           'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME,
-          'title' => ts('Campaign Created Date') ,
-          'description' => 'Date and time that Campaign was created.',
-          'html' => array(
+          'title' => ts('Campaign Created Date'),
+          'description' => ts('Date and time that Campaign was created.'),
+          'table_name' => 'civicrm_campaign',
+          'entity' => 'Campaign',
+          'bao' => 'CRM_Campaign_BAO_Campaign',
+          'localizable' => 0,
+          'html' => [
             'type' => 'Select Date',
-          ) ,
-        ) ,
-        'last_modified_id' => array(
+          ],
+        ],
+        'last_modified_id' => [
           'name' => 'last_modified_id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Campaign Modified By') ,
-          'description' => 'FK to civicrm_contact, who recently edited this Campaign.',
+          'title' => ts('Campaign Modified By'),
+          'description' => ts('FK to civicrm_contact, who recently edited this Campaign.'),
+          'table_name' => 'civicrm_campaign',
+          'entity' => 'Campaign',
+          'bao' => 'CRM_Campaign_BAO_Campaign',
+          'localizable' => 0,
           'FKClassName' => 'CRM_Contact_DAO_Contact',
-        ) ,
-        'last_modified_date' => array(
+        ],
+        'last_modified_date' => [
           'name' => 'last_modified_date',
           'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME,
-          'title' => ts('Campaign Modified Date') ,
-          'description' => 'Date and time that Campaign was edited last time.',
-        ) ,
-        'goal_general' => array(
+          'title' => ts('Campaign Modified Date'),
+          'description' => ts('Date and time that Campaign was edited last time.'),
+          'table_name' => 'civicrm_campaign',
+          'entity' => 'Campaign',
+          'bao' => 'CRM_Campaign_BAO_Campaign',
+          'localizable' => 0,
+        ],
+        'goal_general' => [
           'name' => 'goal_general',
           'type' => CRM_Utils_Type::T_TEXT,
-          'title' => ts('Campaign Goals') ,
-          'description' => 'General goals for Campaign.',
-          'html' => array(
+          'title' => ts('Campaign Goals'),
+          'description' => ts('General goals for Campaign.'),
+          'table_name' => 'civicrm_campaign',
+          'entity' => 'Campaign',
+          'bao' => 'CRM_Campaign_BAO_Campaign',
+          'localizable' => 0,
+          'html' => [
             'type' => 'RichTextEditor',
-          ) ,
-        ) ,
-        'goal_revenue' => array(
+          ],
+        ],
+        'goal_revenue' => [
           'name' => 'goal_revenue',
           'type' => CRM_Utils_Type::T_MONEY,
-          'title' => ts('Goal Revenue') ,
-          'description' => 'The target revenue for this campaign.',
-          'precision' => array(
+          'title' => ts('Goal Revenue'),
+          'description' => ts('The target revenue for this campaign.'),
+          'precision' => [
             20,
             2
-          ) ,
-          'html' => array(
+          ],
+          'table_name' => 'civicrm_campaign',
+          'entity' => 'Campaign',
+          'bao' => 'CRM_Campaign_BAO_Campaign',
+          'localizable' => 0,
+          'html' => [
             'type' => 'Text',
-          ) ,
-        ) ,
-      );
+          ],
+        ],
+      ];
       CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']);
     }
     return Civi::$statics[__CLASS__]['fields'];
   }
+
   /**
    * Return a mapping from field-name to the corresponding key (as used in fields()).
    *
    * @return array
    *   Array(string $name => string $uniqueName).
    */
-  static function &fieldKeys() {
+  public static function &fieldKeys() {
     if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) {
       Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields()));
     }
     return Civi::$statics[__CLASS__]['fieldKeys'];
   }
+
   /**
    * Returns the names of this table
    *
    * @return string
    */
-  static function getTableName() {
+  public static function getTableName() {
     return self::$_tableName;
   }
+
   /**
    * Returns if this table needs to be logged
    *
-   * @return boolean
+   * @return bool
    */
-  function getLog() {
+  public function getLog() {
     return self::$_log;
   }
+
   /**
    * Returns the list of fields that can be imported
    *
@@ -438,10 +507,11 @@ function getLog() {
    *
    * @return array
    */
-  static function &import($prefix = false) {
-    $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'campaign', $prefix, array());
+  public static function &import($prefix = FALSE) {
+    $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'campaign', $prefix, []);
     return $r;
   }
+
   /**
    * Returns the list of fields that can be exported
    *
@@ -449,8 +519,47 @@ static function &import($prefix = false) {
    *
    * @return array
    */
-  static function &export($prefix = false) {
-    $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'campaign', $prefix, array());
+  public static function &export($prefix = FALSE) {
+    $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'campaign', $prefix, []);
     return $r;
   }
+
+  /**
+   * Returns the list of indices
+   *
+   * @param bool $localize
+   *
+   * @return array
+   */
+  public static function indices($localize = TRUE) {
+    $indices = [
+      'UI_campaign_type_id' => [
+        'name' => 'UI_campaign_type_id',
+        'field' => [
+          0 => 'campaign_type_id',
+        ],
+        'localizable' => FALSE,
+        'sig' => 'civicrm_campaign::0::campaign_type_id',
+      ],
+      'UI_campaign_status_id' => [
+        'name' => 'UI_campaign_status_id',
+        'field' => [
+          0 => 'status_id',
+        ],
+        'localizable' => FALSE,
+        'sig' => 'civicrm_campaign::0::status_id',
+      ],
+      'UI_external_identifier' => [
+        'name' => 'UI_external_identifier',
+        'field' => [
+          0 => 'external_identifier',
+        ],
+        'localizable' => FALSE,
+        'unique' => TRUE,
+        'sig' => 'civicrm_campaign::1::external_identifier',
+      ],
+    ];
+    return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices;
+  }
+
 }
diff --git a/CRM/Campaign/DAO/CampaignGroup.php b/CRM/Campaign/DAO/CampaignGroup.php
index a706bffa7f34..39807c1ac6ea 100644
--- a/CRM/Campaign/DAO/CampaignGroup.php
+++ b/CRM/Campaign/DAO/CampaignGroup.php
@@ -1,199 +1,208 @@
 __table = 'civicrm_campaign_group';
     parent::__construct();
   }
+
   /**
-   * Returns foreign keys and entity references
+   * Returns foreign keys and entity references.
    *
    * @return array
    *   [CRM_Core_Reference_Interface]
    */
-  static function getReferenceColumns() {
+  public static function getReferenceColumns() {
     if (!isset(Civi::$statics[__CLASS__]['links'])) {
-      Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__);
-      Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'campaign_id', 'civicrm_campaign', 'id');
-      Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Dynamic(self::getTableName() , 'entity_id', NULL, 'id', 'entity_table');
+      Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__);
+      Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'campaign_id', 'civicrm_campaign', 'id');
+      Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Dynamic(self::getTableName(), 'entity_id', NULL, 'id', 'entity_table');
       CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']);
     }
     return Civi::$statics[__CLASS__]['links'];
   }
+
   /**
    * Returns all the column names of this table
    *
    * @return array
    */
-  static function &fields() {
+  public static function &fields() {
     if (!isset(Civi::$statics[__CLASS__]['fields'])) {
-      Civi::$statics[__CLASS__]['fields'] = array(
-        'id' => array(
+      Civi::$statics[__CLASS__]['fields'] = [
+        'id' => [
           'name' => 'id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Campaign Group ID') ,
-          'description' => 'Campaign Group id.',
-          'required' => true,
-        ) ,
-        'campaign_id' => array(
+          'title' => ts('Campaign Group ID'),
+          'description' => ts('Campaign Group id.'),
+          'required' => TRUE,
+          'table_name' => 'civicrm_campaign_group',
+          'entity' => 'CampaignGroup',
+          'bao' => 'CRM_Campaign_DAO_CampaignGroup',
+          'localizable' => 0,
+        ],
+        'campaign_id' => [
           'name' => 'campaign_id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Campaign') ,
-          'description' => 'Foreign key to the activity Campaign.',
-          'required' => true,
+          'title' => ts('Campaign'),
+          'description' => ts('Foreign key to the activity Campaign.'),
+          'required' => TRUE,
+          'table_name' => 'civicrm_campaign_group',
+          'entity' => 'CampaignGroup',
+          'bao' => 'CRM_Campaign_DAO_CampaignGroup',
+          'localizable' => 0,
           'FKClassName' => 'CRM_Campaign_DAO_Campaign',
-          'pseudoconstant' => array(
+          'pseudoconstant' => [
             'table' => 'civicrm_campaign',
             'keyColumn' => 'id',
             'labelColumn' => 'title',
-          )
-        ) ,
-        'group_type' => array(
+          ]
+        ],
+        'group_type' => [
           'name' => 'group_type',
           'type' => CRM_Utils_Type::T_STRING,
-          'title' => ts('Campaign Group Type') ,
-          'description' => 'Type of Group.',
+          'title' => ts('Campaign Group Type'),
+          'description' => ts('Type of Group.'),
           'maxlength' => 8,
           'size' => CRM_Utils_Type::EIGHT,
           'default' => 'NULL',
-          'html' => array(
+          'table_name' => 'civicrm_campaign_group',
+          'entity' => 'CampaignGroup',
+          'bao' => 'CRM_Campaign_DAO_CampaignGroup',
+          'localizable' => 0,
+          'html' => [
             'type' => 'Select',
-          ) ,
-          'pseudoconstant' => array(
+          ],
+          'pseudoconstant' => [
             'callback' => 'CRM_Core_SelectValues::getCampaignGroupTypes',
-          )
-        ) ,
-        'entity_table' => array(
+          ]
+        ],
+        'entity_table' => [
           'name' => 'entity_table',
           'type' => CRM_Utils_Type::T_STRING,
-          'title' => ts('Entity Table') ,
-          'description' => 'Name of table where item being referenced is stored.',
+          'title' => ts('Entity Table'),
+          'description' => ts('Name of table where item being referenced is stored.'),
           'maxlength' => 64,
           'size' => CRM_Utils_Type::BIG,
           'default' => 'NULL',
-        ) ,
-        'entity_id' => array(
+          'table_name' => 'civicrm_campaign_group',
+          'entity' => 'CampaignGroup',
+          'bao' => 'CRM_Campaign_DAO_CampaignGroup',
+          'localizable' => 0,
+        ],
+        'entity_id' => [
           'name' => 'entity_id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Entity ID') ,
-          'description' => 'Entity id of referenced table.',
+          'title' => ts('Entity ID'),
+          'description' => ts('Entity id of referenced table.'),
           'default' => 'NULL',
-        ) ,
-      );
+          'table_name' => 'civicrm_campaign_group',
+          'entity' => 'CampaignGroup',
+          'bao' => 'CRM_Campaign_DAO_CampaignGroup',
+          'localizable' => 0,
+        ],
+      ];
       CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']);
     }
     return Civi::$statics[__CLASS__]['fields'];
   }
+
   /**
    * Return a mapping from field-name to the corresponding key (as used in fields()).
    *
    * @return array
    *   Array(string $name => string $uniqueName).
    */
-  static function &fieldKeys() {
+  public static function &fieldKeys() {
     if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) {
       Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields()));
     }
     return Civi::$statics[__CLASS__]['fieldKeys'];
   }
+
   /**
    * Returns the names of this table
    *
    * @return string
    */
-  static function getTableName() {
+  public static function getTableName() {
     return self::$_tableName;
   }
+
   /**
    * Returns if this table needs to be logged
    *
-   * @return boolean
+   * @return bool
    */
-  function getLog() {
+  public function getLog() {
     return self::$_log;
   }
+
   /**
    * Returns the list of fields that can be imported
    *
@@ -201,10 +210,11 @@ function getLog() {
    *
    * @return array
    */
-  static function &import($prefix = false) {
-    $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'campaign_group', $prefix, array());
+  public static function &import($prefix = FALSE) {
+    $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'campaign_group', $prefix, []);
     return $r;
   }
+
   /**
    * Returns the list of fields that can be exported
    *
@@ -212,8 +222,21 @@ static function &import($prefix = false) {
    *
    * @return array
    */
-  static function &export($prefix = false) {
-    $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'campaign_group', $prefix, array());
+  public static function &export($prefix = FALSE) {
+    $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'campaign_group', $prefix, []);
     return $r;
   }
+
+  /**
+   * Returns the list of indices
+   *
+   * @param bool $localize
+   *
+   * @return array
+   */
+  public static function indices($localize = TRUE) {
+    $indices = [];
+    return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices;
+  }
+
 }
diff --git a/CRM/Campaign/DAO/Survey.php b/CRM/Campaign/DAO/Survey.php
index 65c96852b576..17cc7fcc90b9 100644
--- a/CRM/Campaign/DAO/Survey.php
+++ b/CRM/Campaign/DAO/Survey.php
@@ -1,413 +1,499 @@
 __table = 'civicrm_survey';
     parent::__construct();
   }
+
   /**
-   * Returns foreign keys and entity references
+   * Returns foreign keys and entity references.
    *
    * @return array
    *   [CRM_Core_Reference_Interface]
    */
-  static function getReferenceColumns() {
+  public static function getReferenceColumns() {
     if (!isset(Civi::$statics[__CLASS__]['links'])) {
-      Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__);
-      Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'campaign_id', 'civicrm_campaign', 'id');
-      Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'created_id', 'civicrm_contact', 'id');
-      Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'last_modified_id', 'civicrm_contact', 'id');
+      Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__);
+      Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'campaign_id', 'civicrm_campaign', 'id');
+      Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'created_id', 'civicrm_contact', 'id');
+      Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'last_modified_id', 'civicrm_contact', 'id');
       CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']);
     }
     return Civi::$statics[__CLASS__]['links'];
   }
+
   /**
    * Returns all the column names of this table
    *
    * @return array
    */
-  static function &fields() {
+  public static function &fields() {
     if (!isset(Civi::$statics[__CLASS__]['fields'])) {
-      Civi::$statics[__CLASS__]['fields'] = array(
-        'id' => array(
+      Civi::$statics[__CLASS__]['fields'] = [
+        'id' => [
           'name' => 'id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Survey ID') ,
-          'description' => 'Survey id.',
-          'required' => true,
-        ) ,
-        'title' => array(
+          'title' => ts('Survey ID'),
+          'description' => ts('Survey id.'),
+          'required' => TRUE,
+          'table_name' => 'civicrm_survey',
+          'entity' => 'Survey',
+          'bao' => 'CRM_Campaign_BAO_Survey',
+          'localizable' => 0,
+        ],
+        'title' => [
           'name' => 'title',
           'type' => CRM_Utils_Type::T_STRING,
-          'title' => ts('Survey Title') ,
-          'description' => 'Title of the Survey.',
-          'required' => true,
+          'title' => ts('Survey Title'),
+          'description' => ts('Title of the Survey.'),
+          'required' => TRUE,
           'maxlength' => 255,
           'size' => CRM_Utils_Type::HUGE,
-          'import' => true,
+          'import' => TRUE,
           'where' => 'civicrm_survey.title',
           'headerPattern' => '',
           'dataPattern' => '',
-          'export' => true,
-        ) ,
-        'campaign_id' => array(
+          'export' => TRUE,
+          'table_name' => 'civicrm_survey',
+          'entity' => 'Survey',
+          'bao' => 'CRM_Campaign_BAO_Survey',
+          'localizable' => 1,
+        ],
+        'campaign_id' => [
           'name' => 'campaign_id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Survey Campaign ID') ,
-          'description' => 'Foreign key to the Campaign.',
+          'title' => ts('Survey Campaign ID'),
+          'description' => ts('Foreign key to the Campaign.'),
           'default' => 'NULL',
+          'table_name' => 'civicrm_survey',
+          'entity' => 'Survey',
+          'bao' => 'CRM_Campaign_BAO_Survey',
+          'localizable' => 0,
           'FKClassName' => 'CRM_Campaign_DAO_Campaign',
-          'pseudoconstant' => array(
+          'pseudoconstant' => [
             'table' => 'civicrm_campaign',
             'keyColumn' => 'id',
             'labelColumn' => 'title',
-          )
-        ) ,
-        'activity_type_id' => array(
+          ]
+        ],
+        'activity_type_id' => [
           'name' => 'activity_type_id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Activity Type') ,
-          'description' => 'Implicit FK to civicrm_option_value where option_group = activity_type',
-          'import' => true,
+          'title' => ts('Activity Type'),
+          'description' => ts('Implicit FK to civicrm_option_value where option_group = activity_type'),
+          'import' => TRUE,
           'where' => 'civicrm_survey.activity_type_id',
           'headerPattern' => '',
           'dataPattern' => '',
-          'export' => true,
+          'export' => TRUE,
           'default' => 'NULL',
-          'html' => array(
+          'table_name' => 'civicrm_survey',
+          'entity' => 'Survey',
+          'bao' => 'CRM_Campaign_BAO_Survey',
+          'localizable' => 0,
+          'html' => [
             'type' => 'Select',
-          ) ,
-          'pseudoconstant' => array(
+          ],
+          'pseudoconstant' => [
             'optionGroupName' => 'activity_type',
             'optionEditPath' => 'civicrm/admin/options/activity_type',
-          )
-        ) ,
-        'recontact_interval' => array(
+          ]
+        ],
+        'recontact_interval' => [
           'name' => 'recontact_interval',
           'type' => CRM_Utils_Type::T_TEXT,
-          'title' => ts('Follow up Interval') ,
-          'description' => 'Recontact intervals for each status.',
+          'title' => ts('Follow up Interval'),
+          'description' => ts('Recontact intervals for each status.'),
           'rows' => 20,
           'cols' => 80,
-          'html' => array(
+          'table_name' => 'civicrm_survey',
+          'entity' => 'Survey',
+          'bao' => 'CRM_Campaign_BAO_Survey',
+          'localizable' => 0,
+          'html' => [
             'type' => 'TextArea',
-          ) ,
-        ) ,
-        'instructions' => array(
+          ],
+        ],
+        'instructions' => [
           'name' => 'instructions',
           'type' => CRM_Utils_Type::T_TEXT,
-          'title' => ts('Instructions') ,
-          'description' => 'Script instructions for volunteers to use for the survey.',
+          'title' => ts('Instructions'),
+          'description' => ts('Script instructions for volunteers to use for the survey.'),
           'rows' => 20,
           'cols' => 80,
-          'html' => array(
+          'table_name' => 'civicrm_survey',
+          'entity' => 'Survey',
+          'bao' => 'CRM_Campaign_BAO_Survey',
+          'localizable' => 1,
+          'html' => [
             'type' => 'TextArea',
-          ) ,
-        ) ,
-        'release_frequency' => array(
+          ],
+        ],
+        'release_frequency' => [
           'name' => 'release_frequency',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Survey Hold Duration') ,
-          'description' => 'Number of days for recurrence of release.',
+          'title' => ts('Survey Hold Duration'),
+          'description' => ts('Number of days for recurrence of release.'),
           'default' => 'NULL',
-        ) ,
-        'max_number_of_contacts' => array(
+          'table_name' => 'civicrm_survey',
+          'entity' => 'Survey',
+          'bao' => 'CRM_Campaign_BAO_Survey',
+          'localizable' => 0,
+        ],
+        'max_number_of_contacts' => [
           'name' => 'max_number_of_contacts',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Maximum number of contacts') ,
-          'description' => 'Maximum number of contacts to allow for survey.',
+          'title' => ts('Maximum number of contacts'),
+          'description' => ts('Maximum number of contacts to allow for survey.'),
           'default' => 'NULL',
-        ) ,
-        'default_number_of_contacts' => array(
+          'table_name' => 'civicrm_survey',
+          'entity' => 'Survey',
+          'bao' => 'CRM_Campaign_BAO_Survey',
+          'localizable' => 0,
+        ],
+        'default_number_of_contacts' => [
           'name' => 'default_number_of_contacts',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Default number of contacts') ,
-          'description' => 'Default number of contacts to allow for survey.',
+          'title' => ts('Default number of contacts'),
+          'description' => ts('Default number of contacts to allow for survey.'),
           'default' => 'NULL',
-        ) ,
-        'is_active' => array(
+          'table_name' => 'civicrm_survey',
+          'entity' => 'Survey',
+          'bao' => 'CRM_Campaign_BAO_Survey',
+          'localizable' => 0,
+        ],
+        'is_active' => [
           'name' => 'is_active',
           'type' => CRM_Utils_Type::T_BOOLEAN,
-          'title' => ts('Survey Is Active') ,
-          'description' => 'Is this survey enabled or disabled/cancelled?',
+          'title' => ts('Survey Is Active'),
+          'description' => ts('Is this survey enabled or disabled/cancelled?'),
           'default' => '1',
-        ) ,
-        'is_default' => array(
+          'table_name' => 'civicrm_survey',
+          'entity' => 'Survey',
+          'bao' => 'CRM_Campaign_BAO_Survey',
+          'localizable' => 0,
+        ],
+        'is_default' => [
           'name' => 'is_default',
           'type' => CRM_Utils_Type::T_BOOLEAN,
-          'title' => ts('Is Default Survey') ,
-          'description' => 'Is this default survey?',
-        ) ,
-        'created_id' => array(
+          'title' => ts('Is Default Survey'),
+          'description' => ts('Is this default survey?'),
+          'default' => '0',
+          'table_name' => 'civicrm_survey',
+          'entity' => 'Survey',
+          'bao' => 'CRM_Campaign_BAO_Survey',
+          'localizable' => 0,
+        ],
+        'created_id' => [
           'name' => 'created_id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Survey Created By') ,
-          'description' => 'FK to civicrm_contact, who created this Survey.',
+          'title' => ts('Survey Created By'),
+          'description' => ts('FK to civicrm_contact, who created this Survey.'),
+          'table_name' => 'civicrm_survey',
+          'entity' => 'Survey',
+          'bao' => 'CRM_Campaign_BAO_Survey',
+          'localizable' => 0,
           'FKClassName' => 'CRM_Contact_DAO_Contact',
-        ) ,
-        'created_date' => array(
+        ],
+        'created_date' => [
           'name' => 'created_date',
           'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME,
-          'title' => ts('Campaign Created Date') ,
-          'description' => 'Date and time that Survey was created.',
-        ) ,
-        'last_modified_id' => array(
+          'title' => ts('Campaign Created Date'),
+          'description' => ts('Date and time that Survey was created.'),
+          'table_name' => 'civicrm_survey',
+          'entity' => 'Survey',
+          'bao' => 'CRM_Campaign_BAO_Survey',
+          'localizable' => 0,
+        ],
+        'last_modified_id' => [
           'name' => 'last_modified_id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Survey Modified') ,
-          'description' => 'FK to civicrm_contact, who recently edited this Survey.',
+          'title' => ts('Survey Modified'),
+          'description' => ts('FK to civicrm_contact, who recently edited this Survey.'),
+          'table_name' => 'civicrm_survey',
+          'entity' => 'Survey',
+          'bao' => 'CRM_Campaign_BAO_Survey',
+          'localizable' => 0,
           'FKClassName' => 'CRM_Contact_DAO_Contact',
-        ) ,
-        'last_modified_date' => array(
+        ],
+        'last_modified_date' => [
           'name' => 'last_modified_date',
           'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME,
-          'title' => ts('Survey Modified On') ,
-          'description' => 'Date and time that Survey was edited last time.',
-        ) ,
-        'result_id' => array(
+          'title' => ts('Survey Modified On'),
+          'description' => ts('Date and time that Survey was edited last time.'),
+          'table_name' => 'civicrm_survey',
+          'entity' => 'Survey',
+          'bao' => 'CRM_Campaign_BAO_Survey',
+          'localizable' => 0,
+        ],
+        'result_id' => [
           'name' => 'result_id',
           'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Survey Result') ,
-          'description' => 'Used to store option group id.',
+          'title' => ts('Survey Result'),
+          'description' => ts('Used to store option group id.'),
           'default' => 'NULL',
-        ) ,
-        'bypass_confirm' => array(
+          'table_name' => 'civicrm_survey',
+          'entity' => 'Survey',
+          'bao' => 'CRM_Campaign_BAO_Survey',
+          'localizable' => 0,
+        ],
+        'bypass_confirm' => [
           'name' => 'bypass_confirm',
           'type' => CRM_Utils_Type::T_BOOLEAN,
-          'title' => ts('No Email Verification') ,
-          'description' => 'Bypass the email verification.',
-        ) ,
-        'thankyou_title' => array(
+          'title' => ts('No Email Verification'),
+          'description' => ts('Bypass the email verification.'),
+          'default' => '0',
+          'table_name' => 'civicrm_survey',
+          'entity' => 'Survey',
+          'bao' => 'CRM_Campaign_BAO_Survey',
+          'localizable' => 0,
+        ],
+        'thankyou_title' => [
           'name' => 'thankyou_title',
           'type' => CRM_Utils_Type::T_STRING,
-          'title' => ts('Thank-you Title') ,
-          'description' => 'Title for Thank-you page (header title tag, and display at the top of the page).',
+          'title' => ts('Thank-you Title'),
+          'description' => ts('Title for Thank-you page (header title tag, and display at the top of the page).'),
           'maxlength' => 255,
           'size' => CRM_Utils_Type::HUGE,
-        ) ,
-        'thankyou_text' => array(
+          'table_name' => 'civicrm_survey',
+          'entity' => 'Survey',
+          'bao' => 'CRM_Campaign_BAO_Survey',
+          'localizable' => 1,
+        ],
+        'thankyou_text' => [
           'name' => 'thankyou_text',
           'type' => CRM_Utils_Type::T_TEXT,
-          'title' => ts('Thank-you Text') ,
-          'description' => 'text and html allowed. displayed above result on success page',
+          'title' => ts('Thank-you Text'),
+          'description' => ts('text and html allowed. displayed above result on success page'),
           'rows' => 8,
           'cols' => 60,
-          'html' => array(
+          'table_name' => 'civicrm_survey',
+          'entity' => 'Survey',
+          'bao' => 'CRM_Campaign_BAO_Survey',
+          'localizable' => 1,
+          'html' => [
             'type' => 'TextArea',
-          ) ,
-        ) ,
-        'is_share' => array(
+          ],
+        ],
+        'is_share' => [
           'name' => 'is_share',
           'type' => CRM_Utils_Type::T_BOOLEAN,
-          'title' => ts('Is shared through social media') ,
-          'description' => 'Can people share the petition through social media?',
+          'title' => ts('Is shared through social media'),
+          'description' => ts('Can people share the petition through social media?'),
           'default' => '1',
-        ) ,
-      );
+          'table_name' => 'civicrm_survey',
+          'entity' => 'Survey',
+          'bao' => 'CRM_Campaign_BAO_Survey',
+          'localizable' => 0,
+        ],
+      ];
       CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']);
     }
     return Civi::$statics[__CLASS__]['fields'];
   }
+
   /**
    * Return a mapping from field-name to the corresponding key (as used in fields()).
    *
    * @return array
    *   Array(string $name => string $uniqueName).
    */
-  static function &fieldKeys() {
+  public static function &fieldKeys() {
     if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) {
       Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields()));
     }
     return Civi::$statics[__CLASS__]['fieldKeys'];
   }
+
   /**
    * Returns the names of this table
    *
    * @return string
    */
-  static function getTableName() {
+  public static function getTableName() {
     return CRM_Core_DAO::getLocaleTableName(self::$_tableName);
   }
+
   /**
    * Returns if this table needs to be logged
    *
-   * @return boolean
+   * @return bool
    */
-  function getLog() {
+  public function getLog() {
     return self::$_log;
   }
+
   /**
    * Returns the list of fields that can be imported
    *
@@ -415,10 +501,11 @@ function getLog() {
    *
    * @return array
    */
-  static function &import($prefix = false) {
-    $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'survey', $prefix, array());
+  public static function &import($prefix = FALSE) {
+    $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'survey', $prefix, []);
     return $r;
   }
+
   /**
    * Returns the list of fields that can be exported
    *
@@ -426,8 +513,30 @@ static function &import($prefix = false) {
    *
    * @return array
    */
-  static function &export($prefix = false) {
-    $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'survey', $prefix, array());
+  public static function &export($prefix = FALSE) {
+    $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'survey', $prefix, []);
     return $r;
   }
+
+  /**
+   * Returns the list of indices
+   *
+   * @param bool $localize
+   *
+   * @return array
+   */
+  public static function indices($localize = TRUE) {
+    $indices = [
+      'UI_activity_type_id' => [
+        'name' => 'UI_activity_type_id',
+        'field' => [
+          0 => 'activity_type_id',
+        ],
+        'localizable' => FALSE,
+        'sig' => 'civicrm_survey::0::activity_type_id',
+      ],
+    ];
+    return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices;
+  }
+
 }
diff --git a/CRM/Campaign/Form/Campaign.php b/CRM/Campaign/Form/Campaign.php
index d8397275af11..06a3c6130bda 100644
--- a/CRM/Campaign/Form/Campaign.php
+++ b/CRM/Campaign/Form/Campaign.php
@@ -1,9 +1,9 @@
 _context = CRM_Utils_Request::retrieve('context', 'String', $this);
+    $this->_context = CRM_Utils_Request::retrieve('context', 'Alphanumeric', $this);
 
     $this->assign('context', $this->_context);
 
@@ -101,11 +101,11 @@ public function preProcess() {
     //load the values;
     $this->_values = $this->get('values');
     if (!is_array($this->_values)) {
-      $this->_values = array();
+      $this->_values = [];
 
       // if we are editing
       if (isset($this->_campaignId) && $this->_campaignId) {
-        $params = array('id' => $this->_campaignId);
+        $params = ['id' => $this->_campaignId];
         CRM_Campaign_BAO_Campaign::retrieve($params, $this->_values);
       }
 
@@ -136,18 +136,8 @@ public function preProcess() {
   public function setDefaultValues() {
     $defaults = $this->_values;
 
-    if (isset($defaults['start_date'])) {
-      list($defaults['start_date'], $defaults['start_date_time'])
-        = CRM_Utils_Date::setDateDefaults($defaults['start_date'], 'activityDateTime');
-    }
-    else {
-      list($defaults['start_date'], $defaults['start_date_time'])
-        = CRM_Utils_Date::setDateDefaults();
-    }
-
-    if (isset($defaults['end_date'])) {
-      list($defaults['end_date'], $defaults['end_date_time'])
-        = CRM_Utils_Date::setDateDefaults($defaults['end_date'], 'activityDateTime');
+    if (empty($defaults['start_date'])) {
+      $defaults['start_date'] = date('Y-m-d H:i:s');
     }
 
     if (!isset($defaults['is_active'])) {
@@ -160,7 +150,7 @@ public function setDefaultValues() {
 
     $dao = new CRM_Campaign_DAO_CampaignGroup();
 
-    $campaignGroups = array();
+    $campaignGroups = [];
     $dao->campaign_id = $this->_campaignId;
     $dao->find();
 
@@ -177,18 +167,17 @@ public function setDefaultValues() {
   public function buildQuickForm() {
     if ($this->_action & CRM_Core_Action::DELETE) {
 
-      $this->addButtons(array(
-          array(
-            'type' => 'next',
-            'name' => ts('Delete'),
-            'isDefault' => TRUE,
-          ),
-          array(
-            'type' => 'cancel',
-            'name' => ts('Cancel'),
-          ),
-        )
-      );
+      $this->addButtons([
+        [
+          'type' => 'next',
+          'name' => ts('Delete'),
+          'isDefault' => TRUE,
+        ],
+        [
+          'type' => 'cancel',
+          'name' => ts('Cancel'),
+        ],
+      ]);
       return;
     }
 
@@ -208,13 +197,13 @@ public function buildQuickForm() {
     $this->add('textarea', 'description', ts('Description'), $attributes['description']);
 
     // add campaign start date
-    $this->addDateTime('start_date', ts('Start Date'), TRUE, array('formatType' => 'activityDateTime'));
+    $this->add('datepicker', 'start_date', ts('Start Date'), [], TRUE);
 
     // add campaign end date
-    $this->addDateTime('end_date', ts('End Date'), FALSE, array('formatType' => 'activityDateTime'));
+    $this->add('datepicker', 'end_date', ts('End Date'));
 
     // add campaign type
-    $this->addSelect('campaign_type_id', array('onChange' => "CRM.buildCustomData( 'Campaign', this.value );"), TRUE);
+    $this->addSelect('campaign_type_id', ['onChange' => "CRM.buildCustomData( 'Campaign', this.value );"], TRUE);
 
     // add campaign status
     $this->addSelect('status_id');
@@ -228,8 +217,8 @@ public function buildQuickForm() {
     $campaigns = CRM_Campaign_BAO_Campaign::getCampaigns(CRM_Utils_Array::value('parent_id', $this->_values), $this->_campaignId);
     if (!empty($campaigns)) {
       $this->addElement('select', 'parent_id', ts('Parent ID'),
-        array('' => ts('- select Parent -')) + $campaigns,
-        array('class' => 'crm-select2')
+        ['' => ts('- select Parent -')] + $campaigns,
+        ['class' => 'crm-select2']
       );
     }
     $groups = CRM_Core_PseudoConstant::nestedGroup();
@@ -238,39 +227,43 @@ public function buildQuickForm() {
       ts('Include Group(s)'),
       $groups,
       FALSE,
-      array(
+      [
         'multiple' => TRUE,
         'class' => 'crm-select2 huge',
         'placeholder' => ts('- none -'),
-      )
+      ]
     );
 
-    $this->add('wysiwyg', 'goal_general', ts('Campaign Goals'), array('rows' => 2, 'cols' => 40));
-    $this->add('text', 'goal_revenue', ts('Revenue Goal'), array('size' => 8, 'maxlength' => 12));
+    $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).',
-      array(1 => CRM_Utils_Money::format('99.99', ' '))
+      [1 => CRM_Utils_Money::format('99.99', ' ')]
     ), 'money');
 
     // is this Campaign active
     $this->addElement('checkbox', 'is_active', ts('Is Active?'));
 
-    $this->addButtons(array(
-        array(
-          'type' => 'upload',
-          'name' => ts('Save'),
-          'isDefault' => TRUE,
-        ),
-        array(
-          'type' => 'upload',
-          'name' => ts('Save and New'),
-          'subName' => 'new',
-        ),
-        array(
-          'type' => 'cancel',
-          'name' => ts('Cancel'),
-        ),
-      )
-    );
+    $buttons = [
+      [
+        'type' => 'upload',
+        'name' => ts('Save'),
+        'isDefault' => TRUE,
+      ],
+    ];
+    // Skip this button when adding a new campaign from an entityRef
+    if (empty($_GET['snippet']) || empty($_GET['returnExtra'])) {
+      $buttons[] = [
+        'type' => 'upload',
+        'name' => ts('Save and New'),
+        'subName' => 'new',
+      ];
+    }
+    $buttons[] = [
+      'type' => 'cancel',
+      'name' => ts('Cancel'),
+    ];
+
+    $this->addButtons($buttons);
   }
 
   /**
@@ -285,7 +278,7 @@ public function buildQuickForm() {
    * @see valid_date
    */
   public static function formRule($fields, $files, $errors) {
-    $errors = array();
+    $errors = [];
 
     return empty($errors) ? TRUE : $errors;
   }
@@ -298,7 +291,7 @@ public function postProcess() {
     $params = $this->controller->exportValues($this->_name);
     $session = CRM_Core_Session::singleton();
 
-    $groups = array();
+    $groups = [];
     if (isset($this->_campaignId)) {
       if ($this->_action & CRM_Core_Action::DELETE) {
         CRM_Campaign_BAO_Campaign::del($this->_campaignId);
@@ -313,8 +306,6 @@ public function postProcess() {
       $params['created_date'] = date('YmdHis');
     }
     // format params
-    $params['start_date'] = CRM_Utils_Date::processDate($params['start_date'], $params['start_date_time']);
-    $params['end_date'] = CRM_Utils_Date::processDate($params['end_date'], $params['end_date_time'], TRUE);
     $params['is_active'] = CRM_Utils_Array::value('is_active', $params, FALSE);
     $params['last_modified_id'] = $session->get('userID');
     $params['last_modified_date'] = date('YmdHis');
@@ -350,8 +341,10 @@ public function postProcess() {
     $result = CRM_Campaign_BAO_Campaign::create($params);
 
     if ($result) {
-      CRM_Core_Session::setStatus(ts('Campaign %1 has been saved.', array(1 => $result->title)), ts('Saved'), 'success');
+      CRM_Core_Session::setStatus(ts('Campaign %1 has been saved.', [1 => $result->title]), ts('Saved'), 'success');
       $session->pushUserContext(CRM_Utils_System::url('civicrm/campaign', 'reset=1&subPage=campaign'));
+      $this->ajaxResponse['id'] = $result->id;
+      $this->ajaxResponse['label'] = $result->title;
     }
 
     $buttonName = $this->controller->getButtonName();
diff --git a/CRM/Campaign/Form/Gotv.php b/CRM/Campaign/Form/Gotv.php
index e6b4c8874b10..d260b93262ef 100644
--- a/CRM/Campaign/Form/Gotv.php
+++ b/CRM/Campaign/Form/Gotv.php
@@ -1,9 +1,9 @@
  ts('Survey(s)'), 'url' => $url)));
+      CRM_Utils_System::appendBreadCrumb([['title' => ts('Survey(s)'), 'url' => $url]]);
     }
 
     //set the form title.
@@ -103,7 +103,7 @@ public function buildQuickForm() {
     CRM_Campaign_BAO_Query::buildSearchForm($this);
 
     //build the array of all search params.
-    $this->_searchParams = array();
+    $this->_searchParams = [];
     foreach ($this->_elements as $element) {
       $name = $element->_attributes['name'];
       if ($name == 'qfKey') {
@@ -114,7 +114,7 @@ public function buildQuickForm() {
     $this->set('searchParams', $this->_searchParams);
     $this->assign('searchParams', json_encode($this->_searchParams));
 
-    $defaults = array();
+    $defaults = [];
 
     if (!$this->_surveyId) {
       $this->_surveyId = key(CRM_Campaign_BAO_Survey::getSurveys(TRUE, TRUE));
@@ -142,7 +142,7 @@ public function buildQuickForm() {
   }
 
   public function validateIds() {
-    $errorMessages = array();
+    $errorMessages = [];
     //check for required permissions.
     if (!CRM_Core_Permission::check('manage campaign') &&
       !CRM_Core_Permission::check('administer CiviCampaign') &&
@@ -153,7 +153,7 @@ public function validateIds() {
 
     $surveys = CRM_Campaign_BAO_Survey::getSurveys();
     if (empty($surveys)) {
-      $errorMessages[] = ts("Oops. It looks like no surveys have been created. Click here to create a new survey.", array(1 => CRM_Utils_System::url('civicrm/survey/add', 'reset=1&action=add')));
+      $errorMessages[] = ts("Oops. It looks like no surveys have been created. Click here to create a new survey.", [1 => CRM_Utils_System::url('civicrm/survey/add', 'reset=1&action=add')]);
     }
 
     if ($this->_force && !$this->_surveyId) {
diff --git a/CRM/Campaign/Form/Petition.php b/CRM/Campaign/Form/Petition.php
index ddb82cbec98a..f203c3d88fe9 100644
--- a/CRM/Campaign/Form/Petition.php
+++ b/CRM/Campaign/Form/Petition.php
@@ -1,9 +1,9 @@
 _surveyId;
+  }
+
   public function preProcess() {
     if (!CRM_Campaign_BAO_Campaign::accessCampaign()) {
       CRM_Utils_System::permissionDenied();
     }
 
-    $this->_context = CRM_Utils_Request::retrieve('context', 'String', $this);
+    $this->_context = CRM_Utils_Request::retrieve('context', 'Alphanumeric', $this);
 
     $this->assign('context', $this->_context);
 
@@ -64,14 +80,8 @@ public function preProcess() {
       }
     }
 
-    // when custom data is included in this page
-    if (!empty($_POST['hidden_custom'])) {
-      $this->set('type', 'Event');
-      $this->set('entityId', $this->_surveyId);
-      CRM_Custom_Form_CustomData::preProcess($this, NULL, NULL, 1, 'Survey', $this->_surveyId);
-      CRM_Custom_Form_CustomData::buildQuickForm($this);
-      CRM_Custom_Form_CustomData::setDefaultValues($this);
-    }
+    // Add custom data to form
+    CRM_Custom_Form_CustomData::addToForm($this);
 
     $session = CRM_Core_Session::singleton();
     $url = CRM_Utils_System::url('civicrm/campaign', 'reset=1&subPage=survey');
@@ -80,9 +90,9 @@ public function preProcess() {
     $this->_values = $this->get('values');
 
     if (!is_array($this->_values)) {
-      $this->_values = array();
+      $this->_values = [];
       if ($this->_surveyId) {
-        $params = array('id' => $this->_surveyId);
+        $params = ['id' => $this->_surveyId];
         CRM_Campaign_BAO_Survey::retrieve($params, $this->_values);
       }
       $this->set('values', $this->_values);
@@ -90,8 +100,6 @@ public function preProcess() {
 
     $this->assign('action', $this->_action);
     $this->assign('surveyId', $this->_surveyId);
-    // for custom data
-    $this->assign('entityID', $this->_surveyId);
 
     if ($this->_action & (CRM_Core_Action::UPDATE | CRM_Core_Action::DELETE)) {
       $this->_surveyId = CRM_Utils_Request::retrieve('id', 'Positive', $this, TRUE);
@@ -108,7 +116,7 @@ public function preProcess() {
     $url = CRM_Utils_System::url('civicrm/campaign', 'reset=1&subPage=petition');
     $session->pushUserContext($url);
 
-    CRM_Utils_System::appendBreadCrumb(array(array('title' => ts('Petition Dashboard'), 'url' => $url)));
+    CRM_Utils_System::appendBreadCrumb([['title' => ts('Petition Dashboard'), 'url' => $url]]);
   }
 
   /**
@@ -121,20 +129,20 @@ public function preProcess() {
   public function setDefaultValues() {
     $defaults = $this->_values;
 
-    $ufContactJoinParams = array(
+    $ufContactJoinParams = [
       'entity_table' => 'civicrm_survey',
       'entity_id' => $this->_surveyId,
       'weight' => 2,
-    );
+    ];
 
     if ($ufContactGroupId = CRM_Core_BAO_UFJoin::findUFGroupId($ufContactJoinParams)) {
       $defaults['contact_profile_id'] = $ufContactGroupId;
     }
-    $ufActivityJoinParams = array(
+    $ufActivityJoinParams = [
       'entity_table' => 'civicrm_survey',
       'entity_id' => $this->_surveyId,
       'weight' => 1,
-    );
+    ];
 
     if ($ufActivityGroupId = CRM_Core_BAO_UFJoin::findUFGroupId($ufActivityJoinParams)) {
       $defaults['profile_id'] = $ufActivityGroupId;
@@ -152,22 +160,21 @@ public function setDefaultValues() {
     return $defaults;
   }
 
-
   public function buildQuickForm() {
 
     if ($this->_action & CRM_Core_Action::DELETE) {
       $this->addButtons(
-        array(
-          array(
+        [
+          [
             'type' => 'next',
             'name' => ts('Delete'),
             'isDefault' => TRUE,
-          ),
-          array(
+          ],
+          [
             'type' => 'cancel',
             'name' => ts('Cancel'),
-          ),
-        )
+          ],
+        ]
       );
       return;
     }
@@ -176,30 +183,32 @@ public function buildQuickForm() {
 
     $attributes = CRM_Core_DAO::getAttribute('CRM_Campaign_DAO_Survey');
 
-    $petitionTypeID = CRM_Core_OptionGroup::getValue('activity_type', 'petition', 'name');
+    $petitionTypeID = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Petition');
     $this->addElement('hidden', 'activity_type_id', $petitionTypeID);
 
     // script / instructions / description of petition purpose
     $this->add('wysiwyg', 'instructions', ts('Introduction'), $attributes['instructions']);
 
-    // Campaign id
-    $campaigns = CRM_Campaign_BAO_Campaign::getCampaigns(CRM_Utils_Array::value('campaign_id', $this->_values));
-    $this->add('select', 'campaign_id', ts('Campaign'), array('' => ts('- select -')) + $campaigns);
+    $this->addEntityRef('campaign_id', ts('Campaign'), [
+      'entity' => 'Campaign',
+      'create' => TRUE,
+      'select' => ['minimumInputLength' => 0],
+    ]);
 
-    $customContactProfiles = CRM_Core_BAO_UFGroup::getProfiles(array('Individual'));
+    $customContactProfiles = CRM_Core_BAO_UFGroup::getProfiles(['Individual']);
     // custom group id
     $this->add('select', 'contact_profile_id', ts('Contact Profile'),
-      array(
+      [
         '' => ts('- select -'),
-      ) + $customContactProfiles, TRUE
+      ] + $customContactProfiles, TRUE
     );
 
-    $customProfiles = CRM_Core_BAO_UFGroup::getProfiles(array('Activity'));
+    $customProfiles = CRM_Core_BAO_UFGroup::getProfiles(['Activity']);
     // custom group id
     $this->add('select', 'profile_id', ts('Activity Profile'),
-      array(
+      [
         '' => ts('- select -'),
-      ) + $customProfiles
+      ] + $customProfiles
     );
 
     // thank you title and text (html allowed in text)
@@ -220,26 +229,26 @@ public function buildQuickForm() {
 
     // add buttons
     $this->addButtons(
-      array(
-        array(
+      [
+        [
           'type' => 'next',
           'name' => ts('Save'),
           'isDefault' => TRUE,
-        ),
-        array(
+        ],
+        [
           'type' => 'next',
           'name' => ts('Save and New'),
           'subName' => 'new',
-        ),
-        array(
+        ],
+        [
           'type' => 'cancel',
           'name' => ts('Cancel'),
-        ),
-      )
+        ],
+      ]
     );
 
     // add a form rule to check default value
-    $this->addFormRule(array('CRM_Campaign_Form_Petition', 'formRule'), $this);
+    $this->addFormRule(['CRM_Campaign_Form_Petition', 'formRule'], $this);
   }
 
   /**
@@ -250,14 +259,14 @@ public function buildQuickForm() {
    * @return array|bool
    */
   public static function formRule($fields, $files, $form) {
-    $errors = array();
+    $errors = [];
     // Petitions should be unique by: title, campaign ID (if assigned) and activity type ID
     // NOTE: This class is called for both Petition create / update AND for Survey Results tab, but this rule is only for Petition.
-    $where = array('activity_type_id = %1', 'title = %2');
-    $params = array(
-      1 => array($fields['activity_type_id'], 'Integer'),
-      2 => array($fields['title'], 'String'),
-    );
+    $where = ['activity_type_id = %1', 'title = %2'];
+    $params = [
+      1 => [$fields['activity_type_id'], 'Integer'],
+      2 => [$fields['title'], 'String'],
+    ];
     $uniqueRuleErrorMessage = ts('This title is already associated with the selected activity type. Please specify a unique title.');
 
     if (empty($fields['campaign_id'])) {
@@ -265,14 +274,14 @@ public static function formRule($fields, $files, $form) {
     }
     else {
       $where[] = 'campaign_id = %3';
-      $params[3] = array($fields['campaign_id'], 'Integer');
+      $params[3] = [$fields['campaign_id'], 'Integer'];
       $uniqueRuleErrorMessage = ts('This title is already associated with the selected campaign and activity type. Please specify a unique title.');
     }
 
     // Exclude current Petition row if UPDATE.
     if ($form->_surveyId) {
       $where[] = 'id != %4';
-      $params[4] = array($form->_surveyId, 'Integer');
+      $params[4] = [$form->_surveyId, 'Integer'];
     }
 
     $whereClause = implode(' AND ', $where);
@@ -290,7 +299,6 @@ public static function formRule($fields, $files, $form) {
     return empty($errors) ? TRUE : $errors;
   }
 
-
   public function postProcess() {
     // store the submitted values in an array
     $params = $this->controller->exportValues($this->_name);
@@ -321,21 +329,17 @@ public function postProcess() {
     $params['is_active'] = CRM_Utils_Array::value('is_active', $params, 0);
     $params['is_default'] = CRM_Utils_Array::value('is_default', $params, 0);
 
-    $customFields = CRM_Core_BAO_CustomField::getFields('Survey');
-    $params['custom'] = CRM_Core_BAO_CustomField::postProcess($params,
-      $this->_surveyId,
-      'Survey'
-    );
+    $params['custom'] = CRM_Core_BAO_CustomField::postProcess($params, $this->getEntityId(), $this->getDefaultEntity());
 
     $surveyId = CRM_Campaign_BAO_Survey::create($params);
 
     // also update the ProfileModule tables
-    $ufJoinParams = array(
+    $ufJoinParams = [
       'is_active' => 1,
       'module' => 'CiviCampaign',
       'entity_table' => 'civicrm_survey',
       'entity_id' => $surveyId->id,
-    );
+    ];
 
     // first delete all past entries
     if ($this->_surveyId) {
diff --git a/CRM/Campaign/Form/Petition/Signature.php b/CRM/Campaign/Form/Petition/Signature.php
index 19b5b453a537..f39166cebff2 100644
--- a/CRM/Campaign/Form/Petition/Signature.php
+++ b/CRM/Campaign/Form/Petition/Signature.php
@@ -1,9 +1,9 @@
 _surveyId;
-    $this->petition = array();
+    $this->petition = [];
     CRM_Campaign_BAO_Survey::retrieve($params, $this->petition);
     if (empty($this->petition)) {
       CRM_Core_Error::fatal('Petition doesn\'t exist.');
@@ -201,12 +201,12 @@ public function preProcess() {
 
     // add the custom contact and activity profile fields to the signature form
 
-    $ufJoinParams = array(
+    $ufJoinParams = [
       'entity_id' => $this->_surveyId,
       'entity_table' => 'civicrm_survey',
       'module' => 'CiviCampaign',
       'weight' => 2,
-    );
+    ];
 
     $this->_contactProfileId = CRM_Core_BAO_UFJoin::findUFGroupId($ufJoinParams);
     if ($this->_contactProfileId) {
@@ -231,7 +231,7 @@ public function preProcess() {
    * Set default values for the form.
    */
   public function setDefaultValues() {
-    $this->_defaults = array();
+    $this->_defaults = [];
     if ($this->_contactId) {
       CRM_Core_BAO_UFGroup::setProfileDefaults($this->_contactId, $this->_contactProfileFields, $this->_defaults, TRUE);
       if ($this->_activityProfileId) {
@@ -296,14 +296,13 @@ public function buildQuickForm() {
       $this->buildCustom($this->_activityProfileId, 'petitionActivityProfile');
     }
     // add buttons
-    $this->addButtons(array(
-        array(
-          'type' => 'upload',
-          'name' => ts('Sign the Petition'),
-          'isDefault' => TRUE,
-        ),
-      )
-    );
+    $this->addButtons([
+      [
+        'type' => 'upload',
+        'name' => ts('Sign the Petition'),
+        'isDefault' => TRUE,
+      ],
+    ]);
   }
 
   /**
@@ -318,7 +317,7 @@ public function buildQuickForm() {
    * @return array|bool
    */
   public static function formRule($fields, $files, $errors) {
-    $errors = array();
+    $errors = [];
 
     return empty($errors) ? TRUE : $errors;
   }
@@ -371,21 +370,11 @@ public function postProcess() {
       $ids[0] = $this->_contactId;
     }
     else {
-      // dupeCheck - check if contact record already exists
-      // code modified from api/v2/Contact.php-function civicrm_contact_check_params()
-      $params['contact_type'] = $this->_ctype;
-      //TODO - current dedupe finds soft deleted contacts - adding param is_deleted not working
-      // ignore soft deleted contacts
-      //$params['is_deleted'] = 0;
-      $dedupeParams = CRM_Dedupe_Finder::formatParams($params, $params['contact_type']);
-      $dedupeParams['check_permission'] = '';
-
-      //dupesByParams($params, $ctype, $level = 'Unsupervised', $except = array())
-      $ids = CRM_Dedupe_Finder::dupesByParams($dedupeParams, $params['contact_type']);
+      $ids = CRM_Contact_BAO_Contact::getDuplicateContacts($params, $this->_ctype, 'Unsupervised', [], FALSE);
     }
 
     $petition_params['id'] = $this->_surveyId;
-    $petition = array();
+    $petition = [];
     CRM_Campaign_BAO_Survey::retrieve($petition_params, $petition);
 
     switch (count($ids)) {
diff --git a/CRM/Campaign/Form/Search.php b/CRM/Campaign/Form/Search.php
index 8849404cffe3..ab7569d01ef9 100644
--- a/CRM/Campaign/Form/Search.php
+++ b/CRM/Campaign/Form/Search.php
@@ -1,9 +1,9 @@
 _printButtonName = $this->getButtonName('next', 'print');
     $this->_actionButtonName = $this->getButtonName('next', 'action');
 
-    //we allow the controller to set force/reset externally,
-    //useful when we are being driven by the wizard framework
-    $this->_limit = CRM_Utils_Request::retrieve('limit', 'Positive', $this);
-    $this->_force = CRM_Utils_Request::retrieve('force', 'Boolean', $this, FALSE);
-    $this->_context = CRM_Utils_Request::retrieve('context', 'String', $this, FALSE, 'search');
-    $this->_reset = CRM_Utils_Request::retrieve('reset', 'Boolean', CRM_Core_DAO::$_nullObject);
+    $this->loadStandardSearchOptionsFromUrl();
 
     //operation for state machine.
     $this->_operation = CRM_Utils_Request::retrieve('op', 'String', $this, FALSE, 'reserve');
@@ -175,8 +171,12 @@ public function preProcess() {
     CRM_Utils_System::setTitle(ts('Find Respondents To %1', array(1 => ucfirst($this->_operation))));
   }
 
+  /**
+   * Load the default survey for all actions.
+   *
+   * @return array
+   */
   public function setDefaultValues() {
-    //load the default survey for all actions.
     if (empty($this->_defaults)) {
       $defaultSurveyId = key(CRM_Campaign_BAO_Survey::getSurveys(TRUE, TRUE));
       if ($defaultSurveyId) {
@@ -201,14 +201,13 @@ public function buildQuickForm() {
         $this->addRowSelectors($rows);
       }
 
-      $permission = CRM_Core_Permission::getPermission();
-      $allTasks = CRM_Campaign_Task::permissionedTaskTitles($permission);
+      $allTasks = CRM_Campaign_Task::permissionedTaskTitles(CRM_Core_Permission::getPermission());
 
       //hack to serve right page to state machine.
       $taskMapping = array(
-        'interview' => 1,
-        'reserve' => 2,
-        'release' => 3,
+        'interview' => CRM_Campaign_Task::INTERVIEW,
+        'reserve' => CRM_Campaign_Task::RESERVE,
+        'release' => CRM_Campaign_Task::RELEASE,
       );
 
       $currentTaskValue = CRM_Utils_Array::value($this->_operation, $taskMapping);
@@ -341,11 +340,7 @@ public function formatParams() {
 
     //apply filter of survey contact type for search.
     $contactType = CRM_Campaign_BAO_Survey::getSurveyContactType(CRM_Utils_Array::value('campaign_survey_id', $this->_formValues));
-    if ($contactType && in_array($this->_operation, array(
-        'reserve',
-        'interview',
-      ))
-    ) {
+    if ($contactType && in_array($this->_operation, ['reserve', 'interview'])) {
       $this->_formValues['contact_type'][$contactType] = 1;
     }
 
@@ -393,14 +388,14 @@ public function fixFormValues() {
     // note that this means that GET over-rides POST :)
 
     //since we have qfKey, no need to manipulate set defaults.
-    $qfKey = CRM_Utils_Request::retrieve('qfKey', 'String', CRM_Core_DAO::$_nullObject);
+    $qfKey = CRM_Utils_Request::retrieve('qfKey', 'String');
 
     if (!$this->_force || CRM_Utils_Rule::qfKey($qfKey)) {
       return;
     }
 
     // get survey id
-    $surveyId = CRM_Utils_Request::retrieve('sid', 'Positive', CRM_Core_DAO::$_nullObject);
+    $surveyId = CRM_Utils_Request::retrieve('sid', 'Positive');
 
     if ($surveyId) {
       $surveyId = CRM_Utils_Type::escape($surveyId, 'Integer');
diff --git a/CRM/Campaign/Form/Search/Campaign.php b/CRM/Campaign/Form/Search/Campaign.php
index fb7a6cbb70de..6fc5d63ab848 100644
--- a/CRM/Campaign/Form/Search/Campaign.php
+++ b/CRM/Campaign/Form/Search/Campaign.php
@@ -1,9 +1,9 @@
 add('text', 'description', ts('Description'), $attributes['description']);
 
     //campaign start date.
-    $this->addDate('start_date', ts('From'), FALSE, array('formatType' => 'searchDate'));
+    $this->addDate('start_date', ts('From'), FALSE, ['formatType' => 'searchDate']);
 
     //campaign end date.
-    $this->addDate('end_date', ts('To'), FALSE, array('formatType' => 'searchDate'));
+    $this->addDate('end_date', ts('To'), FALSE, ['formatType' => 'searchDate']);
 
     //campaign type.
     $campaignTypes = CRM_Campaign_PseudoConstant::campaignType();
     $this->add('select', 'campaign_type_id', ts('Campaign Type'),
-      array(
+      [
         '' => ts('- select -'),
-      ) + $campaignTypes
+      ] + $campaignTypes
     );
 
     $this->set('campaignTypes', $campaignTypes);
@@ -98,23 +98,22 @@ public function buildQuickForm() {
     //campaign status
     $campaignStatus = CRM_Campaign_PseudoConstant::campaignStatus();
     $this->addElement('select', 'status_id', ts('Campaign Status'),
-      array(
+      [
         '' => ts('- select -'),
-      ) + $campaignStatus
+      ] + $campaignStatus
     );
     $this->set('campaignStatus', $campaignStatus);
     $this->assign('campaignStatus', json_encode($campaignStatus));
 
     //active campaigns
-    $this->addElement('select', 'is_active', ts('Is Active?'), array(
+    $this->addElement('select', 'is_active', ts('Is Active?'), [
       '' => ts('- select -'),
       '0' => ts('Yes'),
       '1' => ts('No'),
-        )
-    );
+    ]);
 
     //build the array of all search params.
-    $this->_searchParams = array();
+    $this->_searchParams = [];
     foreach ($this->_elements as $element) {
       $name = $element->_attributes['name'];
       $label = $element->_label;
diff --git a/CRM/Campaign/Form/Search/Petition.php b/CRM/Campaign/Form/Search/Petition.php
index a73236344d2c..d23e9e88295c 100644
--- a/CRM/Campaign/Form/Search/Petition.php
+++ b/CRM/Campaign/Form/Search/Petition.php
@@ -1,9 +1,9 @@
 add('select', 'petition_campaign_id', ts('Campaign'), array('' => ts('- select -')) + $campaigns);
+    $this->add('select', 'petition_campaign_id', ts('Campaign'), ['' => ts('- select -')] + $campaigns);
     $this->set('petitionCampaigns', $campaigns);
     $this->assign('petitionCampaigns', json_encode($campaigns));
 
     //build the array of all search params.
-    $this->_searchParams = array();
+    $this->_searchParams = [];
     foreach ($this->_elements as $element) {
       $name = $element->_attributes['name'];
       $label = $element->_label;
diff --git a/CRM/Campaign/Form/Search/Survey.php b/CRM/Campaign/Form/Search/Survey.php
index b9d4750838d5..3cf35bf2ee5f 100644
--- a/CRM/Campaign/Form/Search/Survey.php
+++ b/CRM/Campaign/Form/Search/Survey.php
@@ -1,9 +1,9 @@
 add('select', 'activity_type_id',
-      ts('Activity Type'), array(
+      ts('Activity Type'), [
         '' => ts('- select -'),
-      ) + $surveyTypes
+      ] + $surveyTypes
     );
     $this->set('surveyTypes', $surveyTypes);
     $this->assign('surveyTypes', json_encode($surveyTypes));
 
     //campaigns
     $campaigns = CRM_Campaign_BAO_Campaign::getCampaigns(NULL, NULL, FALSE, FALSE, FALSE, TRUE);
-    $this->add('select', 'survey_campaign_id', ts('Campaign'), array('' => ts('- select -')) + $campaigns);
+    $this->add('select', 'survey_campaign_id', ts('Campaign'), ['' => ts('- select -')] + $campaigns);
     $this->set('surveyCampaigns', $campaigns);
     $this->assign('surveyCampaigns', json_encode($campaigns));
 
     //build the array of all search params.
-    $this->_searchParams = array();
+    $this->_searchParams = [];
     foreach ($this->_elements as $element) {
       $name = $element->_attributes['name'];
       $label = $element->_label;
diff --git a/CRM/Campaign/Form/Survey.php b/CRM/Campaign/Form/Survey.php
index c8a3006611c7..05eec10ceee6 100644
--- a/CRM/Campaign/Form/Survey.php
+++ b/CRM/Campaign/Form/Survey.php
@@ -1,9 +1,9 @@
 _surveyId;
+  }
+
   public function preProcess() {
     if (!CRM_Campaign_BAO_Campaign::accessCampaign()) {
       CRM_Utils_System::permissionDenied();
@@ -68,29 +84,23 @@ public function preProcess() {
     if ($this->_surveyId) {
       $this->_single = TRUE;
 
-      $params = array('id' => $this->_surveyId);
+      $params = ['id' => $this->_surveyId];
       CRM_Campaign_BAO_Survey::retrieve($params, $surveyInfo);
       $this->_surveyTitle = $surveyInfo['title'];
       $this->assign('surveyTitle', $this->_surveyTitle);
-      CRM_Utils_System::setTitle(ts('Configure Survey - %1', array(1 => $this->_surveyTitle)));
+      CRM_Utils_System::setTitle(ts('Configure Survey - %1', [1 => $this->_surveyTitle]));
     }
 
     $this->assign('action', $this->_action);
     $this->assign('surveyId', $this->_surveyId);
 
-    // when custom data is included in this page
-    if (!empty($_POST['hidden_custom'])) {
-      $this->set('type', 'Survey');
-      $this->set('entityId', $this->_surveyId);
-      CRM_Custom_Form_CustomData::preProcess($this, NULL, NULL, 1, 'Survey', $this->_surveyId);
-      CRM_Custom_Form_CustomData::buildQuickForm($this);
-      CRM_Custom_Form_CustomData::setDefaultValues($this);
-    }
+    // Add custom data to form
+    CRM_Custom_Form_CustomData::addToForm($this);
 
     // CRM-11480, CRM-11682
     // Preload libraries required by the "Questions" tab
     CRM_UF_Page_ProfileEditor::registerProfileScripts();
-    CRM_UF_Page_ProfileEditor::registerSchemas(array('IndividualModel', 'ActivityModel'));
+    CRM_UF_Page_ProfileEditor::registerSchemas(['IndividualModel', 'ActivityModel']);
 
     CRM_Campaign_Form_Survey_TabHeader::build($this);
   }
@@ -101,39 +111,39 @@ public function preProcess() {
   public function buildQuickForm() {
     $session = CRM_Core_Session::singleton();
     if ($this->_surveyId) {
-      $buttons = array(
-        array(
+      $buttons = [
+        [
           'type' => 'upload',
           'name' => ts('Save'),
           'isDefault' => TRUE,
-        ),
-        array(
+        ],
+        [
           'type' => 'upload',
           'name' => ts('Save and Done'),
           'subName' => 'done',
-        ),
-        array(
+        ],
+        [
           'type' => 'upload',
           'name' => ts('Save and Next'),
           'spacing' => '                 ',
           'subName' => 'next',
-        ),
-      );
+        ],
+      ];
     }
     else {
-      $buttons = array(
-        array(
+      $buttons = [
+        [
           'type' => 'upload',
           'name' => ts('Continue'),
           'spacing' => '                 ',
           'isDefault' => TRUE,
-        ),
-      );
+        ],
+      ];
     }
-    $buttons[] = array(
+    $buttons[] = [
       'type' => 'cancel',
       'name' => ts('Cancel'),
-    );
+    ];
     $this->addButtons($buttons);
 
     $url = CRM_Utils_System::url('civicrm/campaign', 'reset=1&subPage=survey');
@@ -148,7 +158,7 @@ public function endPostProcess() {
         $tabTitle = 'Main settings';
       }
       $subPage = strtolower($className);
-      CRM_Core_Session::setStatus(ts("'%1' have been saved.", array(1 => $tabTitle)), ts('Saved'), 'success');
+      CRM_Core_Session::setStatus(ts("'%1' have been saved.", [1 => $tabTitle]), ts('Saved'), 'success');
 
       $this->postProcessHook();
 
diff --git a/CRM/Campaign/Form/Survey/Delete.php b/CRM/Campaign/Form/Survey/Delete.php
index 42188eaf0cfe..0567a598e4f5 100644
--- a/CRM/Campaign/Form/Survey/Delete.php
+++ b/CRM/Campaign/Form/Survey/Delete.php
@@ -1,9 +1,9 @@
 _surveyId = CRM_Utils_Request::retrieve('id', 'Positive', $this, FALSE);
-    $params = array('id' => $this->_surveyId);
+    $params = ['id' => $this->_surveyId];
     CRM_Campaign_BAO_Survey::retrieve($params, $surveyInfo);
     $this->_surveyTitle = $surveyInfo['title'];
     $this->assign('surveyTitle', $this->_surveyTitle);
@@ -71,18 +70,17 @@ public function preProcess() {
    * Build the form object.
    */
   public function buildQuickForm() {
-    $this->addButtons(array(
-        array(
-          'type' => 'next',
-          'name' => ts('Delete'),
-          'isDefault' => TRUE,
-        ),
-        array(
-          'type' => 'cancel',
-          'name' => ts('Cancel'),
-        ),
-      )
-    );
+    $this->addButtons([
+      [
+        'type' => 'next',
+        'name' => ts('Delete'),
+        'isDefault' => TRUE,
+      ],
+      [
+        'type' => 'cancel',
+        'name' => ts('Cancel'),
+      ],
+    ]);
   }
 
   /**
@@ -91,7 +89,7 @@ public function buildQuickForm() {
   public function postProcess() {
     if ($this->_surveyId) {
       CRM_Campaign_BAO_Survey::del($this->_surveyId);
-      CRM_Core_Session::setStatus('', ts("'%1' survey has been deleted.", array(1 => $this->_surveyTitle)), 'success');
+      CRM_Core_Session::setStatus('', ts("'%1' survey has been deleted.", [1 => $this->_surveyTitle]), 'success');
       CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/campaign', 'reset=1&subPage=survey'));
     }
     else {
diff --git a/CRM/Campaign/Form/Survey/Main.php b/CRM/Campaign/Form/Survey/Main.php
index 42b719306ab5..68f074ec19d2 100644
--- a/CRM/Campaign/Form/Survey/Main.php
+++ b/CRM/Campaign/Form/Survey/Main.php
@@ -1,9 +1,9 @@
 _context = CRM_Utils_Request::retrieve('context', 'String', $this);
+    $this->_context = CRM_Utils_Request::retrieve('context', 'Alphanumeric', $this);
 
     $this->assign('context', $this->_context);
 
@@ -70,22 +66,19 @@ public function preProcess() {
       CRM_Utils_System::setTitle(ts('Configure Survey') . ' - ' . $this->_surveyTitle);
     }
 
-    // when custom data is included in this page
-    if (!empty($_POST['hidden_custom'])) {
-      CRM_Custom_Form_CustomData::preProcess($this);
-      CRM_Custom_Form_CustomData::buildQuickForm($this);
-    }
+    // Add custom data to form
+    CRM_Custom_Form_CustomData::addToForm($this);
 
     if ($this->_name != 'Petition') {
       $url = CRM_Utils_System::url('civicrm/campaign', 'reset=1&subPage=survey');
-      CRM_Utils_System::appendBreadCrumb(array(array('title' => ts('Survey Dashboard'), 'url' => $url)));
+      CRM_Utils_System::appendBreadCrumb([['title' => ts('Survey Dashboard'), 'url' => $url]]);
     }
 
     $this->_values = $this->get('values');
     if (!is_array($this->_values)) {
-      $this->_values = array();
+      $this->_values = [];
       if ($this->_surveyId) {
-        $params = array('id' => $this->_surveyId);
+        $params = ['id' => $this->_surveyId];
         CRM_Campaign_BAO_Survey::retrieve($params, $this->_values);
       }
       $this->set('values', $this->_values);
@@ -93,8 +86,6 @@ public function preProcess() {
 
     $this->assign('action', $this->_action);
     $this->assign('surveyId', $this->_surveyId);
-    // for custom data
-    $this->assign('entityID', $this->_surveyId);
   }
 
   /**
@@ -136,31 +127,31 @@ public function setDefaultValues() {
    * Build the form object.
    */
   public function buildQuickForm() {
-
     $this->add('text', 'title', ts('Title'), CRM_Core_DAO::getAttribute('CRM_Campaign_DAO_Survey', 'title'), TRUE);
 
-    $surveyActivityTypes = CRM_Campaign_BAO_Survey::getSurveyActivityType();
     // Activity Type id
-    $this->addSelect('activity_type_id', array('option_url' => 'civicrm/admin/campaign/surveyType'), TRUE);
+    $this->addSelect('activity_type_id', ['option_url' => 'civicrm/admin/campaign/surveyType'], TRUE);
 
-    // Campaign id
-    $campaigns = CRM_Campaign_BAO_Campaign::getCampaigns(CRM_Utils_Array::value('campaign_id', $this->_values));
-    $this->add('select', 'campaign_id', ts('Campaign'), array('' => ts('- select -')) + $campaigns);
+    $this->addEntityRef('campaign_id', ts('Campaign'), [
+      'entity' => 'Campaign',
+      'create' => TRUE,
+      'select' => ['minimumInputLength' => 0],
+    ]);
 
     // script / instructions
-    $this->add('wysiwyg', 'instructions', ts('Instructions for interviewers'), array('rows' => 5, 'cols' => 40));
+    $this->add('wysiwyg', 'instructions', ts('Instructions for interviewers'), ['rows' => 5, 'cols' => 40]);
 
     // release frequency
-    $this->add('text', 'release_frequency', ts('Release Frequency'), CRM_Core_DAO::getAttribute('CRM_Campaign_DAO_Survey', 'release_frequency'));
+    $this->add('number', 'release_frequency', ts('Release Frequency'), CRM_Core_DAO::getAttribute('CRM_Campaign_DAO_Survey', 'release_frequency'));
 
     $this->addRule('release_frequency', ts('Release Frequency interval should be a positive number.'), 'positiveInteger');
 
     // max reserved contacts at a time
-    $this->add('text', 'default_number_of_contacts', ts('Maximum reserved at one time'), CRM_Core_DAO::getAttribute('CRM_Campaign_DAO_Survey', 'default_number_of_contacts'));
+    $this->add('number', 'default_number_of_contacts', ts('Maximum reserved at one time'), CRM_Core_DAO::getAttribute('CRM_Campaign_DAO_Survey', 'default_number_of_contacts'));
     $this->addRule('default_number_of_contacts', ts('Maximum reserved at one time should be a positive number'), 'positiveInteger');
 
     // total reserved per interviewer
-    $this->add('text', 'max_number_of_contacts', ts('Total reserved per interviewer'), CRM_Core_DAO::getAttribute('CRM_Campaign_DAO_Survey', 'max_number_of_contacts'));
+    $this->add('number', 'max_number_of_contacts', ts('Total reserved per interviewer'), CRM_Core_DAO::getAttribute('CRM_Campaign_DAO_Survey', 'max_number_of_contacts'));
     $this->addRule('max_number_of_contacts', ts('Total reserved contacts should be a positive number'), 'positiveInteger');
 
     // is active ?
@@ -195,22 +186,20 @@ public function postProcess() {
     $params['is_active'] = CRM_Utils_Array::value('is_active', $params, 0);
     $params['is_default'] = CRM_Utils_Array::value('is_default', $params, 0);
 
-    $params['custom'] = CRM_Core_BAO_CustomField::postProcess($params,
-      $this->_surveyId,
-      'Survey'
-    );
+    $params['custom'] = CRM_Core_BAO_CustomField::postProcess($params, $this->getEntityId(), $this->getDefaultEntity());
+
     $survey = CRM_Campaign_BAO_Survey::create($params);
     $this->_surveyId = $survey->id;
 
     if (!empty($this->_values['result_id'])) {
       $query = "SELECT COUNT(*) FROM civicrm_survey WHERE result_id = %1";
       $countSurvey = (int) CRM_Core_DAO::singleValueQuery($query,
-        array(
-          1 => array(
+        [
+          1 => [
             $this->_values['result_id'],
             'Positive',
-          ),
-        )
+          ],
+        ]
       );
       // delete option group if no any survey is using it.
       if (!$countSurvey) {
diff --git a/CRM/Campaign/Form/Survey/Questions.php b/CRM/Campaign/Form/Survey/Questions.php
index a728877cf5f1..4a20450668a4 100644
--- a/CRM/Campaign/Form/Survey/Questions.php
+++ b/CRM/Campaign/Form/Survey/Questions.php
@@ -1,9 +1,9 @@
  'civicrm_survey',
       'module' => 'CiviCampaign',
       'entity_id' => $this->_surveyId,
-    );
+    ];
 
     list($defaults['contact_profile_id'], $second)
       = CRM_Core_BAO_UFJoin::getUFGroupIds($ufJoinParams);
@@ -66,28 +66,29 @@ public function setDefaultValues() {
   public function buildQuickForm() {
     $subTypeId = CRM_Core_DAO::getFieldValue('CRM_Campaign_DAO_Survey', $this->_surveyId, 'activity_type_id');
     if (!CRM_Core_BAO_CustomGroup::autoCreateByActivityType($subTypeId)) {
-      $activityTypes = CRM_Core_PseudoConstant::activityType(TRUE, TRUE, FALSE, 'label', TRUE, FALSE); // everything
+      // everything
+      $activityTypes = CRM_Core_PseudoConstant::activityType(TRUE, TRUE, FALSE, 'label', TRUE, FALSE);
       // FIXME: Displays weird "/\ Array" message; doesn't work with tabs
       CRM_Core_Session::setStatus(
         ts(
           'There are no custom data sets for activity type "%1". To create one, click here.',
-          array(
+          [
             1 => $activityTypes[$subTypeId],
             2 => CRM_Utils_System::url('civicrm/admin/custom/group', 'action=add&reset=1'),
             3 => '_blank',
-          )
+          ]
         )
       );
     }
 
     $allowCoreTypes = CRM_Campaign_BAO_Survey::surveyProfileTypes();
-    $allowSubTypes = array(
-      'ActivityType' => array($subTypeId),
-    );
-    $entities = array(
-      array('entity_name' => 'contact_1', 'entity_type' => 'IndividualModel'),
-      array('entity_name' => 'activity_1', 'entity_type' => 'ActivityModel', 'entity_sub_type' => $subTypeId),
-    );
+    $allowSubTypes = [
+      'ActivityType' => [$subTypeId],
+    ];
+    $entities = [
+      ['entity_name' => 'contact_1', 'entity_type' => 'IndividualModel'],
+      ['entity_name' => 'activity_1', 'entity_type' => 'ActivityModel', 'entity_sub_type' => $subTypeId],
+    ];
     $this->addProfileSelector('contact_profile_id', ts('Contact Info'), $allowCoreTypes, $allowSubTypes, $entities);
     $this->addProfileSelector('activity_profile_id', ts('Questions'), $allowCoreTypes, $allowSubTypes, $entities);
     // Note: Because this is in a tab, we also preload the schema via CRM_Campaign_Form_Survey::preProcess
@@ -95,7 +96,6 @@ public function buildQuickForm() {
     parent::buildQuickForm();
   }
 
-
   /**
    * Process the form.
    */
@@ -104,17 +104,17 @@ public function postProcess() {
     $params = $this->controller->exportValues($this->_name);
 
     // also update the ProfileModule tables
-    $ufJoinParams = array(
+    $ufJoinParams = [
       'is_active' => 1,
       'module' => 'CiviCampaign',
       'entity_table' => 'civicrm_survey',
       'entity_id' => $this->_surveyId,
-    );
+    ];
 
     // first delete all past entries
     CRM_Core_BAO_UFJoin::deleteAll($ufJoinParams);
 
-    $uf = array();
+    $uf = [];
     $wt = 2;
     if (!empty($params['contact_profile_id'])) {
       $uf[1] = $params['contact_profile_id'];
diff --git a/CRM/Campaign/Form/Survey/Results.php b/CRM/Campaign/Form/Survey/Results.php
index 97cc04bbee90..56560418d1f3 100644
--- a/CRM/Campaign/Form/Survey/Results.php
+++ b/CRM/Campaign/Form/Survey/Results.php
@@ -1,9 +1,9 @@
 _values = $this->get('values');
     if (!is_array($this->_values)) {
-      $this->_values = array();
+      $this->_values = [];
       if ($this->_surveyId) {
-        $params = array('id' => $this->_surveyId);
+        $params = ['id' => $this->_surveyId];
         CRM_Campaign_BAO_Survey::retrieve($params, $this->_values);
       }
       $this->set('values', $this->_values);
     }
 
     $query = "SELECT MAX(id) as id, title FROM civicrm_report_instance WHERE name = %1 GROUP BY id";
-    $params = array(1 => array("survey_{$this->_surveyId}", 'String'));
+    $params = [1 => ["survey_{$this->_surveyId}", 'String']];
     $result = CRM_Core_DAO::executeQuery($query, $params);
     if ($result->fetch()) {
       $this->_reportId = $result->id;
@@ -100,43 +103,43 @@ public function buildQuickForm() {
     $optionGroups = CRM_Campaign_BAO_Survey::getResultSets();
 
     if (empty($optionGroups)) {
-      $optionTypes = array('1' => ts('Create new result set'));
+      $optionTypes = ['1' => ts('Create new result set')];
     }
     else {
-      $optionTypes = array(
+      $optionTypes = [
         '1' => ts('Create new result set'),
         '2' => ts('Use existing result set'),
-      );
+      ];
       $this->add('select',
         'option_group_id',
         ts('Select Result Set'),
-        array(
+        [
           '' => ts('- select -'),
-        ) + $optionGroups, FALSE,
-        array('onChange' => 'loadOptionGroup( )')
+        ] + $optionGroups, FALSE,
+        ['onChange' => 'loadOptionGroup( )']
       );
     }
 
     $element = &$this->addRadio('option_type',
       ts('Survey Responses'),
       $optionTypes,
-      array(
+      [
         'onclick' => "showOptionSelect();",
-      ), '
', TRUE + ], '
', TRUE ); if (empty($optionGroups) || empty($this->_values['result_id'])) { - $this->setdefaults(array('option_type' => 1)); + $this->setdefaults(['option_type' => 1]); } elseif (!empty($this->_values['result_id'])) { - $this->setdefaults(array( + $this->setdefaults([ 'option_type' => 2, 'option_group_id' => $this->_values['result_id'], - )); + ]); } // form fields of Custom Option rows - $defaultOption = array(); + $defaultOption = []; $_showHide = new CRM_Core_ShowHideBlocks('', ''); $optionAttributes = CRM_Core_DAO::getAttribute('CRM_Core_DAO_OptionValue'); @@ -165,7 +168,7 @@ public function buildQuickForm() { ); // weight - $this->add('text', "option_weight[$i]", ts('Order'), + $this->add('number', "option_weight[$i]", ts('Order'), $optionAttributes['weight'] ); @@ -190,10 +193,10 @@ public function buildQuickForm() { $this->freeze('report_title'); } - $this->addFormRule(array( + $this->addFormRule([ 'CRM_Campaign_Form_Survey_Results', 'formRule', - ), $this); + ], $this); parent::buildQuickForm(); } @@ -208,7 +211,7 @@ public function buildQuickForm() { * @return array|bool */ public static function formRule($fields, $files, $form) { - $errors = array(); + $errors = []; if (!empty($fields['option_label']) && !empty($fields['option_value']) && (count(array_filter($fields['option_label'])) == 0) && (count(array_filter($fields['option_value'])) == 0) @@ -357,7 +360,7 @@ public function postProcess() { $resultSetOptGrpId = $params['option_group_id']; } - $recontactInterval = array(); + $recontactInterval = []; if ($updateResultSet) { $optionValue = new CRM_Core_DAO_OptionValue(); $optionValue->option_group_id = $resultSetOptGrpId; @@ -409,23 +412,24 @@ public function postProcess() { if (!$this->_reportId && $survey->id && !empty($params['create_report'])) { $activityStatus = CRM_Core_PseudoConstant::activityStatus('name'); $activityStatus = array_flip($activityStatus); - $this->_params = array( + $this->_params = [ 'name' => "survey_{$survey->id}", 'title' => $params['report_title'] ? $params['report_title'] : $this->_values['title'], 'status_id_op' => 'eq', - 'status_id_value' => $activityStatus['Scheduled'], // reserved status - 'survey_id_value' => array($survey->id), + // reserved status + 'status_id_value' => $activityStatus['Scheduled'], + 'survey_id_value' => [$survey->id], 'description' => ts('Detailed report for canvassing, phone-banking, walk lists or other surveys.'), - ); + ]; //Default value of order by - $this->_params['order_bys'] = array( - 1 => array( + $this->_params['order_bys'] = [ + 1 => [ 'column' => 'sort_name', 'order' => 'ASC', - ), - ); + ], + ]; // for WalkList or default - $displayFields = array( + $displayFields = [ 'id', 'sort_name', 'result', @@ -433,37 +437,37 @@ public function postProcess() { 'street_name', 'street_unit', 'survey_response', - ); - if (CRM_Core_OptionGroup::getValue('activity_type', 'WalkList') == + ]; + if (CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'WalkList') == $this->_values['activity_type_id'] ) { - $this->_params['order_bys'] = array( - 1 => array( + $this->_params['order_bys'] = [ + 1 => [ 'column' => 'street_name', 'order' => 'ASC', - ), - 2 => array( + ], + 2 => [ 'column' => 'street_number_odd_even', 'order' => 'ASC', - ), - 3 => array( + ], + 3 => [ 'column' => 'street_number', 'order' => 'ASC', - ), - 4 => array( + ], + 4 => [ 'column' => 'sort_name', 'order' => 'ASC', - ), - ); + ], + ]; } - elseif (CRM_Core_OptionGroup::getValue('activity_type', 'PhoneBank') == + elseif (CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'PhoneBank') == $this->_values['activity_type_id'] ) { array_push($displayFields, 'phone'); } - elseif ((CRM_Core_OptionGroup::getValue('activity_type', 'Survey') == + elseif ((CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Survey') == $this->_values['activity_type_id']) || - (CRM_Core_OptionGroup::getValue('activity_type', 'Canvass') == + (CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Canvass') == $this->_values['activity_type_id']) ) { array_push($displayFields, 'phone', 'city', 'state_province_id', 'postal_code', 'email'); @@ -476,16 +480,16 @@ public function postProcess() { CRM_Report_Form_Instance::postProcess($this, FALSE); $query = "SELECT MAX(id) FROM civicrm_report_instance WHERE name = %1"; - $reportID = CRM_Core_DAO::singleValueQuery($query, array( - 1 => array( + $reportID = CRM_Core_DAO::singleValueQuery($query, [ + 1 => [ "survey_{$survey->id}", 'String', - ), - )); + ], + ]); if ($reportID) { $url = CRM_Utils_System::url("civicrm/report/instance/{$reportID}", 'reset=1'); $status = ts("A Survey Detail Report %2 has been created.", - array(1 => $url, 2 => $this->_params['title'])); + [1 => $url, 2 => $this->_params['title']]); } } diff --git a/CRM/Campaign/Form/Survey/TabHeader.php b/CRM/Campaign/Form/Survey/TabHeader.php index 06434dabb699..3aecece43a03 100644 --- a/CRM/Campaign/Form/Survey/TabHeader.php +++ b/CRM/Campaign/Form/Survey/TabHeader.php @@ -1,9 +1,9 @@ assign_by_ref('tabHeader', $tabs); CRM_Core_Resources::singleton() ->addScriptFile('civicrm', 'templates/CRM/common/TabHeader.js', 1, 'html-header') - ->addSetting(array( - 'tabSettings' => array( + ->addSetting([ + 'tabSettings' => [ 'active' => self::getCurrentTab($tabs), - ), - )); + ], + ]); return $tabs; } @@ -70,29 +70,29 @@ public static function process(&$form) { return NULL; } - $tabs = array( - 'main' => array( + $tabs = [ + 'main' => [ 'title' => ts('Main Information'), 'link' => NULL, 'valid' => FALSE, 'active' => FALSE, 'current' => FALSE, - ), - 'questions' => array( + ], + 'questions' => [ 'title' => ts('Questions'), 'link' => NULL, 'valid' => FALSE, 'active' => FALSE, 'current' => FALSE, - ), - 'results' => array( + ], + 'results' => [ 'title' => ts('Results'), 'link' => NULL, 'valid' => FALSE, 'active' => FALSE, 'current' => FALSE, - ), - ); + ], + ]; $surveyID = $form->getVar('_surveyId'); $class = $form->getVar('_name'); diff --git a/CRM/Campaign/Form/SurveyType.php b/CRM/Campaign/Form/SurveyType.php index b009b073a501..f7a38ad6c01e 100644 --- a/CRM/Campaign/Form/SurveyType.php +++ b/CRM/Campaign/Form/SurveyType.php @@ -1,9 +1,9 @@ $this->_gid); + $fieldValues = ['option_group_id' => $this->_gid]; $defaults['weight'] = CRM_Utils_Weight::getDefaultWeight('CRM_Core_DAO_OptionValue', $fieldValues); } @@ -127,9 +127,9 @@ public function buildQuickForm() { if ($this->_action == CRM_Core_Action::UPDATE && CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionGroup', $this->_id, 'is_reserved') ) { - $this->freeze(array('label', 'is_active')); + $this->freeze(['label', 'is_active']); } - $this->add('text', 'weight', ts('Order'), CRM_Core_DAO::getAttribute('CRM_Core_DAO_OptionValue', 'weight'), TRUE); + $this->add('number', 'weight', ts('Order'), CRM_Core_DAO::getAttribute('CRM_Core_DAO_OptionValue', 'weight'), TRUE); $this->assign('id', $this->_id); } @@ -140,7 +140,7 @@ public function buildQuickForm() { public function postProcess() { if ($this->_action & CRM_Core_Action::DELETE) { - $fieldValues = array('option_group_id' => $this->_gid); + $fieldValues = ['option_group_id' => $this->_gid]; $wt = CRM_Utils_Weight::delWeight('CRM_Core_DAO_OptionValue', $this->_id, $fieldValues); if (CRM_Core_BAO_OptionValue::del($this->_id)) { @@ -148,7 +148,7 @@ public function postProcess() { } } else { - $params = $ids = array(); + $params = $ids = []; $params = $this->exportValues(); // set db value of filter in params if filter is non editable @@ -156,11 +156,10 @@ public function postProcess() { $params['filter'] = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionValue', $this->_id, 'filter', 'id'); } - $groupParams = array('name' => ($this->_gName)); $params['component_id'] = CRM_Core_Component::getComponentID('CiviCampaign'); - $optionValue = CRM_Core_OptionValue::addOptionValue($params, $groupParams, $this->_action, $this->_id); + $optionValue = CRM_Core_OptionValue::addOptionValue($params, $this->_gName, $this->_action, $this->_id); - CRM_Core_Session::setStatus(ts('The Survey type \'%1\' has been saved.', array(1 => $optionValue->label)), ts('Saved'), 'success'); + CRM_Core_Session::setStatus(ts('The Survey type \'%1\' has been saved.', [1 => $optionValue->label]), ts('Saved'), 'success'); } } diff --git a/CRM/Campaign/Form/Task.php b/CRM/Campaign/Form/Task.php index 9a351d888021..f6575049df61 100644 --- a/CRM/Campaign/Form/Task.php +++ b/CRM/Campaign/Form/Task.php @@ -1,9 +1,9 @@ _task, $campaignTasks); $this->assign('taskName', $taskName); - $ids = array(); + $ids = []; if ($values['radio_ts'] == 'ts_sel') { foreach ($values as $name => $value) { if (substr($name, 0, CRM_Core_Form::CB_PREFIX_LEN) == CRM_Core_Form::CB_PREFIX) { @@ -93,7 +65,7 @@ public function preProcess() { else { $qfKey = CRM_Utils_Request::retrieve('qfKey', 'String', $this); $cacheKey = "civicrm search {$qfKey}"; - $allCids = CRM_Core_BAO_PrevNextCache::getSelection($cacheKey, "getall"); + $allCids = Civi::service('prevnext')->getSelection($cacheKey, "getall"); $ids = array_keys($allCids[$cacheKey]); $this->assign('totalSelectedVoters', count($ids)); } @@ -136,18 +108,17 @@ public function setContactIDs() { * @param bool $submitOnce */ public function addDefaultButtons($title, $nextType = 'next', $backType = 'back', $submitOnce = FALSE) { - $this->addButtons(array( - array( - 'type' => $nextType, - 'name' => $title, - 'isDefault' => TRUE, - ), - array( - 'type' => $backType, - 'name' => ts('Cancel'), - ), - ) - ); + $this->addButtons([ + [ + 'type' => $nextType, + 'name' => $title, + 'isDefault' => TRUE, + ], + [ + 'type' => $backType, + 'name' => ts('Cancel'), + ], + ]); } } diff --git a/CRM/Campaign/Form/Task/Interview.php b/CRM/Campaign/Form/Task/Interview.php index b8103f9fe7dc..a1668fdb988f 100644 --- a/CRM/Campaign/Form/Task/Interview.php +++ b/CRM/Campaign/Form/Task/Interview.php @@ -1,9 +1,9 @@ _reserveToInterview = $this->get('reserveToInterview'); if ($this->_reserveToInterview || $this->_votingTab) { //user came from voting tab / reserve form. - foreach (array( - 'surveyId', - 'contactIds', - 'interviewerId', - ) as $fld) { + foreach ([ + 'surveyId', + 'contactIds', + 'interviewerId', + ] as $fld) { $this->{"_$fld"} = $this->get($fld); } //get the target voter ids. @@ -94,38 +95,39 @@ public function preProcess() { } if ($this->_surveyId) { - $params = array('id' => $this->_surveyId); + $params = ['id' => $this->_surveyId]; CRM_Campaign_BAO_Survey::retrieve($params, $this->_surveyDetails); } $orderClause = FALSE; $buttonName = $this->controller->getButtonName(); + $walkListActivityId = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'WalkList'); if ($buttonName == '_qf_Interview_submit_orderBy' && !empty($_POST['order_bys'])) { $orderByParams = CRM_Utils_Array::value('order_bys', $_POST); } - elseif (CRM_Core_OptionGroup::getValue('activity_type', 'WalkList') == $this->_surveyDetails['activity_type_id']) { + elseif ($walkListActivityId == $this->_surveyDetails['activity_type_id']) { $orderByParams - = array( - 1 => array( + = [ + 1 => [ 'column' => 'civicrm_address.street_name', 'order' => 'ASC', - ), - 2 => array( + ], + 2 => [ 'column' => 'civicrm_address.street_number%2', 'order' => 'ASC', - ), - 3 => array( + ], + 3 => [ 'column' => 'civicrm_address.street_number', 'order' => 'ASC', - ), - 4 => array( + ], + 4 => [ 'column' => 'contact_a.sort_name', 'order' => 'ASC', - ), - ); + ], + ]; } - $orderBy = array(); + $orderBy = []; if (!empty($orderByParams)) { foreach ($orderByParams as $key => $val) { if (!empty($val['column'])) { @@ -147,7 +149,7 @@ public function preProcess() { WHERE {$clause} {$orderClause}"; - $this->_contactIds = array(); + $this->_contactIds = []; $dao = CRM_Core_DAO::executeQuery($sql); while ($dao->fetch()) { $this->_contactIds[] = $dao->id; @@ -155,10 +157,10 @@ public function preProcess() { } //get the contact read only fields to display. - $readOnlyFields = array_merge(array( + $readOnlyFields = array_merge([ 'contact_type' => '', 'sort_name' => ts('Name'), - )); + ]); //get the read only field data. $returnProperties = array_fill_keys(array_keys($readOnlyFields), 1); @@ -170,10 +172,9 @@ public function preProcess() { $this->_contactIds, $this->_interviewerId ); - $activityStatus = CRM_Core_PseudoConstant::activityStatus('name'); - $scheduledStatusId = array_search('Scheduled', $activityStatus); + $scheduledStatusId = CRM_Core_PseudoConstant::getKey('CRM_Activity_DAO_Activity', 'activity_status_id', 'Scheduled'); - $activityIds = array(); + $activityIds = []; foreach ($this->_contactIds as $key => $voterId) { $actVals = CRM_Utils_Array::value($voterId, $this->_surveyActivityIds); $statusId = CRM_Utils_Array::value('status_id', $actVals); @@ -216,9 +217,9 @@ public function preProcess() { //get the survey values. $this->_surveyValues = $this->get('surveyValues'); if (!is_array($this->_surveyValues)) { - $this->_surveyValues = array(); + $this->_surveyValues = []; if ($this->_surveyId) { - $surveyParams = array('id' => $this->_surveyId); + $surveyParams = ['id' => $this->_surveyId]; CRM_Campaign_BAO_Survey::retrieve($surveyParams, $this->_surveyValues); } $this->set('surveyValues', $this->_surveyValues); @@ -231,7 +232,7 @@ public function preProcess() { //get the survey result options. $this->_resultOptions = $this->get('resultOptions'); if (!is_array($this->_resultOptions)) { - $this->_resultOptions = array(); + $this->_resultOptions = []; if ($resultOptionId = CRM_Utils_Array::value('result_id', $this->_surveyValues)) { $this->_resultOptions = CRM_Core_OptionGroup::valuesByID($resultOptionId); } @@ -244,24 +245,24 @@ public function preProcess() { //append breadcrumb to survey dashboard. if (CRM_Campaign_BAO_Campaign::accessCampaign()) { $url = CRM_Utils_System::url('civicrm/campaign', 'reset=1&subPage=survey'); - CRM_Utils_System::appendBreadCrumb(array(array('title' => ts('Survey(s)'), 'url' => $url))); + CRM_Utils_System::appendBreadCrumb([['title' => ts('Survey(s)'), 'url' => $url]]); } //set the title. - $activityTypes = CRM_Core_PseudoConstant::activityType(FALSE, TRUE, FALSE, 'label', TRUE); $this->_surveyTypeId = CRM_Utils_Array::value('activity_type_id', $this->_surveyValues); - CRM_Utils_System::setTitle(ts('Record %1 Responses', array(1 => $activityTypes[$this->_surveyTypeId]))); + $surveyTypeLabel = CRM_Core_PseudoConstant::getLabel('CRM_Activity_BAO_Activity', 'activity_type_id', $this->_surveyTypeId); + CRM_Utils_System::setTitle(ts('Record %1 Responses', [1 => $surveyTypeLabel])); } public function validateIds() { - $required = array( + $required = [ 'surveyId' => ts('Could not find Survey.'), 'interviewerId' => ts('Could not find Interviewer.'), 'contactIds' => ts('No respondents are currently reserved for you to interview.'), 'resultOptions' => ts('Oops. It looks like there is no response option configured.'), - ); + ]; - $errorMessages = array(); + $errorMessages = []; foreach ($required as $fld => $msg) { if (empty($this->{"_$fld"})) { if (!$this->_votingTab) { @@ -282,19 +283,19 @@ public function buildQuickForm() { $this->assign('surveyTypeId', $this->_surveyTypeId); $options - = array( + = [ '' => ' - none - ', 'civicrm_address.street_name' => 'Street Name', 'civicrm_address.street_number%2' => 'Odd / Even Street Number', 'civicrm_address.street_number' => 'Street Number', 'contact_a.sort_name' => 'Respondent Name', - ); + ]; for ($i = 1; $i < count($options); $i++) { $this->addElement('select', "order_bys[{$i}][column]", ts('Order by Column'), $options); - $this->addElement('select', "order_bys[{$i}][order]", ts('Order by Order'), array( - 'ASC' => ts('Ascending'), - 'DESC' => ts('Descending'), - )); + $this->addElement('select', "order_bys[{$i}][order]", ts('Order by Order'), [ + 'ASC' => ts('Ascending'), + 'DESC' => ts('Descending'), + ]); } //pickup the uf fields. @@ -313,9 +314,9 @@ public function buildQuickForm() { //build the result field. if (!empty($this->_resultOptions)) { $this->add('select', "field[$contactId][result]", ts('Result'), - array( + [ '' => ts('- select -'), - ) + + ] + array_combine($this->_resultOptions, $this->_resultOptions) ); } @@ -326,7 +327,7 @@ public function buildQuickForm() { if ($this->_allowAjaxReleaseButton) { $this->addElement('hidden', "field[{$contactId}][is_release_or_reserve]", 0, - array('id' => "field_{$contactId}_is_release_or_reserve") + ['id' => "field_{$contactId}_is_release_or_reserve"] ); } } @@ -337,20 +338,20 @@ public function buildQuickForm() { return; } - $buttons = array( - array( + $buttons = [ + [ 'type' => 'cancel', 'name' => ts('Done'), 'subName' => 'interview', 'isDefault' => TRUE, - ), - ); + ], + ]; - $buttons[] = array( + $buttons[] = [ 'type' => 'submit', 'name' => ts('Order By >>'), 'subName' => 'orderBy', - ); + ]; $manageCampaign = CRM_Core_Permission::check('manage campaign'); $adminCampaign = CRM_Core_Permission::check('administer CiviCampaign'); @@ -358,21 +359,21 @@ public function buildQuickForm() { $adminCampaign || CRM_Core_Permission::check('release campaign contacts') ) { - $buttons[] = array( + $buttons[] = [ 'type' => 'next', 'name' => ts('Release Respondents >>'), 'subName' => 'interviewToRelease', - ); + ]; } if ($manageCampaign || $adminCampaign || CRM_Core_Permission::check('reserve campaign contacts') ) { - $buttons[] = array( + $buttons[] = [ 'type' => 'done', 'name' => ts('Reserve More Respondents >>'), 'subName' => 'interviewToReserve', - ); + ]; } $this->addButtons($buttons); @@ -383,11 +384,11 @@ public function buildQuickForm() { */ public function setDefaultValues() { //load default data for only contact fields. - $contactFields = $defaults = array(); + $contactFields = $defaults = []; foreach ($this->_surveyFields as $name => $field) { $acceptable_types = CRM_Contact_BAO_ContactType::basicTypes(); $acceptable_types[] = 'Contact'; - if (in_array($field['field_type'], $acceptable_types)) { + if (isset($field['field_type']) && (in_array($field['field_type'], $acceptable_types))) { $contactFields[$name] = $field; } } @@ -397,35 +398,36 @@ public function setDefaultValues() { } } - if (CRM_Core_OptionGroup::getValue('activity_type', 'WalkList') == $this->_surveyDetails['activity_type_id']) { + $walkListActivityId = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'WalkList'); + if ($walkListActivityId == $this->_surveyDetails['activity_type_id']) { $defaults['order_bys'] - = array( - 1 => array( + = [ + 1 => [ 'column' => 'civicrm_address.street_name', 'order' => 'ASC', - ), - 2 => array( + ], + 2 => [ 'column' => 'civicrm_address.street_number%2', 'order' => 'ASC', - ), - 3 => array( + ], + 3 => [ 'column' => 'civicrm_address.street_number', 'order' => 'ASC', - ), - 4 => array( + ], + 4 => [ 'column' => 'contact_a.sort_name', 'order' => 'ASC', - ), - ); + ], + ]; } else { $defaults['order_bys'] - = array( - 1 => array( + = [ + 1 => [ 'column' => 'contact_a.sort_name', 'order' => 'ASC', - ), - ); + ], + ]; } return $defaults; } @@ -441,11 +443,11 @@ public function postProcess() { } elseif ($buttonName == '_qf_Interview_next_interviewToRelease') { //get ready to jump to release form. - foreach (array( - 'surveyId', - 'contactIds', - 'interviewerId', - ) as $fld) { + foreach ([ + 'surveyId', + 'contactIds', + 'interviewerId', + ] as $fld) { $this->controller->set($fld, $this->{"_$fld"}); } $this->controller->set('interviewToRelease', TRUE); @@ -480,7 +482,7 @@ public static function registerInterview($params) { static $statusId; if (!$statusId) { - $statusId = array_search('Completed', CRM_Core_PseudoConstant::activityStatus('name')); + $statusId = CRM_Core_PseudoConstant::getKey('CRM_Activity_DAO_Activity', 'activity_status_id', 'Completed'); } //format custom fields. @@ -492,9 +494,9 @@ public static function registerInterview($params) { CRM_Core_BAO_CustomValueTable::store($customParams, 'civicrm_activity', $activityId); //process contact data. - $contactParams = $fields = array(); + $contactParams = $fields = []; - $contactFieldTypes = array_merge(array('Contact'), CRM_Contact_BAO_ContactType::basicTypes()); + $contactFieldTypes = array_merge(['Contact'], CRM_Contact_BAO_ContactType::basicTypes()); $responseFields = CRM_Campaign_BAO_Survey::getSurveyResponseFields($params['survey_id']); if (!empty($responseFields)) { foreach ($params as $key => $value) { @@ -537,7 +539,7 @@ public static function registerInterview($params) { $subject .= ts('Respondent Interview'); $activity->subject = $subject; - $activityParams = array( + $activityParams = [ 'details' => 'details', 'result' => 'result', 'engagement_level' => 'activity_engagement_level', @@ -547,7 +549,7 @@ public static function registerInterview($params) { 'location' => 'activity_location', 'campaign_id' => 'activity_campaign_id', 'duration' => 'activity_duration', - ); + ]; foreach ($activityParams as $key => $field) { if (!empty($params[$field])) { $activity->$key = $params[$field]; @@ -558,7 +560,6 @@ public static function registerInterview($params) { //really this should use Activity BAO& not be here but refactoring will have to be later //actually the whole ajax call could be done as an api ajax call & post hook would be sorted CRM_Utils_Hook::post('edit', 'Activity', $activity->id, $activity); - $activity->free(); return $activityId; } @@ -580,16 +581,12 @@ public function getVoterIds() { $this->_contactIds = $this->get('contactIds'); if (!is_array($this->_contactIds)) { //get the survey activities. - $activityStatus = CRM_Core_PseudoConstant::activityStatus('name'); - $statusIds = array(); - if ($statusId = array_search('Scheduled', $activityStatus)) { - $statusIds[] = $statusId; - } + $statusIds[] = CRM_Core_PseudoConstant::getKey('CRM_Activity_DAO_Activity', 'activity_status_id', 'Scheduled'); $surveyActivities = CRM_Campaign_BAO_Survey::getSurveyVoterInfo($this->_surveyId, $this->_interviewerId, $statusIds ); - $this->_contactIds = array(); + $this->_contactIds = []; foreach ($surveyActivities as $val) { $this->_contactIds[$val['voter_id']] = $val['voter_id']; } @@ -639,7 +636,7 @@ public function filterVoterIds() { INNER JOIN {$tempTableName} ON ( {$tempTableName}.survey_contact_id = contact.id ) WHERE contact.contact_type != %1"; $removeContact = CRM_Core_DAO::executeQuery($query, - array(1 => array($profileType, 'String')) + [1 => [$profileType, 'String']] ); while ($removeContact->fetch()) { unset($this->_contactIds[$removeContact->id]); diff --git a/CRM/Campaign/Form/Task/Print.php b/CRM/Campaign/Form/Task/Print.php index 1369266669da..bfc08a11000f 100644 --- a/CRM/Campaign/Form/Task/Print.php +++ b/CRM/Campaign/Form/Task/Print.php @@ -1,9 +1,9 @@ addButtons(array( - array( - 'type' => 'next', - 'name' => ts('Print Respondents'), - 'js' => array('onclick' => 'window.print()'), - 'isDefault' => TRUE, - ), - array( - 'type' => 'back', - 'name' => ts('Done'), - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'next', + 'name' => ts('Print Respondents'), + 'js' => ['onclick' => 'window.print()'], + 'isDefault' => TRUE, + ], + [ + 'type' => 'back', + 'name' => ts('Done'), + ], + ]); } /** diff --git a/CRM/Campaign/Form/Task/Release.php b/CRM/Campaign/Form/Task/Release.php index b0e62995f8af..75d994d1557c 100644 --- a/CRM/Campaign/Form/Task/Release.php +++ b/CRM/Campaign/Form/Task/Release.php @@ -1,9 +1,9 @@ _interviewToRelease = $this->get('interviewToRelease'); if ($this->_interviewToRelease) { //user came from interview form. - foreach (array( - 'surveyId', - 'contactIds', - 'interviewerId', - ) as $fld) { + foreach ([ + 'surveyId', + 'contactIds', + 'interviewerId', + ] as $fld) { $this->{"_$fld"} = $this->get($fld); } @@ -95,15 +95,13 @@ public function preProcess() { CRM_Core_Error::statusBounce(ts('Could not find respondents to release.')); } - $surveyDetails = array(); - $params = array('id' => $this->_surveyId); + $surveyDetails = []; + $params = ['id' => $this->_surveyId]; $this->_surveyDetails = CRM_Campaign_BAO_Survey::retrieve($params, $surveyDetails); $activityStatus = CRM_Core_PseudoConstant::activityStatus('name'); - $statusIds = array(); - foreach (array( - 'Scheduled', - ) as $name) { + $statusIds = []; + foreach (['Scheduled'] as $name) { if ($statusId = array_search($name, $activityStatus)) { $statusIds[] = $statusId; } @@ -123,7 +121,7 @@ public function preProcess() { //append breadcrumb to survey dashboard. if (CRM_Campaign_BAO_Campaign::accessCampaign()) { $url = CRM_Utils_System::url('civicrm/campaign', 'reset=1&subPage=survey'); - CRM_Utils_System::appendBreadCrumb(array(array('title' => ts('Survey(s)'), 'url' => $url))); + CRM_Utils_System::appendBreadCrumb([['title' => ts('Survey(s)'), 'url' => $url]]); } //set the title. @@ -139,7 +137,7 @@ public function buildQuickForm() { } public function postProcess() { - $deleteActivityIds = array(); + $deleteActivityIds = []; foreach ($this->_contactIds as $cid) { if (array_key_exists($cid, $this->_surveyActivities)) { $deleteActivityIds[] = $this->_surveyActivities[$cid]['activity_id']; @@ -152,19 +150,19 @@ public function postProcess() { CRM_Core_DAO::executeQuery($query); if ($deleteActivityIds) { - $status = ts("Respondent has been released.", array( + $status = ts("Respondent has been released.", [ 'count' => count($deleteActivityIds), 'plural' => '%count respondents have been released.', - )); + ]); CRM_Core_Session::setStatus($status, ts('Released'), 'success'); } if (count($this->_contactIds) > count($deleteActivityIds)) { $status = ts('1 respondent did not release.', - array( + [ 'count' => (count($this->_contactIds) - count($deleteActivityIds)), 'plural' => '%count respondents did not release.', - ) + ] ); CRM_Core_Session::setStatus($status, ts('Notice'), 'alert'); } diff --git a/CRM/Campaign/Form/Task/Reserve.php b/CRM/Campaign/Form/Task/Reserve.php index c6c2e76b02d2..639156c16451 100644 --- a/CRM/Campaign/Form/Task/Reserve.php +++ b/CRM/Campaign/Form/Task/Reserve.php @@ -1,9 +1,9 @@ $this->_surveyId); + $params = ['id' => $this->_surveyId]; CRM_Campaign_BAO_Survey::retrieve($params, $this->_surveyDetails); //get the survey activities. $activityStatus = CRM_Core_PseudoConstant::activityStatus('name'); - $statusIds = array(); - foreach (array('Scheduled') as $name) { + $statusIds = []; + foreach (['Scheduled'] as $name) { if ($statusId = array_search($name, $activityStatus)) { $statusIds[] = $statusId; } @@ -111,7 +111,7 @@ public function preProcess() { //append breadcrumb to survey dashboard. if (CRM_Campaign_BAO_Campaign::accessCampaign()) { $url = CRM_Utils_System::url('civicrm/campaign', 'reset=1&subPage=survey'); - CRM_Utils_System::appendBreadCrumb(array(array('title' => ts('Survey(s)'), 'url' => $url))); + CRM_Utils_System::appendBreadCrumb([['title' => ts('Survey(s)'), 'url' => $url]]); } //set the title. @@ -127,10 +127,10 @@ public function validateSurvey() { } elseif (count($this->_contactIds) > ($maxVoters - $this->_numVoters)) { $errorMsg = ts('You can reserve a maximum of %count contact at a time for this survey.', - array( + [ 'plural' => 'You can reserve a maximum of %count contacts at a time for this survey.', 'count' => $maxVoters - $this->_numVoters, - ) + ] ); } } @@ -138,10 +138,10 @@ public function validateSurvey() { $defaultNum = CRM_Utils_Array::value('default_number_of_contacts', $this->_surveyDetails); if (!$errorMsg && $defaultNum && (count($this->_contactIds) > $defaultNum)) { $errorMsg = ts('You can reserve a maximum of %count contact at a time for this survey.', - array( + [ 'plural' => 'You can reserve a maximum of %count contacts at a time for this survey.', 'count' => $defaultNum, - ) + ] ); } @@ -163,37 +163,37 @@ public function buildQuickForm() { if (is_array($groups) && !empty($groups)) { $hasExistingGroups = TRUE; $this->addElement('select', 'groups', ts('Add respondent(s) to existing group(s)'), - $groups, array('multiple' => "multiple", 'class' => 'crm-select2') + $groups, ['multiple' => "multiple", 'class' => 'crm-select2'] ); } $this->assign('hasExistingGroups', $hasExistingGroups); - $buttons = array( - array( + $buttons = [ + [ 'type' => 'done', 'name' => ts('Reserve'), 'subName' => 'reserve', 'isDefault' => TRUE, - ), - ); + ], + ]; if (CRM_Core_Permission::check('manage campaign') || CRM_Core_Permission::check('administer CiviCampaign') || CRM_Core_Permission::check('interview campaign contacts') ) { - $buttons[] = array( + $buttons[] = [ 'type' => 'next', 'name' => ts('Reserve and Interview'), 'subName' => 'reserveToInterview', - ); + ]; } - $buttons[] = array( + $buttons[] = [ 'type' => 'back', 'name' => ts('Cancel'), - ); + ]; $this->addButtons($buttons); - $this->addFormRule(array('CRM_Campaign_Form_Task_Reserve', 'formRule'), $this); + $this->addFormRule(['CRM_Campaign_Form_Task_Reserve', 'formRule'], $this); } /** @@ -209,19 +209,19 @@ public function buildQuickForm() { * list of errors to be posted back to the form */ public static function formRule($fields, $files, $self) { - $errors = array(); + $errors = []; $invalidGroupName = FALSE; if (!empty($fields['newGroupName'])) { $title = trim($fields['newGroupName']); $name = CRM_Utils_String::titleToVar($title); $query = 'select count(*) from civicrm_group where name like %1 OR title like %2'; - $grpCnt = CRM_Core_DAO::singleValueQuery($query, array( - 1 => array($name, 'String'), - 2 => array($title, 'String'), - )); + $grpCnt = CRM_Core_DAO::singleValueQuery($query, [ + 1 => [$name, 'String'], + 2 => [$title, 'String'], + ]); if ($grpCnt) { $invalidGroupName = TRUE; - $errors['newGroupName'] = ts('Group \'%1\' already exists.', array(1 => $fields['newGroupName'])); + $errors['newGroupName'] = ts('Group \'%1\' already exists.', [1 => $fields['newGroupName']]); } } $self->assign('invalidGroupName', $invalidGroupName); @@ -239,14 +239,14 @@ public function postProcess() { $activityStatus = CRM_Core_PseudoConstant::activityStatus('name'); $statusHeld = array_search('Scheduled', $activityStatus); - $reservedVoterIds = array(); + $reservedVoterIds = []; foreach ($this->_contactIds as $cid) { $subject = $this->_surveyDetails['title'] . ' - ' . ts('Respondent Reservation'); $session = CRM_Core_Session::singleton(); - $activityParams = array( + $activityParams = [ 'source_contact_id' => $session->get('userID'), - 'assignee_contact_id' => array($this->_interviewerId), - 'target_contact_id' => array($cid), + 'assignee_contact_id' => [$this->_interviewerId], + 'target_contact_id' => [$cid], 'source_record_id' => $this->_surveyId, 'activity_type_id' => $this->_surveyDetails['activity_type_id'], 'subject' => $subject, @@ -254,7 +254,7 @@ public function postProcess() { 'status_id' => $statusHeld, 'skipRecentView' => 1, 'campaign_id' => CRM_Utils_Array::value('campaign_id', $this->_surveyDetails), - ); + ]; $activity = CRM_Activity_BAO_Activity::create($activityParams); if ($activity->id) { $countVoters++; @@ -270,10 +270,10 @@ public function postProcess() { // Success message if ($countVoters > 0) { - $status = '

' . ts("%count contact has been reserved.", array('plural' => '%count contacts have been reserved.', 'count' => $countVoters)) . '

'; + $status = '

' . ts("%count contact has been reserved.", ['plural' => '%count contacts have been reserved.', 'count' => $countVoters]) . '

'; if ($groupAdditions) { $status .= '

' . ts('They have been added to %1.', - array(1 => implode(' ' . ts('and') . ' ', $groupAdditions)) + [1 => implode(' ' . ts('and') . ' ', $groupAdditions)] ) . '

'; } CRM_Core_Session::setStatus($status, ts('Reservation Added'), 'success'); @@ -281,10 +281,10 @@ public function postProcess() { // Error message if (count($this->_contactIds) > $countVoters) { CRM_Core_Session::setStatus(ts('Reservation did not add for %count contact.', - array( + [ 'plural' => 'Reservation did not add for %count contacts.', 'count' => (count($this->_contactIds) - $countVoters), - ) + ] ), ts('Notice')); } @@ -306,24 +306,24 @@ public function postProcess() { * @return array */ private function _addRespondentToGroup($contactIds) { - $groupAdditions = array(); + $groupAdditions = []; if (empty($contactIds)) { return $groupAdditions; } $params = $this->controller->exportValues($this->_name); - $groups = CRM_Utils_Array::value('groups', $params, array()); + $groups = CRM_Utils_Array::value('groups', $params, []); $newGroupName = CRM_Utils_Array::value('newGroupName', $params); $newGroupDesc = CRM_Utils_Array::value('newGroupDesc', $params); $newGroupId = NULL; //create new group. if ($newGroupName) { - $grpParams = array( + $grpParams = [ 'title' => $newGroupName, 'description' => $newGroupDesc, 'is_active' => TRUE, - ); + ]; $group = CRM_Contact_BAO_Group::create($grpParams); $groups[] = $newGroupId = $group->id; } diff --git a/CRM/Campaign/Form/Task/Result.php b/CRM/Campaign/Form/Task/Result.php index 0a76920026e2..25f81fb7fdbd 100644 --- a/CRM/Campaign/Form/Task/Result.php +++ b/CRM/Campaign/Form/Task/Result.php @@ -1,9 +1,9 @@ addButtons(array( - array( - 'type' => 'done', - 'name' => ts('Done'), - 'isDefault' => TRUE, - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'done', + 'name' => ts('Done'), + 'isDefault' => TRUE, + ], + ]); } } diff --git a/CRM/Campaign/Info.php b/CRM/Campaign/Info.php index b9a7c00f32df..9543c059d5b5 100644 --- a/CRM/Campaign/Info.php +++ b/CRM/Campaign/Info.php @@ -1,9 +1,9 @@ 'CiviCampaign', 'translatedName' => ts('CiviCampaign'), - 'title' => 'CiviCRM Campaign Engine', + 'title' => ts('CiviCRM Campaign Engine'), 'search' => 1, 'showActivitiesInCore' => 1, - ); + ]; } - /** * @inheritDoc * @param bool $getAllUnconditionally @@ -64,35 +64,35 @@ public function getInfo() { * @return array */ public function getPermissions($getAllUnconditionally = FALSE, $descriptions = FALSE) { - $permissions = array( - 'administer CiviCampaign' => array( + $permissions = [ + 'administer CiviCampaign' => [ ts('administer CiviCampaign'), ts('Create new campaign, survey and petition types and their status'), - ), - 'manage campaign' => array( + ], + 'manage campaign' => [ ts('manage campaign'), ts('Create new campaigns, surveys and petitions, reserve respondents'), - ), - 'reserve campaign contacts' => array( + ], + 'reserve campaign contacts' => [ ts('reserve campaign contacts'), ts('Reserve campaign contacts for surveys and petitions'), - ), - 'release campaign contacts' => array( + ], + 'release campaign contacts' => [ ts('release campaign contacts'), ts('Release reserved campaign contacts for surveys and petitions'), - ), - 'interview campaign contacts' => array( + ], + 'interview campaign contacts' => [ ts('interview campaign contacts'), ts('Record survey and petition responses from their reserved contacts'), - ), - 'gotv campaign contacts' => array( + ], + 'gotv campaign contacts' => [ ts('GOTV campaign contacts'), ts('Record that contacts voted'), - ), - 'sign CiviCRM Petition' => array( + ], + 'sign CiviCRM Petition' => [ ts('sign CiviCRM Petition'), - ), - ); + ], + ]; if (!$descriptions) { foreach ($permissions as $name => $attr) { @@ -103,7 +103,6 @@ public function getPermissions($getAllUnconditionally = FALSE, $descriptions = F return $permissions; } - /** * @inheritDoc * @return null @@ -130,6 +129,14 @@ public function registerTab() { return NULL; } + /** + * @inheritDoc + * @return string + */ + public function getIcon() { + return 'crm-i fa-star-o'; + } + /** * @inheritDoc * @return null @@ -154,20 +161,20 @@ public function creatNewShortcut(&$shortCuts) { if (CRM_Core_Permission::check('manage campaign') || CRM_Core_Permission::check('administer CiviCampaign') ) { - $shortCuts = array_merge($shortCuts, array( - array( + $shortCuts = array_merge($shortCuts, [ + [ 'path' => 'civicrm/campaign/add', 'query' => "reset=1&action=add", 'ref' => 'new-campaign', 'title' => ts('Campaign'), - ), - array( + ], + [ 'path' => 'civicrm/survey/add', 'query' => "reset=1&action=add", 'ref' => 'new-survey', 'title' => ts('Survey'), - ), - )); + ], + ]); } } diff --git a/CRM/Campaign/Page/AJAX.php b/CRM/Campaign/Page/AJAX.php index 3de8c6055c1c..ecec01993619 100644 --- a/CRM/Campaign/Page/AJAX.php +++ b/CRM/Campaign/Page/AJAX.php @@ -1,9 +1,9 @@ 'fail', 'voter_id' => $voterId, 'activity_id' => $params['interviewer_id'], - ); + ]; //time to validate custom data. $errors = CRM_Core_BAO_CustomField::validateCustomData($params); @@ -108,7 +108,7 @@ public static function loadOptionGroupDetails() { $id = CRM_Utils_Request::retrieve('option_group_id', 'Integer', CRM_Core_DAO::$_nullObject, FALSE, NULL, 'POST'); $status = 'fail'; - $opValues = array(); + $opValues = []; if ($id) { $groupParams['id'] = $id; @@ -136,10 +136,10 @@ public static function loadOptionGroupDetails() { $status = 'success'; } - $result = array( + $result = [ 'status' => $status, 'result' => $opValues, - ); + ]; CRM_Utils_JSON::output($result); } @@ -149,7 +149,7 @@ public function voterList() { $searchCriteria = CRM_Utils_Request::retrieve('searchCriteria', 'String', CRM_Core_DAO::$_nullObject, FALSE, NULL, 'POST'); $searchParams = explode(',', $searchCriteria); - $params = $searchRows = array(); + $params = $searchRows = []; foreach ($searchParams as $param) { if (!empty($_POST[$param])) { $params[$param] = $_POST[$param]; @@ -157,10 +157,10 @@ public function voterList() { } //format multi-select group and contact types. - foreach (array( - 'group', - 'contact_type', - ) as $param) { + foreach ([ + 'group', + 'contact_type', + ] as $param) { $paramValue = CRM_Utils_Array::value($param, $params); if ($paramValue) { unset($params[$param]); @@ -171,12 +171,12 @@ public function voterList() { } } - $voterClauseParams = array(); - foreach (array( - 'campaign_survey_id', - 'survey_interviewer_id', - 'campaign_search_voter_for', - ) as $fld) { + $voterClauseParams = []; + foreach ([ + 'campaign_survey_id', + 'survey_interviewer_id', + 'campaign_search_voter_for', + ] as $fld) { $voterClauseParams[$fld] = CRM_Utils_Array::value($fld, $params); } @@ -224,42 +224,42 @@ public function voterList() { } } - $selectorCols = array( + $selectorCols = [ 'sort_name', 'street_address', 'street_name', 'street_number', 'street_unit', - ); + ]; // get the data table params. - $dataTableParams = array( - 'sEcho' => array( + $dataTableParams = [ + 'sEcho' => [ 'name' => 'sEcho', 'type' => 'Integer', 'default' => 0, - ), - 'offset' => array( + ], + 'offset' => [ 'name' => 'iDisplayStart', 'type' => 'Integer', 'default' => 0, - ), - 'rowCount' => array( + ], + 'rowCount' => [ 'name' => 'iDisplayLength', 'type' => 'Integer', 'default' => 25, - ), - 'sort' => array( + ], + 'sort' => [ 'name' => 'iSortCol_0', 'type' => 'Integer', 'default' => 'sort_name', - ), - 'sortOrder' => array( + ], + 'sortOrder' => [ 'name' => 'sSortDir_0', 'type' => 'String', 'default' => 'asc', - ), - ); + ], + ]; foreach ($dataTableParams as $pName => $pValues) { $$pName = $pValues['default']; if (!empty($_POST[$pValues['name']])) { @@ -291,14 +291,14 @@ public function voterList() { $iTotal = $searchCount; - $selectorCols = array( + $selectorCols = [ 'contact_type', 'sort_name', 'street_address', 'street_name', 'street_number', 'street_unit', - ); + ]; $extraVoterColName = 'is_interview_conducted'; if ($params['campaign_search_voter_for'] == 'reserve') { @@ -328,7 +328,7 @@ public function voterList() { $result->contact_id ); - $searchRows[$contactID] = array('id' => $contactID); + $searchRows[$contactID] = ['id' => $contactID]; foreach ($selectorCols as $col) { $val = $result->$col; if ($col == 'contact_type') { @@ -357,7 +357,7 @@ public function voterList() { } } - $selectorElements = array_merge($selectorCols, array($extraVoterColName)); + $selectorElements = array_merge($selectorCols, [$extraVoterColName]); $iFilteredTotal = $iTotal; @@ -400,13 +400,13 @@ public function processVoterData() { } } if ($createActivity) { - $ids = array( + $ids = [ 'source_record_id', 'source_contact_id', 'target_contact_id', 'assignee_contact_id', - ); - $activityParams = array(); + ]; + $activityParams = []; foreach ($ids as $id) { $val = CRM_Utils_Array::value($id, $_POST); if (!$val) { @@ -421,12 +421,12 @@ public function processVoterData() { $activityStatus = CRM_Core_PseudoConstant::activityStatus('name'); $scheduledStatusId = array_search('Scheduled', $activityStatus); if ($isReserved) { - $surveyValues = array(); - $surveyParams = array('id' => $activityParams['source_record_id']); + $surveyValues = []; + $surveyParams = ['id' => $activityParams['source_record_id']]; CRM_Core_DAO::commonRetrieve('CRM_Campaign_DAO_Survey', $surveyParams, $surveyValues, - array('title', 'activity_type_id', 'campaign_id') + ['title', 'activity_type_id', 'campaign_id'] ); $activityTypeId = $surveyValues['activity_type_id']; @@ -451,11 +451,11 @@ public function processVoterData() { } else { //delete reserved activity for given voter. - $voterIds = array($activityParams['target_contact_id']); + $voterIds = [$activityParams['target_contact_id']]; $activities = CRM_Campaign_BAO_Survey::voterActivityDetails($activityParams['source_record_id'], $voterIds, $activityParams['source_contact_id'], - array($scheduledStatusId) + [$scheduledStatusId] ); foreach ($activities as $voterId => $values) { $activityId = CRM_Utils_Array::value('activity_id', $values); @@ -491,47 +491,14 @@ public function processVoterData() { } } - CRM_Utils_JSON::output(array('status' => $status)); - } - - public function allActiveCampaigns() { - $currentCampaigns = CRM_Campaign_BAO_Campaign::getCampaigns(); - $campaigns = CRM_Campaign_BAO_Campaign::getCampaigns(NULL, NULL, TRUE, FALSE, TRUE); - $options = array( - array( - 'value' => '', - 'title' => ts('- select -'), - ), - ); - foreach ($campaigns as $value => $title) { - $class = NULL; - if (!array_key_exists($value, $currentCampaigns)) { - $class = 'status-past'; - } - $options[] = array( - 'value' => $value, - 'title' => $title, - 'class' => $class, - ); - } - $status = 'fail'; - if (count($options) > 1) { - $status = 'success'; - } - - $results = array( - 'status' => $status, - 'campaigns' => $options, - ); - - CRM_Utils_JSON::output($results); + CRM_Utils_JSON::output(['status' => $status]); } public function campaignGroups() { $surveyId = CRM_Utils_Request::retrieve('survey_id', 'Positive', CRM_Core_DAO::$_nullObject, FALSE, NULL, 'POST' ); - $campGroups = array(); + $campGroups = []; if ($surveyId) { $campaignId = CRM_Core_DAO::getFieldValue('CRM_Campaign_DAO_Survey', $surveyId, 'campaign_id'); if ($campaignId) { @@ -544,22 +511,22 @@ public function campaignGroups() { if (empty($campGroups)) { $campGroups = CRM_Core_PseudoConstant::group(); } - $groups = array( - array( + $groups = [ + [ 'value' => '', 'title' => ts('- select -'), - ), - ); + ], + ]; foreach ($campGroups as $grpId => $title) { - $groups[] = array( + $groups[] = [ 'value' => $grpId, 'title' => $title, - ); + ]; } - $results = array( + $results = [ 'status' => 'success', 'groups' => $groups, - ); + ]; CRM_Utils_JSON::output($results); } @@ -573,7 +540,7 @@ public static function campaignList() { $searchCriteria = CRM_Utils_Request::retrieve('searchCriteria', 'String', CRM_Core_DAO::$_nullObject, FALSE, NULL, 'POST'); $searchParams = explode(',', $searchCriteria); - $params = $searchRows = array(); + $params = $searchRows = []; foreach ($searchParams as $param) { if (isset($_POST[$param])) { $params[$param] = $_POST[$param]; @@ -581,7 +548,7 @@ public static function campaignList() { } //this is sequence columns on datatable. - $selectorCols = array( + $selectorCols = [ 'id', 'name', 'title', @@ -595,36 +562,36 @@ public static function campaignList() { 'is_active', 'isActive', 'action', - ); + ]; // get the data table params. - $dataTableParams = array( - 'sEcho' => array( + $dataTableParams = [ + 'sEcho' => [ 'name' => 'sEcho', 'type' => 'Integer', 'default' => 0, - ), - 'offset' => array( + ], + 'offset' => [ 'name' => 'iDisplayStart', 'type' => 'Integer', 'default' => 0, - ), - 'rowCount' => array( + ], + 'rowCount' => [ 'name' => 'iDisplayLength', 'type' => 'Integer', 'default' => 25, - ), - 'sort' => array( + ], + 'sort' => [ 'name' => 'iSortCol_0', 'type' => 'Integer', 'default' => 'start_date', - ), - 'sortOrder' => array( + ], + 'sortOrder' => [ 'name' => 'sSortDir_0', 'type' => 'String', 'default' => 'desc', - ), - ); + ], + ]; foreach ($dataTableParams as $pName => $pValues) { $$pName = $pValues['default']; if (!empty($_POST[$pValues['name']])) { @@ -634,12 +601,12 @@ public static function campaignList() { } } } - foreach (array( - 'sort', - 'offset', - 'rowCount', - 'sortOrder', - ) as $sortParam) { + foreach ([ + 'sort', + 'offset', + 'rowCount', + 'sortOrder', + ] as $sortParam) { $params[$sortParam] = $$sortParam; } @@ -676,7 +643,7 @@ public function surveyList() { $searchCriteria = CRM_Utils_Request::retrieve('searchCriteria', 'String', CRM_Core_DAO::$_nullObject, FALSE, NULL, 'POST'); $searchParams = explode(',', $searchCriteria); - $params = $searchRows = array(); + $params = $searchRows = []; foreach ($searchParams as $param) { if (!empty($_POST[$param])) { $params[$param] = $_POST[$param]; @@ -684,7 +651,7 @@ public function surveyList() { } //this is sequence columns on datatable. - $selectorCols = array( + $selectorCols = [ 'id', 'title', 'campaign_id', @@ -700,36 +667,36 @@ public function surveyList() { 'result_id', 'action', 'voterLinks', - ); + ]; // get the data table params. - $dataTableParams = array( - 'sEcho' => array( + $dataTableParams = [ + 'sEcho' => [ 'name' => 'sEcho', 'type' => 'Integer', 'default' => 0, - ), - 'offset' => array( + ], + 'offset' => [ 'name' => 'iDisplayStart', 'type' => 'Integer', 'default' => 0, - ), - 'rowCount' => array( + ], + 'rowCount' => [ 'name' => 'iDisplayLength', 'type' => 'Integer', 'default' => 25, - ), - 'sort' => array( + ], + 'sort' => [ 'name' => 'iSortCol_0', 'type' => 'Integer', 'default' => 'created_date', - ), - 'sortOrder' => array( + ], + 'sortOrder' => [ 'name' => 'sSortDir_0', 'type' => 'String', 'default' => 'desc', - ), - ); + ], + ]; foreach ($dataTableParams as $pName => $pValues) { $$pName = $pValues['default']; if (!empty($_POST[$pValues['name']])) { @@ -739,12 +706,12 @@ public function surveyList() { } } } - foreach (array( - 'sort', - 'offset', - 'rowCount', - 'sortOrder', - ) as $sortParam) { + foreach ([ + 'sort', + 'offset', + 'rowCount', + 'sortOrder', + ] as $sortParam) { $params[$sortParam] = $$sortParam; } @@ -781,7 +748,7 @@ public function petitionList() { $searchCriteria = CRM_Utils_Request::retrieve('searchCriteria', 'String', CRM_Core_DAO::$_nullObject, FALSE, NULL, 'POST'); $searchParams = explode(',', $searchCriteria); - $params = $searchRows = array(); + $params = $searchRows = []; foreach ($searchParams as $param) { if (!empty($_POST[$param])) { $params[$param] = $_POST[$param]; @@ -789,7 +756,7 @@ public function petitionList() { } //this is sequence columns on datatable. - $selectorCols = array( + $selectorCols = [ 'id', 'title', 'campaign_id', @@ -800,36 +767,36 @@ public function petitionList() { 'is_active', 'isActive', 'action', - ); + ]; // get the data table params. - $dataTableParams = array( - 'sEcho' => array( + $dataTableParams = [ + 'sEcho' => [ 'name' => 'sEcho', 'type' => 'Integer', 'default' => 0, - ), - 'offset' => array( + ], + 'offset' => [ 'name' => 'iDisplayStart', 'type' => 'Integer', 'default' => 0, - ), - 'rowCount' => array( + ], + 'rowCount' => [ 'name' => 'iDisplayLength', 'type' => 'Integer', 'default' => 25, - ), - 'sort' => array( + ], + 'sort' => [ 'name' => 'iSortCol_0', 'type' => 'Integer', 'default' => 'created_date', - ), - 'sortOrder' => array( + ], + 'sortOrder' => [ 'name' => 'sSortDir_0', 'type' => 'String', 'default' => 'desc', - ), - ); + ], + ]; foreach ($dataTableParams as $pName => $pValues) { $$pName = $pValues['default']; if (!empty($_POST[$pValues['name']])) { @@ -839,12 +806,12 @@ public function petitionList() { } } } - foreach (array( - 'sort', - 'offset', - 'rowCount', - 'sortOrder', - ) as $sortParam) { + foreach ([ + 'sort', + 'offset', + 'rowCount', + 'sortOrder', + ] as $sortParam) { $params[$sortParam] = $$sortParam; } diff --git a/CRM/Campaign/Page/DashBoard.php b/CRM/Campaign/Page/DashBoard.php index 50580f4cff3e..08ac5a08e228 100644 --- a/CRM/Campaign/Page/DashBoard.php +++ b/CRM/Campaign/Page/DashBoard.php @@ -1,9 +1,9 @@ array( + self::$_campaignActionLinks = [ + CRM_Core_Action::UPDATE => [ 'name' => ts('Edit'), 'url' => 'civicrm/campaign/add', 'qs' => 'reset=1&action=update&id=%%id%%', 'title' => ts('Update Campaign'), - ), - CRM_Core_Action::DISABLE => array( + ], + CRM_Core_Action::DISABLE => [ 'name' => ts('Disable'), 'title' => ts('Disable Campaign'), 'ref' => 'crm-enable-disable', - ), - CRM_Core_Action::ENABLE => array( + ], + CRM_Core_Action::ENABLE => [ 'name' => ts('Enable'), 'title' => ts('Enable Campaign'), 'ref' => 'crm-enable-disable', - ), - CRM_Core_Action::DELETE => array( + ], + CRM_Core_Action::DELETE => [ 'name' => ts('Delete'), 'url' => 'civicrm/campaign/add', 'qs' => 'action=delete&reset=1&id=%%id%%', 'title' => ts('Delete Campaign'), - ), - ); + ], + ]; } return self::$_campaignActionLinks; @@ -88,30 +88,30 @@ public static function campaignActionLinks() { public static function surveyActionLinks() { // check if variable _actionsLinks is populated if (!isset(self::$_surveyActionLinks)) { - self::$_surveyActionLinks = array( - CRM_Core_Action::UPDATE => array( + self::$_surveyActionLinks = [ + CRM_Core_Action::UPDATE => [ 'name' => ts('Edit'), 'url' => 'civicrm/survey/configure/main', 'qs' => 'action=update&id=%%id%%&reset=1', 'title' => ts('Update Survey'), - ), - CRM_Core_Action::DISABLE => array( + ], + CRM_Core_Action::DISABLE => [ 'name' => ts('Disable'), 'ref' => 'crm-enable-disable', 'title' => ts('Disable Survey'), - ), - CRM_Core_Action::ENABLE => array( + ], + CRM_Core_Action::ENABLE => [ 'name' => ts('Enable'), 'ref' => 'crm-enable-disable', 'title' => ts('Enable Survey'), - ), - CRM_Core_Action::DELETE => array( + ], + CRM_Core_Action::DELETE => [ 'name' => ts('Delete'), 'url' => 'civicrm/survey/delete', 'qs' => 'id=%%id%%&reset=1', 'title' => ts('Delete Survey'), - ), - ); + ], + ]; } return self::$_surveyActionLinks; @@ -123,43 +123,43 @@ public static function surveyActionLinks() { public static function petitionActionLinks() { if (!isset(self::$_petitionActionLinks)) { self::$_petitionActionLinks = self::surveyActionLinks(); - self::$_petitionActionLinks[CRM_Core_Action::UPDATE] = array( + self::$_petitionActionLinks[CRM_Core_Action::UPDATE] = [ 'name' => ts('Edit'), 'url' => 'civicrm/petition/add', 'qs' => 'action=update&id=%%id%%&reset=1', 'title' => ts('Update Petition'), - ); - self::$_petitionActionLinks[CRM_Core_Action::DISABLE] = array( + ]; + self::$_petitionActionLinks[CRM_Core_Action::DISABLE] = [ 'name' => ts('Disable'), 'ref' => 'crm-enable-disable', 'title' => ts('Disable Petition'), - ); - self::$_petitionActionLinks[CRM_Core_Action::ENABLE] = array( + ]; + self::$_petitionActionLinks[CRM_Core_Action::ENABLE] = [ 'name' => ts('Enable'), 'ref' => 'crm-enable-disable', 'title' => ts('Enable Petition'), - ); - self::$_petitionActionLinks[CRM_Core_Action::DELETE] = array( + ]; + self::$_petitionActionLinks[CRM_Core_Action::DELETE] = [ 'name' => ts('Delete'), 'url' => 'civicrm/petition/add', 'qs' => 'action=delete&id=%%id%%&reset=1', 'title' => ts('Delete Petition'), - ); - self::$_petitionActionLinks[CRM_Core_Action::PROFILE] = array( + ]; + self::$_petitionActionLinks[CRM_Core_Action::PROFILE] = [ 'name' => ts('Sign'), 'url' => 'civicrm/petition/sign', 'qs' => 'sid=%%id%%&reset=1', 'title' => ts('Sign Petition'), 'fe' => TRUE, //CRM_Core_Action::PROFILE is used because there isn't a specific action for sign - ); - self::$_petitionActionLinks[CRM_Core_Action::BROWSE] = array( + ]; + self::$_petitionActionLinks[CRM_Core_Action::BROWSE] = [ 'name' => ts('Signatures'), 'url' => 'civicrm/activity/search', 'qs' => 'survey=%%id%%&force=1', 'title' => ts('List the signatures'), //CRM_Core_Action::PROFILE is used because there isn't a specific action for sign - ); + ]; } return self::$_petitionActionLinks; @@ -196,8 +196,8 @@ public function browseCampaign() { * * @return array */ - public static function getCampaignSummary($params = array()) { - $campaignsData = array(); + public static function getCampaignSummary($params = []) { + $campaignsData = []; //get the campaigns. $campaigns = CRM_Campaign_BAO_Campaign::getCampaignSummary($params); @@ -205,7 +205,7 @@ public static function getCampaignSummary($params = array()) { $config = CRM_Core_Config::singleton(); $campaignType = CRM_Campaign_PseudoConstant::campaignType(); $campaignStatus = CRM_Campaign_PseudoConstant::campaignStatus(); - $properties = array( + $properties = [ 'id', 'name', 'title', @@ -215,7 +215,7 @@ public static function getCampaignSummary($params = array()) { 'is_active', 'start_date', 'end_date', - ); + ]; foreach ($campaigns as $cmpid => $campaign) { foreach ($properties as $prop) { $campaignsData[$cmpid][$prop] = CRM_Utils_Array::value($prop, $campaign); @@ -251,7 +251,7 @@ public static function getCampaignSummary($params = array()) { } $campaignsData[$cmpid]['action'] = CRM_Core_Action::formLink(self::campaignActionLinks(), $action, - array('id' => $campaign['id']), + ['id' => $campaign['id']], ts('more'), FALSE, 'campaign.dashboard.row', @@ -296,8 +296,8 @@ public function browseSurvey() { * * @return array */ - public static function getSurveySummary($params = array()) { - $surveysData = array(); + public static function getSurveySummary($params = []) { + $surveysData = []; //get the survey. $config = CRM_Core_Config::singleton(); @@ -311,7 +311,7 @@ public static function getSurveySummary($params = array()) { $surveysData[$sid]['campaign'] = CRM_Utils_Array::value($campaignId, $campaigns); $surveysData[$sid]['activity_type'] = $surveyType[$survey['activity_type_id']]; if (!empty($survey['release_frequency'])) { - $surveysData[$sid]['release_frequency'] = ts('1 Day', array('plural' => '%count Days', 'count' => $survey['release_frequency'])); + $surveysData[$sid]['release_frequency'] = ts('1 Day', ['plural' => '%count Days', 'count' => $survey['release_frequency']]); } $action = array_sum(array_keys(self::surveyActionLinks($surveysData[$sid]['activity_type']))); @@ -344,7 +344,7 @@ public static function getSurveySummary($params = array()) { } $surveysData[$sid]['action'] = CRM_Core_Action::formLink(self::surveyActionLinks($surveysData[$sid]['activity_type']), $action, - array('id' => $sid), + ['id' => $sid], ts('more'), FALSE, 'survey.dashboard.row', @@ -402,9 +402,9 @@ public function browsePetition() { * * @return array */ - public static function getPetitionSummary($params = array()) { + public static function getPetitionSummary($params = []) { $config = CRM_Core_Config::singleton(); - $petitionsData = array(); + $petitionsData = []; //get the petitions. $petitions = CRM_Campaign_BAO_Petition::getPetitionSummary($params); @@ -439,7 +439,7 @@ public static function getPetitionSummary($params = array()) { $petitionsData[$pid]['action'] = CRM_Core_Action::formLink(self::petitionActionLinks(), $action, - array('id' => $pid), + ['id' => $pid], ts('more'), FALSE, 'petition.dashboard.row', @@ -453,11 +453,11 @@ public static function getPetitionSummary($params = array()) { } public function browse() { - $this->_tabs = array( + $this->_tabs = [ 'campaign' => ts('Campaigns'), 'survey' => ts('Surveys'), 'petition' => ts('Petitions'), - ); + ]; $subPageType = CRM_Utils_Request::retrieve('type', 'String', $this); if ($subPageType) { @@ -474,11 +474,11 @@ public function browse() { } CRM_Core_Resources::singleton() ->addScriptFile('civicrm', 'templates/CRM/common/TabHeader.js', 1, 'html-header') - ->addSetting(array( - 'tabSettings' => array( + ->addSetting([ + 'tabSettings' => [ 'active' => strtolower(CRM_Utils_Array::value('subPage', $_GET, 'campaign')), - ), - )); + ], + ]); } /** @@ -495,14 +495,14 @@ public function run() { } public function buildTabs() { - $allTabs = array(); + $allTabs = []; foreach ($this->_tabs as $name => $title) { - $allTabs[$name] = array( + $allTabs[$name] = [ 'title' => $title, 'valid' => TRUE, 'active' => TRUE, 'link' => CRM_Utils_System::url('civicrm/campaign', "reset=1&type=$name"), - ); + ]; } $allTabs['campaign']['class'] = 'livePage'; $this->assign('tabHeader', $allTabs); diff --git a/CRM/Campaign/Page/Petition.php b/CRM/Campaign/Page/Petition.php index c78e656da6b7..c526df7f0eee 100644 --- a/CRM/Campaign/Page/Petition.php +++ b/CRM/Campaign/Page/Petition.php @@ -1,9 +1,9 @@ '); - $contact_id = CRM_Utils_Request::retrieve('cid', 'Integer', CRM_Core_DAO::$_nullObject); - $subscribe_id = CRM_Utils_Request::retrieve('sid', 'Integer', CRM_Core_DAO::$_nullObject); - $hash = CRM_Utils_Request::retrieve('h', 'String', CRM_Core_DAO::$_nullObject); - $activity_id = CRM_Utils_Request::retrieve('a', 'String', CRM_Core_DAO::$_nullObject); - $petition_id = CRM_Utils_Request::retrieve('pid', 'String', CRM_Core_DAO::$_nullObject); + $contact_id = CRM_Utils_Request::retrieve('cid', 'Integer'); + $subscribe_id = CRM_Utils_Request::retrieve('sid', 'Integer'); + $hash = CRM_Utils_Request::retrieve('h', 'String'); + $activity_id = CRM_Utils_Request::retrieve('a', 'String'); + $petition_id = CRM_Utils_Request::retrieve('pid', 'String'); if (!$petition_id) { - $petition_id = CRM_Utils_Request::retrieve('p', 'String', CRM_Core_DAO::$_nullObject); + $petition_id = CRM_Utils_Request::retrieve('p', 'String'); } if (!$contact_id || @@ -71,7 +72,7 @@ public function run() { $this->assign('survey_id', $petition_id); $pparams['id'] = $petition_id; - $this->petition = array(); + $this->petition = []; CRM_Campaign_BAO_Survey::retrieve($pparams, $this->petition); $this->assign('is_share', CRM_Utils_Array::value('is_share', $this->petition)); $this->assign('thankyou_title', CRM_Utils_Array::value('thankyou_title', $this->petition)); @@ -119,7 +120,7 @@ public static function confirm($contact_id, $subscribe_id, $hash, $activity_id, $ce->save(); CRM_Contact_BAO_GroupContact::addContactsToGroup( - array($contact_id), + [$contact_id], $se->group_id, 'Email', 'Added', diff --git a/CRM/Campaign/Page/Petition/ThankYou.php b/CRM/Campaign/Page/Petition/ThankYou.php index 30a3f8c6bcee..ac4d8d4b8342 100644 --- a/CRM/Campaign/Page/Petition/ThankYou.php +++ b/CRM/Campaign/Page/Petition/ThankYou.php @@ -1,9 +1,9 @@ petition = array(); + $this->petition = []; CRM_Campaign_BAO_Survey::retrieve($params, $this->petition); $this->assign('petitionTitle', $this->petition['title']); $this->assign('thankyou_title', CRM_Utils_Array::value('thankyou_title', $this->petition)); diff --git a/CRM/Campaign/Page/SurveyType.php b/CRM/Campaign/Page/SurveyType.php index babf2893f700..bfe7e4cd906c 100644 --- a/CRM/Campaign/Page/SurveyType.php +++ b/CRM/Campaign/Page/SurveyType.php @@ -1,9 +1,9 @@ assign('gName', $this->_gName); $this->assign('GName', $this->_GName); - CRM_Utils_System::setTitle(ts('%1 Options', array(1 => $this->_GName))); + CRM_Utils_System::setTitle(ts('%1 Options', [1 => $this->_GName])); - $this->assign('addSurveyType', array("civicrm/admin/campaign/surveyType", "reset=1&action=add")); + $this->assign('addSurveyType', ["civicrm/admin/campaign/surveyType", "reset=1&action=add"]); } /** @@ -102,30 +102,30 @@ public function getBAOName() { */ public function &links() { if (!(self::$_links)) { - self::$_links = array( - CRM_Core_Action::UPDATE => array( + self::$_links = [ + CRM_Core_Action::UPDATE => [ 'name' => ts('Edit'), 'url' => 'civicrm/admin/campaign/surveyType', 'qs' => 'action=update&id=%%id%%&reset=1', - 'title' => ts('Edit %1', array(1 => $this->_gName)), - ), - CRM_Core_Action::DISABLE => array( + 'title' => ts('Edit %1', [1 => $this->_gName]), + ], + CRM_Core_Action::DISABLE => [ 'name' => ts('Disable'), 'ref' => 'crm-enable-disable', - 'title' => ts('Disable %1', array(1 => $this->_gName)), - ), - CRM_Core_Action::ENABLE => array( + 'title' => ts('Disable %1', [1 => $this->_gName]), + ], + CRM_Core_Action::ENABLE => [ 'name' => ts('Enable'), 'ref' => 'crm-enable-disable', - 'title' => ts('Enable %1', array(1 => $this->_gName)), - ), - CRM_Core_Action::DELETE => array( + 'title' => ts('Enable %1', [1 => $this->_gName]), + ], + CRM_Core_Action::DELETE => [ 'name' => ts('Delete'), 'url' => 'civicrm/admin/campaign/surveyType', 'qs' => 'action=delete&id=%%id%%', - 'title' => ts('Delete %1 Type', array(1 => $this->_gName)), - ), - ); + 'title' => ts('Delete %1 Type', [1 => $this->_gName]), + ], + ]; } return self::$_links; } @@ -143,7 +143,7 @@ public function run() { */ public function browse() { $campaingCompId = CRM_Core_Component::getComponentID('CiviCampaign'); - $groupParams = array('name' => $this->_gName); + $groupParams = ['name' => $this->_gName]; $optionValues = CRM_Core_OptionValue::getRows($groupParams, $this->links(), 'component_id,weight'); foreach ($optionValues as $key => $optionValue) { diff --git a/CRM/Campaign/Page/Vote.php b/CRM/Campaign/Page/Vote.php index 983a92c28e9c..ad131746ff45 100644 --- a/CRM/Campaign/Page/Vote.php +++ b/CRM/Campaign/Page/Vote.php @@ -1,9 +1,9 @@ _tabs = array( + $this->_tabs = [ 'reserve' => ts('Reserve Respondents'), 'interview' => ts('Interview Respondents'), - ); + ]; $this->_surveyId = CRM_Utils_Request::retrieve('sid', 'Positive', $this); $this->_interviewerId = CRM_Utils_Request::retrieve('cid', 'Positive', $this); @@ -93,11 +93,11 @@ public function browse() { CRM_Core_Resources::singleton() ->addScriptFile('civicrm', 'templates/CRM/common/TabHeader.js', 1, 'html-header') - ->addSetting(array( - 'tabSettings' => array( + ->addSetting([ + 'tabSettings' => [ 'active' => strtolower(CRM_Utils_Array::value('subPage', $_GET, 'reserve')), - ), - )); + ], + ]); } /** @@ -110,17 +110,16 @@ public function run() { } public function buildTabs() { - $allTabs = array(); + $allTabs = []; foreach ($this->_tabs as $name => $title) { // check for required permissions. - if (!CRM_Core_Permission::check(array( - array( + if (!CRM_Core_Permission::check([ + [ 'manage campaign', 'administer CiviCampaign', "{$name} campaign contacts", - ), - )) - ) { + ], + ])) { continue; } @@ -131,12 +130,12 @@ public function buildTabs() { if ($this->_interviewerId) { $urlParams .= "&cid={$this->_interviewerId}"; } - $allTabs[$name] = array( + $allTabs[$name] = [ 'title' => $title, 'valid' => TRUE, 'active' => TRUE, 'link' => CRM_Utils_System::url('civicrm/campaign/vote', $urlParams), - ); + ]; } $this->assign('tabHeader', empty($allTabs) ? FALSE : $allTabs); diff --git a/CRM/Campaign/PseudoConstant.php b/CRM/Campaign/PseudoConstant.php index a71ba31db0fe..e121a86d5b6f 100644 --- a/CRM/Campaign/PseudoConstant.php +++ b/CRM/Campaign/PseudoConstant.php @@ -1,9 +1,9 @@ fetch()) { $this->_query->convertToPseudoNames($result); - $row = array(); + $row = []; // the columns we are interested in foreach (self::$_properties as $property) { if (property_exists($result, $property)) { @@ -267,34 +267,37 @@ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) { */ public function buildPrevNextCache($sort) { //for prev/next pagination - $crmPID = CRM_Utils_Request::retrieve('crmPID', 'Integer', CRM_Core_DAO::$_nullObject); + $crmPID = CRM_Utils_Request::retrieve('crmPID', 'Integer'); if (!$crmPID) { $cacheKey = "civicrm search {$this->_key}"; - CRM_Core_BAO_PrevNextCache::deleteItem(NULL, $cacheKey, 'civicrm_contact'); + Civi::service('prevnext')->deleteItem(NULL, $cacheKey, 'civicrm_contact'); - $sql = $this->_query->searchQuery(0, 0, $sort, + $sql = $this->_query->getSearchSQLParts(0, 0, $sort, FALSE, FALSE, FALSE, FALSE, - TRUE, $this->_campaignWhereClause, + $this->_campaignWhereClause, NULL, $this->_campaignFromClause ); - list($select, $from) = explode(' FROM ', $sql); - $insertSQL = " -INSERT INTO civicrm_prevnext_cache ( entity_table, entity_id1, entity_id2, cacheKey, data ) -SELECT 'civicrm_contact', contact_a.id, contact_a.id, '$cacheKey', contact_a.display_name -FROM {$from} + + $selectSQL = " + SELECT %1, contact_a.id, contact_a.display_name +FROM {$sql['from']} "; - $errorScope = CRM_Core_TemporaryErrorScope::ignoreException(); - $result = CRM_Core_DAO::executeQuery($insertSQL); - unset($errorScope); - if (is_a($result, 'DB_Error')) { + try { + Civi::service('prevnext')->fillWithSql($cacheKey, $selectSQL, [1 => [$cacheKey, 'String']]); + } + catch (CRM_Core_Exception $e) { + // Heavy handed, no? Seems like this merits an explanation. return; } - // also record an entry in the cache key table, so we can delete it periodically - CRM_Core_BAO_Cache::setItem($cacheKey, 'CiviCRM Search PrevNextCache', $cacheKey); + + if (Civi::service('prevnext') instanceof CRM_Core_PrevNextCache_Sql) { + // SQL-backed prevnext cache uses an extra record for pruning the cache. + CRM_Core_BAO_Cache::setItem($cacheKey, 'CiviCRM Search PrevNextCache', $cacheKey); + } } } @@ -319,40 +322,40 @@ public function getQILL() { * the column headers that need to be displayed */ public function &getColumnHeaders($action = NULL, $output = NULL) { - self::$_columnHeaders = array(); + self::$_columnHeaders = []; if (!$this->_single) { - $contactDetails = array( - array( + $contactDetails = [ + [ 'name' => ts('Contact Name'), 'sort' => 'sort_name', 'direction' => CRM_Utils_Sort::ASCENDING, - ), - array( + ], + [ 'name' => ts('Street Number'), 'sort' => 'street_number', - ), - array( + ], + [ 'name' => ts('Street Name'), 'sort' => 'street_name', - ), - array('name' => ts('Street Address')), - array( + ], + ['name' => ts('Street Address')], + [ 'name' => ts('City'), 'sort' => 'city', - ), - array( + ], + [ 'name' => ts('Postal Code'), 'sort' => 'postal_code', - ), - array( + ], + [ 'name' => ts('State'), 'sort' => 'state_province_name', - ), - array('name' => ts('Country')), - array('name' => ts('Email')), - array('name' => ts('Phone')), - ); + ], + ['name' => ts('Country')], + ['name' => ts('Email')], + ['name' => ts('Phone')], + ]; self::$_columnHeaders = array_merge($contactDetails, self::$_columnHeaders); } diff --git a/CRM/Campaign/StateMachine/Search.php b/CRM/Campaign/StateMachine/Search.php index 68352f244d36..6d38b3aa5f61 100644 --- a/CRM/Campaign/StateMachine/Search.php +++ b/CRM/Campaign/StateMachine/Search.php @@ -1,9 +1,9 @@ _pages = array(); + $this->_pages = []; $this->_pages['CRM_Campaign_Form_Search'] = NULL; list($task, $result) = $this->taskName($controller, 'Search'); @@ -78,7 +78,7 @@ public function __construct($controller, $action = CRM_Core_Action::NONE) { * * @param string $formName * - * @return string + * @return array * the name of the form that will handle the task */ public function taskName($controller, $formName = 'Search') { diff --git a/CRM/Campaign/Task.php b/CRM/Campaign/Task.php index a6c1c64eb3fe..6e4367b0a6f8 100644 --- a/CRM/Campaign/Task.php +++ b/CRM/Campaign/Task.php @@ -1,9 +1,9 @@ array( + self::$_tasks = [ + self::INTERVIEW => [ 'title' => ts('Record Respondents Interview'), - 'class' => array( + 'class' => [ 'CRM_Campaign_Form_Task_Interview', 'CRM_Campaign_Form_Task_Release', - ), + ], 'result' => FALSE, - ), - 2 => array( + ], + self::RESERVE => [ 'title' => ts('Reserve Respondents'), - 'class' => array( + 'class' => [ 'CRM_Campaign_Form_Task_Reserve', 'CRM_Campaign_Form_Task_Interview', 'CRM_Campaign_Form_Task_Release', - ), + ], 'result' => FALSE, - ), - 3 => array( + ], + self::RELEASE => [ 'title' => ts('Release Respondents'), 'class' => 'CRM_Campaign_Form_Task_Release', 'result' => FALSE, - ), - 4 => array( + ], + self::TASK_PRINT => [ 'title' => ts('Print Respondents'), 'class' => 'CRM_Campaign_Form_Task_Print', 'result' => FALSE, - ), - ); - } - - CRM_Utils_Hook::searchTasks('campaign', self::$_tasks); - - asort(self::$_tasks); - - return self::$_tasks; - } + ], + ]; - /** - * These tasks are the core set of task titles - * on voters. - * - * @return array - * the set of task titles - */ - public static function &taskTitles() { - self::tasks(); - $titles = array(); - foreach (self::$_tasks as $id => $value) { - $titles[$id] = $value['title']; + parent::tasks(); } - return $titles; + return self::$_tasks; } /** @@ -122,13 +101,15 @@ public static function &taskTitles() { * of the user * * @param int $permission + * @param array $params * * @return array * set of tasks that are valid for the user */ - public static function &permissionedTaskTitles($permission) { + public static function permissionedTaskTitles($permission, $params = []) { $tasks = self::taskTitles(); + $tasks = parent::corePermissionedTaskTitles($tasks, $permission, $params); return $tasks; } @@ -144,14 +125,14 @@ public static function &permissionedTaskTitles($permission) { public static function getTask($value) { self::tasks(); if (!$value || !CRM_Utils_Array::value($value, self::$_tasks)) { - // make the interview task by default - $value = 1; + // Set the interview task as default + $value = self::INTERVIEW; } - return array( + return [ self::$_tasks[$value]['class'], self::$_tasks[$value]['result'], - ); + ]; } } diff --git a/CRM/Case/Audit/Audit.php b/CRM/Case/Audit/Audit.php index 1928fa4e4383..f58660fef31b 100644 --- a/CRM/Case/Audit/Audit.php +++ b/CRM/Case/Audit/Audit.php @@ -22,7 +22,7 @@ public function __construct($xmlString, $confFilename) { * @return array */ public function getActivities($printReport = FALSE) { - $retval = array(); + $retval = []; /* * Loop through the activities in the file and add them to the appropriate region array. @@ -41,16 +41,16 @@ public function getActivities($printReport = FALSE) { $activityindex = 0; $activityList = $doc->getElementsByTagName("Activity"); - $caseActivities = array(); - $activityStatusType = array(); + $caseActivities = []; + $activityStatusType = []; foreach ($activityList as $activity) { - $retval[$activityindex] = array(); + $retval[$activityindex] = []; - $ifBlankReplacements = array(); + $ifBlankReplacements = []; $completed = FALSE; - $sortValues = array('1970-01-01'); + $sortValues = ['1970-01-01']; $category = ''; $fieldindex = 1; $fields = $activity->getElementsByTagName("Field"); @@ -88,7 +88,7 @@ public function getActivities($printReport = FALSE) { } if ($this->auditConfig->includeInRegion($label, $region)) { - $retval[$activityindex][$region][$fieldindex] = array(); + $retval[$activityindex][$region][$fieldindex] = []; $retval[$activityindex][$region][$fieldindex]['label'] = $label; $retval[$activityindex][$region][$fieldindex]['datatype'] = $datatype; $retval[$activityindex][$region][$fieldindex]['value'] = $value; @@ -98,18 +98,18 @@ public function getActivities($printReport = FALSE) { //CRM-4570 if ($printReport) { - if (!in_array($label, array( + if (!in_array($label, [ 'Activity Type', 'Status', - )) + ]) ) { - $caseActivities[$activityindex][$fieldindex] = array(); + $caseActivities[$activityindex][$fieldindex] = []; $caseActivities[$activityindex][$fieldindex]['label'] = $label; $caseActivities[$activityindex][$fieldindex]['datatype'] = $datatype; $caseActivities[$activityindex][$fieldindex]['value'] = $value; } else { - $activityStatusType[$activityindex][$fieldindex] = array(); + $activityStatusType[$activityindex][$fieldindex] = []; $activityStatusType[$activityindex][$fieldindex]['label'] = $label; $activityStatusType[$activityindex][$fieldindex]['datatype'] = $datatype; $activityStatusType[$activityindex][$fieldindex]['value'] = $value; @@ -166,10 +166,10 @@ public function getActivities($printReport = FALSE) { } if ($printReport) { - @uasort($caseActivities, array($this, "compareActivities")); + @uasort($caseActivities, [$this, "compareActivities"]); } else { - @uasort($retval, array($this, "compareActivities")); + @uasort($retval, [$this, "compareActivities"]); } } diff --git a/CRM/Case/Audit/AuditConfig.php b/CRM/Case/Audit/AuditConfig.php index 14bc772ff168..be1ee8a08d47 100644 --- a/CRM/Case/Audit/AuditConfig.php +++ b/CRM/Case/Audit/AuditConfig.php @@ -22,8 +22,8 @@ public function __construct($filename) { // set some defaults $this->completionLabel = "Status"; $this->completionValue = "Completed"; - $this->sortByLabels = array("Actual Date", "Due Date"); - $this->ifBlanks = array(); + $this->sortByLabels = ["Actual Date", "Due Date"]; + $this->ifBlanks = []; $this->loadConfig(); } @@ -57,22 +57,24 @@ public function getIfBlanks() { } public function loadConfig() { - $this->regionFieldList = array(); - $this->includeRules = array(); + $this->regionFieldList = []; + $this->includeRules = []; $doc = new DOMDocument(); - if ($doc->load(dirname(__FILE__) . '/' . $this->filename)) { + $xmlString = file_get_contents(dirname(__FILE__) . '/' . $this->filename); + $load = $doc->loadXML($xmlString); + if ($load) { $regions = $doc->getElementsByTagName("region"); foreach ($regions as $region) { $regionName = $region->getAttribute("name"); - $this->regionFieldList[$regionName] = array(); + $this->regionFieldList[$regionName] = []; // Inclusion/exclusion settings $includeRule = $region->getAttribute("includeRule"); if (empty($includeRule)) { $includeRule = 'include'; } - $this->includeRules[$regionName] = array('rule' => $includeRule); + $this->includeRules[$regionName] = ['rule' => $includeRule]; if ($includeRule == 'exclude') { $altRegion = $region->getAttribute("exclusionCorrespondingRegion"); $this->includeRules[$regionName]['altRegion'] = $altRegion; @@ -122,7 +124,7 @@ public function loadConfig() { $sortElement = $doc->getElementsByTagName("sortByLabels"); if (!empty($sortElement)) { - $this->sortByLabels = array(); + $this->sortByLabels = []; $label_elements = $sortElement->item(0)->getElementsByTagName("label"); foreach ($label_elements as $ele) { $this->sortByLabels[] = $ele->nodeValue; diff --git a/CRM/Case/BAO/Case.php b/CRM/Case/BAO/Case.php index 3af84c56a496..091b4e9f046f 100644 --- a/CRM/Case/BAO/Case.php +++ b/CRM/Case/BAO/Case.php @@ -1,9 +1,9 @@ copyValues($params); - return $caseDAO->save(); + $result = $caseDAO->save(); + // Get other case values (required by XML processor), this adds to $result array + $caseDAO->find(TRUE); + return $result; } /** @@ -87,6 +90,15 @@ public static function add(&$params) { * @return CRM_Case_BAO_Case */ public static function &create(&$params) { + // CRM-20958 - These fields are managed by MySQL triggers. Watch out for clients resaving stale timestamps. + unset($params['created_date']); + unset($params['modified_date']); + $caseStatus = CRM_Case_PseudoConstant::caseStatus('name'); + // for resolved case the end date should set to now + if (!empty($params['status_id']) && $params['status_id'] == array_search('Closed', $caseStatus)) { + $params['end_date'] = date("Ymd"); + } + $transaction = new CRM_Core_Transaction(); if (!empty($params['id'])) { @@ -263,15 +275,16 @@ public static function enableDisableCaseRelationships($caseId, $enable) { * ID of the case. * * @param int $contactID + * @param int $startArrayAt This is to support legacy calls to Case.Get API which may rely on the first array index being set to 1 * * @return array */ - public static function retrieveContactIdsByCaseId($caseId, $contactID = NULL) { + public static function retrieveContactIdsByCaseId($caseId, $contactID = NULL, $startArrayAt = 0) { $caseContact = new CRM_Case_DAO_CaseContact(); $caseContact->case_id = $caseId; $caseContact->find(); $contactArray = array(); - $count = 1; + $count = $startArrayAt; while ($caseContact->fetch()) { if ($contactID != $caseContact->contact_id) { $contactArray[$count] = $caseContact->contact_id; @@ -329,7 +342,8 @@ public static function getContactNames($caseId) { LEFT JOIN civicrm_case_contact ON civicrm_case_contact.contact_id = contact_a.id LEFT JOIN civicrm_email ce ON ( ce.contact_id = contact_a.id AND ce.is_primary = 1) LEFT JOIN civicrm_phone cp ON ( cp.contact_id = contact_a.id AND cp.is_primary = 1) - WHERE civicrm_case_contact.case_id = %1"; + WHERE contact_a.is_deleted = 0 AND civicrm_case_contact.case_id = %1 + ORDER BY civicrm_case_contact.id"; $dao = CRM_Core_DAO::executeQuery($query, array(1 => array($caseId, 'Integer')) @@ -383,7 +397,6 @@ public static function retrieveCaseIdsByContactId($contactID, $includeDeleted = $caseArray[] = $dao->id; } - $dao->free(); return $caseArray; } @@ -394,51 +407,65 @@ public static function retrieveCaseIdsByContactId($contactID, $includeDeleted = * * @return string */ - public static function getCaseActivityQuery($type = 'upcoming', $userID = NULL, $condition = NULL) { - if (!$userID) { - $session = CRM_Core_Session::singleton(); - $userID = $session->get('userID'); - } - - $query = "SELECT -civicrm_case.id as case_id, -civicrm_case.subject as case_subject, -civicrm_contact.id as contact_id, -civicrm_contact.sort_name as sort_name, -civicrm_phone.phone as phone, -civicrm_contact.contact_type as contact_type, -civicrm_contact.contact_sub_type as contact_sub_type, -t_act.activity_type_id, -c_type.title as case_type, -civicrm_case.case_type_id as case_type_id, -cov_status.label as case_status, -cov_status.label as case_status_name, -t_act.status_id, -civicrm_case.start_date as case_start_date, -case_relation_type.label_b_a as case_role, "; + public static function getCaseActivityCountQuery($type = 'upcoming', $userID, $condition = NULL) { + return sprintf(" SELECT COUNT(*) FROM (%s) temp ", self::getCaseActivityQuery($type, $userID, $condition)); + } + + /** + * @param string $type + * @param int $userID + * @param string $condition + * @param string $limit + * @param string $order + * + * @return string + */ + public static function getCaseActivityQuery($type = 'upcoming', $userID, $condition = NULL, $limit = NULL, $order = NULL) { + $selectClauses = array( + 'civicrm_case.id as case_id', + 'civicrm_case.subject as case_subject', + 'civicrm_contact.id as contact_id', + 'civicrm_contact.sort_name as sort_name', + 'civicrm_phone.phone as phone', + 'civicrm_contact.contact_type as contact_type', + 'civicrm_contact.contact_sub_type as contact_sub_type', + 't_act.activity_type_id', + 'c_type.title as case_type', + 'civicrm_case.case_type_id as case_type_id', + 'cov_status.label as case_status', + 'cov_status.label as case_status_name', + 't_act.status_id', + 'civicrm_case.start_date as case_start_date', + 'case_relation_type.label_b_a as case_role', + ); if ($type == 'upcoming') { - $query .= " -t_act.desired_date as case_scheduled_activity_date, -t_act.id as case_scheduled_activity_id, -t_act.act_type_name as case_scheduled_activity_type_name, -t_act.act_type AS case_scheduled_activity_type "; + $selectClauses = array_merge($selectClauses, array( + 't_act.desired_date as case_scheduled_activity_date', + 't_act.id as case_scheduled_activity_id', + 't_act.act_type_name as case_scheduled_activity_type_name', + 't_act.act_type AS case_scheduled_activity_type', + )); } elseif ($type == 'recent') { - $query .= " -t_act.desired_date as case_recent_activity_date, -t_act.id as case_recent_activity_id, -t_act.act_type_name as case_recent_activity_type_name, -t_act.act_type AS case_recent_activity_type "; + $selectClauses = array_merge($selectClauses, array( + 't_act.desired_date as case_recent_activity_date', + 't_act.id as case_recent_activity_id', + 't_act.act_type_name as case_recent_activity_type_name', + 't_act.act_type AS case_recent_activity_type', + )); } elseif ($type == 'any') { - $query .= " -t_act.desired_date as case_activity_date, -t_act.id as case_activity_id, -t_act.act_type_name as case_activity_type_name, -t_act.act_type AS case_activity_type "; + $selectClauses = array_merge($selectClauses, array( + 't_act.desired_date as case_activity_date', + 't_act.id as case_activity_id', + 't_act.act_type_name as case_activity_type_name', + 't_act.act_type AS case_activity_type', + )); } + $query = CRM_Contact_BAO_Query::appendAnyValueToSelect($selectClauses, 'case_id'); + $query .= " FROM civicrm_case INNER JOIN civicrm_case_contact ON civicrm_case.id = civicrm_case_contact.case_id INNER JOIN civicrm_contact ON civicrm_case_contact.contact_id = civicrm_contact.id "; @@ -502,9 +529,7 @@ public static function getCaseActivityQuery($type = 'upcoming', $userID = NULL, ON t_act.case_id = civicrm_case.id LEFT JOIN civicrm_phone ON (civicrm_phone.contact_id = civicrm_contact.id AND civicrm_phone.is_primary=1) LEFT JOIN civicrm_relationship case_relationship - ON ( case_relationship.contact_id_a = civicrm_case_contact.contact_id AND case_relationship.contact_id_b = {$userID} - AND case_relationship.case_id = civicrm_case.id ) - + ON ( case_relationship.contact_id_a = civicrm_case_contact.contact_id AND case_relationship.contact_id_b = {$userID} AND case_relationship.is_active AND case_relationship.case_id = civicrm_case.id ) LEFT JOIN civicrm_relationship_type case_relation_type ON ( case_relation_type.id = case_relationship.relationship_type_id AND case_relation_type.id = case_relationship.relationship_type_id ) @@ -522,17 +547,27 @@ public static function getCaseActivityQuery($type = 'upcoming', $userID = NULL, if ($condition) { // CRM-8749 backwards compatibility - callers of this function expect to start $condition with "AND" - $query .= " WHERE (1) $condition "; + $query .= " WHERE (1) AND $condition "; } + $query .= " GROUP BY case_id "; - if ($type == 'upcoming') { - $query .= " ORDER BY case_scheduled_activity_date ASC "; + if ($order) { + $query .= $order; } - elseif ($type == 'recent') { - $query .= " ORDER BY case_recent_activity_date ASC "; + else { + if ($type == 'upcoming') { + $query .= " ORDER BY case_scheduled_activity_date ASC "; + } + elseif ($type == 'recent') { + $query .= " ORDER BY case_recent_activity_date ASC "; + } + elseif ($type == 'any') { + $query .= " ORDER BY case_activity_date ASC "; + } } - elseif ($type == 'any') { - $query .= " ORDER BY case_activity_date ASC "; + + if ($limit) { + $query .= $limit; } return $query; @@ -542,92 +577,93 @@ public static function getCaseActivityQuery($type = 'upcoming', $userID = NULL, * Retrieve cases related to particular contact or whole contact used in Dashboard and Tab. * * @param bool $allCases - * - * @param int $userID - * - * @param string $type - * /upcoming,recent,all/. - * + * @param array $params * @param string $context + * @param bool $getCount * * @return array * Array of Cases */ - public static function getCases($allCases = TRUE, $userID = NULL, $type = 'upcoming', $context = 'dashboard') { + public static function getCases($allCases = TRUE, $params = array(), $context = 'dashboard', $getCount = FALSE) { $condition = NULL; $casesList = array(); - //validate access for own cases. + // validate access for own cases. if (!self::accessCiviCase()) { - return $casesList; + return $getCount ? 0 : $casesList; } - if (!$userID) { - $session = CRM_Core_Session::singleton(); - $userID = $session->get('userID'); + // Return cached value instead of re-running query + if (isset(Civi::$statics[__CLASS__]['totalCount']) && $getCount) { + return Civi::$statics[__CLASS__]['totalCount']; } - //validate access for all cases. + $type = CRM_Utils_Array::value('type', $params, 'upcoming'); + $userID = CRM_Core_Session::singleton()->get('userID'); + + $caseActivityTypeColumn = 'case_activity_type_name'; + $caseActivityDateColumn = 'case_activity_date'; + $caseActivityIDColumn = 'case_activity_id'; + if ($type == 'upcoming') { + $caseActivityDateColumn = 'case_scheduled_activity_date'; + $caseActivityTypeColumn = 'case_scheduled_activity_type'; + $caseActivityIDColumn = 'case_scheduled_activity_id'; + } + elseif ($type == 'recent') { + $caseActivityDateColumn = 'case_recent_activity_date'; + $caseActivityTypeColumn = 'case_recent_activity_type'; + $caseActivityIDColumn = 'case_recent_activity_id'; + } + + // validate access for all cases. if ($allCases && !CRM_Core_Permission::check('access all cases and activities')) { $allCases = FALSE; } - $condition = " AND civicrm_case.is_deleted = 0 AND civicrm_contact.is_deleted <> 1"; + $whereClauses = array('civicrm_case.is_deleted = 0 AND civicrm_contact.is_deleted <> 1'); if (!$allCases) { - $condition .= " AND case_relationship.contact_id_b = {$userID} "; + $whereClauses[] .= " case_relationship.contact_id_b = {$userID} AND case_relationship.is_active "; } - if ($type == 'upcoming' || $type == 'any') { - $closedId = CRM_Core_OptionGroup::getValue('case_status', 'Closed', 'name'); - $condition .= " -AND civicrm_case.status_id != $closedId"; + if (empty($params['status_id']) && ($type == 'upcoming' || $type == 'any')) { + $whereClauses[] = " civicrm_case.status_id != " . CRM_Core_PseudoConstant::getKey('CRM_Case_BAO_Case', 'case_status_id', 'Closed'); } - $query = self::getCaseActivityQuery($type, $userID, $condition); - - $queryParams = array(); - $result = CRM_Core_DAO::executeQuery($query, - $queryParams - ); - - $caseStatus = CRM_Core_OptionGroup::values('case_status', FALSE, FALSE, FALSE, " AND v.name = 'Urgent' "); - - $resultFields = array( - 'contact_id', - 'contact_type', - 'sort_name', - 'phone', - 'case_id', - 'case_subject', - 'case_type', - 'case_type_id', - 'status_id', - 'case_status', - 'case_status_name', - 'activity_type_id', - 'case_start_date', - 'case_role', - ); + foreach (array('case_type_id', 'status_id') as $column) { + if (!empty($params[$column])) { + $whereClauses[] = sprintf("civicrm_case.%s IN (%s)", $column, $params[$column]); + } + } + $condition = implode(' AND ', $whereClauses); - if ($type == 'upcoming') { - $resultFields[] = 'case_scheduled_activity_date'; - $resultFields[] = 'case_scheduled_activity_type_name'; - $resultFields[] = 'case_scheduled_activity_type'; - $resultFields[] = 'case_scheduled_activity_id'; + Civi::$statics[__CLASS__]['totalCount'] = $totalCount = CRM_Core_DAO::singleValueQuery(self::getCaseActivityCountQuery($type, $userID, $condition)); + if ($getCount) { + return $totalCount; } - elseif ($type == 'recent') { - $resultFields[] = 'case_recent_activity_date'; - $resultFields[] = 'case_recent_activity_type_name'; - $resultFields[] = 'case_recent_activity_type'; - $resultFields[] = 'case_recent_activity_id'; + $casesList['total'] = $totalCount; + + $limit = ''; + if (!empty($params['rp'])) { + $params['offset'] = ($params['page'] - 1) * $params['rp']; + $params['rowCount'] = $params['rp']; + if (!empty($params['rowCount']) && $params['rowCount'] > 0) { + $limit = " LIMIT {$params['offset']}, {$params['rowCount']} "; + } } - elseif ($type == 'any') { - $resultFields[] = 'case_activity_date'; - $resultFields[] = 'case_activity_type_name'; - $resultFields[] = 'case_activity_type'; - $resultFields[] = 'case_activity_id'; + + $order = NULL; + if (!empty($params['sortBy'])) { + if (strstr($params['sortBy'], 'date ')) { + $params['sortBy'] = str_replace('date', $caseActivityDateColumn, $params['sortBy']); + } + $order = "ORDER BY " . $params['sortBy']; } + $query = self::getCaseActivityQuery($type, $userID, $condition, $limit, $order); + $result = CRM_Core_DAO::executeQuery($query); + + $caseStatus = CRM_Core_OptionGroup::values('case_status', FALSE, FALSE, FALSE, " AND v.name = 'Urgent' "); + // we're going to use the usual actions, so doesn't make sense to duplicate definitions $actions = CRM_Case_Selector_Search::links(); @@ -643,68 +679,75 @@ public static function getCases($allCases = TRUE, $userID = NULL, $type = 'upcom } $mask = CRM_Core_Action::mask($permissions); - while ($result->fetch()) { - foreach ($resultFields as $donCare => $field) { - $casesList[$result->case_id][$field] = $result->$field; - if ($field == 'contact_type') { - $casesList[$result->case_id]['contact_type_icon'] = CRM_Contact_BAO_Contact_Utils::getImage($result->contact_sub_type ? $result->contact_sub_type : $result->contact_type - ); - $casesList[$result->case_id]['action'] = CRM_Core_Action::formLink($actions['primaryActions'], $mask, - array( - 'id' => $result->case_id, - 'cid' => $result->contact_id, - 'cxt' => $context, - ), - ts('more'), - FALSE, - 'case.actions.primary', - 'Case', - $result->case_id - ); - $casesList[$result->case_id]['moreActions'] = CRM_Core_Action::formLink($actions['moreActions'], - $mask, - array( - 'id' => $result->case_id, - 'cid' => $result->contact_id, - 'cxt' => $context, - ), - ts('more'), - TRUE, - 'case.actions.more', - 'Case', - $result->case_id - ); - } - elseif ($field == 'case_status') { - if (in_array($result->$field, $caseStatus)) { - $casesList[$result->case_id]['class'] = "status-urgent"; + $caseTypes = CRM_Case_PseudoConstant::caseType('name'); + foreach ($result->fetchAll() as $case) { + $key = $case['case_id']; + $casesList[$key] = array(); + $casesList[$key]['DT_RowId'] = $case['case_id']; + $casesList[$key]['DT_RowAttr'] = array('data-entity' => 'case', 'data-id' => $case['case_id']); + $casesList[$key]['DT_RowClass'] = "crm-entity"; + + $casesList[$key]['activity_list'] = sprintf('', + ts('Activities'), + CRM_Utils_System::url('civicrm/case/details', array('caseId' => $case['case_id'], 'cid' => $case['contact_id'], 'type' => $type)) + ); + + $phone = empty($case['phone']) ? '' : '
' . $case['phone'] . ''; + $casesList[$key]['contact_id'] = sprintf('%s%s
%s: %d', + CRM_Utils_System::url('civicrm/contact/view', array('cid' => $case['contact_id'])), + $case['sort_name'], + $phone, + ts('Case ID'), + $case['case_id'] + ); + $casesList[$key]['subject'] = $case['case_subject']; + $casesList[$key]['case_status'] = in_array($case['case_status'], $caseStatus) ? sprintf('%s', strtoupper($case['case_status'])) : $case['case_status']; + $casesList[$key]['case_type'] = $case['case_type']; + $casesList[$key]['case_role'] = CRM_Utils_Array::value('case_role', $case, '---'); + $casesList[$key]['manager'] = self::getCaseManagerContact($caseTypes[$case['case_type_id']], $case['case_id']); + + $casesList[$key]['date'] = $case[$caseActivityTypeColumn]; + if (($actId = CRM_Utils_Array::value('case_scheduled_activity_id', $case)) || + ($actId = CRM_Utils_Array::value('case_recent_activity_id', $case)) + ) { + if (self::checkPermission($actId, 'view', $case['activity_type_id'], $userID)) { + if ($type == 'recent') { + $casesList[$key]['date'] = sprintf('%s', + CRM_Utils_System::url('civicrm/case/activity/view', array('reset' => 1, 'cid' => $case['contact_id'], 'aid' => $case[$caseActivityIDColumn])), + ts('View activity'), + $case[$caseActivityTypeColumn] + ); } else { - $casesList[$result->case_id]['class'] = "status-normal"; + $status = CRM_Utils_Date::overdue($case[$caseActivityDateColumn]) ? 'status-overdue' : 'status-scheduled'; + $casesList[$key]['date'] = sprintf('%s   ', + $status, + CRM_Utils_System::url('civicrm/case/activity/view', array('reset' => 1, 'cid' => $case['contact_id'], 'aid' => $case[$caseActivityIDColumn])), + ts('View activity'), + $case[$caseActivityTypeColumn] + ); } } + if (isset($case['activity_type_id']) && self::checkPermission($actId, 'edit', $case['activity_type_id'], $userID)) { + $casesList[$key]['date'] .= sprintf('', + CRM_Utils_System::url('civicrm/case/activity', array('reset' => 1, 'cid' => $case['contact_id'], 'caseid' => $case['case_id'], 'action' => 'update', 'id' => $actId)), + ts('Edit activity') + ); + } } - //CRM-4510. - $caseTypes = CRM_Case_PseudoConstant::caseType('name'); - $caseManagerContact = self::getCaseManagerContact($caseTypes[$result->case_type_id], $result->case_id); - if (!empty($caseManagerContact)) { - $casesList[$result->case_id]['casemanager_id'] = CRM_Utils_Array::value('casemanager_id', $caseManagerContact); - $casesList[$result->case_id]['casemanager'] = CRM_Utils_Array::value('casemanager', $caseManagerContact); - } - - //do check user permissions for edit/view activity. - if (($actId = CRM_Utils_Array::value('case_scheduled_activity_id', $casesList[$result->case_id])) || - ($actId = CRM_Utils_Array::value('case_recent_activity_id', $casesList[$result->case_id])) - ) { - $casesList[$result->case_id]["case_{$type}_activity_editable"] = self::checkPermission($actId, - 'edit', - $casesList[$result->case_id]['activity_type_id'], $userID - ); - $casesList[$result->case_id]["case_{$type}_activity_viewable"] = self::checkPermission($actId, - 'view', - $casesList[$result->case_id]['activity_type_id'], $userID - ); - } + $casesList[$key]['date'] .= "
" . CRM_Utils_Date::customFormat($case[$caseActivityDateColumn]); + $casesList[$key]['links'] = CRM_Core_Action::formLink($actions['primaryActions'], $mask, + array( + 'id' => $case['case_id'], + 'cid' => $case['contact_id'], + 'cxt' => $context, + ), + ts('more'), + FALSE, + 'case.actions.primary', + 'Case', + $case['case_id'] + ); } return $casesList; @@ -714,10 +757,9 @@ public static function getCases($allCases = TRUE, $userID = NULL, $type = 'upcom * Get the summary of cases counts by type and status. * * @param bool $allCases - * @param int $userID * @return array */ - public static function getCasesSummary($allCases = TRUE, $userID) { + public static function getCasesSummary($allCases = TRUE) { $caseSummary = array(); //validate access for civicase. @@ -725,6 +767,8 @@ public static function getCasesSummary($allCases = TRUE, $userID) { return $caseSummary; } + $userID = CRM_Core_Session::singleton()->get('userID'); + //validate access for all cases. if ($allCases && !CRM_Core_Permission::check('access all cases and activities')) { $allCases = FALSE; @@ -754,7 +798,7 @@ public static function getCasesSummary($allCases = TRUE, $userID) { else { $all = 0; $case_owner = 2; - $myCaseWhereClause = " AND case_relationship.contact_id_b = {$userID}"; + $myCaseWhereClause = " AND case_relationship.contact_id_b = {$userID} AND case_relationship.is_active "; $myGroupByClause = " GROUP BY CONCAT(case_relationship.case_id,'-',case_relationship.contact_id_b)"; } $myGroupByClause .= ", case_status.label, status_id, case_type_id"; @@ -770,7 +814,7 @@ public static function getCasesSummary($allCases = TRUE, $userID) { LEFT JOIN civicrm_option_value case_status ON ( civicrm_case.status_id = case_status.value AND option_group_case_status.id = case_status.option_group_id ) LEFT JOIN civicrm_relationship case_relationship ON ( case_relationship.case_id = civicrm_case.id - AND case_relationship.contact_id_b = {$userID}) + AND case_relationship.contact_id_b = {$userID} AND case_relationship.is_active ) WHERE is_deleted = 0 AND cc.contact_id IN (SELECT id FROM civicrm_contact WHERE is_deleted <> 1) {$myCaseWhereClause} {$myGroupByClause}"; @@ -801,12 +845,13 @@ public static function getCasesSummary($allCases = TRUE, $userID) { * @param int $caseID * Case id. * @param int $relationshipID + * @param bool $activeOnly * * @return array * case role / relationships * */ - public static function getCaseRoles($contactID, $caseID, $relationshipID = NULL) { + public static function getCaseRoles($contactID, $caseID, $relationshipID = NULL, $activeOnly = TRUE) { $query = ' SELECT rel.id as civicrm_relationship_id, con.sort_name as sort_name, @@ -818,11 +863,15 @@ public static function getCaseRoles($contactID, $caseID, $relationshipID = NULL) IF(rel.contact_id_a = %1, "a_b", "b_a") as relationship_direction FROM civicrm_relationship rel INNER JOIN civicrm_relationship_type ON rel.relationship_type_id = civicrm_relationship_type.id - INNER JOIN civicrm_contact con ON ((con.id <> %1 AND con.id IN (rel.contact_id_a, rel.contact_id_b)) OR (con.id = %1 AND rel.contact_id_b = rel.contact_id_a AND rel.contact_id_a = %1)) + INNER JOIN civicrm_contact con ON ((con.id <> %1 AND con.id IN (rel.contact_id_a, rel.contact_id_b)) OR (con.id = %1 AND rel.contact_id_b = rel.contact_id_a AND rel.contact_id_a = %1 AND rel.is_active)) LEFT JOIN civicrm_phone ON (civicrm_phone.contact_id = con.id AND civicrm_phone.is_primary = 1) LEFT JOIN civicrm_email ON (civicrm_email.contact_id = con.id AND civicrm_email.is_primary = 1) WHERE (rel.contact_id_a = %1 OR rel.contact_id_b = %1) AND rel.case_id = %2 - AND rel.is_active = 1 AND (rel.end_date IS NULL OR rel.end_date > NOW())'; + AND con.is_deleted = 0'; + + if ($activeOnly) { + $query .= ' AND rel.is_active = 1 AND (rel.end_date IS NULL OR rel.end_date > NOW())'; + } $params = array( 1 => array($contactID, 'Positive'), @@ -830,7 +879,7 @@ public static function getCaseRoles($contactID, $caseID, $relationshipID = NULL) ); if ($relationshipID) { - $query .= ' AND civicrm_relationship.id = %3 '; + $query .= ' AND rel.id = %3 '; $params[3] = array($relationshipID, 'Integer'); } $dao = CRM_Core_DAO::executeQuery($query, $params); @@ -849,7 +898,6 @@ public static function getCaseRoles($contactID, $caseID, $relationshipID = NULL) $values[$rid]['relationship_direction'] = $dao->relationship_direction; } - $dao->free(); return $values; } @@ -865,16 +913,14 @@ public static function getCaseRoles($contactID, $caseID, $relationshipID = NULL) * * @param null $context * @param int $userID - * @param null $type + * @param null $type (deprecated) * * @return array * Array of case activities * */ public static function getCaseActivity($caseID, &$params, $contactID, $context = NULL, $userID = NULL, $type = NULL) { - $values = array(); - - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts); $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts); $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts); @@ -1017,226 +1063,235 @@ public static function getCaseActivity($caseID, &$params, $contactID, $context = $caseCount = CRM_Core_DAO::singleValueQuery('SELECT FOUND_ROWS()'); $activityTypes = CRM_Case_PseudoConstant::caseActivityType(FALSE, TRUE); - $activityStatuses = CRM_Core_PseudoConstant::activityStatus(); - $url = CRM_Utils_System::url("civicrm/case/activity", - "reset=1&cid={$contactID}&caseid={$caseID}", FALSE, NULL, FALSE - ); - - $contextUrl = ''; - if ($context == 'fulltext') { - $contextUrl = "&context={$context}"; - } - $editUrl = "{$url}&action=update{$contextUrl}"; - $deleteUrl = "{$url}&action=delete{$contextUrl}"; - $restoreUrl = "{$url}&action=renew{$contextUrl}"; - $viewTitle = ts('View activity'); - $statusTitle = ts('Edit Status'); - - $emailActivityTypeIDs = array( - 'Email' => CRM_Core_OptionGroup::getValue('activity_type', - 'Email', - 'name' - ), - 'Inbound Email' => CRM_Core_OptionGroup::getValue('activity_type', - 'Inbound Email', - 'name' - ), + $compStatusValues = array_keys( + CRM_Activity_BAO_Activity::getStatusesByType(CRM_Activity_BAO_Activity::COMPLETED) + + CRM_Activity_BAO_Activity::getStatusesByType(CRM_Activity_BAO_Activity::CANCELLED) ); - $emailActivityTypeIDs = array( - 'Email' => CRM_Core_OptionGroup::getValue('activity_type', - 'Email', - 'name' - ), - 'Inbound Email' => CRM_Core_OptionGroup::getValue('activity_type', - 'Inbound Email', - 'name' - ), - ); - - $caseDeleted = CRM_Core_DAO::getFieldValue('CRM_Case_DAO_Case', $caseID, 'is_deleted'); - - // define statuses which are handled like Completed status (others are assumed to be handled like Scheduled status) - $compStatusValues = array(); - $compStatusNames = array('Completed', 'Left Message', 'Cancelled', 'Unreachable', 'Not Required'); - foreach ($compStatusNames as $name) { - $compStatusValues[] = CRM_Core_OptionGroup::getValue('activity_status', $name, 'name'); - } - - $contactViewUrl = CRM_Utils_System::url("civicrm/contact/view", "reset=1&cid=", FALSE, NULL, FALSE); - $hasViewContact = CRM_Core_Permission::giveMeAllACLs(); - $clientIds = self::retrieveContactIdsByCaseId($caseID); - if (!$userID) { - $session = CRM_Core_Session::singleton(); - $userID = $session->get('userID'); + $userID = CRM_Core_Session::getLoggedInContactID(); } - $caseActivities = array(); + $caseActivities = []; while ($dao->fetch()) { - $caseActivity = array(); $caseActivityId = $dao->id; - $allowView = self::checkPermission($caseActivityId, 'view', $dao->activity_type_id, $userID); - $allowEdit = self::checkPermission($caseActivityId, 'edit', $dao->activity_type_id, $userID); - $allowDelete = self::checkPermission($caseActivityId, 'delete', $dao->activity_type_id, $userID); - - //do not have sufficient permission - //to access given case activity record. - if (!$allowView && !$allowEdit && !$allowDelete) { + //Do we have permission to access given case activity record. + if (!self::checkPermission($caseActivityId, 'view', $dao->activity_type_id, $userID)) { continue; } - $caseActivity['DT_RowId'] = $caseActivityId; + $caseActivities[$caseActivityId]['DT_RowId'] = $caseActivityId; //Add classes to the row, via DataTables syntax - $caseActivity['DT_RowClass'] = "crm-entity"; + $caseActivities[$caseActivityId]['DT_RowClass'] = "crm-entity status-id-$dao->status"; if (CRM_Utils_Array::crmInArray($dao->status, $compStatusValues)) { - $caseActivity['DT_RowClass'] .= " status-completed"; + $caseActivities[$caseActivityId]['DT_RowClass'] .= " status-completed"; } else { if (CRM_Utils_Date::overdue($dao->display_date)) { - $caseActivity['DT_RowClass'] .= " status-overdue"; + $caseActivities[$caseActivityId]['DT_RowClass'] .= " status-overdue"; } else { - $caseActivity['DT_RowClass'] .= " status-scheduled"; + $caseActivities[$caseActivityId]['DT_RowClass'] .= " status-scheduled"; } } if (!empty($dao->priority)) { - if ($dao->priority == CRM_Core_OptionGroup::getValue('priority', 'Urgent', 'name')) { - $caseActivity['DT_RowClass'] .= " priority-urgent "; + if ($dao->priority == CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'priority_id', 'Urgent')) { + $caseActivities[$caseActivityId]['DT_RowClass'] .= " priority-urgent "; } - elseif ($dao->priority == CRM_Core_OptionGroup::getValue('priority', 'Low', 'name')) { - $caseActivity['DT_RowClass'] .= " priority-low "; + elseif ($dao->priority == CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'priority_id', 'Low')) { + $caseActivities[$caseActivityId]['DT_RowClass'] .= " priority-low "; } } //Add data to the row for inline editing, via DataTable syntax - $caseActivity['DT_RowAttr'] = array(); - $caseActivity['DT_RowAttr']['data-entity'] = 'activity'; - $caseActivity['DT_RowAttr']['data-id'] = $caseActivityId; + $caseActivities[$caseActivityId]['DT_RowAttr'] = array(); + $caseActivities[$caseActivityId]['DT_RowAttr']['data-entity'] = 'activity'; + $caseActivities[$caseActivityId]['DT_RowAttr']['data-id'] = $caseActivityId; //Activity Date and Time - $caseActivity['activity_date_time'] = CRM_Utils_Date::customFormat($dao->display_date); + $caseActivities[$caseActivityId]['activity_date_time'] = CRM_Utils_Date::customFormat($dao->display_date); //Activity Subject - $caseActivity['subject'] = $dao->subject; + $caseActivities[$caseActivityId]['subject'] = $dao->subject; //Activity Type - $caseActivity['type'] = $activityTypes[$dao->type]['label']; - - //Activity Target (With) - $targetContact = ''; - if (isset($dao->target_contact_id)) { - $targetContact = $dao->target_contact_name; - if ($hasViewContact) { - $targetContact = '' . $dao->target_contact_name . ''; - } - } - $caseActivity['target_contact_name'] = $targetContact; + $caseActivities[$caseActivityId]['type'] = (!empty($activityTypes[$dao->type]['icon']) ? ' ' : '') + . $activityTypes[$dao->type]['label']; - //Activity Source Contact (Reporter) - $sourceContact = $dao->source_contact_name; - if ($hasViewContact) { - $sourceContact = '' . $dao->source_contact_name . ''; + // Activity Target (With Contact) (There can be more than one) + $targetContact = self::formatContactLink($dao->target_contact_id, $dao->target_contact_name); + if (empty($caseActivities[$caseActivityId]['target_contact_name'])) { + $caseActivities[$caseActivityId]['target_contact_name'] = $targetContact; } - $caseActivity['source_contact_name'] = $sourceContact; - - //Activity Assignee. CRM-4485. - $assigneeContact = ''; - if (isset($dao->assignee_contact_id)) { - $assigneeContact = $dao->assignee_contact_name; - if ($hasViewContact) { - $assigneeContact = '' . $dao->assignee_contact_name . ''; + else { + if (strpos($caseActivities[$caseActivityId]['target_contact_name'], $targetContact) === FALSE) { + $caseActivities[$caseActivityId]['target_contact_name'] .= '; ' . $targetContact; } } - $caseActivity['assignee_contact_name'] = $assigneeContact; - - //Activity Status - $caseActivity['status_id'] = $activityStatuses[$dao->status]; - - // FIXME: Why are we not using CRM_Core_Action for these links? This is too much manual work and likely to get out-of-sync with core markup. - $url = ""; - $css = 'class="action-item crm-hover-button"'; - if ($allowView) { - $viewUrl = CRM_Utils_System::url('civicrm/case/activity/view', array('cid' => $contactID, 'aid' => $caseActivityId)); - $url = '' . ts('View') . ''; - } - $additionalUrl = "&id={$caseActivityId}"; - if (!$dao->deleted) { - //hide edit link of activity type email.CRM-4530. - if (!in_array($dao->type, $emailActivityTypeIDs)) { - //hide Edit link if activity type is NOT editable (special case activities).CRM-5871 - if ($allowEdit) { - $url .= '' . ts('Edit') . ' '; - } - } - if ($allowDelete) { - $url .= ' ' . ts('Delete') . ''; - } + + // Activity Source Contact (Reporter) (There can only be one) + $sourceContact = self::formatContactLink($dao->source_contact_id, $dao->source_contact_name); + $caseActivities[$caseActivityId]['source_contact_name'] = $sourceContact; + + // Activity Assignee (There can be more than one) + $assigneeContact = self::formatContactLink($dao->assignee_contact_id, $dao->assignee_contact_name); + if (empty($caseActivities[$caseActivityId]['assignee_contact_name'])) { + $caseActivities[$caseActivityId]['assignee_contact_name'] = $assigneeContact; } - elseif (!$caseDeleted) { - $url = ' ' . ts('Restore') . ''; - $caseActivity['status_id'] = $caseActivity['status_id'] . '
(deleted)'; - } - - //check for operations. - if (self::checkPermission($caseActivityId, 'Move To Case', $dao->activity_type_id)) { - $url .= ' ' . ts('Move To Case') . ' '; - } - if (self::checkPermission($caseActivityId, 'Copy To Case', $dao->activity_type_id)) { - $url .= ' ' . ts('Copy To Case') . ' '; - } - // if there are file attachments we will return how many and, if only one, add a link to it - if (!empty($dao->attachment_ids)) { - $attachmentIDs = explode(',', $dao->attachment_ids); - $caseActivity['no_attachments'] = count($attachmentIDs); - if ($caseActivity['no_attachments'] == 1) { - // if there is only one it's easy to do a link - otherwise just flag it - $attachmentViewUrl = CRM_Utils_System::url( - "civicrm/file", - "reset=1&eid=" . $caseActivityId . "&id=" . $dao->attachment_ids, - FALSE, - NULL, - FALSE - ); - $url .= " "; + else { + if (strpos($caseActivities[$caseActivityId]['assignee_contact_name'], $assigneeContact) === FALSE) { + $caseActivities[$caseActivityId]['assignee_contact_name'] .= '; ' . $assigneeContact; } } - $caseActivity['links'] = $url; + // Activity Status Label for Case activities list + $caseActivities[$caseActivityId]['status_id'] = CRM_Core_PseudoConstant::getLabel('CRM_Activity_BAO_Activity', 'activity_status_id', $dao->status); - array_push($caseActivities, $caseActivity); + $caseActivities[$caseActivityId] + = self::addCaseActivityLinks($caseID, $contactID, $userID, $context, $dao, $caseActivities[$caseActivityId]); } - $dao->free(); $caseActivitiesDT = array(); - $caseActivitiesDT['data'] = $caseActivities; + $caseActivitiesDT['data'] = array_values($caseActivities); $caseActivitiesDT['recordsTotal'] = $caseCount; $caseActivitiesDT['recordsFiltered'] = $caseCount; return $caseActivitiesDT; } + /** + * FIXME: This is a transitional function to facilitate a refactor of this to use CRM_Core_Action and actionLinks + * Add the set of "actionLinks" to the case activity + * + * @param int $caseID + * @param int $contactID + * @param int $userID + * @param string $context + * @param \CRM_Core_DAO $dao + * @param array $caseActivity + * + * @return array caseActivity + */ + public static function addCaseActivityLinks($caseID, $contactID, $userID, $context, $dao, $caseActivity) { + // FIXME: Why are we not using CRM_Core_Action for these links? This is too much manual work and likely to get out-of-sync with core markup. + $caseActivityId = $dao->id; + $allowView = self::checkPermission($caseActivityId, 'view', $dao->activity_type_id, $userID); + $allowEdit = self::checkPermission($caseActivityId, 'edit', $dao->activity_type_id, $userID); + $allowDelete = self::checkPermission($caseActivityId, 'delete', $dao->activity_type_id, $userID); + $emailActivityTypeIDs = [ + 'Email' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Email'), + 'Inbound Email' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Inbound Email'), + ]; + $url = CRM_Utils_System::url("civicrm/case/activity", + "reset=1&cid={$contactID}&caseid={$caseID}", FALSE, NULL, FALSE + ); + $contextUrl = ''; + if ($context == 'fulltext') { + $contextUrl = "&context={$context}"; + } + $editUrl = "{$url}&action=update{$contextUrl}"; + $deleteUrl = "{$url}&action=delete{$contextUrl}"; + $restoreUrl = "{$url}&action=renew{$contextUrl}"; + $viewTitle = ts('View activity'); + $caseDeleted = CRM_Core_DAO::getFieldValue('CRM_Case_DAO_Case', $caseID, 'is_deleted'); + + $url = ""; + $css = 'class="action-item crm-hover-button"'; + if ($allowView) { + $viewUrl = CRM_Utils_System::url('civicrm/case/activity/view', array('cid' => $contactID, 'aid' => $caseActivityId)); + $url = '' . ts('View') . ''; + } + $additionalUrl = "&id={$caseActivityId}"; + if (!$dao->deleted) { + //hide edit link of activity type email.CRM-4530. + if (!in_array($dao->type, $emailActivityTypeIDs)) { + //hide Edit link if activity type is NOT editable (special case activities).CRM-5871 + if ($allowEdit) { + $url .= '' . ts('Edit') . ' '; + } + } + if ($allowDelete) { + $url .= ' ' . ts('Delete') . ''; + } + } + elseif (!$caseDeleted) { + $url = ' ' . ts('Restore') . ''; + $caseActivity['status_id'] = $caseActivity['status_id'] . '
(deleted)'; + } + + //check for operations. + if (self::checkPermission($caseActivityId, 'Move To Case', $dao->activity_type_id)) { + $url .= ' ' . ts('Move To Case') . ' '; + } + if (self::checkPermission($caseActivityId, 'Copy To Case', $dao->activity_type_id)) { + $url .= ' ' . ts('Copy To Case') . ' '; + } + // if there are file attachments we will return how many and, if only one, add a link to it + if (!empty($dao->attachment_ids)) { + $attachmentIDs = array_unique(explode(',', $dao->attachment_ids)); + $caseActivity['no_attachments'] = count($attachmentIDs); + $url .= implode(' ', CRM_Core_BAO_File::paperIconAttachment('civicrm_activity', $caseActivityId)); + } + $caseActivity['links'] = $url; + return $caseActivity; + } + + /** + * Helper function to generate a formatted contact link/name for display in the Case activities tab + * + * @param $contactId + * @param $contactName + * + * @return string + */ + private static function formatContactLink($contactId, $contactName) { + if (empty($contactId)) { + return NULL; + } + + $hasViewContact = CRM_Contact_BAO_Contact_Permission::allow($contactId); + + if ($hasViewContact) { + $contactViewUrl = CRM_Utils_System::url("civicrm/contact/view", "reset=1&cid={$contactId}"); + return "" . $contactName . ""; + } + else { + return $contactName; + } + } + /** * Get Case Related Contacts. * * @param int $caseID * Case id. - * @param bool $skipDetails + * @param bool $includeDetails * If true include details of contacts. * * @return array * array of return properties * */ - public static function getRelatedContacts($caseID, $skipDetails = FALSE) { + public static function getRelatedContacts($caseID, $includeDetails = TRUE) { + $caseRoles = array(); + if ($includeDetails) { + $caseInfo = civicrm_api3('Case', 'getsingle', array( + 'id' => $caseID, + // Most efficient way of retrieving definition is to also include case type id and name so the api doesn't have to look it up separately + 'return' => array('case_type_id', 'case_type_id.name', 'case_type_id.definition'), + )); + if (!empty($caseInfo['case_type_id.definition']['caseRoles'])) { + $caseRoles = CRM_Utils_Array::rekey($caseInfo['case_type_id.definition']['caseRoles'], 'name'); + } + } $values = array(); $query = ' - SELECT cc.display_name as name, cc.sort_name as sort_name, cc.id, crt.label_b_a as role, ce.email + SELECT cc.display_name as name, cc.sort_name as sort_name, cc.id, cr.relationship_type_id, crt.label_b_a as role, crt.name_b_a, ce.email, cp.phone FROM civicrm_relationship cr LEFT JOIN civicrm_relationship_type crt ON crt.id = cr.relationship_type_id @@ -1245,26 +1300,37 @@ public static function getRelatedContacts($caseID, $skipDetails = FALSE) { LEFT JOIN civicrm_email ce ON ce.contact_id = cc.id AND ce.is_primary= 1 - WHERE cr.case_id = %1'; + LEFT JOIN civicrm_phone cp + ON cp.contact_id = cc.id + AND cp.is_primary= 1 + WHERE cr.case_id = %1 AND cr.is_active AND cc.is_deleted <> 1'; $params = array(1 => array($caseID, 'Integer')); $dao = CRM_Core_DAO::executeQuery($query, $params); while ($dao->fetch()) { - if ($skipDetails) { + if (!$includeDetails) { $values[$dao->id] = 1; } else { - $values[] = array( + $details = array( 'contact_id' => $dao->id, 'display_name' => $dao->name, 'sort_name' => $dao->sort_name, + 'relationship_type_id' => $dao->relationship_type_id, 'role' => $dao->role, 'email' => $dao->email, + 'phone' => $dao->phone, ); + // Add more info about the role (creator, manager) + $role = CRM_Utils_Array::value($dao->name_b_a, $caseRoles); + if ($role) { + unset($role['name']); + $details += $role; + } + $values[] = $details; } } - $dao->free(); return $values; } @@ -1289,9 +1355,9 @@ public static function sendActivityCopy($clientId, $activityId, $contacts, $atta } $tplParams = $activityInfo = array(); - //if its a case activity + $activityTypeId = CRM_Core_DAO::getFieldValue('CRM_Activity_DAO_Activity', $activityId, 'activity_type_id'); + // If it's a case activity if ($caseId) { - $activityTypeId = CRM_Core_DAO::getFieldValue('CRM_Activity_DAO_Activity', $activityId, 'activity_type_id'); $nonCaseActivityTypes = CRM_Core_PseudoConstant::activityType(); if (!empty($nonCaseActivityTypes[$activityTypeId])) { $anyActivity = TRUE; @@ -1315,6 +1381,7 @@ public static function sendActivityCopy($clientId, $activityId, $contacts, $atta if ($caseId) { $activityInfo['fields'][] = array('label' => 'Case ID', 'type' => 'String', 'value' => $caseId); } + $tplParams['activityTypeName'] = CRM_Core_PseudoConstant::getLabel('CRM_Activity_DAO_Activity', 'activity_type_id', $activityTypeId); $tplParams['activity'] = $activityInfo; foreach ($tplParams['activity']['fields'] as $k => $val) { if (CRM_Utils_Array::value('label', $val) == ts('Subject')) { @@ -1333,10 +1400,10 @@ public static function sendActivityCopy($clientId, $activityId, $contacts, $atta $activityParams['source_record_id'] = $activityId; $activityParams['source_contact_id'] = $userID; - $activityParams['activity_type_id'] = CRM_Core_OptionGroup::getValue('activity_type', 'Email', 'name'); + $activityParams['activity_type_id'] = CRM_Core_PseudoConstant::getKey('CRM_Activity_DAO_Activity', 'activity_type_id', 'Email'); $activityParams['activity_date_time'] = date('YmdHis'); - $activityParams['status_id'] = CRM_Core_OptionGroup::getValue('activity_status', 'Completed', 'name'); - $activityParams['medium_id'] = CRM_Core_OptionGroup::getValue('encounter_medium', 'email', 'name'); + $activityParams['status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Activity_DAO_Activity', 'activity_status_id', 'Completed'); + $activityParams['medium_id'] = CRM_Core_PseudoConstant::getKey('CRM_Activity_DAO_Activity', 'encounter_medium', 'email'); $activityParams['case_id'] = $caseId; $activityParams['is_auto'] = 0; $activityParams['target_id'] = $clientId; @@ -1349,9 +1416,8 @@ public static function sendActivityCopy($clientId, $activityId, $contacts, $atta } $result = array(); - list($name, $address) = CRM_Contact_BAO_Contact_Location::getEmailDetails($userID); - - $receiptFrom = "$name <$address>"; + // CRM-20308 get receiptFrom defaults see https://issues.civicrm.org/jira/browse/CRM-20308 + $receiptFrom = self::getReceiptFrom($activityId); $recordedActivityParams = array(); @@ -1374,7 +1440,7 @@ public static function sendActivityCopy($clientId, $activityId, $contacts, $atta ) ); - $activityParams['subject'] = $activitySubject . ' - copy sent to ' . $displayName; + $activityParams['subject'] = ts('%1 - copy sent to %2', [1 => $activitySubject, 2 => $displayName]); $activityParams['details'] = $message; if (!empty($result[$info['contact_id']])) { @@ -1487,7 +1553,7 @@ public static function recordActivityViaEmail($file) { // TODO: May want to replace this with a call to getRelatedAndGlobalContacts() when this feature is revisited. // (Or for efficiency call the global one outside the loop and then union with this each time.) - $contactDetails = self::getRelatedContacts($caseId, TRUE); + $contactDetails = self::getRelatedContacts($caseId, FALSE); if (!empty($contactDetails[$result['from']['id']])) { $params = array(); @@ -1495,10 +1561,7 @@ public static function recordActivityViaEmail($file) { $params['activity_date_time'] = $result['date']; $params['details'] = $result['body']; $params['source_contact_id'] = $result['from']['id']; - $params['status_id'] = CRM_Core_OptionGroup::getValue('activity_status', - 'Completed', - 'name' - ); + $params['status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', 'Completed'); $details = CRM_Case_PseudoConstant::caseActivityType(); $matches = array(); @@ -1513,7 +1576,7 @@ public static function recordActivityViaEmail($file) { } } if (!isset($params['activity_type_id'])) { - $params['activity_type_id'] = CRM_Core_OptionGroup::getValue('activity_type', 'Inbound Email', 'name'); + $params['activity_type_id'] = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Inbound Email'); } // create activity @@ -1553,12 +1616,11 @@ public static function getNextScheduledActivity($cases, $type = 'upcoming') { $caseID = implode(',', $cases['case_id']); $contactID = implode(',', $cases['contact_id']); - $condition = " - AND civicrm_case_contact.contact_id IN( {$contactID} ) + $condition = " civicrm_case_contact.contact_id IN( {$contactID} ) AND civicrm_case.id IN( {$caseID}) AND civicrm_case.is_deleted = {$cases['case_deleted']}"; - $query = self::getCaseActivityQuery($type, $userID, $condition, $cases['case_deleted']); + $query = self::getCaseActivityQuery($type, $userID, $condition); $res = CRM_Core_DAO::executeQuery($query); @@ -1753,7 +1815,6 @@ public static function getCaseActivityDates($caseID, $criteriaParams = array(), $values[$dao->id]['id'] = $dao->id; $values[$dao->id]['activity_date'] = $dao->activity_date; } - $dao->free(); return $values; } @@ -1816,23 +1877,16 @@ public static function createCaseRoleActivity($caseId, $relationshipId, $relCont 'source_contact_id' => $session->get('userID'), 'subject' => $caseRelationship . ' : ' . $assigneContactName, 'activity_date_time' => date('YmdHis'), - 'status_id' => CRM_Core_OptionGroup::getValue('activity_status', 'Completed', 'name'), + 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', 'Completed'), ); //if $relContactId is passed, role is added or modified. if (!empty($relContactId)) { $activityParams['assignee_contact_id'] = $assigneContactIds; - - $activityTypeID = CRM_Core_OptionGroup::getValue('activity_type', - 'Assign Case Role', - 'name' - ); + $activityTypeID = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Assign Case Role'); } else { - $activityTypeID = CRM_Core_OptionGroup::getValue('activity_type', - 'Remove Case Role', - 'name' - ); + $activityTypeID = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Remove Case Role'); } $activityParams['activity_type_id'] = $activityTypeID; @@ -1857,8 +1911,8 @@ public static function createCaseRoleActivity($caseId, $relationshipId, $relCont * @param int $caseId * Case id. * - * @return array - * array of contact on success otherwise empty + * @return string + * html hyperlink of manager contact view page * */ public static function getCaseManagerContact($caseType, $caseId) { @@ -1866,7 +1920,7 @@ public static function getCaseManagerContact($caseType, $caseId) { return NULL; } - $caseManagerContact = array(); + $caseManagerName = '---'; $xmlProcessor = new CRM_Case_XMLProcessor_Process(); $managerRoleId = $xmlProcessor->getCaseManagerRoleId($caseType); @@ -1876,9 +1930,9 @@ public static function getCaseManagerContact($caseType, $caseId) { SELECT civicrm_contact.id as casemanager_id, civicrm_contact.sort_name as casemanager FROM civicrm_contact - LEFT JOIN civicrm_relationship ON (civicrm_relationship.contact_id_b = civicrm_contact.id AND civicrm_relationship.relationship_type_id = %1) + LEFT JOIN civicrm_relationship ON (civicrm_relationship.contact_id_b = civicrm_contact.id AND civicrm_relationship.relationship_type_id = %1) AND civicrm_relationship.is_active LEFT JOIN civicrm_case ON civicrm_case.id = civicrm_relationship.case_id - WHERE civicrm_case.id = %2"; + WHERE civicrm_case.id = %2 AND is_active = 1"; $managerRoleParams = array( 1 => array($managerRoleId, 'Integer'), @@ -1887,12 +1941,14 @@ public static function getCaseManagerContact($caseType, $caseId) { $dao = CRM_Core_DAO::executeQuery($managerRoleQuery, $managerRoleParams); if ($dao->fetch()) { - $caseManagerContact['casemanager_id'] = $dao->casemanager_id; - $caseManagerContact['casemanager'] = $dao->casemanager; + $caseManagerName = sprintf('%s', + CRM_Utils_System::url('civicrm/contact/view', array('cid' => $dao->casemanager_id)), + $dao->casemanager + ); } } - return $caseManagerContact; + return $caseManagerName; } /** @@ -1919,30 +1975,24 @@ public static function caseCount($contactId = NULL, $excludeDeleted = TRUE) { } /** - * Retrieve related cases for give case. + * Retrieve related case ids for given case. * - * @param int $mainCaseId - * Id of main case. - * @param int $contactId - * Id of contact. + * @param int $caseId * @param bool $excludeDeleted * Do not include deleted cases. * * @return array */ - public static function getRelatedCases($mainCaseId, $contactId, $excludeDeleted = TRUE) { + public static function getRelatedCaseIds($caseId, $excludeDeleted = TRUE) { //FIXME : do check for permissions. - $relatedCases = array(); - if (!$mainCaseId || !$contactId) { - return $relatedCases; + if (!$caseId) { + return array(); } - $linkActType = array_search('Link Cases', - CRM_Core_PseudoConstant::activityType(TRUE, TRUE, FALSE, 'name') - ); + $linkActType = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Link Cases'); if (!$linkActType) { - return $relatedCases; + return array(); } $whereClause = "mainCase.id = %2"; @@ -1950,7 +2000,6 @@ public static function getRelatedCases($mainCaseId, $contactId, $excludeDeleted $whereClause .= " AND ( relAct.is_deleted = 0 OR relAct.is_deleted IS NULL )"; } - //1. first fetch related case ids. $query = " SELECT relCaseAct.case_id FROM civicrm_case mainCase @@ -1962,17 +2011,31 @@ public static function getRelatedCases($mainCaseId, $contactId, $excludeDeleted $dao = CRM_Core_DAO::executeQuery($query, array( 1 => array($linkActType, 'Integer'), - 2 => array($mainCaseId, 'Integer'), + 2 => array($caseId, 'Integer'), )); $relatedCaseIds = array(); while ($dao->fetch()) { $relatedCaseIds[$dao->case_id] = $dao->case_id; } - $dao->free(); - // there are no related cases. - if (empty($relatedCaseIds)) { - return $relatedCases; + return array_values($relatedCaseIds); + } + + /** + * Retrieve related case details for given case. + * + * @param int $caseId + * @param bool $excludeDeleted + * Do not include deleted cases. + * + * @return array + */ + public static function getRelatedCases($caseId, $excludeDeleted = TRUE) { + $relatedCaseIds = self::getRelatedCaseIds($caseId, $excludeDeleted); + $relatedCases = array(); + + if (!$relatedCaseIds) { + return array(); } $whereClause = 'relCase.id IN ( ' . implode(',', $relatedCaseIds) . ' )'; @@ -1985,8 +2048,7 @@ public static function getRelatedCases($mainCaseId, $contactId, $excludeDeleted $doFilterCases = FALSE; if (!CRM_Core_Permission::check('access all cases and activities')) { $doFilterCases = TRUE; - $session = CRM_Core_Session::singleton(); - $filterCases = CRM_Case_BAO_Case::getCases(FALSE, $session->get('userID')); + $filterCases = CRM_Case_BAO_Case::getCases(FALSE); } //2. fetch the details of related cases. @@ -1994,7 +2056,8 @@ public static function getRelatedCases($mainCaseId, $contactId, $excludeDeleted SELECT relCase.id as id, civicrm_case_type.title as case_type, client.display_name as client_name, - client.id as client_id + client.id as client_id, + relCase.status_id FROM civicrm_case relCase INNER JOIN civicrm_case_contact relCaseContact ON ( relCase.id = relCaseContact.case_id ) INNER JOIN civicrm_contact client ON ( client.id = relCaseContact.contact_id ) @@ -2004,6 +2067,7 @@ public static function getRelatedCases($mainCaseId, $contactId, $excludeDeleted $dao = CRM_Core_DAO::executeQuery($query); $contactViewUrl = CRM_Utils_System::url("civicrm/contact/view", "reset=1&cid="); $hasViewContact = CRM_Core_Permission::giveMeAllACLs(); + $statuses = CRM_Case_BAO_Case::buildOptions('status_id'); while ($dao->fetch()) { $caseView = NULL; @@ -2021,10 +2085,10 @@ public static function getRelatedCases($mainCaseId, $contactId, $excludeDeleted 'case_id' => $dao->id, 'case_type' => $dao->case_type, 'client_name' => $clientView, + 'case_status' => $statuses[$dao->status_id], 'links' => $caseView, ); } - $dao->free(); return $relatedCases; } @@ -2085,9 +2149,9 @@ public static function mergeCases( return $mainCaseIds; } - $activityTypes = CRM_Core_PseudoConstant::activityType(TRUE, TRUE, FALSE, 'name'); - $activityStatuses = CRM_Core_PseudoConstant::activityStatus('name'); - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityTypes = CRM_Activity_BAO_Activity::buildOptions('activity_type_id', 'validate'); + $completedActivityStatus = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', 'Completed'); + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts); $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts); $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts); @@ -2142,8 +2206,6 @@ public static function mergeCases( } } - $mainCase->free(); - $mainCaseIds[] = $mainCaseId; //insert record for case contact. $otherCaseContact = new CRM_Case_DAO_CaseContact(); @@ -2160,9 +2222,7 @@ public static function mergeCases( if (!$mainCaseContact->find(TRUE)) { $mainCaseContact->save(); } - $mainCaseContact->free(); } - $otherCaseContact->free(); } elseif (!$otherContactId) { $otherContactId = $mainContactId; @@ -2198,7 +2258,6 @@ public static function mergeCases( while ($dao->fetch()) { $singletonActivityIds[] = $dao->id; } - $dao->free(); } } @@ -2247,8 +2306,6 @@ public static function mergeCases( // insert log of all activities CRM_Activity_BAO_Activity::logActivityAction($mainActivity); - $otherActivity->free(); - $mainActivity->free(); $copiedActivityIds[] = $otherActivityId; //create case activity record. @@ -2256,7 +2313,6 @@ public static function mergeCases( $mainCaseActivity->case_id = $mainCaseId; $mainCaseActivity->activity_id = $mainActivityId; $mainCaseActivity->save(); - $mainCaseActivity->free(); //migrate source activity. $otherSourceActivity = new CRM_Activity_DAO_ActivityContact(); @@ -2275,9 +2331,7 @@ public static function mergeCases( if (!$mainActivitySource->find(TRUE)) { $mainActivitySource->save(); } - $mainActivitySource->free(); } - $otherSourceActivity->free(); //migrate target activities. $otherTargetActivity = new CRM_Activity_DAO_ActivityContact(); @@ -2296,9 +2350,7 @@ public static function mergeCases( if (!$mainActivityTarget->find(TRUE)) { $mainActivityTarget->save(); } - $mainActivityTarget->free(); } - $otherTargetActivity->free(); //migrate assignee activities. $otherAssigneeActivity = new CRM_Activity_DAO_ActivityContact(); @@ -2317,9 +2369,7 @@ public static function mergeCases( if (!$mainAssigneeActivity->find(TRUE)) { $mainAssigneeActivity->save(); } - $mainAssigneeActivity->free(); } - $otherAssigneeActivity->free(); // copy custom fields and attachments $aparams = array( @@ -2365,14 +2415,12 @@ public static function mergeCases( if (!$mainRelationship->find(TRUE)) { $mainRelationship->save(); } - $mainRelationship->free(); //get the other relationship ids to update end date. if ($updateOtherRel) { $otherRelationshipIds[$otherRelationship->id] = $otherRelationship->id; } } - $otherRelationship->free(); //update other relationships end dates if (!empty($otherRelationshipIds)) { @@ -2434,11 +2482,11 @@ public static function mergeCases( } } - //Create merge activity record. Source for merge activity is the logged in user's contact ID ($currentUserId). + // Create merge activity record. Source for merge activity is the logged in user's contact ID ($currentUserId). $activityParams = array( 'subject' => $mergeActSubject, 'details' => $mergeActSubjectDetails, - 'status_id' => array_search('Completed', $activityStatuses), + 'status_id' => $completedActivityStatus, 'activity_type_id' => $mergeActType, 'source_contact_id' => $currentUserId, 'activity_date_time' => date('YmdHis'), @@ -2449,7 +2497,6 @@ public static function mergeCases( if (!$mergeActivityId) { continue; } - $mergeActivity->free(); //connect merge activity to case. $mergeCaseAct = array( @@ -2625,7 +2672,7 @@ public static function checkPermission($activityId, $operation, $actTypeId = NUL //edit - contact must be source or assignee //view - contact must be source/assignee/target $isTarget = $isAssignee = $isSource = FALSE; - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts); $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts); $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts); @@ -2684,10 +2731,7 @@ public static function checkPermission($activityId, $operation, $actTypeId = NUL //do further only when operation is granted. if ($allow) { - $activityTypes = CRM_Core_PseudoConstant::activityType(TRUE, TRUE, FALSE, 'name'); - - //get the activity type name. - $actTypeName = CRM_Utils_Array::value($actTypeId, $activityTypes); + $actTypeName = CRM_Core_PseudoConstant::getName('CRM_Activity_BAO_Activity', 'activity_type_id', $actTypeId); //do not allow multiple copy / edit action. $singletonNames = array( @@ -2706,6 +2750,12 @@ public static function checkPermission($activityId, $operation, $actTypeId = NUL //allow edit operation. $allowEditNames = array('Open Case'); + if (CRM_Core_Permission::check('edit inbound email basic information') || + CRM_Core_Permission::check('edit inbound email basic information and content') + ) { + $allowEditNames[] = 'Inbound Email'; + } + // do not allow File on Case $doNotFileNames = array( 'Open Case', @@ -2994,15 +3044,19 @@ public static function createCaseViews() { */ public static function createCaseViewsQuery($section = 'upcoming') { $sql = ""; - $scheduled_id = CRM_Core_OptionGroup::getValue('activity_status', 'Scheduled', 'name'); + $scheduled_id = CRM_Core_Pseudoconstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', 'Scheduled'); switch ($section) { case 'upcoming': $sql = "CREATE OR REPLACE VIEW `civicrm_view_case_activity_upcoming` AS SELECT ca.case_id, a.id, a.activity_date_time, a.status_id, a.activity_type_id FROM civicrm_case_activity ca INNER JOIN civicrm_activity a ON ca.activity_id=a.id - WHERE a.activity_date_time <= DATE_ADD( NOW(), INTERVAL 14 DAY ) - AND a.is_current_revision = 1 AND a.is_deleted=0 AND a.status_id = $scheduled_id"; + WHERE a.activity_date_time = +(SELECT b.activity_date_time FROM civicrm_case_activity bca + INNER JOIN civicrm_activity b ON bca.activity_id=b.id + WHERE b.activity_date_time <= DATE_ADD( NOW(), INTERVAL 14 DAY ) + AND b.is_current_revision = 1 AND b.is_deleted=0 AND b.status_id = $scheduled_id + AND bca.case_id = ca.case_id ORDER BY b.activity_date_time ASC LIMIT 1)"; break; case 'recent': @@ -3010,9 +3064,12 @@ public static function createCaseViewsQuery($section = 'upcoming') { AS SELECT ca.case_id, a.id, a.activity_date_time, a.status_id, a.activity_type_id FROM civicrm_case_activity ca INNER JOIN civicrm_activity a ON ca.activity_id=a.id - WHERE a.activity_date_time <= NOW() - AND a.activity_date_time >= DATE_SUB( NOW(), INTERVAL 14 DAY ) - AND a.is_current_revision = 1 AND a.is_deleted=0 AND a.status_id <> $scheduled_id"; + WHERE a.activity_date_time = +(SELECT b.activity_date_time FROM civicrm_case_activity bca + INNER JOIN civicrm_activity b ON bca.activity_id=b.id + WHERE b.activity_date_time >= DATE_SUB( NOW(), INTERVAL 14 DAY ) + AND b.is_current_revision = 1 AND b.is_deleted=0 AND b.status_id <> $scheduled_id + AND bca.case_id = ca.case_id ORDER BY b.activity_date_time DESC LIMIT 1)"; break; } return $sql; @@ -3051,7 +3108,6 @@ public static function addCaseRelationships($caseId, $contactId) { if (!$newRelationship->find(TRUE)) { $newRelationship->save(); } - $newRelationship->free(); // store relationship type of newly created relationship $relationshipTypes[] = $caseRelationships->relationship_type_id; @@ -3070,6 +3126,7 @@ public static function getCaseClients($caseId) { $clients = array(); $caseContact = new CRM_Case_DAO_CaseContact(); $caseContact->case_id = $caseId; + $caseContact->orderBy('id'); $caseContact->find(); while ($caseContact->fetch()) { @@ -3129,6 +3186,17 @@ public static function buildOptions($fieldName, $context = NULL, $props = array( case 'medium_id': $className = 'CRM_Activity_BAO_Activity'; break; + + // Filter status id by case type id + case 'status_id': + if (!empty($props['case_type_id'])) { + $idField = is_numeric($props['case_type_id']) ? 'id' : 'name'; + $caseType = civicrm_api3('CaseType', 'getsingle', array($idField => $props['case_type_id'], 'return' => 'definition')); + if (!empty($caseType['definition']['statuses'])) { + $params['condition'] = 'v.name IN ("' . implode('","', $caseType['definition']['statuses']) . '")'; + } + } + break; } return CRM_Core_PseudoConstant::get($className, $fieldName, $params, $context); } @@ -3164,4 +3232,65 @@ public function addSelectWhereClause() { return $clauses; } + /** + * CRM-20308: Method to get the contact id to use as from contact for email copy + * 1. Activity Added by Contact's email address + * 2. System Default From Address + * 3. Default Organization Contact email address + * 4. Logged in user + * + * @param int $activityID + * + * @return mixed $emailFromContactId + * @see https://issues.civicrm.org/jira/browse/CRM-20308 + */ + public static function getReceiptFrom($activityID) { + $name = $address = NULL; + + if (!empty($activityID) && (Civi::settings()->get('allow_mail_from_logged_in_contact'))) { + // This breaks SPF/DMARC if email is sent from an email address that the server is not authorised to send from. + // so we can disable this behaviour with the "allow_mail_from_logged_in_contact" setting. + // There is always a 'Added by' contact for a activity, + // so we can safely use ActivityContact.Getvalue API + $sourceContactId = civicrm_api3('ActivityContact', 'getvalue', array( + 'activity_id' => $activityID, + 'record_type_id' => 'Activity Source', + 'return' => 'contact_id', + )); + list($name, $address) = CRM_Contact_BAO_Contact_Location::getEmailDetails($sourceContactId); + } + + // If 'From' email address not found for Source Activity Contact then + // fetch the email from domain or logged in user. + if (empty($address)) { + list($name, $address) = CRM_Core_BAO_Domain::getDefaultReceiptFrom(); + } + + return "$name <$address>"; + } + + /** + * @return array + */ + public static function getEntityRefFilters() { + $filters = [ + [ + 'key' => 'case_id.case_type_id', + 'value' => ts('Case Type'), + 'entity' => 'Case', + ], + [ + 'key' => 'case_id.status_id', + 'value' => ts('Case Status'), + 'entity' => 'Case', + ], + ]; + foreach (CRM_Contact_BAO_Contact::getEntityRefFilters() as $filter) { + $filter += ['entity' => 'Contact']; + $filter['key'] = 'contact_id.' . $filter['key']; + $filters[] = $filter; + } + return $filters; + } + } diff --git a/CRM/Case/BAO/CaseContact.php b/CRM/Case/BAO/CaseContact.php index cef1700a795e..a8cfcdbb0280 100644 --- a/CRM/Case/BAO/CaseContact.php +++ b/CRM/Case/BAO/CaseContact.php @@ -1,9 +1,9 @@ contact_id) . ' - ' . $caseType; - $recentOther = array(); + $recentOther = []; if (CRM_Core_Permission::checkActionPermission('CiviCase', CRM_Core_Action::DELETE)) { $recentOther['deleteUrl'] = CRM_Utils_System::url('civicrm/contact/view/case', "action=delete&reset=1&id={$caseContact->case_id}&cid={$caseContact->contact_id}&context=home" @@ -81,12 +81,12 @@ public static function create($params) { * @inheritDoc */ public function addSelectWhereClause() { - return array( + return [ // Reuse case acls 'case_id' => CRM_Utils_SQL::mergeSubquery('Case'), // Case acls already check for contact access so we can just mark contact_id as handled - 'contact_id' => array(), - ); + 'contact_id' => [], + ]; // Don't call hook selectWhereClause, the case query already did } diff --git a/CRM/Case/BAO/CaseType.php b/CRM/Case/BAO/CaseType.php index 65feb33fd454..2127b92e37d4 100644 --- a/CRM/Case/BAO/CaseType.php +++ b/CRM/Case/BAO/CaseType.php @@ -1,9 +1,9 @@ copyValues($params); - return $caseTypeDAO->save(); + $result = $caseTypeDAO->save(); + CRM_Case_XMLRepository::singleton()->flush(); + return $result; } /** @@ -100,7 +102,6 @@ protected function assignTestValue($fieldName, &$fieldDef, $counter) { } } - /** * Format / convert submitted array to xml for case type definition * @@ -130,6 +131,14 @@ public static function convertDefinitionToXML($name, $definition) { $xmlFile .= "\n"; } + if (!empty($definition['statuses'])) { + $xmlFile .= "\n"; + foreach ($definition['statuses'] as $value) { + $xmlFile .= "$value\n"; + } + $xmlFile .= "\n"; + } + if (isset($definition['activitySets'])) { $xmlFile .= "\n"; foreach ($definition['activitySets'] as $k => $val) { @@ -150,7 +159,8 @@ public static function convertDefinitionToXML($name, $definition) { } break; - case 'sequence': // passthrough + // passthrough + case 'sequence': case 'timeline': if ($setVal) { $xmlFile .= "<{$index}>true\n"; @@ -180,7 +190,20 @@ public static function convertDefinitionToXML($name, $definition) { $xmlFile .= "\n"; } + if (array_key_exists('restrictActivityAsgmtToCmsUser', $definition)) { + $xmlFile .= "" . $definition['restrictActivityAsgmtToCmsUser'] . "\n"; + } + + if (!empty($definition['activityAsgmtGrps'])) { + $xmlFile .= "\n"; + foreach ($definition['activityAsgmtGrps'] as $value) { + $xmlFile .= "$value\n"; + } + $xmlFile .= "\n"; + } + $xmlFile .= ''; + return $xmlFile; } @@ -210,26 +233,41 @@ protected static function encodeXmlString($str) { */ public static function convertXmlToDefinition($xml) { // build PHP array based on definition - $definition = array(); + $definition = []; if (isset($xml->forkable)) { $definition['forkable'] = (int) $xml->forkable; } + if (isset($xml->RestrictActivityAsgmtToCmsUser)) { + $definition['restrictActivityAsgmtToCmsUser'] = (int) $xml->RestrictActivityAsgmtToCmsUser; + } + + if (isset($xml->ActivityAsgmtGrps)) { + $definition['activityAsgmtGrps'] = (array) $xml->ActivityAsgmtGrps->Group; + } + // set activity types if (isset($xml->ActivityTypes)) { - $definition['activityTypes'] = array(); + $definition['activityTypes'] = []; foreach ($xml->ActivityTypes->ActivityType as $activityTypeXML) { $definition['activityTypes'][] = json_decode(json_encode($activityTypeXML), TRUE); } } + // set statuses + if (isset($xml->Statuses)) { + $definition['statuses'] = (array) $xml->Statuses->Status; + } + // set activity sets if (isset($xml->ActivitySets)) { - $definition['activitySets'] = array(); + $definition['activitySets'] = []; + $definition['timelineActivityTypes'] = []; + foreach ($xml->ActivitySets->ActivitySet as $activitySetXML) { // parse basic properties - $activitySet = array(); + $activitySet = []; $activitySet['name'] = (string) $activitySetXML->name; $activitySet['label'] = (string) $activitySetXML->label; if ('true' == (string) $activitySetXML->timeline) { @@ -240,9 +278,13 @@ public static function convertXmlToDefinition($xml) { } if (isset($activitySetXML->ActivityTypes)) { - $activitySet['activityTypes'] = array(); + $activitySet['activityTypes'] = []; foreach ($activitySetXML->ActivityTypes->ActivityType as $activityTypeXML) { - $activitySet['activityTypes'][] = json_decode(json_encode($activityTypeXML), TRUE); + $activityType = json_decode(json_encode($activityTypeXML), TRUE); + $activitySet['activityTypes'][] = $activityType; + if ($activitySetXML->timeline) { + $definition['timelineActivityTypes'][] = $activityType; + } } } $definition['activitySets'][] = $activitySet; @@ -251,7 +293,7 @@ public static function convertXmlToDefinition($xml) { // set case roles if (isset($xml->CaseRoles)) { - $definition['caseRoles'] = array(); + $definition['caseRoles'] = []; foreach ($xml->CaseRoles->RelationshipType as $caseRoleXml) { $definition['caseRoles'][] = json_decode(json_encode($caseRoleXml), TRUE); } @@ -316,6 +358,7 @@ public static function &create(&$params) { } $transaction->commit(); CRM_Case_XMLRepository::singleton(TRUE); + CRM_Core_OptionGroup::flushAll(); return $caseType; } @@ -350,7 +393,7 @@ public static function del($caseTypeId) { $refCounts = $caseType->getReferenceCounts(); $total = array_sum(CRM_Utils_Array::collect('count', $refCounts)); if ($total) { - throw new CRM_Core_Exception(ts("You can not delete this case type -- it is assigned to %1 existing case record(s). If you do not want this case type to be used going forward, consider disabling it instead.", array(1 => $total))); + throw new CRM_Core_Exception(ts("You can not delete this case type -- it is assigned to %1 existing case record(s). If you do not want this case type to be used going forward, consider disabling it instead.", [1 => $total])); } $result = $caseType->delete(); CRM_Case_XMLRepository::singleton(TRUE); diff --git a/CRM/Case/BAO/Query.php b/CRM/Case/BAO/Query.php index 4a27d40c03fe..e5bf54125eb7 100644 --- a/CRM/Case/BAO/Query.php +++ b/CRM/Case/BAO/Query.php @@ -1,9 +1,9 @@ _where[$grouping][] = CRM_Contact_BAO_Query::buildClause("civicrm_case.{$name}", $op, $value, "Integer"); list($op, $value) = CRM_Contact_BAO_Query::buildQillForFieldValue('CRM_Case_DAO_Case', $name, $value, $op); - $query->_qill[$grouping][] = ts('%1 %2 %3', array(1 => $label, 2 => $op, 3 => $value)); + $query->_qill[$grouping][] = ts('%1 %2 %3', [1 => $label, 2 => $op, 3 => $value]); $query->_tables['civicrm_case'] = $query->_whereTables['civicrm_case'] = 1; return; @@ -296,12 +296,12 @@ public static function whereClauseSingle(&$values, &$query) { if ($value == 2) { $session = CRM_Core_Session::singleton(); $userID = $session->get('userID'); - $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause("case_relationship.contact_id_b", $op, $userID, 'Int'); - $query->_qill[$grouping][] = ts('Case %1 My Cases', array(1 => $op)); + $query->_where[$grouping][] = ' ( ' . CRM_Contact_BAO_Query::buildClause("case_relationship.contact_id_b", $op, $userID, 'Int') . ' AND ' . CRM_Contact_BAO_Query::buildClause("case_relationship.is_active", '<>', 0, 'Int') . ' ) '; + $query->_qill[$grouping][] = ts('Case %1 My Cases', [1 => $op]); $query->_tables['case_relationship'] = $query->_whereTables['case_relationship'] = 1; } elseif ($value == 1) { - $query->_qill[$grouping][] = ts('Case %1 All Cases', array(1 => $op)); + $query->_qill[$grouping][] = ts('Case %1 All Cases', [1 => $op]); $query->_where[$grouping][] = "civicrm_case_contact.contact_id = contact_a.id"; } $query->_tables['civicrm_case'] = $query->_whereTables['civicrm_case'] = 1; @@ -319,7 +319,7 @@ public static function whereClauseSingle(&$values, &$query) { case 'case_activity_subject': $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause("case_activity.subject", $op, $value, 'String'); - $query->_qill[$grouping][] = ts("Activity Subject %1 '%2'", array(1 => $op, 2 => $value)); + $query->_qill[$grouping][] = ts("Activity Subject %1 '%2'", [1 => $op, 2 => $value]); $query->_tables['case_activity'] = $query->_whereTables['case_activity'] = 1; $query->_tables['civicrm_case'] = $query->_whereTables['civicrm_case'] = 1; $query->_tables['civicrm_case_contact'] = $query->_whereTables['civicrm_case_contact'] = 1; @@ -327,14 +327,14 @@ public static function whereClauseSingle(&$values, &$query) { case 'case_subject': $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause("civicrm_case.subject", $op, $value, 'String'); - $query->_qill[$grouping][] = ts("Case Subject %1 '%2'", array(1 => $op, 2 => $value)); + $query->_qill[$grouping][] = ts("Case Subject %1 '%2'", [1 => $op, 2 => $value]); $query->_tables['civicrm_case'] = $query->_whereTables['civicrm_case'] = 1; $query->_tables['civicrm_case_contact'] = $query->_whereTables['civicrm_case_contact'] = 1; return; case 'case_source_contact_id': $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause("civicrm_case_reporter.sort_name", $op, $value, 'String'); - $query->_qill[$grouping][] = ts("Activity Reporter %1 '%2'", array(1 => $op, 2 => $value)); + $query->_qill[$grouping][] = ts("Activity Reporter %1 '%2'", [1 => $op, 2 => $value]); $query->_tables['case_activity'] = $query->_whereTables['case_activity'] = 1; $query->_tables['civicrm_case_reporter'] = $query->_whereTables['civicrm_case_reporter'] = 1; $query->_tables['civicrm_case'] = $query->_whereTables['civicrm_case'] = 1; @@ -346,7 +346,7 @@ public static function whereClauseSingle(&$values, &$query) { $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause("case_activity.activity_date_time", $op, $date, 'Date'); if ($date) { $date = CRM_Utils_Date::customFormat($date); - $query->_qill[$grouping][] = ts("Activity Actual Date %1 %2", array(1 => $op, 2 => $date)); + $query->_qill[$grouping][] = ts("Activity Actual Date %1 %2", [1 => $op, 2 => $date]); } $query->_tables['case_activity'] = $query->_whereTables['case_activity'] = 1; $query->_tables['civicrm_case'] = $query->_whereTables['civicrm_case'] = 1; @@ -358,7 +358,7 @@ public static function whereClauseSingle(&$values, &$query) { $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause("case_activity.activity_date_time", $op, $date, 'Date'); if ($date) { $date = CRM_Utils_Date::customFormat($date); - $query->_qill[$grouping][] = ts("Activity Schedule Date %1 %2", array(1 => $op, 2 => $date)); + $query->_qill[$grouping][] = ts("Activity Schedule Date %1 %2", [1 => $op, 2 => $date]); } $query->_tables['case_activity'] = $query->_whereTables['case_activity'] = 1; $query->_tables['civicrm_case'] = $query->_whereTables['civicrm_case'] = 1; @@ -372,7 +372,7 @@ public static function whereClauseSingle(&$values, &$query) { } $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause("case_activity.activity_type_id", $op, $value, 'Int'); - $query->_qill[$grouping][] = ts("Activity Type %1 %2", array(1 => $op, 2 => $names)); + $query->_qill[$grouping][] = ts("Activity Type %1 %2", [1 => $op, 2 => $names]); $query->_tables['case_activity'] = $query->_whereTables['case_activity'] = 1; $query->_tables['civicrm_case'] = $query->_whereTables['civicrm_case'] = 1; $query->_tables['case_activity_type'] = 1; @@ -386,7 +386,7 @@ public static function whereClauseSingle(&$values, &$query) { } $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause("case_activity.status_id", $op, $value, 'Int'); - $query->_qill[$grouping][] = ts("Activity Type %1 %2", array(1 => $op, 2 => $names)); + $query->_qill[$grouping][] = ts("Activity Type %1 %2", [1 => $op, 2 => $names]); $query->_tables['case_activity'] = $query->_whereTables['case_activity'] = 1; $query->_tables['civicrm_case'] = $query->_whereTables['civicrm_case'] = 1; $query->_tables['case_activity_status'] = 1; @@ -395,7 +395,7 @@ public static function whereClauseSingle(&$values, &$query) { case 'case_activity_duration': $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause("case_activity.duration", $op, $value, 'Int'); - $query->_qill[$grouping][] = ts("Activity Duration %1 %2", array(1 => $op, 2 => $value)); + $query->_qill[$grouping][] = ts("Activity Duration %1 %2", [1 => $op, 2 => $value]); $query->_tables['case_activity'] = $query->_whereTables['case_activity'] = 1; $query->_tables['civicrm_case'] = $query->_whereTables['civicrm_case'] = 1; $query->_tables['civicrm_case_contact'] = $query->_whereTables['civicrm_case_contact'] = 1; @@ -408,7 +408,7 @@ public static function whereClauseSingle(&$values, &$query) { } $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause("case_activity.medium_id", $op, $value, 'Int'); - $query->_qill[$grouping][] = ts("Activity Medium %1 %2", array(1 => $op, 2 => $names)); + $query->_qill[$grouping][] = ts("Activity Medium %1 %2", [1 => $op, 2 => $names]); $query->_tables['case_activity'] = $query->_whereTables['case_activity'] = 1; $query->_tables['civicrm_case'] = $query->_whereTables['civicrm_case'] = 1; $query->_tables['case_activity_medium'] = 1; @@ -417,7 +417,7 @@ public static function whereClauseSingle(&$values, &$query) { case 'case_activity_details': $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause("case_activity.details", $op, $value, 'String'); - $query->_qill[$grouping][] = ts("Activity Details %1 '%2'", array(1 => $op, 2 => $value)); + $query->_qill[$grouping][] = ts("Activity Details %1 '%2'", [1 => $op, 2 => $value]); $query->_tables['case_activity'] = $query->_whereTables['case_activity'] = 1; $query->_tables['civicrm_case'] = $query->_whereTables['civicrm_case'] = 1; $query->_tables['civicrm_case_contact'] = $query->_whereTables['civicrm_case_contact'] = 1; @@ -425,7 +425,7 @@ public static function whereClauseSingle(&$values, &$query) { case 'case_activity_is_auto': $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause("case_activity.is_auto", $op, $value, 'Boolean'); - $query->_qill[$grouping][] = ts("Activity Auto Genrated %1 '%2'", array(1 => $op, 2 => $value)); + $query->_qill[$grouping][] = ts("Activity Auto Genrated %1 '%2'", [1 => $op, 2 => $value]); $query->_tables['case_activity'] = $query->_whereTables['case_activity'] = 1; $query->_tables['civicrm_case'] = $query->_whereTables['civicrm_case'] = 1; $query->_tables['civicrm_case_contact'] = $query->_whereTables['civicrm_case_contact'] = 1; @@ -435,7 +435,7 @@ public static function whereClauseSingle(&$values, &$query) { case 'case_role': $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause("case_relation_type.name_b_a", $op, $value, 'String'); - $query->_qill[$grouping][] = ts("Role in Case %1 '%2'", array(1 => $op, 2 => $value)); + $query->_qill[$grouping][] = ts("Role in Case %1 '%2'", [1 => $op, 2 => $value]); $query->_tables['case_relation_type'] = $query->_whereTables['case_relationship_type'] = 1; $query->_tables['civicrm_case'] = $query->_whereTables['civicrm_case'] = 1; $query->_tables['civicrm_case_contact'] = $query->_whereTables['civicrm_case_contact'] = 1; @@ -467,7 +467,7 @@ public static function whereClauseSingle(&$values, &$query) { case 'case_taglist': $taglist = $value; - $value = array(); + $value = []; foreach ($taglist as $val) { if ($val) { $val = explode(',', $val); @@ -479,19 +479,19 @@ public static function whereClauseSingle(&$values, &$query) { } } case 'case_tags': - $tags = CRM_Core_PseudoConstant::get('CRM_Core_DAO_EntityTag', 'tag_id', array('onlyActive' => FALSE)); + $tags = CRM_Core_PseudoConstant::get('CRM_Core_DAO_EntityTag', 'tag_id', ['onlyActive' => FALSE]); - if (is_array($value)) { - foreach ($value as $k => $v) { + if (!empty($value)) { + $val = explode(',', $value); + foreach ($val as $v) { if ($v) { - $val[$k] = $k; - $names[] = $tags[$k]; + $names[] = $tags[$v]; } } } $query->_where[$grouping][] = " civicrm_case_tag.tag_id IN (" . implode(',', $val) . " )"; - $query->_qill[$grouping][] = ts('Case Tags %1', array(1 => $op)) . ' ' . implode(' ' . ts('or') . ' ', $names); + $query->_qill[$grouping][] = ts('Case Tags %1', [1 => $op]) . ' ' . implode(' ' . ts('or') . ' ', $names); $query->_tables['civicrm_case'] = $query->_whereTables['civicrm_case'] = 1; $query->_tables['civicrm_case_contact'] = $query->_whereTables['civicrm_case_contact'] = 1; $query->_tables['civicrm_case_tag'] = $query->_whereTables['civicrm_case_tag'] = 1; @@ -517,7 +517,7 @@ public static function from($name, $mode, $side) { break; case 'civicrm_case_reporter': - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts); $from .= " $side JOIN civicrm_activity_contact as case_activity_contact ON (case_activity.id = case_activity_contact.activity_id AND case_activity_contact.record_type_id = {$sourceID} ) "; $from .= " $side JOIN civicrm_contact as civicrm_case_reporter ON case_activity_contact.contact_id = civicrm_case_reporter.id "; @@ -598,7 +598,7 @@ public static function defaultReturnProperties( $properties = NULL; if ($mode & CRM_Contact_BAO_Query::MODE_CASE) { - $properties = array( + $properties = [ 'contact_type' => 1, 'contact_sub_type' => 1, 'contact_id' => 1, @@ -616,7 +616,7 @@ public static function defaultReturnProperties( 'case_scheduled_activity_date' => 1, 'phone' => 1, // 'case_scheduled_activity_type'=> 1 - ); + ]; if ($includeCustomFields) { // also get all the custom case properties @@ -643,7 +643,7 @@ public static function extraReturnProperties($mode) { $properties = NULL; if ($mode & CRM_Contact_BAO_Query::MODE_CASE) { - $properties = array( + $properties = [ 'case_start_date' => 1, 'case_end_date' => 1, 'case_subject' => 1, @@ -653,7 +653,7 @@ public static function extraReturnProperties($mode) { 'case_activity_medium_id' => 1, 'case_activity_details' => 1, 'case_activity_is_auto' => 1, - ); + ]; } return $properties; } @@ -663,11 +663,11 @@ public static function extraReturnProperties($mode) { */ public static function tableNames(&$tables) { if (!empty($tables['civicrm_case'])) { - $tables = array_merge(array('civicrm_case_contact' => 1), $tables); + $tables = array_merge(['civicrm_case_contact' => 1], $tables); } if (!empty($tables['case_relation_type'])) { - $tables = array_merge(array('case_relationship' => 1), $tables); + $tables = array_merge(['case_relationship' => 1], $tables); } } @@ -677,20 +677,18 @@ public static function tableNames(&$tables) { * @param CRM_Core_Form $form */ public static function buildSearchForm(&$form) { - $config = CRM_Core_Config::singleton(); - //validate case configuration. $configured = CRM_Case_BAO_Case::isCaseConfigured(); $form->assign('notConfigured', !$configured['configured']); - $form->addField('case_type_id', array('context' => 'search', 'entity' => 'Case')); - $form->addField('case_status_id', array('context' => 'search', 'entity' => 'Case')); + $form->addField('case_type_id', ['context' => 'search', 'entity' => 'Case']); + $form->addField('case_status_id', ['context' => 'search', 'entity' => 'Case']); CRM_Core_Form_Date::buildDateRange($form, 'case_from', 1, '_start_date_low', '_start_date_high', ts('From'), FALSE); CRM_Core_Form_Date::buildDateRange($form, 'case_to', 1, '_end_date_low', '_end_date_high', ts('From'), FALSE); $form->addElement('hidden', 'case_from_start_date_range_error'); $form->addElement('hidden', 'case_to_end_date_range_error'); - $form->addFormRule(array('CRM_Case_BAO_Query', 'formRule'), $form); + $form->addFormRule(['CRM_Case_BAO_Query', 'formRule'], $form); $form->assign('validCiviCase', TRUE); @@ -698,17 +696,18 @@ public static function buildSearchForm(&$form) { $accessAllCases = FALSE; if (CRM_Core_Permission::check('access all cases and activities')) { $accessAllCases = TRUE; - $caseOwner = array(1 => ts('Search All Cases'), 2 => ts('Only My Cases')); + $caseOwner = [1 => ts('Search All Cases'), 2 => ts('Only My Cases')]; $form->addRadio('case_owner', ts('Cases'), $caseOwner); + if ($form->get('context') != 'dashboard') { + $form->add('checkbox', 'upcoming', ts('Search Cases with Upcoming Activities')); + } } $form->assign('accessAllCases', $accessAllCases); - $caseTags = CRM_Core_BAO_Tag::getTags('civicrm_case'); + $caseTags = CRM_Core_BAO_Tag::getColorTags('civicrm_case'); if ($caseTags) { - foreach ($caseTags as $tagID => $tagName) { - $form->_tagElement = &$form->addElement('checkbox', "case_tags[$tagID]", NULL, $tagName); - } + $form->add('select2', 'case_tags', ts('Case Tag(s)'), $caseTags, FALSE, ['class' => 'big', 'placeholder' => ts('- select -'), 'multiple' => TRUE]); } $parentNames = CRM_Core_BAO_Tag::getTagSet('civicrm_case'); @@ -718,27 +717,19 @@ public static function buildSearchForm(&$form) { $form->addElement('checkbox', 'case_deleted', ts('Deleted Cases')); } - // add all the custom searchable fields - $extends = array('Case'); - $groupDetails = CRM_Core_BAO_CustomGroup::getGroupDetail(NULL, TRUE, $extends); - if ($groupDetails) { - $form->assign('caseGroupTree', $groupDetails); - foreach ($groupDetails as $group) { - foreach ($group['fields'] as $field) { - $fieldId = $field['id']; - $elementName = 'custom_' . $fieldId; - CRM_Core_BAO_CustomField::addQuickFormElement($form, $elementName, $fieldId, FALSE, TRUE); - } - } - } - $form->setDefaults(array('case_owner' => 1)); - } + $form->addElement('text', + 'case_subject', + ts('Case Subject'), + ['class' => 'huge'] + ); + $form->addElement('text', + 'case_id', + ts('Case ID') + ); - /** - * @param $row - * @param int $id - */ - public static function searchAction(&$row, $id) { + self::addCustomFormFields($form, ['Case']); + + $form->setDefaults(['case_owner' => 1]); } /** @@ -751,7 +742,7 @@ public static function searchAction(&$row, $id) { * @return bool|array */ public static function formRule($fields, $files, $form) { - $errors = array(); + $errors = []; if ((empty($fields['case_from_start_date_low']) || empty($fields['case_from_start_date_high'])) && (empty($fields['case_to_end_date_low']) || empty($fields['case_to_end_date_high']))) { return TRUE; diff --git a/CRM/Case/Controller/Search.php b/CRM/Case/Controller/Search.php index 74811c4cab64..9f3c0d133c8e 100644 --- a/CRM/Case/Controller/Search.php +++ b/CRM/Case/Controller/Search.php @@ -1,9 +1,9 @@ __table = 'civicrm_case'; parent::__construct(); } + /** - * Returns foreign keys and entity references + * Returns foreign keys and entity references. * * @return array * [CRM_Core_Reference_Interface] */ - static function getReferenceColumns() { + public static function getReferenceColumns() { if (!isset(Civi::$statics[__CLASS__]['links'])) { - Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'case_type_id', 'civicrm_case_type', 'id'); + Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'case_type_id', 'civicrm_case_type', 'id'); CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']); } return Civi::$statics[__CLASS__]['links']; } + /** * Returns all the column names of this table * * @return array */ - static function &fields() { + public static function &fields() { if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = array( - 'case_id' => array( + Civi::$statics[__CLASS__]['fields'] = [ + 'case_id' => [ 'name' => 'id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Case ID') , - 'description' => 'Unique Case ID', - 'required' => true, - 'import' => true, + 'title' => ts('Case ID'), + 'description' => ts('Unique Case ID'), + 'required' => TRUE, + 'import' => TRUE, 'where' => 'civicrm_case.id', 'headerPattern' => '', 'dataPattern' => '', - 'export' => true, - ) , - 'case_type_id' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_case', + 'entity' => 'Case', + 'bao' => 'CRM_Case_BAO_Case', + 'localizable' => 0, + ], + 'case_type_id' => [ 'name' => 'case_type_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Case Type') , - 'description' => 'FK to civicrm_case_type.id', - 'import' => true, + 'title' => ts('Case Type'), + 'description' => ts('FK to civicrm_case_type.id'), + 'required' => TRUE, + 'import' => TRUE, 'where' => 'civicrm_case.case_type_id', 'headerPattern' => '', 'dataPattern' => '', - 'export' => false, + 'export' => FALSE, + 'table_name' => 'civicrm_case', + 'entity' => 'Case', + 'bao' => 'CRM_Case_BAO_Case', + 'localizable' => 0, 'FKClassName' => 'CRM_Case_DAO_CaseType', - 'html' => array( + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'table' => 'civicrm_case_type', 'keyColumn' => 'id', 'labelColumn' => 'title', - ) - ) , - 'case_subject' => array( + ] + ], + 'case_subject' => [ 'name' => 'subject', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Case Subject') , - 'description' => 'Short name of the case.', + 'title' => ts('Case Subject'), + 'description' => ts('Short name of the case.'), 'maxlength' => 128, 'size' => CRM_Utils_Type::HUGE, - 'import' => true, + 'import' => TRUE, 'where' => 'civicrm_case.subject', 'headerPattern' => '', 'dataPattern' => '', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_case', + 'entity' => 'Case', + 'bao' => 'CRM_Case_BAO_Case', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'case_start_date' => array( + ], + ], + 'case_start_date' => [ 'name' => 'start_date', 'type' => CRM_Utils_Type::T_DATE, - 'title' => ts('Case Start Date') , - 'description' => 'Date on which given case starts.', - 'import' => true, + 'title' => ts('Case Start Date'), + 'description' => ts('Date on which given case starts.'), + 'import' => TRUE, 'where' => 'civicrm_case.start_date', 'headerPattern' => '', 'dataPattern' => '', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_case', + 'entity' => 'Case', + 'bao' => 'CRM_Case_BAO_Case', + 'localizable' => 0, + 'html' => [ 'type' => 'Select Date', - ) , - ) , - 'case_end_date' => array( + 'formatType' => 'activityDateTime', + ], + ], + 'case_end_date' => [ 'name' => 'end_date', 'type' => CRM_Utils_Type::T_DATE, - 'title' => ts('Case End Date') , - 'description' => 'Date on which given case ends.', - 'import' => true, + 'title' => ts('Case End Date'), + 'description' => ts('Date on which given case ends.'), + 'import' => TRUE, 'where' => 'civicrm_case.end_date', 'headerPattern' => '', 'dataPattern' => '', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_case', + 'entity' => 'Case', + 'bao' => 'CRM_Case_BAO_Case', + 'localizable' => 0, + 'html' => [ 'type' => 'Select Date', - ) , - ) , - 'details' => array( + 'formatType' => 'activityDateTime', + ], + ], + 'details' => [ 'name' => 'details', 'type' => CRM_Utils_Type::T_TEXT, - 'title' => ts('Details') , - 'description' => 'Details about the meeting (agenda, notes, etc).', + 'title' => ts('Details'), + 'description' => ts('Details about the meeting (agenda, notes, etc).'), 'rows' => 8, 'cols' => 60, - 'html' => array( + 'table_name' => 'civicrm_case', + 'entity' => 'Case', + 'bao' => 'CRM_Case_BAO_Case', + 'localizable' => 0, + 'html' => [ 'type' => 'TextArea', - ) , - ) , - 'case_status_id' => array( + ], + ], + 'case_status_id' => [ 'name' => 'status_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Case Status') , - 'description' => 'Id of case status.', - 'required' => true, - 'import' => true, + 'title' => ts('Case Status'), + 'description' => ts('Id of case status.'), + 'required' => TRUE, + 'import' => TRUE, 'where' => 'civicrm_case.status_id', 'headerPattern' => '', 'dataPattern' => '', - 'export' => false, - 'html' => array( + 'export' => FALSE, + 'table_name' => 'civicrm_case', + 'entity' => 'Case', + 'bao' => 'CRM_Case_BAO_Case', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'optionGroupName' => 'case_status', 'optionEditPath' => 'civicrm/admin/options/case_status', - ) - ) , - 'case_deleted' => array( + ] + ], + 'case_deleted' => [ 'name' => 'is_deleted', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Case is in the Trash') , - 'import' => true, + 'title' => ts('Case is in the Trash'), + 'import' => TRUE, 'where' => 'civicrm_case.is_deleted', 'headerPattern' => '', 'dataPattern' => '', - 'export' => true, - ) , - ); + 'export' => TRUE, + 'default' => '0', + 'table_name' => 'civicrm_case', + 'entity' => 'Case', + 'bao' => 'CRM_Case_BAO_Case', + 'localizable' => 0, + ], + 'case_created_date' => [ + 'name' => 'created_date', + 'type' => CRM_Utils_Type::T_TIMESTAMP, + 'title' => ts('Created Date'), + 'description' => ts('When was the case was created.'), + 'required' => FALSE, + 'export' => TRUE, + 'where' => 'civicrm_case.created_date', + 'headerPattern' => '', + 'dataPattern' => '', + 'default' => 'NULL', + 'table_name' => 'civicrm_case', + 'entity' => 'Case', + 'bao' => 'CRM_Case_BAO_Case', + 'localizable' => 0, + ], + 'case_modified_date' => [ + 'name' => 'modified_date', + 'type' => CRM_Utils_Type::T_TIMESTAMP, + 'title' => ts('Modified Date'), + 'description' => ts('When was the case (or closely related entity) was created or modified or deleted.'), + 'required' => FALSE, + 'export' => TRUE, + 'where' => 'civicrm_case.modified_date', + 'headerPattern' => '', + 'dataPattern' => '', + 'default' => 'CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP', + 'table_name' => 'civicrm_case', + 'entity' => 'Case', + 'bao' => 'CRM_Case_BAO_Case', + 'localizable' => 0, + ], + ]; CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } return Civi::$statics[__CLASS__]['fields']; } + /** * Return a mapping from field-name to the corresponding key (as used in fields()). * * @return array * Array(string $name => string $uniqueName). */ - static function &fieldKeys() { + public static function &fieldKeys() { if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); } return Civi::$statics[__CLASS__]['fieldKeys']; } + /** * Returns the names of this table * * @return string */ - static function getTableName() { + public static function getTableName() { return self::$_tableName; } + /** * Returns if this table needs to be logged * - * @return boolean + * @return bool */ - function getLog() { + public function getLog() { return self::$_log; } + /** * Returns the list of fields that can be imported * @@ -282,10 +355,11 @@ function getLog() { * * @return array */ - static function &import($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'case', $prefix, array()); + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'case', $prefix, []); return $r; } + /** * Returns the list of fields that can be exported * @@ -293,8 +367,38 @@ static function &import($prefix = false) { * * @return array */ - static function &export($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'case', $prefix, array()); + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'case', $prefix, []); return $r; } + + /** + * Returns the list of indices + * + * @param bool $localize + * + * @return array + */ + public static function indices($localize = TRUE) { + $indices = [ + 'index_case_type_id' => [ + 'name' => 'index_case_type_id', + 'field' => [ + 0 => 'case_type_id', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_case::0::case_type_id', + ], + 'index_is_deleted' => [ + 'name' => 'index_is_deleted', + 'field' => [ + 0 => 'is_deleted', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_case::0::is_deleted', + ], + ]; + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + } diff --git a/CRM/Case/DAO/CaseActivity.php b/CRM/Case/DAO/CaseActivity.php index 78c5803da8b9..f941d5cdd266 100644 --- a/CRM/Case/DAO/CaseActivity.php +++ b/CRM/Case/DAO/CaseActivity.php @@ -1,159 +1,158 @@ __table = 'civicrm_case_activity'; parent::__construct(); } + /** - * Returns foreign keys and entity references + * Returns foreign keys and entity references. * * @return array * [CRM_Core_Reference_Interface] */ - static function getReferenceColumns() { + public static function getReferenceColumns() { if (!isset(Civi::$statics[__CLASS__]['links'])) { - Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'case_id', 'civicrm_case', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'activity_id', 'civicrm_activity', 'id'); + Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'case_id', 'civicrm_case', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'activity_id', 'civicrm_activity', 'id'); CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']); } return Civi::$statics[__CLASS__]['links']; } + /** * Returns all the column names of this table * * @return array */ - static function &fields() { + public static function &fields() { if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = array( - 'id' => array( + Civi::$statics[__CLASS__]['fields'] = [ + 'id' => [ 'name' => 'id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Case Activity ID') , - 'description' => 'Unique case-activity association id', - 'required' => true, - ) , - 'case_id' => array( + 'title' => ts('Case Activity ID'), + 'description' => ts('Unique case-activity association id'), + 'required' => TRUE, + 'table_name' => 'civicrm_case_activity', + 'entity' => 'CaseActivity', + 'bao' => 'CRM_Case_DAO_CaseActivity', + 'localizable' => 0, + ], + 'case_id' => [ 'name' => 'case_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Case') , - 'description' => 'Case ID of case-activity association.', - 'required' => true, + 'title' => ts('Case'), + 'description' => ts('Case ID of case-activity association.'), + 'required' => TRUE, + 'table_name' => 'civicrm_case_activity', + 'entity' => 'CaseActivity', + 'bao' => 'CRM_Case_DAO_CaseActivity', + 'localizable' => 0, 'FKClassName' => 'CRM_Case_DAO_Case', - ) , - 'activity_id' => array( + ], + 'activity_id' => [ 'name' => 'activity_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Activity ID') , - 'description' => 'Activity ID of case-activity association.', - 'required' => true, + 'title' => ts('Activity ID'), + 'description' => ts('Activity ID of case-activity association.'), + 'required' => TRUE, + 'table_name' => 'civicrm_case_activity', + 'entity' => 'CaseActivity', + 'bao' => 'CRM_Case_DAO_CaseActivity', + 'localizable' => 0, 'FKClassName' => 'CRM_Activity_DAO_Activity', - ) , - ); + ], + ]; CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } return Civi::$statics[__CLASS__]['fields']; } + /** * Return a mapping from field-name to the corresponding key (as used in fields()). * * @return array * Array(string $name => string $uniqueName). */ - static function &fieldKeys() { + public static function &fieldKeys() { if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); } return Civi::$statics[__CLASS__]['fieldKeys']; } + /** * Returns the names of this table * * @return string */ - static function getTableName() { + public static function getTableName() { return self::$_tableName; } + /** * Returns if this table needs to be logged * - * @return boolean + * @return bool */ - function getLog() { + public function getLog() { return self::$_log; } + /** * Returns the list of fields that can be imported * @@ -161,10 +160,11 @@ function getLog() { * * @return array */ - static function &import($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'case_activity', $prefix, array()); + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'case_activity', $prefix, []); return $r; } + /** * Returns the list of fields that can be exported * @@ -172,8 +172,31 @@ static function &import($prefix = false) { * * @return array */ - static function &export($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'case_activity', $prefix, array()); + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'case_activity', $prefix, []); return $r; } + + /** + * Returns the list of indices + * + * @param bool $localize + * + * @return array + */ + public static function indices($localize = TRUE) { + $indices = [ + 'UI_case_activity_id' => [ + 'name' => 'UI_case_activity_id', + 'field' => [ + 0 => 'case_id', + 1 => 'activity_id', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_case_activity::0::case_id::activity_id', + ], + ]; + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + } diff --git a/CRM/Case/DAO/CaseContact.php b/CRM/Case/DAO/CaseContact.php index ff1ef5f5e231..641b5e5ae506 100644 --- a/CRM/Case/DAO/CaseContact.php +++ b/CRM/Case/DAO/CaseContact.php @@ -1,162 +1,161 @@ __table = 'civicrm_case_contact'; parent::__construct(); } + /** - * Returns foreign keys and entity references + * Returns foreign keys and entity references. * * @return array * [CRM_Core_Reference_Interface] */ - static function getReferenceColumns() { + public static function getReferenceColumns() { if (!isset(Civi::$statics[__CLASS__]['links'])) { - Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'case_id', 'civicrm_case', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'contact_id', 'civicrm_contact', 'id'); + Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'case_id', 'civicrm_case', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'contact_id', 'civicrm_contact', 'id'); CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']); } return Civi::$statics[__CLASS__]['links']; } + /** * Returns all the column names of this table * * @return array */ - static function &fields() { + public static function &fields() { if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = array( - 'id' => array( + Civi::$statics[__CLASS__]['fields'] = [ + 'id' => [ 'name' => 'id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Case Contact ID') , - 'description' => 'Unique case-contact association id', - 'required' => true, - ) , - 'case_id' => array( + 'title' => ts('Case Contact ID'), + 'description' => ts('Unique case-contact association id'), + 'required' => TRUE, + 'table_name' => 'civicrm_case_contact', + 'entity' => 'CaseContact', + 'bao' => 'CRM_Case_BAO_CaseContact', + 'localizable' => 0, + ], + 'case_id' => [ 'name' => 'case_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Case') , - 'description' => 'Case ID of case-contact association.', - 'required' => true, + 'title' => ts('Case'), + 'description' => ts('Case ID of case-contact association.'), + 'required' => TRUE, + 'table_name' => 'civicrm_case_contact', + 'entity' => 'CaseContact', + 'bao' => 'CRM_Case_BAO_CaseContact', + 'localizable' => 0, 'FKClassName' => 'CRM_Case_DAO_Case', - ) , - 'case_contact_id' => array( + ], + 'contact_id' => [ 'name' => 'contact_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Contact ID') , - 'description' => 'Contact ID of contact record given case belongs to.', - 'required' => true, + 'title' => ts('Contact ID'), + 'description' => ts('Contact ID of contact record given case belongs to.'), + 'required' => TRUE, + 'table_name' => 'civicrm_case_contact', + 'entity' => 'CaseContact', + 'bao' => 'CRM_Case_BAO_CaseContact', + 'localizable' => 0, 'FKClassName' => 'CRM_Contact_DAO_Contact', - 'html' => array( + 'html' => [ 'type' => 'EntityRef', - ) , - ) , - ); + ], + ], + ]; CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } return Civi::$statics[__CLASS__]['fields']; } + /** * Return a mapping from field-name to the corresponding key (as used in fields()). * * @return array * Array(string $name => string $uniqueName). */ - static function &fieldKeys() { + public static function &fieldKeys() { if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); } return Civi::$statics[__CLASS__]['fieldKeys']; } + /** * Returns the names of this table * * @return string */ - static function getTableName() { + public static function getTableName() { return self::$_tableName; } + /** * Returns if this table needs to be logged * - * @return boolean + * @return bool */ - function getLog() { + public function getLog() { return self::$_log; } + /** * Returns the list of fields that can be imported * @@ -164,10 +163,11 @@ function getLog() { * * @return array */ - static function &import($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'case_contact', $prefix, array()); + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'case_contact', $prefix, []); return $r; } + /** * Returns the list of fields that can be exported * @@ -175,8 +175,32 @@ static function &import($prefix = false) { * * @return array */ - static function &export($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'case_contact', $prefix, array()); + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'case_contact', $prefix, []); return $r; } + + /** + * Returns the list of indices + * + * @param bool $localize + * + * @return array + */ + public static function indices($localize = TRUE) { + $indices = [ + 'UI_case_contact_id' => [ + 'name' => 'UI_case_contact_id', + 'field' => [ + 0 => 'case_id', + 1 => 'contact_id', + ], + 'localizable' => FALSE, + 'unique' => TRUE, + 'sig' => 'civicrm_case_contact::1::case_id::contact_id', + ], + ]; + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + } diff --git a/CRM/Case/DAO/CaseType.php b/CRM/Case/DAO/CaseType.php index 6ae4cf7d1398..f187f9d650a0 100644 --- a/CRM/Case/DAO/CaseType.php +++ b/CRM/Case/DAO/CaseType.php @@ -1,210 +1,233 @@ __table = 'civicrm_case_type'; parent::__construct(); } + /** * Returns all the column names of this table * * @return array */ - static function &fields() { + public static function &fields() { if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = array( - 'id' => array( + Civi::$statics[__CLASS__]['fields'] = [ + 'id' => [ 'name' => 'id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Case Type ID') , - 'description' => 'Autoincremented type id', - 'required' => true, - ) , - 'name' => array( + 'title' => ts('Case Type ID'), + 'description' => ts('Autoincremented type id'), + 'required' => TRUE, + 'table_name' => 'civicrm_case_type', + 'entity' => 'CaseType', + 'bao' => 'CRM_Case_BAO_CaseType', + 'localizable' => 0, + ], + 'name' => [ 'name' => 'name', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Case Type Name') , - 'description' => 'Machine name for Case Type', - 'required' => true, + 'title' => ts('Case Type Name'), + 'description' => ts('Machine name for Case Type'), + 'required' => TRUE, 'maxlength' => 64, 'size' => CRM_Utils_Type::BIG, - ) , - 'title' => array( + 'table_name' => 'civicrm_case_type', + 'entity' => 'CaseType', + 'bao' => 'CRM_Case_BAO_CaseType', + 'localizable' => 0, + ], + 'title' => [ 'name' => 'title', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Case Type Title') , - 'description' => 'Natural language name for Case Type', - 'required' => true, + 'title' => ts('Case Type Title'), + 'description' => ts('Natural language name for Case Type'), + 'required' => TRUE, 'maxlength' => 64, 'size' => CRM_Utils_Type::BIG, - ) , - 'description' => array( + 'table_name' => 'civicrm_case_type', + 'entity' => 'CaseType', + 'bao' => 'CRM_Case_BAO_CaseType', + 'localizable' => 1, + ], + 'description' => [ 'name' => 'description', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Case Type Description') , - 'description' => 'Description of the Case Type', + 'title' => ts('Case Type Description'), + 'description' => ts('Description of the Case Type'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - ) , - 'is_active' => array( + 'table_name' => 'civicrm_case_type', + 'entity' => 'CaseType', + 'bao' => 'CRM_Case_BAO_CaseType', + 'localizable' => 1, + ], + 'is_active' => [ 'name' => 'is_active', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Case Type Is Active') , - 'description' => 'Is this entry active?', - ) , - 'is_reserved' => array( + 'title' => ts('Case Type Is Active'), + 'description' => ts('Is this entry active?'), + 'table_name' => 'civicrm_case_type', + 'entity' => 'CaseType', + 'bao' => 'CRM_Case_BAO_CaseType', + 'localizable' => 0, + ], + 'is_reserved' => [ 'name' => 'is_reserved', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Case Type Is Reserved') , - 'description' => 'Is this case type a predefined system type?', - ) , - 'weight' => array( + 'title' => ts('Case Type Is Reserved'), + 'description' => ts('Is this case type a predefined system type?'), + 'table_name' => 'civicrm_case_type', + 'entity' => 'CaseType', + 'bao' => 'CRM_Case_BAO_CaseType', + 'localizable' => 0, + ], + 'weight' => [ 'name' => 'weight', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Order') , - 'description' => 'Ordering of the case types', - 'required' => true, + 'title' => ts('Order'), + 'description' => ts('Ordering of the case types'), + 'required' => TRUE, 'default' => '1', - ) , - 'definition' => array( + 'table_name' => 'civicrm_case_type', + 'entity' => 'CaseType', + 'bao' => 'CRM_Case_BAO_CaseType', + 'localizable' => 0, + ], + 'definition' => [ 'name' => 'definition', 'type' => CRM_Utils_Type::T_BLOB, - 'title' => ts('Case Type Definition') , - 'description' => 'xml definition of case type', - ) , - ); + 'title' => ts('Case Type Definition'), + 'description' => ts('xml definition of case type'), + 'table_name' => 'civicrm_case_type', + 'entity' => 'CaseType', + 'bao' => 'CRM_Case_BAO_CaseType', + 'localizable' => 0, + ], + ]; CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } return Civi::$statics[__CLASS__]['fields']; } + /** * Return a mapping from field-name to the corresponding key (as used in fields()). * * @return array * Array(string $name => string $uniqueName). */ - static function &fieldKeys() { + public static function &fieldKeys() { if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); } return Civi::$statics[__CLASS__]['fieldKeys']; } + /** * Returns the names of this table * * @return string */ - static function getTableName() { + public static function getTableName() { return CRM_Core_DAO::getLocaleTableName(self::$_tableName); } + /** * Returns if this table needs to be logged * - * @return boolean + * @return bool */ - function getLog() { + public function getLog() { return self::$_log; } + /** * Returns the list of fields that can be imported * @@ -212,10 +235,11 @@ function getLog() { * * @return array */ - static function &import($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'case_type', $prefix, array()); + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'case_type', $prefix, []); return $r; } + /** * Returns the list of fields that can be exported * @@ -223,8 +247,31 @@ static function &import($prefix = false) { * * @return array */ - static function &export($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'case_type', $prefix, array()); + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'case_type', $prefix, []); return $r; } + + /** + * Returns the list of indices + * + * @param bool $localize + * + * @return array + */ + public static function indices($localize = TRUE) { + $indices = [ + 'case_type_name' => [ + 'name' => 'case_type_name', + 'field' => [ + 0 => 'name', + ], + 'localizable' => FALSE, + 'unique' => TRUE, + 'sig' => 'civicrm_case_type::1::name', + ], + ]; + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + } diff --git a/CRM/Case/Form/Activity.php b/CRM/Case/Form/Activity.php index ee9b585dd0a9..4b9cdbe1dde9 100644 --- a/CRM/Case/Form/Activity.php +++ b/CRM/Case/Form/Activity.php @@ -1,9 +1,9 @@ _caseId = explode(',', $caseIds); - $this->_context = CRM_Utils_Request::retrieve('context', 'String', $this); + $caseIds = CRM_Utils_Request::retrieve('caseid', 'CommaSeparatedIntegers', $this); + $this->_caseId = $caseIds ? explode(',', $caseIds) : []; + $this->_context = CRM_Utils_Request::retrieve('context', 'Alphanumeric', $this); if (!$this->_context) { $this->_context = 'caseActivity'; } $this->_crmDir = 'Case'; $this->assign('context', $this->_context); - $result = parent::preProcess(); + parent::preProcess(); - $scheduleStatusId = CRM_Core_OptionGroup::getValue('activity_status', 'Scheduled', 'name'); + $scheduleStatusId = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', 'Scheduled'); $this->assign('scheduleStatusId', $scheduleStatusId); if (!$this->_caseId && $this->_activityId) { - $this->_caseId = CRM_Core_DAO::getFieldValue('CRM_Case_DAO_CaseActivity', $this->_activityId, + $this->_caseId = (array) CRM_Core_DAO::getFieldValue('CRM_Case_DAO_CaseActivity', $this->_activityId, 'case_id', 'activity_id' ); } @@ -100,8 +108,8 @@ public function preProcess() { if ($this->_caseId && !CRM_Core_Permission::check('access all cases and activities') ) { - $session = CRM_Core_Session::singleton(); - $allCases = CRM_Case_BAO_Case::getCases(TRUE, $session->get('userID'), 'any'); + $params = ['type' => 'any']; + $allCases = CRM_Case_BAO_Case::getCases(TRUE, $params); if (count(array_intersect($this->_caseId, array_keys($allCases))) == 0) { CRM_Core_Error::fatal(ts('You are not authorized to access this page.')); } @@ -124,6 +132,8 @@ public function preProcess() { } $this->assign('caseType', $this->_caseType); + $this->_caseTypeDefinition = $this->getCaseTypeDefinition(); + $xmlProcessorProcess = new CRM_Case_XMLProcessor_Process(); $isMultiClient = $xmlProcessorProcess->getAllowMultipleCaseClients(); $this->assign('multiClient', $isMultiClient); @@ -173,22 +183,21 @@ public function preProcess() { $activityCount = CRM_Case_BAO_Case::getCaseActivityCount($caseId, $this->_activityTypeId); if ($activityCount >= $activityInst[$this->_activityTypeName]) { if ($activityInst[$this->_activityTypeName] == 1) { - $atArray = array('activity_type_id' => $this->_activityTypeId); + $atArray = ['activity_type_id' => $this->_activityTypeId]; $activities = CRM_Case_BAO_Case::getCaseActivity($caseId, $atArray, $this->_currentUserId ); - $activities = array_keys($activities); - $activities = $activities[0]; + $activityId = CRM_Utils_Array::first(array_keys($activities['data'])); $editUrl = CRM_Utils_System::url('civicrm/case/activity', - "reset=1&cid={$this->_currentlyViewedContactId}&caseid={$caseId}&action=update&id={$activities}" + "reset=1&cid={$this->_currentlyViewedContactId}&caseid={$caseId}&action=update&id={$activityId}" ); } CRM_Core_Error::statusBounce(ts("You can not add another '%1' activity to this case. %2", - array( + [ 1 => $this->_activityTypeName, - 2 => ts("Do you want to edit the existing activity?", array(1 => "href='$editUrl'")), - ) + 2 => ts("Do you want to edit the existing activity?", [1 => "href='$editUrl'"]), + ] ), $url ); @@ -197,6 +206,11 @@ public function preProcess() { } } + // Turn off the prompt which asks the user if they want to create separate + // activities when specifying multiple contacts "with" a new activity. + // Instead, always create one activity with all contacts together. + $this->supportsActivitySeparation = FALSE; + $session = CRM_Core_Session::singleton(); $session->pushUserContext($url); } @@ -206,7 +220,7 @@ public function preProcess() { */ public function setDefaultValues() { $this->_defaults = parent::setDefaultValues(); - $targetContactValues = array(); + $targetContactValues = []; foreach ($this->_caseId as $key => $val) { //get all clients. $clients = CRM_Case_BAO_Case::getContactNames($val); @@ -244,19 +258,40 @@ public function buildQuickForm() { $this->_fields['source_contact_id']['label'] = ts('Reported By'); unset($this->_fields['status_id']['attributes']['required']); + if ($this->restrictAssignmentByUserAccount()) { + $assigneeParameters['uf_user'] = 1; + } + + $activityAssignmentGroups = $this->getActivityAssignmentGroups(); + if (!empty($activityAssignmentGroups)) { + $assigneeParameters['group'] = ['IN' => $activityAssignmentGroups]; + } + + if (!empty($assigneeParameters)) { + $this->_fields['assignee_contact_id']['attributes']['api']['params'] + = array_merge($this->_fields['assignee_contact_id']['attributes']['api']['params'], $assigneeParameters); + + $this->_fields['followup_assignee_contact_id']['attributes']['api']['params'] + = array_merge($this->_fields['followup_assignee_contact_id']['attributes']['api']['params'], $assigneeParameters); + + //Disallow creating a contact from the assignee field UI. + $this->_fields['assignee_contact_id']['attributes']['create'] = FALSE; + $this->_fields['followup_assignee_contact_id']['attributes']['create'] = FALSE; + } + if ($this->_caseType) { $xmlProcessor = new CRM_Case_XMLProcessor_Process(); - $aTypes = array(); - foreach ($this->_caseType as $key => $val) { + $aTypes = []; + foreach (array_unique($this->_caseType) as $val) { $activityTypes = $xmlProcessor->get($val, 'ActivityTypes', TRUE); $aTypes = $aTypes + $activityTypes; } // remove Open Case activity type since we're inside an existing case - $openCaseID = CRM_Core_OptionGroup::getValue('activity_type', 'Open Case', 'name'); + $openCaseID = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Open Case'); unset($aTypes[$openCaseID]); asort($aTypes); - $this->_fields['followup_activity_type_id']['attributes'] = array('' => '- select activity type -') + $aTypes; + $this->_fields['followup_activity_type_id']['attributes'] = ['' => '- select activity type -'] + $aTypes; } parent::buildQuickForm(); @@ -268,17 +303,17 @@ public function buildQuickForm() { $this->assign('urlPath', 'civicrm/case/activity'); $encounterMediums = CRM_Case_PseudoConstant::encounterMedium(); - // Fixme: what's the justification for this? It seems like it is just re-adding an option in case it is the default and disabled. - // Is that really a big problem? - if ($this->_activityTypeFile == 'OpenCase') { - $this->_encounterMedium = CRM_Core_DAO::getFieldValue('CRM_Activity_DAO_Activity', $this->_activityId, - 'medium_id' - ); - if (!array_key_exists($this->_encounterMedium, $encounterMediums)) { - $encounterMediums[$this->_encounterMedium] = CRM_Core_OptionGroup::getLabel('encounter_medium', - $this->_encounterMedium, - FALSE - ); + + if ($this->_activityTypeFile == 'OpenCase' && $this->_action == CRM_Core_Action::UPDATE) { + $this->getElement('activity_date_time')->freeze(); + + if ($this->_activityId) { + // Fixme: what's the justification for this? It seems like it is just re-adding an option in case it is the default and disabled. + // Is that really a big problem? + $this->_encounterMedium = CRM_Core_DAO::getFieldValue('CRM_Activity_DAO_Activity', $this->_activityId, 'medium_id'); + if (!array_key_exists($this->_encounterMedium, $encounterMediums)) { + $encounterMediums[$this->_encounterMedium] = CRM_Core_PseudoConstant::getLabel('CRM_Activity_BAO_Activity', 'medium_id', $this->_encounterMedium); + } } } @@ -305,22 +340,22 @@ public function buildQuickForm() { } if (!empty($this->_relatedContacts)) { - $checkBoxes = array(); + $checkBoxes = []; foreach ($this->_relatedContacts as $id => $row) { foreach ($row as $key => $value) { - $checkBoxes[$key] = $this->addElement('checkbox', $key, NULL, NULL, array('class' => 'select-row')); + $checkBoxes[$key] = $this->addElement('checkbox', $key, NULL, NULL, ['class' => 'select-row']); } } $this->addGroup($checkBoxes, 'contact_check'); $this->addElement('checkbox', 'toggleSelect', NULL, NULL, - array('class' => 'select-rows') + ['class' => 'select-rows'] ); $this->assign('searchRows', $this->_relatedContacts); } $this->_relatedContacts = $rgc + $relCon; - $this->addFormRule(array('CRM_Case_Form_Activity', 'formRule'), $this); + $this->addFormRule(['CRM_Case_Form_Activity', 'formRule'], $this); } /** @@ -341,7 +376,7 @@ public static function formRule($fields, $files, $self) { return TRUE; } - return parent::formrule($fields, $files, $self); + return parent::formRule($fields, $files, $self); } /** @@ -361,7 +396,7 @@ public function postProcess($params = NULL) { $caseAttributeActivities = CRM_Core_OptionGroup::values('activity_type', FALSE, FALSE, FALSE, $activityCondition); if (!array_key_exists($this->_activityTypeId, $caseAttributeActivities)) { - $params = array('id' => $this->_activityId); + $params = ['id' => $this->_activityId]; $activityDelete = CRM_Activity_BAO_Activity::deleteActivity($params, TRUE); if ($activityDelete) { $statusMsg = ts('The selected activity has been moved to the Trash. You can view and / or restore deleted activities by checking "Deleted Activities" from the Case Activities search filter (under Manage Case).
'); @@ -371,10 +406,10 @@ public function postProcess($params = NULL) { $statusMsg = ts("Selected Activity cannot be deleted."); } - $tagParams = array( + $tagParams = [ 'entity_table' => 'civicrm_activity', 'entity_id' => $this->_activityId, - ); + ]; CRM_Core_BAO_EntityTag::del($tagParams); CRM_Core_Session::setStatus('', $statusMsg, 'info'); @@ -383,7 +418,7 @@ public function postProcess($params = NULL) { if ($this->_action & CRM_Core_Action::RENEW) { $statusMsg = NULL; - $params = array('id' => $this->_activityId); + $params = ['id' => $this->_activityId]; $activityRestore = CRM_Activity_BAO_Activity::restoreActivity($params); if ($activityRestore) { $statusMsg = ts('The selected activity has been restored.
'); @@ -400,8 +435,6 @@ public function postProcess($params = NULL) { $params['parent_id'] = $parentId; } - // store the dates with proper format - $params['activity_date_time'] = CRM_Utils_Date::processDate($params['activity_date_time'], $params['activity_date_time_time']); $params['activity_type_id'] = $this->_activityTypeId; // format with contact (target contact) values @@ -409,20 +442,20 @@ public function postProcess($params = NULL) { $params['target_contact_id'] = explode(',', $params['target_contact_id']); } else { - $params['target_contact_id'] = array(); + $params['target_contact_id'] = []; } // format activity custom data if (!empty($params['hidden_custom'])) { if ($this->_activityId) { // retrieve and include the custom data of old Activity - $oldActivity = civicrm_api3('Activity', 'getsingle', array('id' => $this->_activityId)); + $oldActivity = civicrm_api3('Activity', 'getsingle', ['id' => $this->_activityId]); $params = array_merge($oldActivity, $params); // unset custom fields-id from params since we want custom // fields to be saved for new activity. foreach ($params as $key => $value) { - $match = array(); + $match = []; if (preg_match('/^(custom_\d+_)(\d+)$/', $key, $match)) { $params[$match[1] . '-1'] = $params[$key]; @@ -454,7 +487,7 @@ public function postProcess($params = NULL) { $params['assignee_contact_id'] = explode(',', $params['assignee_contact_id']); } else { - $params['assignee_contact_id'] = array(); + $params['assignee_contact_id'] = []; } if (isset($this->_activityId)) { @@ -485,7 +518,7 @@ public function postProcess($params = NULL) { $params['case_id'] = $val; // activity create/update $activity = CRM_Activity_BAO_Activity::create($params); - $vvalue[] = array('case_id' => $val, 'actId' => $activity->id); + $vvalue[] = ['case_id' => $val, 'actId' => $activity->id]; // call end post process, after the activity has been created/updated. $this->endPostProcess($params, $activity); } @@ -493,7 +526,7 @@ public function postProcess($params = NULL) { else { // since the params we need to set are very few, and we don't want rest of the // work done by bao create method , lets use dao object to make the changes - $params = array('id' => $this->_activityId); + $params = ['id' => $this->_activityId]; $params['is_current_revision'] = 0; $activity = new CRM_Activity_DAO_Activity(); $activity->copyValues($params); @@ -523,13 +556,13 @@ public function postProcess($params = NULL) { foreach ($this->_caseId as $key => $val) { $newActParams['case_id'] = $val; $activity = CRM_Activity_BAO_Activity::create($newActParams); - $vvalue[] = array('case_id' => $val, 'actId' => $activity->id); + $vvalue[] = ['case_id' => $val, 'actId' => $activity->id]; // call end post process, after the activity has been created/updated. $this->endPostProcess($newActParams, $activity); } // copy files attached to old activity if any, to new one, // as long as users have not selected the 'delete attachment' option. - if (empty($newActParams['is_delete_attachment'])) { + if (empty($newActParams['is_delete_attachment']) && ($this->_activityId != $activity->id)) { CRM_Core_BAO_File::copyEntityFile('civicrm_activity', $this->_activityId, 'civicrm_activity', $activity->id ); @@ -542,11 +575,13 @@ public function postProcess($params = NULL) { foreach ($vvalue as $vkey => $vval) { if ($vval['actId']) { // add tags if exists - $tagParams = array(); + $tagParams = []; if (!empty($params['tag'])) { - foreach ($params['tag'] as $tag) { - $tagParams[$tag] = 1; + if (!is_array($params['tag'])) { + $params['tag'] = explode(',', $params['tag']); } + + $tagParams = array_fill_keys($params['tag'], 1); } //save static tags @@ -569,28 +604,22 @@ public function postProcess($params = NULL) { unset($caseParams['subject'], $caseParams['details'], $caseParams['status_id'], $caseParams['custom'] ); - $case = CRM_Case_BAO_Case::create($caseParams); + CRM_Case_BAO_Case::create($caseParams); // create case activity record - $caseParams = array( + $caseParams = [ 'activity_id' => $vval['actId'], 'case_id' => $vval['case_id'], - ); + ]; CRM_Case_BAO_Case::processCaseActivity($caseParams); } - // Insert civicrm_log record for the activity (e.g. store the - // created / edited by contact id and date for the activity) - // Note - civicrm_log is already created by CRM_Activity_BAO_Activity::create() - // send copy to selected contacts. $mailStatus = ''; - $mailToContacts = array(); + $mailToContacts = []; //CRM-5695 //check for notification settings for assignee contacts - $selectedContacts = array('contact_check'); - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); - $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts); + $selectedContacts = ['contact_check']; if (Civi::settings()->get('activity_assignee_notification')) { $selectedContacts[] = 'assignee_contact_id'; } @@ -602,7 +631,7 @@ public function postProcess($params = NULL) { $mailStatus = ts("A copy of the activity has also been sent to selected contacts(s)."); } else { - $this->_relatedContacts = CRM_Activity_BAO_ActivityAssignment::getAssigneeNames(array($vval['actId']), TRUE, FALSE); + $this->_relatedContacts = CRM_Activity_BAO_ActivityAssignment::getAssigneeNames([$vval['actId']], TRUE, FALSE); $mailStatus .= ' ' . ts("A copy of the activity has also been sent to assignee contacts(s)."); } //build an associative array with unique email addresses. @@ -628,7 +657,7 @@ public function postProcess($params = NULL) { } } - $extraParams = array('case_id' => $vval['case_id'], 'client_id' => $this->_currentlyViewedContactId); + $extraParams = ['case_id' => $vval['case_id'], 'client_id' => $this->_currentlyViewedContactId]; $result = CRM_Activity_BAO_Activity::sendToAssignee($activity, $mailToContacts, $extraParams); if (empty($result)) { $mailStatus = ''; @@ -640,17 +669,77 @@ public function postProcess($params = NULL) { $followupActivity = CRM_Activity_BAO_Activity::createFollowupActivity($vval['actId'], $params); if ($followupActivity) { - $caseParams = array( + $caseParams = [ 'activity_id' => $followupActivity->id, 'case_id' => $vval['case_id'], - ); + ]; CRM_Case_BAO_Case::processCaseActivity($caseParams); $followupStatus = ts("A followup activity has been scheduled.") . '

'; } } - $title = ts("%1 Saved", array(1 => $this->_activityTypeName)); + $title = ts("%1 Saved", [1 => $this->_activityTypeName]); CRM_Core_Session::setStatus($followupStatus . $mailStatus, $title, 'success'); } } + /** + * Returns the groups that contacts must belong to in order to be assigned + * an activity for this case. It returns an empty array if no groups are found for + * the case type linked to the caseId. + * + * @return array + */ + private function getActivityAssignmentGroups() { + if (!$this->_caseTypeDefinition) { + return []; + } + + $assignmentGroups = []; + foreach ($this->_caseTypeDefinition as $caseId => $definition) { + if (!empty($definition['activityAsgmtGrps'])) { + $assignmentGroups = array_merge($assignmentGroups, $definition['activityAsgmtGrps']); + } + } + + return $assignmentGroups; + } + + /** + * Returns whether contacts must have a user account in order to be + * assigned an activity for this case. + * + * @return bool + */ + private function restrictAssignmentByUserAccount() { + if (!$this->_caseTypeDefinition) { + return FALSE; + } + + foreach ($this->_caseTypeDefinition as $caseId => $definition) { + if (!empty($definition['restrictActivityAsgmtToCmsUser'])) { + return TRUE; + } + } + + return FALSE; + } + + /** + * Returns the case type definition column value for the case type linked to the caseId. + * + * @return array + */ + private function getCaseTypeDefinition() { + if (!$this->_caseId) { + return []; + } + + $definitions = civicrm_api3('CaseType', 'get', [ + 'return' => ['name', 'definition'], + 'name' => ['IN' => array_unique($this->_caseType)], + ]); + + return array_column($definitions['values'], 'definition', 'name'); + } + } diff --git a/CRM/Case/Form/Activity/ChangeCaseStartDate.php b/CRM/Case/Form/Activity/ChangeCaseStartDate.php index 1e0ea9e3068b..aecb3caef7e8 100644 --- a/CRM/Case/Form/Activity/ChangeCaseStartDate.php +++ b/CRM/Case/Form/Activity/ChangeCaseStartDate.php @@ -1,9 +1,9 @@ _caseId); - $openCaseParams = array('activity_type_id' => $openCaseActivityType); + $openCaseParams = ['activity_type_id' => $openCaseActivityType]; $openCaseInfo = CRM_Case_BAO_Case::getCaseActivityDates($caseId, $openCaseParams, TRUE); if (empty($openCaseInfo)) { - list($defaults['start_date'], $defaults['start_date_time']) = CRM_Utils_Date::setDateDefaults(); + $defaults['start_date'] = date('Y-m-d H:i:s'); } else { // We know there can only be one result @@ -79,7 +76,7 @@ public static function setDefaultValues(&$form) { // store activity id for updating it later $form->openCaseActivityId = $openCaseInfo['id']; - list($defaults['start_date'], $defaults['start_date_time']) = CRM_Utils_Date::setDateDefaults($openCaseInfo['activity_date'], 'activityDateTime'); + $defaults['start_date'] = $openCaseInfo['activity_date']; } return $defaults; } @@ -94,7 +91,7 @@ public static function buildQuickForm(&$form) { $currentStartDate = CRM_Core_DAO::getFieldValue('CRM_Case_DAO_Case', $caseId, 'start_date'); $form->assign('current_start_date', $currentStartDate); - $form->addDate('start_date', ts('New Start Date'), FALSE, array('formatType' => 'activityDateTime')); + $form->add('datepicker', 'start_date', ts('New Start Date'), [], TRUE); } /** @@ -135,10 +132,6 @@ public static function beginPostProcess(&$form, &$params) { * @param $activity */ public static function endPostProcess(&$form, &$params, $activity) { - if (!empty($params['start_date'])) { - $params['start_date'] = CRM_Utils_Date::processDate($params['start_date'], $params['start_date_time']); - } - $caseType = CRM_Utils_Array::first($form->_caseType); $caseId = CRM_Utils_Array::first($form->_caseId); @@ -156,22 +149,22 @@ public static function endPostProcess(&$form, &$params, $activity) { $config = CRM_Core_Config::singleton(); - $params['status_id'] = CRM_Core_OptionGroup::getValue('activity_status', 'Completed', 'name'); + $params['status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', 'Completed'); $activity->status_id = $params['status_id']; - $params['priority_id'] = CRM_Core_OptionGroup::getValue('priority', 'Normal', 'name'); + $params['priority_id'] = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'priority_id', 'Normal'); $activity->priority_id = $params['priority_id']; // 1. save activity subject with new start date $currentStartDate = CRM_Utils_Date::customFormat(CRM_Core_DAO::getFieldValue('CRM_Case_DAO_Case', $caseId, 'start_date' ), $config->dateformatFull); - $newStartDate = CRM_Utils_Date::customFormat(CRM_Utils_Date::mysqlToIso($params['start_date']), $config->dateformatFull); + $newStartDate = CRM_Utils_Date::customFormat($params['start_date'], $config->dateformatFull); $subject = 'Change Case Start Date from ' . $currentStartDate . ' to ' . $newStartDate; $activity->subject = $subject; $activity->save(); // 2. initiate xml processor $xmlProcessor = new CRM_Case_XMLProcessor_Process(); - $xmlProcessorParams = array( + $xmlProcessorParams = [ 'clientID' => $form->_currentlyViewedContactId, 'creatorID' => $form->_currentUserId, 'standardTimeline' => 0, @@ -181,7 +174,7 @@ public static function endPostProcess(&$form, &$params, $activity) { 'activityTypeName' => 'Change Case Start Date', 'activitySetName' => 'standard_timeline', 'resetTimeline' => 1, - ); + ]; $xmlProcessor->run($caseType, $xmlProcessorParams); @@ -190,21 +183,21 @@ public static function endPostProcess(&$form, &$params, $activity) { if ($form->openCaseActivityId) { $abao = new CRM_Activity_BAO_Activity(); - $oldParams = array('id' => $form->openCaseActivityId); - $oldActivityDefaults = array(); + $oldParams = ['id' => $form->openCaseActivityId]; + $oldActivityDefaults = []; $oldActivity = $abao->retrieve($oldParams, $oldActivityDefaults); // save the old values require_once 'api/v3/utils.php'; - $openCaseParams = array(); + $openCaseParams = []; //@todo calling api functions directly is not supported _civicrm_api3_object_to_array($oldActivity, $openCaseParams); // update existing revision - $oldParams = array( + $oldParams = [ 'id' => $form->openCaseActivityId, 'is_current_revision' => 0, - ); + ]; $oldActivity = new CRM_Activity_DAO_Activity(); $oldActivity->copyValues($oldParams); $oldActivity->save(); @@ -226,17 +219,17 @@ public static function endPostProcess(&$form, &$params, $activity) { } else { // Create linkage to case - $caseActivityParams = array( + $caseActivityParams = [ 'activity_id' => $newActivity->id, 'case_id' => $caseId, - ); + ]; CRM_Case_BAO_Case::processCaseActivity($caseActivityParams); - $caseActivityParams = array( + $caseActivityParams = [ 'activityID' => $form->openCaseActivityId, 'mainActivityId' => $newActivity->id, - ); + ]; CRM_Activity_BAO_Activity::copyExtendedActivityData($caseActivityParams); } } diff --git a/CRM/Case/Form/Activity/ChangeCaseStatus.php b/CRM/Case/Form/Activity/ChangeCaseStatus.php index 7e6f56d1b3f3..d0059b0cf23d 100644 --- a/CRM/Case/Form/Activity/ChangeCaseStatus.php +++ b/CRM/Case/Form/Activity/ChangeCaseStatus.php @@ -1,9 +1,9 @@ _caseId)) { CRM_Core_Error::fatal(ts('Case Id not found.')); } + + $form->addElement('checkbox', 'updateLinkedCases', NULL, NULL, ['class' => 'select-row']); + + $caseID = CRM_Utils_Array::first($form->_caseId); + $cases = CRM_Case_BAO_Case::getRelatedCases($caseID); + $form->assign('linkedCases', $cases); } /** @@ -58,7 +64,7 @@ public static function preProcess(&$form) { * @return array */ public static function setDefaultValues(&$form) { - $defaults = array(); + $defaults = []; // Retrieve current case status $defaults['case_status_id'] = $form->_defaultCaseStatus; @@ -72,7 +78,26 @@ public static function buildQuickForm(&$form) { $form->removeElement('status_id'); $form->removeElement('priority_id'); + $caseTypes = []; + $form->_caseStatus = CRM_Case_PseudoConstant::caseStatus(); + $statusNames = CRM_Case_PseudoConstant::caseStatus('name'); + + // Limit case statuses to allowed types for these case(s) + $allCases = civicrm_api3('Case', 'get', ['return' => 'case_type_id', 'id' => ['IN' => (array) $form->_caseId]]); + foreach ($allCases['values'] as $case) { + $caseTypes[$case['case_type_id']] = $case['case_type_id']; + } + $caseTypes = civicrm_api3('CaseType', 'get', ['id' => ['IN' => $caseTypes]]); + foreach ($caseTypes['values'] as $ct) { + if (!empty($ct['definition']['statuses'])) { + foreach ($form->_caseStatus as $id => $label) { + if (!in_array($statusNames[$id], $ct['definition']['statuses'])) { + unset($form->_caseStatus[$id]); + } + } + } + } foreach ($form->_caseId as $key => $val) { $form->_oldCaseStatus[] = $form->_defaultCaseStatus[] = CRM_Core_DAO::getFieldValue('CRM_Case_DAO_Case', $val, 'status_id'); @@ -80,10 +105,7 @@ public static function buildQuickForm(&$form) { foreach ($form->_defaultCaseStatus as $keydefault => $valdefault) { if (!array_key_exists($valdefault, $form->_caseStatus)) { - $form->_caseStatus[$valdefault] = CRM_Core_OptionGroup::getLabel('case_status', - $valdefault, - FALSE - ); + $form->_caseStatus[$valdefault] = CRM_Core_PseudoConstant::getLabel('CRM_Case_BAO_Case', 'status_id', $valdefault); } } $element = $form->add('select', 'case_status_id', ts('Case Status'), @@ -117,28 +139,38 @@ public static function formRule($values, $files, $form) { /** * Process the form submission. * - * * @param CRM_Core_Form $form * @param array $params */ public static function beginPostProcess(&$form, &$params) { $params['id'] = CRM_Utils_Array::value('case_id', $params); + + if ($params['updateLinkedCases'] === '1') { + $caseID = CRM_Utils_Array::first($form->_caseId); + $cases = CRM_Case_BAO_Case::getRelatedCases($caseID); + + foreach ($cases as $currentCase) { + if ($currentCase['status_id'] != $params['case_status_id']) { + $form->_caseId[] = $currentCase['case_id']; + } + } + } } /** * Process the form submission. * - * * @param CRM_Core_Form $form * @param array $params * @param CRM_Activity_BAO_Activity $activity */ public static function endPostProcess(&$form, &$params, $activity) { + $groupingValues = CRM_Core_OptionGroup::values('case_status', FALSE, TRUE, FALSE, NULL, 'value'); // Set case end_date if we're closing the case. Clear end_date if we're (re)opening it. if (CRM_Utils_Array::value($params['case_status_id'], $groupingValues) == 'Closed' && !empty($params['activity_date_time'])) { - $params['end_date'] = $params['activity_date_time']; + $params['end_date'] = CRM_Utils_Date::isoToMysql($params['activity_date_time']); // End case-specific relationships (roles) foreach ($params['target_contact_id'] as $cid) { @@ -146,10 +178,10 @@ public static function endPostProcess(&$form, &$params, $activity) { // FIXME: Is there an existing function to close a relationship? $query = 'UPDATE civicrm_relationship SET end_date=%2 WHERE id=%1'; foreach ($rels as $relId => $relData) { - $relParams = array( - 1 => array($relId, 'Integer'), - 2 => array($params['end_date'], 'Timestamp'), - ); + $relParams = [ + 1 => [$relId, 'Integer'], + 2 => [$params['end_date'], 'Timestamp'], + ]; CRM_Core_DAO::executeQuery($query, $relParams); } } @@ -159,33 +191,29 @@ public static function endPostProcess(&$form, &$params, $activity) { // Reopen case-specific relationships (roles) foreach ($params['target_contact_id'] as $cid) { - $rels = CRM_Case_BAO_Case::getCaseRoles($cid, $params['case_id']); + $rels = CRM_Case_BAO_Case::getCaseRoles($cid, $params['case_id'], NULL, FALSE); // FIXME: Is there an existing function? $query = 'UPDATE civicrm_relationship SET end_date=NULL WHERE id=%1'; foreach ($rels as $relId => $relData) { - $relParams = array(1 => array($relId, 'Integer')); + $relParams = [1 => [$relId, 'Integer']]; CRM_Core_DAO::executeQuery($query, $relParams); } } } - $params['status_id'] = CRM_Core_OptionGroup::getValue('activity_status', 'Completed', 'name'); + $params['status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', 'Completed'); $activity->status_id = $params['status_id']; - $params['priority_id'] = CRM_Core_OptionGroup::getValue('priority', 'Normal', 'name'); + $params['priority_id'] = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'priority_id', 'Normal'); $activity->priority_id = $params['priority_id']; foreach ($form->_oldCaseStatus as $statuskey => $statusval) { if ($activity->subject == 'null') { - $activity->subject = ts('Case status changed from %1 to %2', array( - 1 => CRM_Utils_Array::value($statusval, $form->_caseStatus), - 2 => CRM_Utils_Array::value($params['case_status_id'], $form->_caseStatus), - ) - ); + $activity->subject = ts('Case status changed from %1 to %2', [ + 1 => CRM_Utils_Array::value($statusval, $form->_caseStatus), + 2 => CRM_Utils_Array::value($params['case_status_id'], $form->_caseStatus), + ]); $activity->save(); } } - - // FIXME: does this do anything ? - $params['statusMsg'] = ts('Case Status changed successfully.'); } } diff --git a/CRM/Case/Form/Activity/ChangeCaseType.php b/CRM/Case/Form/Activity/ChangeCaseType.php index d1c99102f271..2a8ac64e850c 100644 --- a/CRM/Case/Form/Activity/ChangeCaseType.php +++ b/CRM/Case/Form/Activity/ChangeCaseType.php @@ -1,9 +1,9 @@ _caseTypeId; return $defaults; @@ -85,11 +84,11 @@ public static function buildQuickForm(&$form) { $form->_caseType[$form->_caseTypeId] = CRM_Core_DAO::getFieldValue('CRM_Case_DAO_CaseType', $form->_caseTypeId, 'title'); } - $form->addField('case_type_id', array('context' => 'create', 'entity' => 'Case')); + $form->addField('case_type_id', ['context' => 'create', 'entity' => 'Case']); // timeline - $form->addYesNo('is_reset_timeline', ts('Reset Case Timeline?'), NULL, TRUE, array('onclick' => "return showHideByValue('is_reset_timeline','','resetTimeline','table-row','radio',false);")); - $form->addDateTime('reset_date_time', ts('Reset Start Date'), FALSE, array('formatType' => 'activityDateTime')); + $form->addYesNo('is_reset_timeline', ts('Reset Case Timeline?'), NULL, TRUE); + $form->add('datepicker', 'reset_date_time', ts('Reset Start Date'), NULL, FALSE, ['allowClear' => FALSE]); } /** @@ -123,10 +122,6 @@ public static function beginPostProcess(&$form, &$params) { if (CRM_Utils_Array::value('is_reset_timeline', $params) == 0) { unset($params['reset_date_time']); } - else { - // store the date with proper format - $params['reset_date_time'] = CRM_Utils_Date::processDate($params['reset_date_time'], $params['reset_date_time_time']); - } } /** @@ -158,17 +153,17 @@ public static function endPostProcess(&$form, &$params, $activity) { CRM_Core_Error::fatal('Required parameter missing for ChangeCaseType - end post processing'); } - $params['status_id'] = CRM_Core_OptionGroup::getValue('activity_status', 'Completed', 'name'); + $params['status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', 'Completed'); $activity->status_id = $params['status_id']; - $params['priority_id'] = CRM_Core_OptionGroup::getValue('priority', 'Normal', 'name'); + $params['priority_id'] = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'priority_id', 'Normal'); $activity->priority_id = $params['priority_id']; if ($activity->subject == 'null') { $activity->subject = ts('Case type changed from %1 to %2', - array( + [ 1 => CRM_Utils_Array::value($form->_defaults['case_type_id'], $allCaseTypes), 2 => CRM_Utils_Array::value($params['case_type_id'], $allCaseTypes), - ) + ] ); $activity->save(); } @@ -176,7 +171,7 @@ public static function endPostProcess(&$form, &$params, $activity) { // 1. initiate xml processor $xmlProcessor = new CRM_Case_XMLProcessor_Process(); $caseId = CRM_Utils_Array::first($form->_caseId); - $xmlProcessorParams = array( + $xmlProcessorParams = [ 'clientID' => $form->_currentlyViewedContactId, 'creatorID' => $form->_currentUserId, 'standardTimeline' => 1, @@ -184,7 +179,7 @@ public static function endPostProcess(&$form, &$params, $activity) { 'activity_date_time' => CRM_Utils_Array::value('reset_date_time', $params), 'caseID' => $caseId, 'resetTimeline' => CRM_Utils_Array::value('is_reset_timeline', $params), - ); + ]; $xmlProcessor->run($caseType, $xmlProcessorParams); // status msg diff --git a/CRM/Case/Form/Activity/LinkCases.php b/CRM/Case/Form/Activity/LinkCases.php index 43f8cee5963c..322225c52b39 100644 --- a/CRM/Case/Form/Activity/LinkCases.php +++ b/CRM/Case/Form/Activity/LinkCases.php @@ -1,9 +1,9 @@ get('relatedCases'); if (!isset($relatedCases)) { - $relatedCases = CRM_Case_BAO_Case::getRelatedCases($caseId, $form->_currentlyViewedContactId); + $relatedCases = CRM_Case_BAO_Case::getRelatedCases($caseId); $form->set('relatedCases', empty($relatedCases) ? FALSE : $relatedCases); } } @@ -70,7 +71,11 @@ public static function preProcess(&$form) { * @return array */ public static function setDefaultValues(&$form) { - return $defaults = array(); + $defaults = []; + if (!empty($_GET['link_to_case_id']) && CRM_Utils_Rule::positiveInteger($_GET['link_to_case_id'])) { + $defaults['link_to_case_id'] = $_GET['link_to_case_id']; + } + return $defaults; } /** @@ -82,16 +87,16 @@ public static function buildQuickForm(&$form) { if (is_array($relatedCases) && !empty($relatedCases)) { $excludeCaseIds = array_merge($excludeCaseIds, array_keys($relatedCases)); } - $form->addEntityRef('link_to_case_id', ts('Link To Case'), array( + $form->addEntityRef('link_to_case_id', ts('Link To Case'), [ 'entity' => 'Case', - 'api' => array( - 'extra' => array('case_id.case_type_id.title', 'contact_id.sort_name'), - 'params' => array( - 'case_id' => array('NOT IN' => $excludeCaseIds), + 'api' => [ + 'extra' => ['case_id.case_type_id.title', 'contact_id.sort_name'], + 'params' => [ + 'case_id' => ['NOT IN' => $excludeCaseIds], 'case_id.is_deleted' => 0, - ), - ), - ), TRUE); + ], + ], + ], TRUE); } /** @@ -107,7 +112,7 @@ public static function buildQuickForm(&$form) { * list of errors to be posted back to the form */ public static function formRule($values, $files, $form) { - $errors = array(); + $errors = []; $linkCaseId = CRM_Utils_Array::value('link_to_case_id', $values); assert('is_numeric($linkCaseId)'); @@ -148,10 +153,10 @@ public static function endPostProcess(&$form, &$params, &$activity) { //create a link between two cases. if ($activityId && $linkCaseID) { - $caseParams = array( + $caseParams = [ 'case_id' => $linkCaseID, 'activity_id' => $activityId, - ); + ]; CRM_Case_BAO_Case::processCaseActivity($caseParams); } } diff --git a/CRM/Case/Form/Activity/OpenCase.php b/CRM/Case/Form/Activity/OpenCase.php index 91aff34516fa..ed571d76f3ad 100644 --- a/CRM/Case/Form/Activity/OpenCase.php +++ b/CRM/Case/Form/Activity/OpenCase.php @@ -1,9 +1,9 @@ _context == 'caseActivity') { $contactID = CRM_Utils_Request::retrieve('cid', 'Positive', $form); - $atype = CRM_Core_OptionGroup::getValue('activity_type', - 'Change Case Start Date', - 'name' - ); + $atype = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Change Case Start Date'); $caseId = CRM_Utils_Array::first($form->_caseId); $form->assign('changeStartURL', CRM_Utils_System::url('civicrm/case/activity', "action=add&reset=1&cid=$contactID&caseid={$caseId}&atype=$atype" @@ -65,7 +64,7 @@ public static function preProcess(&$form) { return; } - $form->_context = CRM_Utils_Request::retrieve('context', 'String', $form); + $form->_context = CRM_Utils_Request::retrieve('context', 'Alphanumeric', $form); $form->_contactID = CRM_Utils_Request::retrieve('cid', 'Positive', $form); $form->assign('context', $form->_context); @@ -89,16 +88,17 @@ public static function preProcess(&$form) { * Set default values for the form. For edit/view mode * the default values are retrieved from the database * + * @param CRM_Case_Form_Case $form * - * @param CRM_Core_Form $form + * @return array $defaults */ public static function setDefaultValues(&$form) { - $defaults = array(); + $defaults = []; if ($form->_context == 'caseActivity') { return $defaults; } - list($defaults['start_date'], $defaults['start_date_time']) = CRM_Utils_Date::setDateDefaults(NULL, 'activityDateTime'); + $defaults['start_date'] = date('Y-m-d H:i:s'); // set default case status, case type, encounter medium, location type and phone type defaults are set in DB if ($form->_caseStatusId) { @@ -107,7 +107,8 @@ public static function setDefaultValues(&$form) { else { $caseStatus = CRM_Core_OptionGroup::values('case_status', FALSE, FALSE, FALSE, 'AND is_default = 1'); if (count($caseStatus) == 1) { - $caseStatus = key($caseStatus); //$defaults['status_id'] = key($caseStatus); + //$defaults['status_id'] = key($caseStatus); + $caseStatus = key($caseStatus); } } $defaults['status_id'] = $caseStatus; @@ -152,30 +153,30 @@ public static function buildQuickForm(&$form) { return; } if ($form->_context == 'standalone') { - $form->addEntityRef('client_id', ts('Client'), array( - 'create' => TRUE, - 'multiple' => $form->_allowMultiClient, - ), TRUE); + $form->addEntityRef('client_id', ts('Client'), [ + 'create' => TRUE, + 'multiple' => $form->_allowMultiClient, + ], TRUE); } - $element = $form->addField('case_type_id', array( + $element = $form->addField('case_type_id', [ 'context' => 'create', 'entity' => 'Case', 'onchange' => "CRM.buildCustomData('Case', this.value);", - ), TRUE); + ], TRUE); if ($form->_caseTypeId) { $element->freeze(); } - $csElement = $form->addField('status_id', array( + $csElement = $form->addField('status_id', [ 'context' => 'create', 'entity' => 'Case', - ), TRUE); + ], TRUE); if ($form->_caseStatusId) { $csElement->freeze(); } - $form->add('text', 'duration', ts('Activity Duration'), array('size' => 4, 'maxlength' => 8)); + $form->add('number', 'duration', ts('Activity Duration'), ['class' => 'four', 'min' => 1]); $form->addRule('duration', ts('Please enter the duration as number of minutes (integers only).'), 'positiveInteger'); if ($form->_currentlyViewedContactId) { @@ -183,39 +184,38 @@ public static function buildQuickForm(&$form) { $form->assign('clientName', $displayName); } - $form->addDate('start_date', ts('Case Start Date'), TRUE, array('formatType' => 'activityDateTime')); + $form->add('datepicker', 'start_date', ts('Case Start Date'), [], TRUE); - $form->addField('medium_id', array('entity' => 'activity', 'context' => 'create'), TRUE); + $form->addField('medium_id', ['entity' => 'activity', 'context' => 'create'], TRUE); // calling this field activity_location to prevent conflict with contact location fields $form->add('text', 'activity_location', ts('Location'), CRM_Core_DAO::getAttribute('CRM_Activity_DAO_Activity', 'location')); - $form->add('wysiwyg', 'activity_details', ts('Details'), array('rows' => 4, 'cols' => 60), FALSE); - - $form->addButtons(array( - array( - 'type' => 'upload', - 'name' => ts('Save'), - 'isDefault' => TRUE, - ), - array( - 'type' => 'upload', - 'name' => ts('Save and New'), - 'subName' => 'new', - ), - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - ), - ) - ); + $form->add('wysiwyg', 'activity_details', ts('Details'), ['rows' => 4, 'cols' => 60], FALSE); + + $form->addButtons([ + [ + 'type' => 'upload', + 'name' => ts('Save'), + 'isDefault' => TRUE, + 'submitOnce' => TRUE, + ], + [ + 'type' => 'upload', + 'name' => ts('Save and New'), + 'subName' => 'new', + ], + [ + 'type' => 'cancel', + 'name' => ts('Cancel'), + ], + ]); } /** * Process the form submission. * - * - * @param CRM_Core_Form $form + * @param CRM_Case_Form_Case $form * @param array $params */ public static function beginPostProcess(&$form, &$params) { @@ -228,14 +228,6 @@ public static function beginPostProcess(&$form, &$params) { $form->_currentlyViewedContactId = $params['client_id'][0]; } - // for open case start date should be set to current date - $params['start_date'] = CRM_Utils_Date::processDate($params['start_date'], $params['start_date_time']); - $caseStatus = CRM_Case_PseudoConstant::caseStatus('name'); - // for resolved case the end date should set to now - if ($params['status_id'] == array_search('Closed', $caseStatus)) { - $params['end_date'] = $params['now']; - } - // rename activity_location param to the correct column name for activity DAO $params['location'] = CRM_Utils_Array::value('activity_location', $params); @@ -254,7 +246,7 @@ public static function beginPostProcess(&$form, &$params) { * * @param $fields * @param $files - * @param CRM_Core_Form $form + * @param CRM_Case_Form_Case $form * * @return array * list of errors to be posted back to the form @@ -264,15 +256,17 @@ public static function formRule($fields, $files, $form) { return TRUE; } - $errors = array(); + $errors = []; return $errors; } /** * Process the form submission. * - * @param CRM_Core_Form $form + * @param CRM_Case_Form_Case $form * @param array $params + * + * @throws \Exception */ public static function endPostProcess(&$form, &$params) { if ($form->_context == 'caseActivity') { @@ -299,25 +293,25 @@ public static function endPostProcess(&$form, &$params) { if (empty($cliId)) { CRM_Core_Error::fatal('client_id cannot be empty'); } - $contactParams = array( + $contactParams = [ 'case_id' => $params['case_id'], 'contact_id' => $cliId, - ); + ]; CRM_Case_BAO_CaseContact::create($contactParams); } } else { - $contactParams = array( + $contactParams = [ 'case_id' => $params['case_id'], 'contact_id' => $form->_currentlyViewedContactId, - ); + ]; CRM_Case_BAO_CaseContact::create($contactParams); } // 2. initiate xml processor $xmlProcessor = new CRM_Case_XMLProcessor_Process(); - $xmlProcessorParams = array( + $xmlProcessorParams = [ 'clientID' => $form->_currentlyViewedContactId, 'creatorID' => $form->_currentUserId, 'standardTimeline' => 1, @@ -329,7 +323,8 @@ public static function endPostProcess(&$form, &$params) { 'duration' => CRM_Utils_Array::value('duration', $params), 'medium_id' => $params['medium_id'], 'details' => $params['activity_details'], - ); + 'relationship_end_date' => CRM_Utils_Array::value('end_date', $params), + ]; if (array_key_exists('custom', $params) && is_array($params['custom'])) { $xmlProcessorParams['custom'] = $params['custom']; diff --git a/CRM/Case/Form/ActivityToCase.php b/CRM/Case/Form/ActivityToCase.php index fb08fbb5607c..d165d4c07984 100644 --- a/CRM/Case/Form/ActivityToCase.php +++ b/CRM/Case/Form/ActivityToCase.php @@ -1,9 +1,9 @@ _activityId = CRM_Utils_Request::retrieve('activityId', 'Positive', CRM_Core_DAO::$_nullObject); + $this->_activityId = CRM_Utils_Request::retrieve('activityId', 'Positive'); if (!$this->_activityId) { CRM_Core_Error::fatal('required activity id is missing.'); } - $this->_currentCaseId = CRM_Utils_Request::retrieve('caseId', 'Positive', CRM_Core_DAO::$_nullObject); + $this->_currentCaseId = CRM_Utils_Request::retrieve('caseId', 'Positive'); $this->assign('currentCaseId', $this->_currentCaseId); $this->assign('buildCaseActivityForm', TRUE); } @@ -58,8 +58,8 @@ public function preProcess() { * @return array */ public function setDefaultValues() { - $defaults = array(); - $params = array('id' => $this->_activityId); + $defaults = []; + $params = ['id' => $this->_activityId]; CRM_Activity_BAO_Activity::retrieve($params, $defaults); $defaults['file_on_case_activity_subject'] = $defaults['subject']; @@ -67,16 +67,22 @@ public function setDefaultValues() { // If this contact has an open case, supply it as a default $cid = CRM_Utils_Request::retrieve('cid', 'Integer'); + if (!$cid) { + $act = civicrm_api3('Activity', 'getsingle', ['id' => $this->_activityId, 'return' => 'target_contact_id']); + if (!empty($act['target_contact_id'])) { + $cid = $act['target_contact_id'][0]; + } + } if ($cid) { - $cases = civicrm_api3('CaseContact', 'get', array( + $cases = civicrm_api3('CaseContact', 'get', [ 'contact_id' => $cid, - 'case_id' => array('!=' => $this->_currentCaseId), - 'case_id.status_id' => array('!=' => "Closed"), + 'case_id' => ['!=' => $this->_currentCaseId], + 'case_id.status_id' => ['!=' => "Closed"], 'case_id.is_deleted' => 0, - 'case_id.end_date' => array('IS NULL' => 1), - 'options' => array('limit' => 1), + 'case_id.end_date' => ['IS NULL' => 1], + 'options' => ['limit' => 1], 'return' => 'case_id', - )); + ]); foreach ($cases['values'] as $record) { $defaults['file_on_case_unclosed_case_id'] = $record['case_id']; break; @@ -89,20 +95,20 @@ public function setDefaultValues() { * Build the form object. */ public function buildQuickForm() { - $this->addEntityRef('file_on_case_unclosed_case_id', ts('Select Case'), array( + $this->addEntityRef('file_on_case_unclosed_case_id', ts('Select Case'), [ 'entity' => 'Case', - 'api' => array( - 'extra' => array('contact_id'), - 'params' => array( - 'case_id' => array('!=' => $this->_currentCaseId), + 'api' => [ + 'extra' => ['contact_id'], + 'params' => [ + 'case_id' => ['!=' => $this->_currentCaseId], 'case_id.is_deleted' => 0, - 'case_id.status_id' => array('!=' => "Closed"), - 'case_id.end_date' => array('IS NULL' => 1), - ), - ), - ), TRUE); - $this->addEntityRef('file_on_case_target_contact_id', ts('With Contact(s)'), array('multiple' => TRUE)); - $this->add('text', 'file_on_case_activity_subject', ts('Subject'), array('size' => 50)); + 'case_id.status_id' => ['!=' => "Closed"], + 'case_id.end_date' => ['IS NULL' => 1], + ], + ], + ], TRUE); + $this->addEntityRef('file_on_case_target_contact_id', ts('With Contact(s)'), ['multiple' => TRUE]); + $this->add('text', 'file_on_case_activity_subject', ts('Subject'), ['size' => 50]); } } diff --git a/CRM/Case/Form/ActivityView.php b/CRM/Case/Form/ActivityView.php index 3aaa8dbb8c6a..b18be74fdc36 100644 --- a/CRM/Case/Form/ActivityView.php +++ b/CRM/Case/Form/ActivityView.php @@ -1,9 +1,9 @@ 'Attachment(s)', 'value' => $attachmentUrl, 'type' => 'Link', - ); + ]; } $tags = CRM_Core_BAO_EntityTag::getTag($activityID, 'civicrm_activity'); if (!empty($tags)) { - $allTag = CRM_Core_PseudoConstant::get('CRM_Core_DAO_EntityTag', 'tag_id', array('onlyActive' => FALSE)); + $allTag = CRM_Core_PseudoConstant::get('CRM_Core_DAO_EntityTag', 'tag_id', ['onlyActive' => FALSE]); foreach ($tags as $tid) { $tags[$tid] = $allTag[$tid]; } - $report['fields'][] = array( + $report['fields'][] = [ 'label' => 'Tags', 'value' => implode('
', $tags), 'type' => 'String', - ); + ]; } $this->assign('report', $report); $latestRevisionID = CRM_Activity_BAO_Activity::getLatestActivityId($activityID); - $viewPriorActivities = array(); + $viewPriorActivities = []; $priorActivities = CRM_Activity_BAO_Activity::getPriorAcitivities($activityID); foreach ($priorActivities as $activityId => $activityValues) { if (CRM_Case_BAO_Case::checkPermission($activityId, 'view', NULL, $contactID)) { @@ -123,7 +123,7 @@ public function preProcess() { //viewing activity should get diplayed in recent list.CRM-4670 $activityTypeID = CRM_Core_DAO::getFieldValue('CRM_Activity_DAO_Activity', $activityID, 'activity_type_id'); - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts); $activityTargetContacts = CRM_Activity_BAO_ActivityContact::retrieveContactIdsByActivityId($activityID, $targetID); if (!empty($activityTargetContacts)) { @@ -152,7 +152,7 @@ public function preProcess() { $title = $title . $recentContactDisplay . ' (' . $activityTypes[$activityTypeID] . ')'; - $recentOther = array(); + $recentOther = []; if (CRM_Case_BAO_Case::checkPermission($activityID, 'edit')) { $recentOther['editUrl'] = CRM_Utils_System::url('civicrm/case/activity', "reset=1&action=update&id={$activityID}&cid={$recentContactId}&caseid={$caseID}&context=home" @@ -172,6 +172,38 @@ public function preProcess() { $recentContactDisplay, $recentOther ); + + // Set breadcrumb to take the user back to the case being viewed + $caseTypeId = CRM_Core_DAO::getFieldValue('CRM_Case_DAO_Case', $caseID, 'case_type_id'); + $caseType = CRM_Core_PseudoConstant::getLabel('CRM_Case_BAO_Case', 'case_type_id', $caseTypeId); + $caseContact = CRM_Core_DAO::getFieldValue('CRM_Case_DAO_CaseContact', $caseID, 'contact_id', 'case_id'); + + CRM_Utils_System::resetBreadCrumb(); + $breadcrumb = [ + [ + 'title' => ts('Home'), + 'url' => CRM_Utils_System::url(), + ], + [ + 'title' => ts('CiviCRM'), + 'url' => CRM_Utils_System::url('civicrm', 'reset=1'), + ], + [ + 'title' => ts('CiviCase Dashboard'), + 'url' => CRM_Utils_System::url('civicrm/case', 'reset=1'), + ], + [ + 'title' => $caseType, + 'url' => CRM_Utils_System::url('civicrm/contact/view/case', [ + 'reset' => 1, + 'id' => $caseID, + 'context' => 'case', + 'action' => 'view', + 'cid' => $caseContact, + ]), + ], + ]; + CRM_Utils_System::appendBreadCrumb($breadcrumb); } } diff --git a/CRM/Case/Form/AddToCaseAsRole.php b/CRM/Case/Form/AddToCaseAsRole.php new file mode 100644 index 000000000000..20be94a95ad2 --- /dev/null +++ b/CRM/Case/Form/AddToCaseAsRole.php @@ -0,0 +1,93 @@ +getRoleTypes(); + $this->add( + 'select', + 'role_type', + ts('Relationship Type'), + ['' => ts('- select type -')] + $roleTypes, + TRUE, + ['class' => 'crm-select2 twenty'] + ); + + $this->addEntityRef( + 'assign_to', + ts('Assign to'), + ['entity' => 'Case'], + TRUE + ); + + $this->addButtons([ + [ + 'type' => 'submit', + 'name' => ts('Submit'), + 'isDefault' => TRUE, + ], + [ + 'type' => 'cancel', + 'name' => ts('Cancel'), + ], + ]); + } + + /** + * Returns list of configured role types for individuals. + * + * @return array + * List of role types + */ + private function getRoleTypes() { + $relType = CRM_Contact_BAO_Relationship::getRelationType('Individual'); + $roleTypes = []; + + foreach ($relType as $k => $v) { + $roleTypes[substr($k, 0, strpos($k, '_'))] = $v; + } + + return $roleTypes; + } + + /** + * @inheritdoc + */ + public function postProcess() { + $values = $this->controller->exportValues(); + + $caseId = (int) $values['assign_to']; + $roleTypeId = (int) $values['role_type']; + $contacts = $this->_contactIds; + + $clients = CRM_Case_BAO_Case::getCaseClients($caseId); + + $params = [ + 'contact_id_a' => $clients[0], + 'contact_id_b' => $contacts, + 'case_id' => $caseId, + 'relationship_type_id' => $roleTypeId, + ]; + + CRM_Contact_BAO_Relationship::createMultiple($params, 'a'); + + $url = CRM_Utils_System::url( + 'civicrm/contact/view/case', + [ + 'cid' => $clients[0], + 'id' => $caseId, + 'reset' => 1, + 'action' => 'view', + ] + ); + CRM_Utils_System::redirect($url); + } + +} diff --git a/CRM/Case/Form/Case.php b/CRM/Case/Form/Case.php index 76c61baaf54b..685ce79553d0 100644 --- a/CRM/Case/Form/Case.php +++ b/CRM/Case/Form/Case.php @@ -1,9 +1,9 @@ _caseId; + } + + /** + * Get the entity subtype ID being edited + * + * @param $subTypeId + * + * @return int|null + */ + public function getEntitySubTypeId($subTypeId) { + if ($subTypeId) { + return $subTypeId; + } + return $this->_caseTypeId; + } + /** * Build the form object. */ public function preProcess() { + if (empty($this->_action)) { + $this->_action = CRM_Core_Action::ADD; + } $this->_caseId = CRM_Utils_Request::retrieve('id', 'Positive', $this); @@ -110,24 +151,21 @@ public function preProcess() { } if (!$this->_caseId) { - $caseAttributes = array( + $caseAttributes = [ 'case_type_id' => ts('Case Type'), 'status_id' => ts('Case Status'), 'medium_id' => ts('Activity Medium'), - ); + ]; foreach ($caseAttributes as $key => $label) { if (!CRM_Case_BAO_Case::buildOptions($key, 'create')) { - CRM_Core_Error::fatal(ts('You do not have any active %1', array(1 => $label))); + CRM_Core_Error::fatal(ts('You do not have any active %1', [1 => $label])); } } } if ($this->_action & CRM_Core_Action::ADD) { - $this->_activityTypeId = CRM_Core_OptionGroup::getValue('activity_type', - 'Open Case', - 'name' - ); + $this->_activityTypeId = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Open Case'); if (!$this->_activityTypeId) { CRM_Core_Error::fatal(ts('The Open Case activity type is missing or disabled. Please have your site administrator check Administer > Option Lists > Activity Types for the CiviCase component.')); } @@ -162,7 +200,7 @@ public function preProcess() { $contact = new CRM_Contact_DAO_Contact(); $contact->id = $this->_currentlyViewedContactId; if (!$contact->find(TRUE)) { - CRM_Core_Error::statusBounce(ts('Client contact does not exist: %1', array(1 => $this->_currentlyViewedContactId))); + CRM_Core_Error::statusBounce(ts('Client contact does not exist: %1', [1 => $this->_currentlyViewedContactId])); } $this->assign('clientName', $contact->display_name); } @@ -170,18 +208,15 @@ public function preProcess() { $session = CRM_Core_Session::singleton(); $this->_currentUserId = $session->get('userID'); - //when custom data is included in this page + //Add activity custom data is included in this page CRM_Custom_Form_CustomData::preProcess($this, NULL, $this->_activityTypeId, 1, 'Activity'); $className = "CRM_Case_Form_Activity_{$this->_activityTypeFile}"; $className::preProcess($this); $activityGroupTree = $this->_groupTree; - // for case custom fields to populate with defaults - if (!empty($_POST['hidden_custom'])) { - $params = CRM_Utils_Request::exportValues(); - CRM_Custom_Form_CustomData::preProcess($this, NULL, CRM_Utils_Array::value('case_type_id', $params, $this->_caseTypeId), 1, 'Case', $this->_caseId); - CRM_Custom_Form_CustomData::buildQuickForm($this); - } + // Add case custom data to form + $caseTypeId = CRM_Utils_Array::value('case_type_id', CRM_Utils_Request::exportValues(), $this->_caseTypeId); + CRM_Custom_Form_CustomData::addToForm($this, $caseTypeId); // so that grouptree is not populated with case fields, since the grouptree is used // for populating activity custom fields. @@ -207,49 +242,46 @@ public function buildQuickForm() { $this->assign('multiClient', $isMultiClient); if ($this->_action & CRM_Core_Action::DELETE || $this->_action & CRM_Core_Action::RENEW) { - $title = 'Delete'; + $title = ts('Delete'); if ($this->_action & CRM_Core_Action::RENEW) { - $title = 'Restore'; + $title = ts('Restore'); } - $this->addButtons(array( - array( - 'type' => 'next', - 'name' => $title, - 'spacing' => '         ', - 'isDefault' => TRUE, - ), - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'next', + 'name' => $title, + 'spacing' => '         ', + 'isDefault' => TRUE, + ], + [ + 'type' => 'cancel', + 'name' => ts('Cancel'), + ], + ]); return; } - //need to assign custom data type and subtype to the template - $this->assign('customDataType', 'Case'); - + // Add the activity custom data to the form CRM_Custom_Form_CustomData::buildQuickForm($this); + // we don't want to show button on top of custom form $this->assign('noPreCustomButton', TRUE); $s = CRM_Core_DAO::getAttribute('CRM_Activity_DAO_Activity', 'subject'); if (!is_array($s)) { - $s = array(); + $s = []; } $this->add('text', 'activity_subject', ts('Subject'), - array_merge($s, array( + array_merge($s, [ 'maxlength' => '128', - )), TRUE + ]), TRUE ); - CRM_Core_BAO_Tag::getTags('civicrm_case', $tags, NULL, - '  ', TRUE); + $tags = CRM_Core_BAO_Tag::getColorTags('civicrm_case'); if (!empty($tags)) { - $this->add('select', 'tag', ts('Select Tags'), $tags, FALSE, - array('id' => 'tags', 'multiple' => 'multiple', 'class' => 'crm-select2') + $this->add('select2', 'tag', ts('Tags'), $tags, FALSE, + ['class' => 'huge', 'multiple' => 'multiple'] ); } @@ -257,19 +289,18 @@ public function buildQuickForm() { $parentNames = CRM_Core_BAO_Tag::getTagSet('civicrm_case'); CRM_Core_Form_Tag::buildQuickForm($this, $parentNames, 'civicrm_case', NULL, FALSE, TRUE); - $this->addButtons(array( - array( - 'type' => 'next', - 'name' => ts('Save'), - 'spacing' => '         ', - 'isDefault' => TRUE, - ), - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'next', + 'name' => ts('Save'), + 'spacing' => '         ', + 'isDefault' => TRUE, + ], + [ + 'type' => 'cancel', + 'name' => ts('Cancel'), + ], + ]); $className = "CRM_Case_Form_Activity_{$this->_activityTypeFile}"; $className::buildQuickForm($this); @@ -285,8 +316,8 @@ public function addRules() { return TRUE; } $className = "CRM_Case_Form_Activity_{$this->_activityTypeFile}"; - $this->addFormRule(array($className, 'formRule'), $this); - $this->addFormRule(array('CRM_Case_Form_Case', 'formRule'), $this); + $this->addFormRule([$className, 'formRule'], $this); + $this->addFormRule(['CRM_Case_Form_Case', 'formRule'], $this); } /** @@ -306,38 +337,30 @@ public static function formRule($values, $files, $form) { } /** - * Process the form submission. + * Wrapper for unit testing the post process submit function. + * + * @param $params + * @param $activityTypeFile + * @param $contactId + * @param $context + * @return CRM_Case_BAO_Case */ - public function postProcess() { - $transaction = new CRM_Core_Transaction(); + public function testSubmit($params, $activityTypeFile, $contactId, $context = "case") { + $this->controller = new CRM_Core_Controller(); - // check if dedupe button, if so return. - $buttonName = $this->controller->getButtonName(); - if (isset($this->_dedupeButtonName) && $buttonName == $this->_dedupeButtonName) { - return; - } + $this->_activityTypeFile = $activityTypeFile; + $this->_currentUserId = $contactId; + $this->_context = $context; - if ($this->_action & CRM_Core_Action::DELETE) { - $statusMsg = NULL; - $caseDelete = CRM_Case_BAO_Case::deleteCase($this->_caseId, TRUE); - if ($caseDelete) { - $statusMsg = ts('The selected case has been moved to the Trash. You can view and / or restore deleted cases by checking the "Deleted Cases" option under Find Cases.
'); - } - CRM_Core_Session::setStatus($statusMsg, ts('Case Deleted'), 'success'); - return; - } + return $this->submit($params); + } - if ($this->_action & CRM_Core_Action::RENEW) { - $statusMsg = NULL; - $caseRestore = CRM_Case_BAO_Case::restoreCase($this->_caseId); - if ($caseRestore) { - $statusMsg = ts('The selected case has been restored.
'); - } - CRM_Core_Session::setStatus($statusMsg, ts('Restored'), 'success'); - return; - } - // store the submitted values in an array - $params = $this->controller->exportValues($this->_name); + /** + * Submit the form with given params. + * + * @param $params + */ + public function submit(&$params) { $params['now'] = date("Ymd"); // 1. call begin post process @@ -349,7 +372,6 @@ public function postProcess() { if (!empty($params['hidden_custom']) && !isset($params['custom']) ) { - $customFields = array(); $params['custom'] = CRM_Core_BAO_CustomField::postProcess( $params, NULL, @@ -363,14 +385,17 @@ public function postProcess() { $params['subject'] = $params['activity_subject']; } $caseObj = CRM_Case_BAO_Case::create($params); - $params['case_id'] = $caseObj->id; + $this->_caseId = $params['case_id'] = $caseObj->id; // unset any ids, custom data unset($params['id'], $params['custom']); // add tags if exists - $tagParams = array(); + $tagParams = []; if (!empty($params['tag'])) { - $tagParams = array(); + $tagParams = []; + if (!is_array($params['tag'])) { + $params['tag'] = explode(',', $params['tag']); + } foreach ($params['tag'] as $tag) { $tagParams[$tag] = 1; } @@ -386,8 +411,7 @@ public function postProcess() { $url = CRM_Utils_System::url('civicrm/contact/view/case', "reset=1&action=view&cid={$this->_currentlyViewedContactId}&id={$caseObj->id}" ); - $session = CRM_Core_Session::singleton(); - $session->pushUserContext($url); + CRM_Core_Session::singleton()->pushUserContext($url); // 3. format activity custom data if (!empty($params['hidden_custom'])) { @@ -408,10 +432,42 @@ public function postProcess() { $className::endPostProcess($this, $params); } - // 5. auto populate activities + return $caseObj; + } + + /** + * Process the form submission. + */ + public function postProcess() { + $transaction = new CRM_Core_Transaction(); + + // check if dedupe button, if so return. + $buttonName = $this->controller->getButtonName(); + if (isset($this->_dedupeButtonName) && $buttonName == $this->_dedupeButtonName) { + return; + } + + if ($this->_action & CRM_Core_Action::DELETE) { + $caseDelete = CRM_Case_BAO_Case::deleteCase($this->_caseId, TRUE); + if ($caseDelete) { + CRM_Core_Session::setStatus(ts('You can view and / or restore deleted cases by checking the "Deleted Cases" option under Find Cases.'), ts('Case Deleted'), 'success'); + } + return; + } + + if ($this->_action & CRM_Core_Action::RENEW) { + $caseRestore = CRM_Case_BAO_Case::restoreCase($this->_caseId); + if ($caseRestore) { + CRM_Core_Session::setStatus(ts('The selected case has been restored.'), ts('Restored'), 'success'); + } + return; + } + // store the submitted values in an array + $params = $this->controller->exportValues($this->_name); + $this->submit($params); - // 6. set status CRM_Core_Session::setStatus($params['statusMsg'], ts('Saved'), 'success'); + } } diff --git a/CRM/Case/Form/CaseView.php b/CRM/Case/Form/CaseView.php index 886e3ef27f05..fdf587ca58df 100644 --- a/CRM/Case/Form/CaseView.php +++ b/CRM/Case/Form/CaseView.php @@ -1,9 +1,9 @@ _showRelatedCases) { $relatedCases = $this->get('relatedCases'); if (!isset($relatedCases)) { - $cId = CRM_Utils_Request::retrieve('cid', 'Integer', CRM_Core_DAO::$_nullObject); - $caseId = CRM_Utils_Request::retrieve('id', 'Integer', CRM_Core_DAO::$_nullObject); - $relatedCases = CRM_Case_BAO_Case::getRelatedCases($caseId, $cId); + $cId = CRM_Utils_Request::retrieve('cid', 'Integer'); + $caseId = CRM_Utils_Request::retrieve('id', 'Integer'); + $relatedCases = CRM_Case_BAO_Case::getRelatedCases($caseId); } $this->assign('relatedCases', $relatedCases); $this->assign('showRelatedCases', TRUE); @@ -74,10 +74,10 @@ public function preProcess() { // Access check. if (!CRM_Case_BAO_Case::accessCase($this->_caseID, FALSE)) { - CRM_Core_Error::fatal(ts('You are not authorized to access this page.')); + CRM_Core_Error::statusBounce(ts('You do not have permission to access this case.')); } - $fulltext = CRM_Utils_Request::retrieve('context', 'String', CRM_Core_DAO::$_nullObject); + $fulltext = CRM_Utils_Request::retrieve('context', 'Alphanumeric'); if ($fulltext == 'fulltext') { $this->assign('fulltext', $fulltext); } @@ -86,21 +86,21 @@ public function preProcess() { $this->assign('userID', CRM_Core_Session::getLoggedInContactID()); //retrieve details about case - $params = array('id' => $this->_caseID); + $params = ['id' => $this->_caseID]; - $returnProperties = array('case_type_id', 'subject', 'status_id', 'start_date'); + $returnProperties = ['case_type_id', 'subject', 'status_id', 'start_date']; CRM_Core_DAO::commonRetrieve('CRM_Case_BAO_Case', $params, $values, $returnProperties); $statuses = CRM_Case_PseudoConstant::caseStatus('label', FALSE); $caseTypeName = CRM_Case_BAO_Case::getCaseType($this->_caseID, 'name'); $caseType = CRM_Case_BAO_Case::getCaseType($this->_caseID); - $this->_caseDetails = array( + $this->_caseDetails = [ 'case_type' => $caseType, 'case_status' => CRM_Utils_Array::value($values['case_status_id'], $statuses), 'case_subject' => CRM_Utils_Array::value('subject', $values), 'case_start_date' => $values['case_start_date'], - ); + ]; $this->_caseType = $caseTypeName; $this->assign('caseDetails', $this->_caseDetails); @@ -121,7 +121,7 @@ public function preProcess() { CRM_Utils_System::setTitle($displayName . ' - ' . $caseType); - $recentOther = array(); + $recentOther = []; if (CRM_Core_Permission::checkActionPermission('CiviCase', CRM_Core_Action::DELETE)) { $recentOther['deleteUrl'] = CRM_Utils_System::url('civicrm/contact/view/case', "action=delete&reset=1&id={$this->_caseID}&cid={$this->_contactID}&context=home" @@ -141,28 +141,28 @@ public function preProcess() { //get the related cases for given case. $relatedCases = $this->get('relatedCases'); if (!isset($relatedCases)) { - $relatedCases = CRM_Case_BAO_Case::getRelatedCases($this->_caseID, $this->_contactID); + $relatedCases = CRM_Case_BAO_Case::getRelatedCases($this->_caseID); $relatedCases = empty($relatedCases) ? FALSE : $relatedCases; $this->set('relatedCases', $relatedCases); } $this->assign('hasRelatedCases', (bool) $relatedCases); if ($relatedCases) { - $this->assign('relatedCaseLabel', ts('%1 Related Case', array( - 'count' => count($relatedCases), - 'plural' => '%1 Related Cases', - ))); - $this->assign('relatedCaseUrl', CRM_Utils_System::url('civicrm/contact/view/case', array( + $this->assign('relatedCaseLabel', ts('%1 Related Case', [ + 'count' => count($relatedCases), + 'plural' => '%1 Related Cases', + ])); + $this->assign('relatedCaseUrl', CRM_Utils_System::url('civicrm/contact/view/case', [ 'id' => $this->_caseID, 'cid' => $this->_contactID, 'relatedCases' => 1, 'action' => 'view', - ))); + ])); } $entitySubType = !empty($values['case_type_id']) ? $values['case_type_id'] : NULL; $this->assign('caseTypeID', $entitySubType); $groupTree = CRM_Core_BAO_CustomGroup::getTree('Case', - $this, + NULL, $this->_caseID, NULL, $entitySubType @@ -176,7 +176,7 @@ public function preProcess() { * @return array; */ public function setDefaultValues() { - $defaults = array(); + $defaults = []; return $defaults; } @@ -208,8 +208,7 @@ public function buildQuickForm() { $aTypes = $xmlProcessor->get($this->_caseType, 'ActivityTypes', TRUE); - $allActTypes = CRM_Core_PseudoConstant::activityType(TRUE, TRUE, FALSE, 'name'); - + $allActTypes = CRM_Activity_BAO_Activity::buildOptions('activity_type_id', 'validate'); $emailActivityType = array_search('Email', $allActTypes); $pdfActivityType = array_search('Print PDF Letter', $allActTypes); @@ -226,11 +225,11 @@ public function buildQuickForm() { // Only show "link cases" activity if other cases exist. $linkActTypeId = array_search('Link Cases', $allActTypes); if ($linkActTypeId) { - $count = civicrm_api3('Case', 'getcount', array( + $count = civicrm_api3('Case', 'getcount', [ 'check_permissions' => TRUE, - 'id' => array('!=' => $this->_caseID), + 'id' => ['!=' => $this->_caseID], 'is_deleted' => 0, - )); + ]); if (!$count) { unset($aTypes[$linkActTypeId]); } @@ -240,7 +239,7 @@ public function buildQuickForm() { asort($aTypes); } - $activityLinks = array('' => ts('Add Activity')); + $activityLinks = ['' => ts('Add Activity')]; foreach ($aTypes as $type => $label) { if ($type == $emailActivityType) { $url = CRM_Utils_System::url('civicrm/activity/email/add', @@ -262,20 +261,20 @@ public function buildQuickForm() { $activityLinks[$url] = $label; } - $this->add('select', 'add_activity_type_id', '', $activityLinks, FALSE, array('class' => 'crm-select2 crm-action-menu fa-calendar-check-o twenty')); + $this->add('select', 'add_activity_type_id', '', $activityLinks, FALSE, ['class' => 'crm-select2 crm-action-menu fa-calendar-check-o twenty']); if ($this->_hasAccessToAllCases) { $this->add('select', 'report_id', '', - array('' => ts('Activity Audit')) + $reports, + ['' => ts('Activity Audit')] + $reports, FALSE, - array('class' => 'crm-select2 crm-action-menu fa-list-alt') + ['class' => 'crm-select2 crm-action-menu fa-list-alt'] ); $this->add('select', 'timeline_id', '', - array('' => ts('Add Timeline')) + $reports, + ['' => ts('Add Timeline')] + $reports, FALSE, - array('class' => 'crm-select2 crm-action-menu fa-list-ol') + ['class' => 'crm-select2 crm-action-menu fa-list-ol'] ); } - $this->addElement('submit', $this->getButtonName('next'), ' ', array('class' => 'hiddenElement')); + $this->addElement('submit', $this->getButtonName('next'), ' ', ['class' => 'hiddenElement']); $this->buildMergeCaseForm(); @@ -316,7 +315,7 @@ public function buildQuickForm() { // Now build 'Other Relationships' array by removing relationships that are already listed under Case Roles // so they don't show up twice. - $clientRelationships = array(); + $clientRelationships = []; foreach ($relClient as $r) { if (!array_key_exists($r['id'], $caseRelationships)) { $clientRelationships[] = $r; @@ -325,7 +324,7 @@ public function buildQuickForm() { $this->assign('clientRelationships', $clientRelationships); // Now global contact list that appears on all cases. - $globalGroupInfo = array(); + $globalGroupInfo = []; CRM_Case_BAO_Case::getGlobalContacts($globalGroupInfo); $this->assign('globalGroupInfo', $globalGroupInfo); @@ -333,9 +332,9 @@ public function buildQuickForm() { $this->add('select', 'role_type', ts('Relationship Type'), - array('' => ts('- select type -')) + $allowedRelationshipTypes, + ['' => ts('- select type -')] + $allowedRelationshipTypes, FALSE, - array('class' => 'crm-select2 twenty', 'data-select-params' => '{"allowClear": false}') + ['class' => 'crm-select2 twenty', 'data-select-params' => '{"allowClear": false}'] ); $hookCaseSummary = CRM_Utils_Hook::caseSummary($this->_caseID); @@ -343,28 +342,28 @@ public function buildQuickForm() { $this->assign('hookCaseSummary', $hookCaseSummary); } - CRM_Core_BAO_Tag::getTags('civicrm_case', $allTags, NULL, - '  ', TRUE); + $allTags = CRM_Core_BAO_Tag::getColorTags('civicrm_case'); if (!empty($allTags)) { - $this->add('select', 'case_tag', ts('Tags'), $allTags, FALSE, - array('id' => 'tags', 'multiple' => 'multiple', 'class' => 'crm-select2') + $this->add('select2', 'case_tag', ts('Tags'), $allTags, FALSE, + ['id' => 'tags', 'multiple' => 'multiple'] ); $tags = CRM_Core_BAO_EntityTag::getTag($this->_caseID, 'civicrm_case'); - $this->setDefaults(array('case_tag' => $tags)); - foreach ($tags as $tid) { - if (isset($allTags[$tid])) { - $tags[$tid] = $allTags[$tid]; + $tagInfo = CRM_Utils_Array::findInTree($tid, $allTags); + if ($tagInfo) { + $tags[$tid] = $tagInfo; } else { unset($tags[$tid]); } } - $this->assign('tags', implode(', ', array_filter($tags))); + $this->setDefaults(['case_tag' => implode(',', array_keys($tags))]); + + $this->assign('tags', $tags); $this->assign('showTags', TRUE); } else { @@ -375,23 +374,37 @@ public function buildQuickForm() { // see if we have any tagsets which can be assigned to cases $parentNames = CRM_Core_BAO_Tag::getTagSet('civicrm_case'); + $tagSetTags = []; if ($parentNames) { - $this->assign('showTagsets', TRUE); - } - else { - $this->assign('showTagsets', FALSE); + $this->assign('showTags', TRUE); + $tagSetItems = civicrm_api3('entityTag', 'get', [ + 'entity_id' => $this->_caseID, + 'entity_table' => 'civicrm_case', + 'tag_id.parent_id.is_tagset' => 1, + 'options' => ['limit' => 0], + 'return' => ["tag_id.parent_id", "tag_id.parent_id.name", "tag_id.name"], + ]); + foreach ($tagSetItems['values'] as $tag) { + $tagSetTags += [ + $tag['tag_id.parent_id'] => [ + 'name' => $tag['tag_id.parent_id.name'], + 'items' => [], + ], + ]; + $tagSetTags[$tag['tag_id.parent_id']]['items'][] = $tag['tag_id.name']; + } } + $this->assign('tagSetTags', $tagSetTags); CRM_Core_Form_Tag::buildQuickForm($this, $parentNames, 'civicrm_case', $this->_caseID, FALSE, TRUE); - $this->addButtons(array( - array( - 'type' => 'cancel', - 'name' => ts('Done'), - 'spacing' => '         ', - 'isDefault' => TRUE, - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'cancel', + 'name' => ts('Done'), + 'spacing' => '         ', + 'isDefault' => TRUE, + ], + ]); } /** @@ -409,23 +422,15 @@ public function postProcess() { $session->pushUserContext($url); if (!empty($params['timeline_id']) && !empty($_POST['_qf_CaseView_next'])) { - $session = CRM_Core_Session::singleton(); - $this->_uid = $session->get('userID'); + civicrm_api3('Case', 'addtimeline', [ + 'case_id' => $this->_caseID, + 'timeline' => $params['timeline_id'], + ]); + $xmlProcessor = new CRM_Case_XMLProcessor_Process(); - $xmlProcessorParams = array( - 'clientID' => $this->_contactID, - 'creatorID' => $this->_uid, - 'standardTimeline' => 0, - 'activity_date_time' => date('YmdHis'), - 'caseID' => $this->_caseID, - 'caseType' => $this->_caseType, - 'activitySetName' => $params['timeline_id'], - ); - $xmlProcessor->run($this->_caseType, $xmlProcessorParams); $reports = $xmlProcessor->get($this->_caseType, 'ActivitySets'); - CRM_Core_Session::setStatus(ts('Activities from the %1 activity set have been added to this case.', - array(1 => $reports[$params['timeline_id']]) + [1 => $reports[$params['timeline_id']]] ), ts('Done'), 'success'); } elseif ($this->_mergeCases && @@ -453,36 +458,36 @@ public function postProcess() { * @param array $aTypes * To include acivities related to current case id $form->_caseID. */ - public static function activityForm($form, $aTypes = array()) { + public static function activityForm($form, $aTypes = []) { $caseRelationships = CRM_Case_BAO_Case::getCaseRoles($form->_contactID, $form->_caseID); //build reporter select - $reporters = array("" => ts(' - any reporter - ')); + $reporters = ["" => ts(' - any reporter - ')]; foreach ($caseRelationships as $key => & $value) { $reporters[$value['cid']] = $value['name'] . " ( {$value['relation']} )"; } - $form->add('select', 'reporter_id', ts('Reporter/Role'), $reporters, FALSE, array('id' => 'reporter_id_' . $form->_caseID)); + $form->add('select', 'reporter_id', ts('Reporter/Role'), $reporters, FALSE, ['id' => 'reporter_id_' . $form->_caseID]); // take all case activity types for search filter, CRM-7187 - $aTypesFilter = array(); + $aTypesFilter = []; $allCaseActTypes = CRM_Case_PseudoConstant::caseActivityType(); foreach ($allCaseActTypes as $typeDetails) { - if (!in_array($typeDetails['name'], array('Open Case'))) { + if (!in_array($typeDetails['name'], ['Open Case'])) { $aTypesFilter[$typeDetails['id']] = CRM_Utils_Array::value('label', $typeDetails); } } $aTypesFilter = $aTypesFilter + $aTypes; asort($aTypesFilter); - $form->add('select', 'activity_type_filter_id', ts('Activity Type'), array('' => ts('- select activity type -')) + $aTypesFilter, FALSE, array('id' => 'activity_type_filter_id_' . $form->_caseID)); + $form->add('select', 'activity_type_filter_id', ts('Activity Type'), ['' => ts('- select activity type -')] + $aTypesFilter, FALSE, ['id' => 'activity_type_filter_id_' . $form->_caseID]); $activityStatus = CRM_Core_PseudoConstant::activityStatus(); - $form->add('select', 'status_id', ts('Status'), array("" => ts(' - any status - ')) + $activityStatus, FALSE, array('id' => 'status_id_' . $form->_caseID)); + $form->add('select', 'status_id', ts('Status'), ["" => ts(' - any status - ')] + $activityStatus, FALSE, ['id' => 'status_id_' . $form->_caseID]); - // activity dates - $form->addDate('activity_date_low_' . $form->_caseID, ts('Activity Dates - From'), FALSE, array('formatType' => 'searchDate')); - $form->addDate('activity_date_high_' . $form->_caseID, ts('To'), FALSE, array('formatType' => 'searchDate')); + // activity date search filters + $form->add('datepicker', 'activity_date_low_' . $form->_caseID, ts('Activity Dates - From'), [], FALSE, ['time' => FALSE]); + $form->add('datepicker', 'activity_date_high_' . $form->_caseID, ts('To'), [], FALSE, ['time' => FALSE]); if (CRM_Core_Permission::check('administer CiviCRM')) { - $form->add('checkbox', 'activity_deleted', ts('Deleted Activities'), '', FALSE, array('id' => 'activity_deleted_' . $form->_caseID)); + $form->add('checkbox', 'activity_deleted', ts('Deleted Activities'), '', FALSE, ['id' => 'activity_deleted_' . $form->_caseID]); } } @@ -490,16 +495,16 @@ public static function activityForm($form, $aTypes = array()) { * Form elements for merging cases */ public function buildMergeCaseForm() { - $otherCases = array(); - $result = civicrm_api3('Case', 'get', array( + $otherCases = []; + $result = civicrm_api3('Case', 'get', [ 'check_permissions' => TRUE, 'contact_id' => $this->_contactID, 'is_deleted' => 0, - 'id' => array('!=' => $this->_caseID), - 'return' => array('id', 'start_date', 'case_type_id.title'), - )); + 'id' => ['!=' => $this->_caseID], + 'return' => ['id', 'start_date', 'case_type_id.title'], + ]); foreach ($result['values'] as $id => $case) { - $otherCases[$id] = "#$id: {$case['case_type_id.title']} " . ts('(opened %1)', array(1 => $case['start_date'])); + $otherCases[$id] = "#$id: {$case['case_type_id.title']} " . ts('(opened %1)', [1 => $case['start_date']]); } $this->assign('mergeCases', $this->_mergeCases = (bool) $otherCases); @@ -507,18 +512,18 @@ public function buildMergeCaseForm() { if ($otherCases) { $this->add('select', 'merge_case_id', ts('Select Case for Merge'), - array( + [ '' => ts('- select case -'), - ) + $otherCases, + ] + $otherCases, FALSE, - array('class' => 'crm-select2 huge') + ['class' => 'crm-select2 huge'] ); $this->addElement('submit', $this->getButtonName('next', 'merge_case'), ts('Merge'), - array( + [ 'class' => 'hiddenElement', - ) + ] ); } } diff --git a/CRM/Case/Form/CustomData.php b/CRM/Case/Form/CustomData.php index 81419825fc68..46f375414973 100644 --- a/CRM/Case/Form/CustomData.php +++ b/CRM/Case/Form/CustomData.php @@ -1,9 +1,9 @@ _contactID = CRM_Utils_Request::retrieve('cid', 'Positive', $this, TRUE); $groupTree = CRM_Core_BAO_CustomGroup::getTree('Case', - $this, + NULL, $this->_entityID, $this->_groupID, $this->_subTypeID @@ -76,10 +76,10 @@ public function preProcess() { // Array contains only one item foreach ($groupTree as $groupValues) { $this->_customTitle = $groupValues['title']; - CRM_Utils_System::setTitle(ts('Edit %1', array(1 => $groupValues['title']))); + CRM_Utils_System::setTitle(ts('Edit %1', [1 => $groupValues['title']])); } - $this->_defaults = array(); + $this->_defaults = []; CRM_Core_BAO_CustomGroup::setDefaults($groupTree, $this->_defaults); $this->setDefaults($this->_defaults); @@ -98,18 +98,17 @@ public function preProcess() { public function buildQuickForm() { // make this form an upload since we dont know if the custom data injected dynamically // is of type file etc - $this->addButtons(array( - array( - 'type' => 'upload', - 'name' => ts('Save'), - 'isDefault' => TRUE, - ), - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'upload', + 'name' => ts('Save'), + 'isDefault' => TRUE, + ], + [ + 'type' => 'cancel', + 'name' => ts('Cancel'), + ], + ]); } /** @@ -129,27 +128,23 @@ public function postProcess() { $session = CRM_Core_Session::singleton(); $session->pushUserContext(CRM_Utils_System::url('civicrm/contact/view/case', "reset=1&id={$this->_entityID}&cid={$this->_contactID}&action=view")); - $session = CRM_Core_Session::singleton(); - $activityTypeID = CRM_Core_OptionGroup::getValue('activity_type', 'Change Custom Data', 'name'); - $activityParams = array( + $activityTypeID = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Change Custom Data'); + $activityParams = [ 'activity_type_id' => $activityTypeID, 'source_contact_id' => $session->get('userID'), 'is_auto' => TRUE, 'subject' => $this->_customTitle . " : change data", - 'status_id' => CRM_Core_OptionGroup::getValue('activity_status', - 'Completed', - 'name' - ), + 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', 'Completed'), 'target_contact_id' => $this->_contactID, 'details' => json_encode($this->_defaults), 'activity_date_time' => date('YmdHis'), - ); + ]; $activity = CRM_Activity_BAO_Activity::create($activityParams); - $caseParams = array( + $caseParams = [ 'activity_id' => $activity->id, 'case_id' => $this->_entityID, - ); + ]; CRM_Case_BAO_Case::processCaseActivity($caseParams); $transaction->commit(); diff --git a/CRM/Case/Form/EditClient.php b/CRM/Case/Form/EditClient.php index 8fd5b67e56e4..6c38dd0928dc 100644 --- a/CRM/Case/Form/EditClient.php +++ b/CRM/Case/Form/EditClient.php @@ -1,9 +1,9 @@ assign('currentClientName', CRM_Contact_BAO_Contact::displayName($cid)); @@ -61,10 +61,10 @@ public function preProcess() { elseif ($context == 'dashboard') { $url = CRM_Utils_System::url('civicrm/case', 'reset=1'); } - elseif (in_array($context, array( + elseif (in_array($context, [ 'dashlet', 'dashletFullscreen', - ))) { + ])) { $url = CRM_Utils_System::url('civicrm/dashboard', 'reset=1'); } $session = CRM_Core_Session::singleton(); @@ -75,25 +75,24 @@ public function preProcess() { * Build the form object. */ public function buildQuickForm() { - $this->addEntityRef('reassign_contact_id', ts('Select Contact'), array('create' => TRUE), TRUE); - $this->addButtons(array( - array( + $this->addEntityRef('reassign_contact_id', ts('Select Contact'), ['create' => TRUE], TRUE); + $this->addButtons([ + [ 'type' => 'done', 'name' => ts('Reassign Case'), - ), - array( + ], + [ 'type' => 'cancel', 'name' => ts('Cancel'), - ), - )); + ], + ]); // This form may change the url structure so should not submit via ajax $this->preventAjaxSubmit(); } - public function addRules() { - $this->addFormRule(array(get_class($this), 'formRule'), $this); + $this->addFormRule([get_class($this), 'formRule'], $this); } /** @@ -104,7 +103,7 @@ public function addRules() { * @return array */ public static function formRule($vals, $rule, $form) { - $errors = array(); + $errors = []; if (empty($vals['reassign_contact_id']) || $vals['reassign_contact_id'] == $form->get('cid')) { $errors['reassign_contact_id'] = ts("Please select a different contact."); } diff --git a/CRM/Case/Form/Report.php b/CRM/Case/Form/Report.php index f2b65efc8f73..0c926c66bf10 100644 --- a/CRM/Case/Form/Report.php +++ b/CRM/Case/Form/Report.php @@ -1,9 +1,9 @@ ts('All Activities'), 2 => ts('Exclude Completed Activities'), - ); + ]; $includeActivitesGroup = $this->addRadio('include_activities', NULL, $includeActivites, @@ -97,18 +100,17 @@ public function buildQuickForm() { ts('Redact (hide) Client and Service Provider Data') ); - $this->addButtons(array( - array( - 'type' => 'refresh', - 'name' => ts('Generate Report'), - 'isDefault' => TRUE, - ), - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'refresh', + 'name' => ts('Generate Report'), + 'isDefault' => TRUE, + ], + [ + 'type' => 'cancel', + 'name' => ts('Cancel'), + ], + ]); // We want this form to redirect to a full page $this->preventAjaxSubmit(); } diff --git a/CRM/Case/Form/Search.php b/CRM/Case/Form/Search.php index cf2199f31978..7aabdfe48958 100644 --- a/CRM/Case/Form/Search.php +++ b/CRM/Case/Form/Search.php @@ -1,9 +1,9 @@ _actionButtonName = $this->getButtonName('next', 'action'); $this->_done = FALSE; - $this->defaults = array(); - /* - * we allow the controller to set force/reset externally, useful when we are being - * driven by the wizard framework - */ - - $this->_reset = CRM_Utils_Request::retrieve('reset', 'Boolean', CRM_Core_DAO::$_nullObject); - $this->_force = CRM_Utils_Request::retrieve('force', 'Boolean', $this, FALSE); - $this->_limit = CRM_Utils_Request::retrieve('limit', 'Positive', $this); - $this->_context = CRM_Utils_Request::retrieve('context', 'String', $this, FALSE, 'search'); - - $this->assign('context', $this->_context); - - // get user submitted values - // get it from controller only if form has been submitted, else preProcess has set this - if (!empty($_POST) && !$this->controller->isModal()) { - $this->_formValues = $this->controller->exportValues($this->_name); - } - else { - $this->_formValues = $this->get('formValues'); - } - - if (empty($this->_formValues)) { - if (isset($this->_ssID)) { - $this->_formValues = CRM_Contact_BAO_SavedSearch::getFormValues($this->_ssID); - } - } + $this->loadStandardSearchOptionsFromUrl(); + $this->loadFormValues(); if ($this->_force) { $this->postProcess(); @@ -174,15 +150,13 @@ public function buildQuickForm() { $this->addRowSelectors($rows); } - $permission = CRM_Core_Permission::getPermission(); - - $tasks = CRM_Case_Task::permissionedTaskTitles($permission); + $tasks = CRM_Case_Task::permissionedTaskTitles(CRM_Core_Permission::getPermission()); if (!empty($this->_formValues['case_deleted'])) { - unset($tasks[1]); + unset($tasks[CRM_Case_Task::TASK_DELETE]); } else { - unset($tasks[4]); + unset($tasks[CRM_Case_Task::RESTORE_CASES]); } $this->addTaskMenu($tasks); @@ -317,7 +291,7 @@ public function postProcess() { * @see valid_date */ public function addRules() { - $this->addFormRule(array('CRM_Case_Form_Search', 'formRule')); + $this->addFormRule(['CRM_Case_Form_Search', 'formRule']); } /** @@ -325,11 +299,13 @@ public function addRules() { * * @param array $fields * Posted values of the form. + * @param array $files + * @param object $form * * @return array|bool */ - public static function formRule($fields) { - $errors = array(); + public static function formRule($fields, $files, $form) { + $errors = []; if (!empty($errors)) { return $errors; @@ -346,7 +322,7 @@ public static function formRule($fields) { * the default array reference */ public function setDefaultValues() { - $defaults = array(); + $defaults = []; $defaults = $this->_formValues; return $defaults; } @@ -356,33 +332,25 @@ public function fixFormValues() { return; } - $caseStatus = CRM_Utils_Request::retrieve('status', 'Positive', - CRM_Core_DAO::$_nullObject - ); + $caseStatus = CRM_Utils_Request::retrieve('status', 'Positive'); if ($caseStatus) { $this->_formValues['case_status_id'] = $caseStatus; $this->_defaults['case_status_id'] = $caseStatus; } - $caseType = CRM_Utils_Request::retrieve('type', 'Positive', - CRM_Core_DAO::$_nullObject - ); + $caseType = CRM_Utils_Request::retrieve('type', 'Positive'); if ($caseType) { $this->_formValues['case_type_id'] = (array) $caseType; $this->_defaults['case_type_id'] = (array) $caseType; } - $caseFromDate = CRM_Utils_Request::retrieve('pstart', 'Date', - CRM_Core_DAO::$_nullObject - ); + $caseFromDate = CRM_Utils_Request::retrieve('pstart', 'Date'); if ($caseFromDate) { list($date) = CRM_Utils_Date::setDateDefaults($caseFromDate); $this->_formValues['case_start_date_low'] = $date; $this->_defaults['case_start_date_low'] = $date; } - $caseToDate = CRM_Utils_Request::retrieve('pend', 'Date', - CRM_Core_DAO::$_nullObject - ); + $caseToDate = CRM_Utils_Request::retrieve('pend', 'Date'); if ($caseToDate) { list($date) = CRM_Utils_Date::setDateDefaults($caseToDate); $this->_formValues['case_start_date_high'] = $date; @@ -415,9 +383,7 @@ public function fixFormValues() { } // Now if case_owner is set in the url/post, use that instead. - $caseOwner = CRM_Utils_Request::retrieve('case_owner', 'Positive', - CRM_Core_DAO::$_nullObject - ); + $caseOwner = CRM_Utils_Request::retrieve('case_owner', 'Positive'); if ($caseOwner) { $this->_formValues['case_owner'] = $caseOwner; $this->_defaults['case_owner'] = $caseOwner; diff --git a/CRM/Case/Form/Task.php b/CRM/Case/Form/Task.php index 69ba68394e7e..43d77f1ced67 100644 --- a/CRM/Case/Form/Task.php +++ b/CRM/Case/Form/Task.php @@ -1,9 +1,9 @@ _caseIds = array(); - - $values = $form->controller->exportValues($form->get('searchFormName')); - - $form->_task = $values['task']; - $caseTasks = CRM_Case_Task::tasks(); - $form->assign('taskName', $caseTasks[$form->_task]); - - $ids = array(); - if ($values['radio_ts'] == 'ts_sel') { - foreach ($values as $name => $value) { - if (substr($name, 0, CRM_Core_Form::CB_PREFIX_LEN) == CRM_Core_Form::CB_PREFIX) { - $ids[] = substr($name, CRM_Core_Form::CB_PREFIX_LEN); - } - } - } - else { - $queryParams = $form->get('queryParams'); - $query = new CRM_Contact_BAO_Query($queryParams, NULL, NULL, FALSE, FALSE, - CRM_Contact_BAO_Query::MODE_CASE - ); - $query->_distinctComponentClause = " ( civicrm_case.id )"; - $query->_groupByComponentClause = " GROUP BY civicrm_case.id "; - $result = $query->searchQuery(0, 0, NULL); - while ($result->fetch()) { - $ids[] = $result->case_id; - } - } - - if (!empty($ids)) { - $form->_componentClause = ' civicrm_case.id IN ( ' . implode(',', $ids) . ' ) '; - $form->assign('totalSelectedCases', count($ids)); - } - - $form->_caseIds = $form->_componentIds = $ids; - - //set the context for redirection for any task actions - $qfKey = CRM_Utils_Request::retrieve('qfKey', 'String', $form); - $urlParams = 'force=1'; - if (CRM_Utils_Rule::qfKey($qfKey)) { - $urlParams .= "&qfKey=$qfKey"; - } - - $session = CRM_Core_Session::singleton(); - $searchFormName = strtolower($form->get('searchFormName')); - if ($searchFormName == 'search') { - $session->replaceUserContext(CRM_Utils_System::url('civicrm/case/search', $urlParams)); - } - else { - $session->replaceUserContext(CRM_Utils_System::url("civicrm/contact/search/$searchFormName", - $urlParams - )); - } - } + public static $entityShortname = 'case'; /** - * Given the signer id, compute the contact id - * since its used for things like send email + * @inheritDoc */ public function setContactIDs() { - $this->_contactIds = &CRM_Core_DAO::getContactIDsFromComponent($this->_caseIds, - 'civicrm_case_contact' + $this->_contactIds = CRM_Core_DAO::getContactIDsFromComponent($this->_entityIds, + 'civicrm_case_contact', 'case_id' ); } /** - * Simple shell that derived classes can call to add buttons to - * the form with a customized title for the main Submit + * Get the query mode (eg. CRM_Core_BAO_Query::MODE_CASE) * - * @param string $title - * Title of the main button. - * @param string $nextType - * Button type for the form after processing. - * @param string $backType - * @param bool $submitOnce + * @return int */ - public function addDefaultButtons($title, $nextType = 'next', $backType = 'back', $submitOnce = FALSE) { - $this->addButtons(array( - array( - 'type' => $nextType, - 'name' => $title, - 'isDefault' => TRUE, - ), - array( - 'type' => $backType, - 'name' => ts('Cancel'), - ), - ) - ); + public function getQueryMode() { + return CRM_Contact_BAO_Query::MODE_CASE; } } diff --git a/CRM/Case/Form/Task/Batch.php b/CRM/Case/Form/Task/Batch.php new file mode 100644 index 000000000000..c7960774ff1b --- /dev/null +++ b/CRM/Case/Form/Task/Batch.php @@ -0,0 +1,127 @@ +exportValues(); + + if (!isset($params['field'])) { + CRM_Core_Session::setStatus(ts('No updates have been saved.'), ts('Not Saved'), 'alert'); + return; + } + + $customFields = []; + $dateFields = [ + 'case_created_date', + 'case_start_date', + 'case_end_date', + 'case_modified_date', + ]; + foreach ($params['field'] as $key => $value) { + $value['id'] = $key; + + if (!empty($value['case_type'])) { + $caseTypeId = $value['case_type_id'] = $value['case_type'][1]; + } + unset($value['case_type']); + + // Get the case status + $daoClass = 'CRM_Case_DAO_Case'; + $caseStatus = CRM_Utils_Array::value('case_status', $value); + if (!$caseStatus) { + // default to existing status ID + $caseStatus = CRM_Core_DAO::getFieldValue($daoClass, $key, 'status_id'); + } + $value['status_id'] = $caseStatus; + unset($value['case_status']); + + foreach ($dateFields as $val) { + if (isset($value[$val])) { + $value[$val] = CRM_Utils_Date::processDate($value[$val]); + } + } + if (empty($customFields)) { + if (empty($value['case_type_id'])) { + $caseTypeId = CRM_Core_DAO::getFieldValue('CRM_Case_DAO_Case', $key, 'case_type_id'); + } + + // case type custom data + $customFields = CRM_Core_BAO_CustomField::getFields('Case', FALSE, FALSE, $caseTypeId); + + $customFields = CRM_Utils_Array::crmArrayMerge($customFields, + CRM_Core_BAO_CustomField::getFields('Case', + FALSE, FALSE, NULL, NULL, TRUE + ) + ); + } + //check for custom data + // @todo extract submit functions & + // extend CRM_Event_Form_Task_BatchTest::testSubmit with a data provider to test + // handling of custom data, specifically checkbox fields. + $value['custom'] = CRM_Core_BAO_CustomField::postProcess($params['field'][$key], + $key, + 'Case', + $caseTypeId + ); + + $case = CRM_Case_BAO_Case::add($value); + + // add custom field values + if (!empty($value['custom']) && is_array($value['custom'])) { + CRM_Core_BAO_CustomValueTable::store($value['custom'], 'civicrm_case', $case->id); + } + } + + CRM_Core_Session::setStatus(ts('Your updates have been saved.'), ts('Saved'), 'success'); + } + +} diff --git a/CRM/Case/Form/Task/Delete.php b/CRM/Case/Form/Task/Delete.php index dcc325b45d07..d32a9300ea13 100644 --- a/CRM/Case/Form/Task/Delete.php +++ b/CRM/Case/Form/Task/Delete.php @@ -1,9 +1,9 @@ _caseIds as $caseId) { + foreach ($this->_entityIds as $caseId) { if (CRM_Case_BAO_Case::deleteCase($caseId, $this->_moveToTrash)) { $deleted++; } @@ -84,16 +84,16 @@ public function postProcess() { if ($deleted) { if ($this->_moveToTrash) { - $msg = ts('%count case moved to trash.', array('plural' => '%count cases moved to trash.', 'count' => $deleted)); + $msg = ts('%count case moved to trash.', ['plural' => '%count cases moved to trash.', 'count' => $deleted]); } else { - $msg = ts('%count case permanently deleted.', array('plural' => '%count cases permanently deleted.', 'count' => $deleted)); + $msg = ts('%count case permanently deleted.', ['plural' => '%count cases permanently deleted.', 'count' => $deleted]); } CRM_Core_Session::setStatus($msg, ts('Removed'), 'success'); } if ($failed) { - CRM_Core_Session::setStatus(ts('1 could not be deleted.', array('plural' => '%count could not be deleted.', 'count' => $failed)), ts('Error'), 'error'); + CRM_Core_Session::setStatus(ts('1 could not be deleted.', ['plural' => '%count could not be deleted.', 'count' => $failed]), ts('Error'), 'error'); } } diff --git a/CRM/Case/Form/Task/PDF.php b/CRM/Case/Form/Task/PDF.php index 24e3ba963150..3afeb6275af9 100644 --- a/CRM/Case/Form/Task/PDF.php +++ b/CRM/Case/Form/Task/PDF.php @@ -1,9 +1,9 @@ skipOnHold = $this->skipDeceased = FALSE; parent::preProcess(); $this->setContactIDs(); - CRM_Contact_Form_Task_PDFLetterCommon::preProcess($this); } + /** + * Set defaults for the pdf. + * + * @return array + */ public function setDefaultValues() { return CRM_Contact_Form_Task_PDFLetterCommon::setDefaultValues(); } @@ -81,7 +86,7 @@ public function postProcess() { */ public function listTokens() { $tokens = CRM_Core_SelectValues::contactTokens(); - foreach ($this->_caseIds as $key => $caseId) { + foreach ($this->_entityIds as $key => $caseId) { $caseTypeId = CRM_Core_DAO::getFieldValue('CRM_Case_DAO_Case', $caseId, 'case_type_id'); $tokens += CRM_Core_SelectValues::caseTokens($caseTypeId); } diff --git a/CRM/Case/Form/Task/PickProfile.php b/CRM/Case/Form/Task/PickProfile.php new file mode 100644 index 000000000000..c03d48df1e42 --- /dev/null +++ b/CRM/Case/Form/Task/PickProfile.php @@ -0,0 +1,52 @@ +controller->setPrint(1); @@ -73,19 +73,18 @@ public function buildQuickForm() { // // just need to add a javacript to popup the window for printing // - $this->addButtons(array( - array( - 'type' => 'next', - 'name' => ts('Print Case List'), - 'js' => array('onclick' => 'window.print()'), - 'isDefault' => TRUE, - ), - array( - 'type' => 'back', - 'name' => ts('Done'), - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'next', + 'name' => ts('Print Case List'), + 'js' => ['onclick' => 'window.print()'], + 'isDefault' => TRUE, + ], + [ + 'type' => 'back', + 'name' => ts('Done'), + ], + ]); } /** diff --git a/CRM/Case/Form/Task/Restore.php b/CRM/Case/Form/Task/Restore.php index 384cfd7fa682..db78e81b73cc 100644 --- a/CRM/Case/Form/Task/Restore.php +++ b/CRM/Case/Form/Task/Restore.php @@ -1,9 +1,9 @@ _caseIds as $caseId) { + foreach ($this->_entityIds as $caseId) { if (CRM_Case_BAO_Case::restoreCase($caseId)) { $restoredCases++; } @@ -73,15 +73,15 @@ public function postProcess() { } if ($restoredCases) { - $msg = ts('%count case restored from trash.', array( + $msg = ts('%count case restored from trash.', [ 'plural' => '%count cases restored from trash.', 'count' => $restoredCases, - )); + ]); CRM_Core_Session::setStatus($msg, ts('Restored'), 'success'); } if ($failed) { - CRM_Core_Session::setStatus(ts('1 could not be restored.', array('plural' => '%count could not be restored.', 'count' => $failed)), ts('Error'), 'error'); + CRM_Core_Session::setStatus(ts('1 could not be restored.', ['plural' => '%count could not be restored.', 'count' => $failed]), ts('Error'), 'error'); } } diff --git a/CRM/Case/Form/Task/Result.php b/CRM/Case/Form/Task/Result.php index a32a4112be5e..d7a3a4678b48 100644 --- a/CRM/Case/Form/Task/Result.php +++ b/CRM/Case/Form/Task/Result.php @@ -1,9 +1,9 @@ addButtons(array( - array( - 'type' => 'done', - 'name' => ts('Done'), - 'isDefault' => TRUE, - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'done', + 'name' => ts('Done'), + 'isDefault' => TRUE, + ], + ]); } } diff --git a/CRM/Case/Form/Task/SearchTaskHookSample.php b/CRM/Case/Form/Task/SearchTaskHookSample.php index 844cfe81e5d9..ec17a006c2de 100644 --- a/CRM/Case/Form/Task/SearchTaskHookSample.php +++ b/CRM/Case/Form/Task/SearchTaskHookSample.php @@ -1,9 +1,9 @@ _caseIds); + $caseIDs = implode(',', $this->_entityIds); $statusId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionGroup', 'case_status', 'id', 'name'); $query = " SELECT ct.display_name as display_name, @@ -59,11 +59,11 @@ public function preProcess() { $dao = CRM_Core_DAO::executeQuery($query); while ($dao->fetch()) { - $rows[] = array( + $rows[] = [ 'display_name' => $dao->display_name, 'start_date' => CRM_Utils_Date::customFormat($dao->start_date), 'status' => $dao->status, - ); + ]; } $this->assign('rows', $rows); } @@ -72,14 +72,13 @@ public function preProcess() { * Build the form object. */ public function buildQuickForm() { - $this->addButtons(array( - array( - 'type' => 'done', - 'name' => ts('Done'), - 'isDefault' => TRUE, - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'done', + 'name' => ts('Done'), + 'isDefault' => TRUE, + ], + ]); } } diff --git a/CRM/Case/Info.php b/CRM/Case/Info.php index f361cfb6b06d..2a57400c683a 100644 --- a/CRM/Case/Info.php +++ b/CRM/Case/Info.php @@ -1,9 +1,9 @@ 'CiviCase', 'translatedName' => ts('CiviCase'), 'title' => ts('CiviCase Engine'), 'search' => 1, 'showActivitiesInCore' => 0, - ); + ]; } /** * @inheritDoc */ public function getAngularModules() { - $result = array(); - $result['crmCaseType'] = array( - 'ext' => 'civicrm', - 'js' => array('ang/crmCaseType.js'), - 'css' => array('ang/crmCaseType.css'), - 'partials' => array('ang/crmCaseType'), - ); + global $civicrm_root; - CRM_Core_Resources::singleton()->addSetting(array( - 'crmCaseType' => array( - 'REL_TYPE_CNAME' => CRM_Case_XMLProcessor::REL_TYPE_CNAME, - ), - )); + $result = []; + $result['crmCaseType'] = include "$civicrm_root/ang/crmCaseType.ang.php"; return $result; } @@ -98,28 +90,28 @@ public function getManagedEntities() { * @return array */ public function getPermissions($getAllUnconditionally = FALSE, $descriptions = FALSE) { - $permissions = array( - 'delete in CiviCase' => array( + $permissions = [ + 'delete in CiviCase' => [ ts('delete in CiviCase'), ts('Delete cases'), - ), - 'administer CiviCase' => array( + ], + 'administer CiviCase' => [ ts('administer CiviCase'), ts('Define case types, access deleted cases'), - ), - 'access my cases and activities' => array( + ], + 'access my cases and activities' => [ ts('access my cases and activities'), ts('View and edit only those cases managed by this user'), - ), - 'access all cases and activities' => array( + ], + 'access all cases and activities' => [ ts('access all cases and activities'), ts('View and edit all cases (for visible contacts)'), - ), - 'add cases' => array( + ], + 'add cases' => [ ts('add cases'), ts('Open a new case'), - ), - ); + ], + ]; if (!$descriptions) { foreach ($permissions as $name => $attr) { @@ -134,7 +126,7 @@ public function getPermissions($getAllUnconditionally = FALSE, $descriptions = F * @inheritDoc */ public function getReferenceCounts($dao) { - $result = array(); + $result = []; if ($dao instanceof CRM_Core_DAO_OptionValue) { /** @var $dao CRM_Core_DAO_OptionValue */ $activity_type_gid = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionGroup', 'activity_type', 'id', 'name'); @@ -142,11 +134,11 @@ public function getReferenceCounts($dao) { $count = CRM_Case_XMLRepository::singleton() ->getActivityReferenceCount($dao->name); if ($count > 0) { - $result[] = array( + $result[] = [ 'name' => 'casetypexml:activities', 'type' => 'casetypexml', 'count' => $count, - ); + ]; } } } @@ -155,11 +147,11 @@ public function getReferenceCounts($dao) { $count = CRM_Case_XMLRepository::singleton() ->getRelationshipReferenceCount($dao->{CRM_Case_XMLProcessor::REL_TYPE_CNAME}); if ($count > 0) { - $result[] = array( + $result[] = [ 'name' => 'casetypexml:relationships', 'type' => 'casetypexml', 'count' => $count, - ); + ]; } } return $result; @@ -170,7 +162,7 @@ public function getReferenceCounts($dao) { * @return array */ public function getUserDashboardElement() { - return array(); + return []; } /** @@ -178,11 +170,19 @@ public function getUserDashboardElement() { * @return array */ public function registerTab() { - return array( + return [ 'title' => ts('Cases'), 'url' => 'case', 'weight' => 50, - ); + ]; + } + + /** + * @inheritDoc + * @return string + */ + public function getIcon() { + return 'crm-i fa-folder-open-o'; } /** @@ -190,10 +190,10 @@ public function registerTab() { * @return array */ public function registerAdvancedSearchPane() { - return array( + return [ 'title' => ts('Cases'), 'weight' => 50, - ); + ]; } /** @@ -212,19 +212,16 @@ public function creatNewShortcut(&$shortCuts) { if (CRM_Core_Permission::check('access all cases and activities') || CRM_Core_Permission::check('add cases') ) { - $atype = CRM_Core_OptionGroup::getValue('activity_type', - 'Open Case', - 'name' - ); - if ($atype) { - $shortCuts = array_merge($shortCuts, array( - array( + $activityType = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Open Case'); + if ($activityType) { + $shortCuts = array_merge($shortCuts, [ + [ 'path' => 'civicrm/case/add', - 'query' => "reset=1&action=add&atype=$atype&context=standalone", + 'query' => "reset=1&action=add&atype={$activityType}&context=standalone", 'ref' => 'new-case', 'title' => ts('Case'), - ), - )); + ], + ]); } } } @@ -257,4 +254,40 @@ public static function onToggleComponents($oldValue, $newValue, $metadata) { } } + /** + * @return array + * Array(string $value => string $label). + */ + public static function getRedactOptions() { + return [ + 'default' => ts('Default'), + '0' => ts('Do not redact emails'), + '1' => ts('Redact emails'), + ]; + } + + /** + * @return array + * Array(string $value => string $label). + */ + public static function getMultiClientOptions() { + return [ + 'default' => ts('Default'), + '0' => ts('Single client per case'), + '1' => ts('Multiple client per case'), + ]; + } + + /** + * @return array + * Array(string $value => string $label). + */ + public static function getSortOptions() { + return [ + 'default' => ts('Default'), + '0' => ts('Definition order'), + '1' => ts('Alphabetical order'), + ]; + } + } diff --git a/CRM/Case/ManagedEntities.php b/CRM/Case/ManagedEntities.php index 382f9a05a7b3..461b51f5dafc 100644 --- a/CRM/Case/ManagedEntities.php +++ b/CRM/Case/ManagedEntities.php @@ -14,14 +14,14 @@ class CRM_Case_ManagedEntities { * @throws CRM_Core_Exception */ public static function createManagedCaseTypes() { - $entities = array(); + $entities = []; // Use hook_civicrm_caseTypes to build a list of OptionValues // In the long run, we may want more specialized logic for this, but // this design is fairly convenient and will allow us to replace it // without changing the hook_civicrm_caseTypes interface. - $caseTypes = array(); + $caseTypes = []; CRM_Utils_Hook::caseTypes($caseTypes); $proc = new CRM_Case_XMLProcessor(); @@ -32,11 +32,11 @@ public static function createManagedCaseTypes() { } if (isset($caseType['module'], $caseType['name'], $caseType['file'])) { - $entities[] = array( + $entities[] = [ 'module' => $caseType['module'], 'name' => $caseType['name'], 'entity' => 'CaseType', - 'params' => array( + 'params' => [ 'version' => 3, 'name' => $caseType['name'], 'title' => (string) $xml->name, @@ -44,8 +44,8 @@ public static function createManagedCaseTypes() { 'is_reserved' => 1, 'is_active' => 1, 'weight' => $xml->weight ? $xml->weight : 1, - ), - ); + ], + ]; } else { throw new CRM_Core_Exception("Invalid case type"); @@ -64,26 +64,26 @@ public static function createManagedCaseTypes() { * @see CRM_Utils_Hook::managed */ public static function createManagedActivityTypes(CRM_Case_XMLRepository $xmlRepo, CRM_Core_ManagedEntities $me) { - $result = array(); + $result = []; $validActTypes = CRM_Core_PseudoConstant::activityType(TRUE, TRUE, TRUE, 'name'); $actTypes = $xmlRepo->getAllDeclaredActivityTypes(); foreach ($actTypes as $actType) { - $managed = array( + $managed = [ 'module' => 'civicrm', 'name' => "civicase:act:$actType", 'entity' => 'OptionValue', 'update' => 'never', 'cleanup' => 'unused', - 'params' => array( + 'params' => [ 'version' => 3, 'option_group_id' => 'activity_type', 'label' => $actType, 'name' => $actType, 'description' => $actType, 'component_id' => 'CiviCase', - ), - ); + ], + ]; // We'll create managed-entity if this record doesn't exist yet // or if we previously decided to manage this record. @@ -108,7 +108,7 @@ public static function createManagedActivityTypes(CRM_Case_XMLRepository $xmlRep * @see CRM_Utils_Hook::managed */ public static function createManagedRelationshipTypes(CRM_Case_XMLRepository $xmlRepo, CRM_Core_ManagedEntities $me) { - $result = array(); + $result = []; if (!isset(Civi::$statics[__CLASS__]['reltypes'])) { $relationshipInfo = CRM_Core_PseudoConstant::relationshipType('label', TRUE, NULL); @@ -118,13 +118,13 @@ public static function createManagedRelationshipTypes(CRM_Case_XMLRepository $xm $relTypes = $xmlRepo->getAllDeclaredRelationshipTypes(); foreach ($relTypes as $relType) { - $managed = array( + $managed = [ 'module' => 'civicrm', 'name' => "civicase:rel:$relType", 'entity' => 'RelationshipType', 'update' => 'never', 'cleanup' => 'unused', - 'params' => array( + 'params' => [ 'version' => 3, 'name_a_b' => "$relType is", 'name_b_a' => $relType, @@ -135,8 +135,8 @@ public static function createManagedRelationshipTypes(CRM_Case_XMLRepository $xm 'contact_type_b' => 'Individual', 'contact_sub_type_a' => NULL, 'contact_sub_type_b' => NULL, - ), - ); + ], + ]; // We'll create managed-entity if this record doesn't exist yet // or if we previously decided to manage this record. diff --git a/CRM/Case/Page/AJAX.php b/CRM/Case/Page/AJAX.php index 8c7d12528191..4507d161a96b 100644 --- a/CRM/Case/Page/AJAX.php +++ b/CRM/Case/Page/AJAX.php @@ -1,9 +1,9 @@ $caseId, - 'entity_table' => 'civicrm_case', - ); + $params = [ + 'entity_id' => $caseId, + 'entity_table' => 'civicrm_case', + ]; - CRM_Core_BAO_EntityTag::del($params); + CRM_Core_BAO_EntityTag::del($params); - foreach ($tagIds as $tagid) { - if (is_numeric($tagid)) { - $params['tag_id'] = $tagid; - CRM_Core_BAO_EntityTag::add($params); - } + foreach ($tagIds as $tagid) { + if (is_numeric($tagid)) { + $params['tag_id'] = $tagid; + CRM_Core_BAO_EntityTag::add($params); } } if (!empty($tagList)) { - CRM_Core_Form_Tag::postProcess($tagList, $caseId, 'civicrm_case', CRM_Core_DAO::$_nullObject); + CRM_Core_Form_Tag::postProcess($tagList, $caseId, 'civicrm_case'); } $session = CRM_Core_Session::singleton(); - $activityParams = array(); + $activityParams = []; $activityParams['source_contact_id'] = $session->get('userID'); - $activityParams['activity_type_id'] = CRM_Core_OptionGroup::getValue('activity_type', 'Change Case Tags', 'name'); + $activityParams['activity_type_id'] = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Change Case Tags'); $activityParams['activity_date_time'] = date('YmdHis'); - $activityParams['status_id'] = CRM_Core_OptionGroup::getValue('activity_status', 'Completed', 'name'); + $activityParams['status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', 'Completed'); $activityParams['case_id'] = $caseId; $activityParams['is_auto'] = 0; $activityParams['subject'] = 'Change Case Tags'; $activity = CRM_Activity_BAO_Activity::create($activityParams); - $caseParams = array( + $caseParams = [ 'activity_id' => $activity->id, 'case_id' => $caseId, - ); + ]; CRM_Case_BAO_Case::processCaseActivity($caseParams); @@ -105,11 +103,11 @@ public function processCaseTags() { public function caseDetails() { $caseId = CRM_Utils_Type::escape($_GET['caseId'], 'Positive'); - $case = civicrm_api3('Case', 'getsingle', array( + $case = civicrm_api3('Case', 'getsingle', [ 'id' => $caseId, 'check_permissions' => TRUE, - 'return' => array('subject', 'case_type_id', 'status_id', 'start_date', 'end_date')) - ); + 'return' => ['subject', 'case_type_id', 'status_id', 'start_date', 'end_date'], + ]); $caseStatuses = CRM_Case_PseudoConstant::caseStatus(); $caseTypes = CRM_Case_PseudoConstant::caseType('title', FALSE); @@ -138,10 +136,10 @@ public function addClient() { CRM_Utils_System::permissionDenied(); } - $params = array( + $params = [ 'case_id' => $caseId, 'contact_id' => $contactId, - ); + ]; CRM_Case_BAO_CaseContact::create($params); @@ -150,21 +148,21 @@ public function addClient() { $session = CRM_Core_Session::singleton(); - $activityParams = array(); + $activityParams = []; $activityParams['source_contact_id'] = $session->get('userID'); - $activityParams['activity_type_id'] = CRM_Core_OptionGroup::getValue('activity_type', 'Add Client To Case', 'name'); + $activityParams['activity_type_id'] = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Add Client To Case'); $activityParams['activity_date_time'] = date('YmdHis'); - $activityParams['status_id'] = CRM_Core_OptionGroup::getValue('activity_status', 'Completed', 'name'); + $activityParams['status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', 'Completed'); $activityParams['case_id'] = $caseId; $activityParams['is_auto'] = 0; $activityParams['subject'] = 'Client Added To Case'; $activity = CRM_Activity_BAO_Activity::create($activityParams); - $caseParams = array( + $caseParams = [ 'activity_id' => $activity->id, 'case_id' => $caseId, - ); + ]; CRM_Case_BAO_Case::processCaseActivity($caseParams); CRM_Utils_JSON::output(TRUE); @@ -188,4 +186,30 @@ public static function deleteCaseRoles() { CRM_Utils_System::civiExit(); } + public static function getCases() { + $requiredParameters = [ + 'type' => 'String', + ]; + $optionalParameters = [ + 'case_type_id' => 'CommaSeparatedIntegers', + 'status_id' => 'CommaSeparatedIntegers', + 'all' => 'Positive', + ]; + $params = CRM_Core_Page_AJAX::defaultSortAndPagerParams(); + $params += CRM_Core_Page_AJAX::validateParams($requiredParameters, $optionalParameters); + + $allCases = (bool) $params['all']; + + $cases = CRM_Case_BAO_Case::getCases($allCases, $params); + + $casesDT = [ + 'recordsFiltered' => $cases['total'], + 'recordsTotal' => $cases['total'], + ]; + unset($cases['total']); + $casesDT['data'] = array_values($cases); + + CRM_Utils_JSON::output($casesDT); + } + } diff --git a/CRM/Case/Page/CaseDetails.php b/CRM/Case/Page/CaseDetails.php index ea2e349f68af..77b09b57be2a 100644 --- a/CRM/Case/Page/CaseDetails.php +++ b/CRM/Case/Page/CaseDetails.php @@ -1,9 +1,9 @@ _action = CRM_Utils_Request::retrieve('action', 'String', $this, FALSE, 'browse'); - $this->_context = CRM_Utils_Request::retrieve('context', 'String', $this); + $this->_context = CRM_Utils_Request::retrieve('context', 'Alphanumeric', $this); $this->assign('action', $this->_action); $this->assign('context', $this->_context); @@ -50,7 +50,7 @@ public function run() { $caseId = CRM_Utils_Request::retrieve('caseId', 'Positive', $this); - CRM_Case_Page_Tab::setContext(); + CRM_Case_Page_Tab::setContext($this); $this->assign('caseID', $caseId); $this->assign('contactID', $this->_contactId); diff --git a/CRM/Case/Page/DashBoard.php b/CRM/Case/Page/DashBoard.php index 2ea0811ad212..fd0002c3cffe 100644 --- a/CRM/Case/Page/DashBoard.php +++ b/CRM/Case/Page/DashBoard.php @@ -1,9 +1,9 @@ assign('all', $allCases); if (!$allCases) { $this->assign('myCases', TRUE); } @@ -82,22 +83,27 @@ public function preProcess() { ) { $this->assign('newClient', TRUE); } - $summary = CRM_Case_BAO_Case::getCasesSummary($allCases, $userID); - $upcoming = CRM_Case_BAO_Case::getCases($allCases, $userID, 'upcoming'); - $recent = CRM_Case_BAO_Case::getCases($allCases, $userID, 'recent'); + $summary = CRM_Case_BAO_Case::getCasesSummary($allCases); + $upcoming = CRM_Case_BAO_Case::getCases($allCases, [], 'dashboard', TRUE); + $recent = CRM_Case_BAO_Case::getCases($allCases, ['type' => 'recent'], 'dashboard', TRUE); - foreach ($upcoming as $key => $value) { - if (strtotime($value['case_scheduled_activity_date']) < time()) { - $upcoming[$key]['activity_status'] = 'status-overdue'; - } - } $this->assign('casesSummary', $summary); if (!empty($upcoming)) { - $this->assign('upcomingCases', $upcoming); + $this->assign('upcomingCases', TRUE); } if (!empty($recent)) { - $this->assign('recentCases', $recent); + $this->assign('recentCases', TRUE); } + + $controller = new CRM_Core_Controller_Simple('CRM_Case_Form_Search', + ts('Case'), CRM_Core_Action::BROWSE, + NULL, + FALSE, FALSE, TRUE + ); + $controller->set('context', 'dashboard'); + $controller->setEmbedded(TRUE); + $controller->process(); + $controller->run(); } /** diff --git a/CRM/Case/Page/Tab.php b/CRM/Case/Page/Tab.php index 9ad7e348a92a..d7c7055ca2c4 100644 --- a/CRM/Case/Page/Tab.php +++ b/CRM/Case/Page/Tab.php @@ -1,9 +1,9 @@ _id = CRM_Utils_Request::retrieve('id', 'Positive', $this); - $this->_context = CRM_Utils_Request::retrieve('context', 'String', $this); + $this->_context = CRM_Utils_Request::retrieve('context', 'Alphanumeric', $this); if ($this->_contactId) { $this->assign('contactId', $this->_contactId); @@ -67,8 +67,7 @@ public function preProcess() { if ($this->_id && ($this->_action & CRM_Core_Action::VIEW)) { //user might have special permissions to view this case, CRM-5666 if (!CRM_Core_Permission::check('access all cases and activities')) { - $session = CRM_Core_Session::singleton(); - $userCases = CRM_Case_BAO_Case::getCases(FALSE, $session->get('userID'), 'any'); + $userCases = CRM_Case_BAO_Case::getCases(FALSE, ['type' => 'any']); if (!array_key_exists($this->_id, $userCases)) { CRM_Core_Error::fatal(ts('You are not authorized to access this page.')); } @@ -179,7 +178,7 @@ public function edit() { */ public function run() { $contactID = CRM_Utils_Request::retrieve('cid', 'Positive', CRM_Core_DAO::$_nullArray); - $context = CRM_Utils_Request::retrieve('context', 'String', $this); + $context = CRM_Utils_Request::retrieve('context', 'Alphanumeric', $this); if ($context == 'standalone' && !$contactID) { $this->_action = CRM_Core_Action::ADD; @@ -191,7 +190,7 @@ public function run() { $this->assign('action', $this->_action); - $this->setContext(); + self::setContext($this); if ($this->_action & CRM_Core_Action::VIEW) { $this->view(); @@ -217,35 +216,38 @@ public function run() { * @return array * (reference) of action links */ - static public function &links() { + public static function &links() { $config = CRM_Core_Config::singleton(); if (!(self::$_links)) { $deleteExtra = ts('Are you sure you want to delete this case?'); - self::$_links = array( - CRM_Core_Action::VIEW => array( + self::$_links = [ + CRM_Core_Action::VIEW => [ 'name' => ts('Manage'), 'url' => 'civicrm/contact/view/case', 'qs' => 'action=view&reset=1&cid=%%cid%%&id=%%id%%', 'class' => 'no-popup', 'title' => ts('Manage Case'), - ), - CRM_Core_Action::DELETE => array( + ], + CRM_Core_Action::DELETE => [ 'name' => ts('Delete'), 'url' => 'civicrm/contact/view/case', 'qs' => 'action=delete&reset=1&cid=%%cid%%&id=%%id%%', 'title' => ts('Delete Case'), - ), - ); + ], + ]; } return self::$_links; } - public function setContext() { - $context = $this->get('context'); + /** + * @param CRM_Core_Form $form + */ + public static function setContext(&$form) { + $context = $form->get('context'); $url = NULL; - $qfKey = CRM_Utils_Request::retrieve('key', 'String', $this); + $qfKey = CRM_Utils_Request::retrieve('key', 'String', $form); //validate the qfKey if (!CRM_Utils_Rule::qfKey($qfKey)) { $qfKey = NULL; @@ -253,9 +255,9 @@ public function setContext() { switch ($context) { case 'activity': - if ($this->_contactId) { + if ($form->_contactId) { $url = CRM_Utils_System::url('civicrm/contact/view', - "reset=1&force=1&cid={$this->_contactId}&selectedChild=activity" + "reset=1&force=1&cid={$form->_contactId}&selectedChild=activity" ); } break; @@ -281,12 +283,12 @@ public function setContext() { break; case 'fulltext': - $action = CRM_Utils_Request::retrieve('action', 'String', $this); + $action = CRM_Utils_Request::retrieve('action', 'String', $form); $urlParams = 'force=1'; $urlString = 'civicrm/contact/search/custom'; if ($action == CRM_Core_Action::RENEW) { - if ($this->_contactId) { - $urlParams .= '&cid=' . $this->_contactId; + if ($form->_contactId) { + $urlParams .= '&cid=' . $form->_contactId; } $urlParams .= '&context=fulltext&action=view'; $urlString = 'civicrm/contact/view/case'; @@ -298,9 +300,9 @@ public function setContext() { break; default: - if ($this->_contactId) { + if ($form->_contactId) { $url = CRM_Utils_System::url('civicrm/contact/view', - "reset=1&force=1&cid={$this->_contactId}&selectedChild=case" + "reset=1&force=1&cid={$form->_contactId}&selectedChild=case" ); } break; diff --git a/CRM/Case/PseudoConstant.php b/CRM/Case/PseudoConstant.php index c80425acfaf5..7cc943acfa58 100644 --- a/CRM/Case/PseudoConstant.php +++ b/CRM/Case/PseudoConstant.php @@ -1,9 +1,9 @@ fetch()) { if ($indexName) { $index = $dao->name; @@ -227,10 +187,11 @@ public static function &caseActivityType($indexName = TRUE, $all = FALSE) { else { $index = $dao->value; } - $activityTypes[$index] = array(); + $activityTypes[$index] = []; $activityTypes[$index]['id'] = $dao->value; $activityTypes[$index]['label'] = $dao->label; $activityTypes[$index]['name'] = $dao->name; + $activityTypes[$index]['icon'] = $dao->icon; $activityTypes[$index]['description'] = $dao->description; } self::$activityTypeList[$cache] = $activityTypes; diff --git a/CRM/Case/Selector/Search.php b/CRM/Case/Selector/Search.php index b47f2b148355..55c396e60ee1 100644 --- a/CRM/Case/Selector/Search.php +++ b/CRM/Case/Selector/Search.php @@ -1,9 +1,9 @@ array( + self::$_links = [ + CRM_Core_Action::RENEW => [ 'name' => ts('Restore'), 'url' => 'civicrm/contact/view/case', 'qs' => 'reset=1&action=renew&id=%%id%%&cid=%%cid%%&context=%%cxt%%' . $extraParams, 'ref' => 'restore-case', 'title' => ts('Restore Case'), - ), - ); + ], + ]; } else { - self::$_links = array( - CRM_Core_Action::VIEW => array( + self::$_links = [ + CRM_Core_Action::VIEW => [ 'name' => ts('Manage'), 'url' => 'civicrm/contact/view/case', 'qs' => 'reset=1&id=%%id%%&cid=%%cid%%&action=view&context=%%cxt%%&selectedChild=case' . $extraParams, 'ref' => 'manage-case', 'class' => 'no-popup', 'title' => ts('Manage Case'), - ), - CRM_Core_Action::DELETE => array( + ], + CRM_Core_Action::DELETE => [ 'name' => ts('Delete'), 'url' => 'civicrm/contact/view/case', 'qs' => 'reset=1&action=delete&id=%%id%%&cid=%%cid%%&context=%%cxt%%' . $extraParams, 'ref' => 'delete-case', 'title' => ts('Delete Case'), - ), - CRM_Core_Action::UPDATE => array( + ], + CRM_Core_Action::UPDATE => [ 'name' => ts('Assign to Another Client'), 'url' => 'civicrm/contact/view/case/editClient', 'qs' => 'reset=1&action=update&id=%%id%%&cid=%%cid%%&context=%%cxt%%' . $extraParams, 'ref' => 'reassign', 'class' => 'medium-popup', 'title' => ts('Assign to Another Client'), - ), - ); + ], + ]; } - $actionLinks = array(); + $actionLinks = []; foreach (self::$_links as $key => $value) { - if ($value['ref'] == 'reassign') { - $actionLinks['moreActions'][$key] = $value; - } - else { - $actionLinks['primaryActions'][$key] = $value; - } + $actionLinks['primaryActions'][$key] = $value; } return $actionLinks; @@ -297,10 +292,10 @@ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) { $this->_additionalClause ); // process the result of the query - $rows = array(); + $rows = []; //CRM-4418 check for view, edit, delete - $permissions = array(CRM_Core_Permission::VIEW); + $permissions = [CRM_Core_Permission::VIEW]; if (CRM_Core_Permission::check('access all cases and activities') || CRM_Core_Permission::check('access my cases and activities') ) { @@ -313,10 +308,10 @@ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) { $caseStatus = CRM_Core_OptionGroup::values('case_status', FALSE, FALSE, FALSE, " AND v.name = 'Urgent' "); - $scheduledInfo = array(); + $scheduledInfo = []; while ($result->fetch()) { - $row = array(); + $row = []; // the columns we are interested in foreach (self::$_properties as $property) { if (isset($result->$property)) { @@ -337,41 +332,24 @@ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) { $links = self::links($isDeleted, $this->_key); $row['action'] = CRM_Core_Action::formLink($links['primaryActions'], - $mask, array( + $mask, [ 'id' => $result->case_id, 'cid' => $result->contact_id, 'cxt' => $this->_context, - ), + ], ts('more'), FALSE, 'case.selector.actions', 'Case', $result->case_id ); - $row['moreActions'] = CRM_Core_Action::formLink(CRM_Utils_Array::value('moreActions', $links), - $mask, array( - 'id' => $result->case_id, - 'cid' => $result->contact_id, - 'cxt' => $this->_context, - ), - ts('more'), - TRUE, - 'case.selector.moreActions', - 'Case', - $result->case_id - ); $row['contact_type'] = CRM_Contact_BAO_Contact_Utils::getImage($result->contact_sub_type ? $result->contact_sub_type : $result->contact_type ); //adding case manager to case selector.CRM-4510. $caseType = CRM_Case_BAO_Case::getCaseType($result->case_id, 'name'); - $caseManagerContact = CRM_Case_BAO_Case::getCaseManagerContact($caseType, $result->case_id); - - if (!empty($caseManagerContact)) { - $row['casemanager_id'] = CRM_Utils_Array::value('casemanager_id', $caseManagerContact); - $row['casemanager'] = CRM_Utils_Array::value('casemanager', $caseManagerContact); - } + $row['casemanager'] = CRM_Case_BAO_Case::getCaseManagerContact($caseType, $result->case_id); if (isset($result->case_status_id) && array_key_exists($result->case_status_id, $caseStatus) @@ -422,51 +400,51 @@ public function getQILL() { */ public function &getColumnHeaders($action = NULL, $output = NULL) { if (!isset(self::$_columnHeaders)) { - self::$_columnHeaders = array( - array( + self::$_columnHeaders = [ + [ 'name' => ts('Subject'), 'direction' => CRM_Utils_Sort::DONTCARE, - ), - array( + ], + [ 'name' => ts('Status'), 'sort' => 'case_status', 'direction' => CRM_Utils_Sort::DONTCARE, - ), - array( + ], + [ 'name' => ts('Case Type'), 'sort' => 'case_type', 'direction' => CRM_Utils_Sort::DONTCARE, - ), - array( + ], + [ 'name' => ts('My Role'), 'sort' => 'case_role', 'direction' => CRM_Utils_Sort::DONTCARE, - ), - array( + ], + [ 'name' => ts('Case Manager'), 'direction' => CRM_Utils_Sort::DONTCARE, - ), - array( + ], + [ 'name' => ts('Most Recent'), 'sort' => 'case_recent_activity_date', 'direction' => CRM_Utils_Sort::DONTCARE, - ), - array( + ], + [ 'name' => ts('Next Sched.'), 'sort' => 'case_scheduled_activity_date', 'direction' => CRM_Utils_Sort::DONTCARE, - ), - array('name' => ts('Actions')), - ); + ], + ['name' => ts('Actions')], + ]; if (!$this->_single) { - $pre = array( - array( + $pre = [ + [ 'name' => ts('Client'), 'sort' => 'sort_name', 'direction' => CRM_Utils_Sort::ASCENDING, - ), - ); + ], + ]; self::$_columnHeaders = array_merge($pre, self::$_columnHeaders); } @@ -478,7 +456,7 @@ public function &getColumnHeaders($action = NULL, $output = NULL) { * @return mixed */ public function alphabetQuery() { - return $this->_query->searchQuery(NULL, NULL, NULL, FALSE, FALSE, TRUE); + return $this->_query->alphabetQuery(); } /** diff --git a/CRM/Case/StateMachine/Search.php b/CRM/Case/StateMachine/Search.php index 82b43ef781cb..8f29c530ad4a 100644 --- a/CRM/Case/StateMachine/Search.php +++ b/CRM/Case/StateMachine/Search.php @@ -1,9 +1,9 @@ _pages = array(); + $this->_pages = []; $this->_pages['CRM_Case_Form_Search'] = NULL; list($task, $result) = $this->taskName($controller, 'Search'); @@ -80,7 +80,7 @@ public function __construct($controller, $action = CRM_Core_Action::NONE) { * * @param string $formName * - * @return string + * @return array * the name of the form that will handle the task */ public function taskName($controller, $formName = 'Search') { diff --git a/CRM/Case/Task.php b/CRM/Case/Task.php index 4f9dcdea0aab..5907da813d54 100644 --- a/CRM/Case/Task.php +++ b/CRM/Case/Task.php @@ -1,9 +1,9 @@ array( + self::$_tasks = [ + self::TASK_DELETE => [ 'title' => ts('Delete cases'), 'class' => 'CRM_Case_Form_Task_Delete', 'result' => FALSE, - ), - 2 => array( + ], + self::TASK_PRINT => [ 'title' => ts('Print selected rows'), 'class' => 'CRM_Case_Form_Task_Print', 'result' => FALSE, - ), - 3 => array( + ], + self::TASK_EXPORT => [ 'title' => ts('Export cases'), - 'class' => array( - 'CRM_Export_Form_Select', + 'class' => [ + 'CRM_Export_Form_Select_Case', 'CRM_Export_Form_Map', - ), + ], 'result' => FALSE, - ), - 4 => array( + ], + self::RESTORE_CASES => [ 'title' => ts('Restore cases'), 'class' => 'CRM_Case_Form_Task_Restore', 'result' => FALSE, - ), - 5 => array( - 'title' => ts('Print/merge Document'), + ], + self::PDF_LETTER => [ + 'title' => ts('Print/merge document'), 'class' => 'CRM_Case_Form_Task_PDF', 'result' => FALSE, - ), - ); + ], + self::BATCH_UPDATE => [ + 'title' => ts('Update multiple cases'), + 'class' => [ + 'CRM_Case_Form_Task_PickProfile', + 'CRM_Case_Form_Task_Batch', + ], + 'result' => FALSE, + ], + ]; + //CRM-4418, check for delete if (!CRM_Core_Permission::check('delete in CiviCase')) { - unset(self::$_tasks[1]); + unset(self::$_tasks[self::TASK_DELETE]); } - } - CRM_Utils_Hook::searchTasks('case', self::$_tasks); - asort(self::$_tasks); - return self::$_tasks; - } - - /** - * These tasks are the core set of task titles. - * - * @return array - * the set of task titles - */ - public static function &taskTitles() { - self::tasks(); - $titles = array(); - foreach (self::$_tasks as $id => $value) { - $titles[$id] = $value['title']; + parent::tasks(); } - return $titles; - } - /** - * These tasks get added based on the context the user is in. - * - * @return array - * the set of optional tasks for a group of contacts - */ - public static function &optionalTaskTitle() { - $tasks = array(); - return $tasks; + return self::$_tasks; } /** @@ -134,12 +112,12 @@ public static function &optionalTaskTitle() { * of the user * * @param int $permission + * @param array $params * * @return array * set of tasks that are valid for the user */ - public static function &permissionedTaskTitles($permission) { - $tasks = array(); + public static function permissionedTaskTitles($permission, $params = []) { if (($permission == CRM_Core_Permission::EDIT) || CRM_Core_Permission::check('access all cases and activities') || CRM_Core_Permission::check('access my cases and activities') @@ -147,14 +125,16 @@ public static function &permissionedTaskTitles($permission) { $tasks = self::taskTitles(); } else { - $tasks = array( - 3 => self::$_tasks[3]['title'], - ); + $tasks = [ + self::TASK_EXPORT => self::$_tasks[self::TASK_EXPORT]['title'], + ]; //CRM-4418, if (CRM_Core_Permission::check('delete in CiviCase')) { - $tasks[1] = self::$_tasks[1]['title']; + $tasks[self::TASK_DELETE] = self::$_tasks[self::TASK_DELETE]['title']; } } + + $tasks = parent::corePermissionedTaskTitles($tasks, $permission, $params); return $tasks; } @@ -170,13 +150,13 @@ public static function getTask($value) { self::tasks(); if (!$value || !CRM_Utils_Array::value($value, self::$_tasks)) { // make the print task by default - $value = 2; + $value = self::TASK_PRINT; } - return array( + return [ self::$_tasks[$value]['class'], self::$_tasks[$value]['result'], - ); + ]; } } diff --git a/CRM/Case/XMLProcessor.php b/CRM/Case/XMLProcessor.php index 286a64c5709d..d30b3cd06a86 100644 --- a/CRM/Case/XMLProcessor.php +++ b/CRM/Case/XMLProcessor.php @@ -1,9 +1,9 @@ $info) { self::$relationshipTypes[$id] = $info[CRM_Case_XMLProcessor::REL_TYPE_CNAME]; } diff --git a/CRM/Case/XMLProcessor/Process.php b/CRM/Case/XMLProcessor/Process.php index 9abc42bf05e4..b83e11cce663 100644 --- a/CRM/Case/XMLProcessor/Process.php +++ b/CRM/Case/XMLProcessor/Process.php @@ -1,9 +1,9 @@ retrieve($caseType); if ($xml === FALSE) { - $docLink = CRM_Utils_System::docURL2("user/case-management/setup"); + $docLink = CRM_Utils_System::docURL2("user/case-management/set-up"); CRM_Core_Error::fatal(ts("Configuration file could not be retrieved for case type = '%1' %2.", - array(1 => $caseType, 2 => $docLink) + [1 => $caseType, 2 => $docLink] )); return FALSE; } @@ -69,9 +71,9 @@ public function run($caseType, &$params) { public function get($caseType, $fieldSet, $isLabel = FALSE, $maskAction = FALSE) { $xml = $this->retrieve($caseType); if ($xml === FALSE) { - $docLink = CRM_Utils_System::docURL2("user/case-management/setup"); + $docLink = CRM_Utils_System::docURL2("user/case-management/set-up"); CRM_Core_Error::fatal(ts("Unable to load configuration file for the referenced case type: '%1' %2.", - array(1 => $caseType, 2 => $docLink) + [1 => $caseType, 2 => $docLink] )); return FALSE; } @@ -97,7 +99,6 @@ public function get($caseType, $fieldSet, $isLabel = FALSE, $maskAction = FALSE) public function process($xml, &$params) { $standardTimeline = CRM_Utils_Array::value('standardTimeline', $params); $activitySetName = CRM_Utils_Array::value('activitySetName', $params); - $activityTypeName = CRM_Utils_Array::value('activityTypeName', $params); if ('Open Case' == CRM_Utils_Array::value('activityTypeName', $params)) { // create relationships for the ones that are required @@ -182,7 +183,7 @@ public function processActivitySet($activitySetXML, &$params) { public function &caseRoles($caseRolesXML, $isCaseManager = FALSE) { $relationshipTypes = &$this->allRelationshipTypes(); - $result = array(); + $result = []; foreach ($caseRolesXML as $caseRoleXML) { foreach ($caseRoleXML->RelationshipType as $relationshipTypeXML) { $relationshipTypeName = (string ) $relationshipTypeXML->name; @@ -196,7 +197,7 @@ public function &caseRoles($caseRolesXML, $isCaseManager = FALSE) { if (!$isCaseManager) { $result[$relationshipTypeID] = $relationshipTypeName; } - elseif ($relationshipTypeXML->manager) { + elseif ($relationshipTypeXML->manager == 1) { return $relationshipTypeID; } } @@ -217,27 +218,28 @@ public function createRelationships($relationshipTypeName, &$params) { $relationshipTypeID = array_search($relationshipTypeName, $relationshipTypes); if ($relationshipTypeID === FALSE) { - $docLink = CRM_Utils_System::docURL2("user/case-management/setup"); + $docLink = CRM_Utils_System::docURL2("user/case-management/set-up"); CRM_Core_Error::fatal(ts('Relationship type %1, found in case configuration file, is not present in the database %2', - array(1 => $relationshipTypeName, 2 => $docLink) + [1 => $relationshipTypeName, 2 => $docLink] )); return FALSE; } $client = $params['clientID']; if (!is_array($client)) { - $client = array($client); + $client = [$client]; } foreach ($client as $key => $clientId) { - $relationshipParams = array( + $relationshipParams = [ 'relationship_type_id' => $relationshipTypeID, 'contact_id_a' => $clientId, 'contact_id_b' => $params['creatorID'], 'is_active' => 1, 'case_id' => $params['caseID'], 'start_date' => date("Ymd"), - ); + 'end_date' => CRM_Utils_Array::value('relationship_end_date', $params), + ]; if (!$this->createRelationship($relationshipParams)) { CRM_Core_Error::fatal(); @@ -272,7 +274,7 @@ public function createRelationship(&$params) { */ public function activityTypes($activityTypesXML, $maxInst = FALSE, $isLabel = FALSE, $maskAction = FALSE) { $activityTypes = &$this->allActivityTypes(TRUE, TRUE); - $result = array(); + $result = []; foreach ($activityTypesXML as $activityTypeXML) { foreach ($activityTypeXML as $recordXML) { $activityTypeName = (string ) $recordXML->name; @@ -313,10 +315,11 @@ public function activityTypes($activityTypesXML, $maxInst = FALSE, $isLabel = FA /** * @param SimpleXMLElement $caseTypeXML + * * @return array symbolic activity-type names */ public function getDeclaredActivityTypes($caseTypeXML) { - $result = array(); + $result = []; if (!empty($caseTypeXML->ActivityTypes) && $caseTypeXML->ActivityTypes->ActivityType) { foreach ($caseTypeXML->ActivityTypes->ActivityType as $activityTypeXML) { @@ -341,10 +344,11 @@ public function getDeclaredActivityTypes($caseTypeXML) { /** * @param SimpleXMLElement $caseTypeXML + * * @return array symbolic relationship-type names */ public function getDeclaredRelationshipTypes($caseTypeXML) { - $result = array(); + $result = []; if (!empty($caseTypeXML->CaseRoles) && $caseTypeXML->CaseRoles->RelationshipType) { foreach ($caseTypeXML->CaseRoles->RelationshipType as $relTypeXML) { @@ -361,7 +365,7 @@ public function getDeclaredRelationshipTypes($caseTypeXML) { * @param array $params */ public function deleteEmptyActivity(&$params) { - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts); $query = " @@ -375,7 +379,7 @@ public function deleteEmptyActivity(&$params) { AND a.is_current_revision = 1 AND ca.case_id = %2 "; - $sqlParams = array(1 => array($params['clientID'], 'Integer'), 2 => array($params['caseID'], 'Integer')); + $sqlParams = [1 => [$params['clientID'], 'Integer'], 2 => [$params['caseID'], 'Integer']]; CRM_Core_DAO::executeQuery($query, $sqlParams); } @@ -394,10 +398,10 @@ public function isActivityPresent(&$params) { AND a.is_deleted = 0 "; - $sqlParams = array( - 1 => array($params['activityTypeID'], 'Integer'), - 2 => array($params['caseID'], 'Integer'), - ); + $sqlParams = [ + 1 => [$params['activityTypeID'], 'Integer'], + 2 => [$params['caseID'], 'Integer'], + ]; $count = CRM_Core_DAO::singleValueQuery($query, $sqlParams); // check for max instance @@ -421,9 +425,9 @@ public function createActivity($activityTypeXML, &$params) { $activityTypeInfo = CRM_Utils_Array::value($activityTypeName, $activityTypes); if (!$activityTypeInfo) { - $docLink = CRM_Utils_System::docURL2("user/case-management/setup"); + $docLink = CRM_Utils_System::docURL2("user/case-management/set-up"); CRM_Core_Error::fatal(ts('Activity type %1, found in case configuration file, is not present in the database %2', - array(1 => $activityTypeName, 2 => $docLink) + [1 => $activityTypeName, 2 => $docLink] )); return FALSE; } @@ -437,12 +441,7 @@ public function createActivity($activityTypeXML, &$params) { $statusName = 'Scheduled'; } - if ($this->_isMultiClient) { - $client = $params['clientID']; - } - else { - $client = array(1 => $params['clientID']); - } + $client = (array) $params['clientID']; //set order $orderVal = ''; @@ -451,39 +450,35 @@ public function createActivity($activityTypeXML, &$params) { } if ($activityTypeName == 'Open Case') { - $activityParams = array( + $activityParams = [ 'activity_type_id' => $activityTypeID, 'source_contact_id' => $params['creatorID'], 'is_auto' => FALSE, 'is_current_revision' => 1, 'subject' => CRM_Utils_Array::value('subject', $params) ? $params['subject'] : $activityTypeName, - 'status_id' => CRM_Core_OptionGroup::getValue('activity_status', - $statusName, - 'name' - ), + 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', $statusName), 'target_contact_id' => $client, 'medium_id' => CRM_Utils_Array::value('medium_id', $params), 'location' => CRM_Utils_Array::value('location', $params), 'details' => CRM_Utils_Array::value('details', $params), 'duration' => CRM_Utils_Array::value('duration', $params), 'weight' => $orderVal, - ); + ]; } else { - $activityParams = array( + $activityParams = [ 'activity_type_id' => $activityTypeID, 'source_contact_id' => $params['creatorID'], 'is_auto' => TRUE, 'is_current_revision' => 1, - 'status_id' => CRM_Core_OptionGroup::getValue('activity_status', - $statusName, - 'name' - ), + 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', $statusName), 'target_contact_id' => $client, 'weight' => $orderVal, - ); + ]; } + $activityParams['assignee_contact_id'] = $this->getDefaultAssigneeForActivity($activityParams, $activityTypeXML); + //parsing date to default preference format $params['activity_date_time'] = CRM_Utils_Date::processDate($params['activity_date_time']); @@ -517,7 +512,7 @@ public function createActivity($activityTypeXML, &$params) { else { $referenceActivityInfo = CRM_Utils_Array::value($referenceActivityName, $activityTypes); if ($referenceActivityInfo['id']) { - $caseActivityParams = array('activity_type_id' => $referenceActivityInfo['id']); + $caseActivityParams = ['activity_type_id' => $referenceActivityInfo['id']]; //if reference_select is set take according activity. if ($referenceSelect = (string) $activityTypeXML->reference_select) { @@ -561,6 +556,7 @@ public function createActivity($activityTypeXML, &$params) { $activityParams['skipRecentView'] = TRUE; } + // @todo - switch to using api & remove the parameter pre-wrangling above. $activity = CRM_Activity_BAO_Activity::create($activityParams); if (!$activity) { @@ -569,21 +565,170 @@ public function createActivity($activityTypeXML, &$params) { } // create case activity record - $caseParams = array( + $caseParams = [ 'activity_id' => $activity->id, 'case_id' => $params['caseID'], - ); + ]; CRM_Case_BAO_Case::processCaseActivity($caseParams); return TRUE; } + /** + * Return the default assignee contact for the activity. + * + * @param array $activityParams + * @param object $activityTypeXML + * + * @return int|null the ID of the default assignee contact or null if none. + */ + protected function getDefaultAssigneeForActivity($activityParams, $activityTypeXML) { + if (!isset($activityTypeXML->default_assignee_type)) { + return NULL; + } + + $defaultAssigneeOptionsValues = $this->getDefaultAssigneeOptionValues(); + + switch ($activityTypeXML->default_assignee_type) { + case $defaultAssigneeOptionsValues['BY_RELATIONSHIP']: + return $this->getDefaultAssigneeByRelationship($activityParams, $activityTypeXML); + + break; + case $defaultAssigneeOptionsValues['SPECIFIC_CONTACT']: + return $this->getDefaultAssigneeBySpecificContact($activityTypeXML); + + break; + case $defaultAssigneeOptionsValues['USER_CREATING_THE_CASE']: + return $activityParams['source_contact_id']; + + break; + case $defaultAssigneeOptionsValues['NONE']: + default: + return NULL; + } + } + + /** + * Fetches and caches the activity's default assignee options. + * + * @return array + */ + protected function getDefaultAssigneeOptionValues() { + if (!empty($this->defaultAssigneeOptionsValues)) { + return $this->defaultAssigneeOptionsValues; + } + + $defaultAssigneeOptions = civicrm_api3('OptionValue', 'get', [ + 'option_group_id' => 'activity_default_assignee', + 'options' => ['limit' => 0], + ]); + + foreach ($defaultAssigneeOptions['values'] as $option) { + $this->defaultAssigneeOptionsValues[$option['name']] = $option['value']; + } + + return $this->defaultAssigneeOptionsValues; + } + + /** + * Returns the default assignee for the activity by searching for the target's + * contact relationship type defined in the activity's details. + * + * @param array $activityParams + * @param object $activityTypeXML + * + * @return int|null the ID of the default assignee contact or null if none. + */ + protected function getDefaultAssigneeByRelationship($activityParams, $activityTypeXML) { + $isDefaultRelationshipDefined = isset($activityTypeXML->default_assignee_relationship) + && preg_match('/\d+_[ab]_[ab]/', $activityTypeXML->default_assignee_relationship); + + if (!$isDefaultRelationshipDefined) { + return NULL; + } + + $targetContactId = is_array($activityParams['target_contact_id']) + ? CRM_Utils_Array::first($activityParams['target_contact_id']) + : $activityParams['target_contact_id']; + list($relTypeId, $a, $b) = explode('_', $activityTypeXML->default_assignee_relationship); + + $params = [ + 'relationship_type_id' => $relTypeId, + "contact_id_$b" => $targetContactId, + 'is_active' => 1, + ]; + + if ($this->isBidirectionalRelationshipType($relTypeId)) { + $params["contact_id_$a"] = $targetContactId; + $params['options']['or'] = [['contact_id_a', 'contact_id_b']]; + } + + $relationships = civicrm_api3('Relationship', 'get', $params); + + if ($relationships['count']) { + $relationship = CRM_Utils_Array::first($relationships['values']); + + // returns the contact id on the other side of the relationship: + return (int) $relationship['contact_id_a'] === (int) $targetContactId + ? $relationship['contact_id_b'] + : $relationship['contact_id_a']; + } + else { + return NULL; + } + } + + /** + * Determines if the given relationship type is bidirectional or not by + * comparing their labels. + * + * @return bool + */ + protected function isBidirectionalRelationshipType($relationshipTypeId) { + $relationshipTypeResult = civicrm_api3('RelationshipType', 'get', [ + 'id' => $relationshipTypeId, + 'options' => ['limit' => 1], + ]); + + if ($relationshipTypeResult['count'] === 0) { + return FALSE; + } + + $relationshipType = CRM_Utils_Array::first($relationshipTypeResult['values']); + + return $relationshipType['label_b_a'] === $relationshipType['label_a_b']; + } + + /** + * Returns the activity's default assignee for a specific contact if the contact exists, + * otherwise returns null. + * + * @param object $activityTypeXML + * + * @return int|null + */ + protected function getDefaultAssigneeBySpecificContact($activityTypeXML) { + if (!$activityTypeXML->default_assignee_contact) { + return NULL; + } + + $contact = civicrm_api3('Contact', 'get', [ + 'id' => $activityTypeXML->default_assignee_contact, + ]); + + if ($contact['count'] == 1) { + return $activityTypeXML->default_assignee_contact; + } + + return NULL; + } + /** * @param $activitySetsXML * * @return array */ public static function activitySets($activitySetsXML) { - $result = array(); + $result = []; foreach ($activitySetsXML as $activitySetXML) { foreach ($activitySetXML as $recordXML) { $activitySetName = (string ) $recordXML->name; @@ -626,11 +771,12 @@ public function getCaseManagerRoleId($caseType) { /** * @param string $caseType + * * @return array<\Civi\CCase\CaseChangeListener> */ public function getListeners($caseType) { $xml = $this->retrieve($caseType); - $listeners = array(); + $listeners = []; if ($xml->Listeners && $xml->Listeners->Listener) { foreach ($xml->Listeners->Listener as $listenerXML) { $class = (string) $listenerXML; @@ -644,8 +790,7 @@ public function getListeners($caseType) { * @return int */ public function getRedactActivityEmail() { - $xml = $this->retrieve("Settings"); - return ( string ) $xml->RedactActivityEmail ? 1 : 0; + return $this->getBoolSetting('civicaseRedactActivityEmail', 'RedactActivityEmail'); } /** @@ -655,11 +800,7 @@ public function getRedactActivityEmail() { * 1 if allowed, 0 if not */ public function getAllowMultipleCaseClients() { - $xml = $this->retrieve("Settings"); - if ($xml) { - return ( string ) $xml->AllowMultipleCaseClients ? 1 : 0; - } - return 0; + return $this->getBoolSetting('civicaseAllowMultipleClients', 'AllowMultipleCaseClients'); } /** @@ -669,8 +810,25 @@ public function getAllowMultipleCaseClients() { * 1 if natural, 0 if alphabetic */ public function getNaturalActivityTypeSort() { - $xml = $this->retrieve("Settings"); - return ( string ) $xml->NaturalActivityTypeSort ? 1 : 0; + return $this->getBoolSetting('civicaseNaturalActivityTypeSort', 'NaturalActivityTypeSort'); + } + + /** + * @param string $settingKey + * @param string $xmlTag + * @param mixed $default + * + * @return int + */ + private function getBoolSetting($settingKey, $xmlTag, $default = 0) { + $setting = Civi::settings()->get($settingKey); + if ($setting !== 'default') { + return (int) $setting; + } + if ($xml = $this->retrieve("Settings")) { + return (string) $xml->{$xmlTag} ? 1 : 0; + } + return $default; } } diff --git a/CRM/Case/XMLProcessor/Report.php b/CRM/Case/XMLProcessor/Report.php index d25141f90430..8224e22c1d6b 100644 --- a/CRM/Case/XMLProcessor/Report.php +++ b/CRM/Case/XMLProcessor/Report.php @@ -1,9 +1,9 @@ $rule) { + 'redactionStringRules', + 'redactionRegexRules', + ) as $key => $rule) { $$rule = CRM_Case_PseudoConstant::redactionRule($key); if (!empty($$rule)) { @@ -129,20 +129,9 @@ public function &caseInfo( $case['subject'] = $dao->subject; $case['start_date'] = $dao->start_date; $case['end_date'] = $dao->end_date; - // FIXME: when we resolve if case_type_is single or multi-select - if (strpos($dao->case_type_id, CRM_Core_DAO::VALUE_SEPARATOR) !== FALSE) { - $caseTypeID = substr($dao->case_type_id, 1, -1); - } - else { - $caseTypeID = $dao->case_type_id; - } - $caseTypeIDs = explode(CRM_Core_DAO::VALUE_SEPARATOR, - $dao->case_type_id - ); - $case['caseType'] = CRM_Case_BAO_Case::getCaseType($caseID); $case['caseTypeName'] = CRM_Case_BAO_Case::getCaseType($caseID, 'name'); - $case['status'] = CRM_Core_OptionGroup::getLabel('case_status', $dao->status_id, FALSE); + $case['status'] = CRM_Core_PseudoConstant::getLabel('CRM_Case_BAO_Case', 'status_id', $dao->status_id); } return $case; } @@ -267,7 +256,7 @@ public function &getActivityInfo($clientID, $activityID, $anyActivity = FALSE, $ $joinCaseActivity = " INNER JOIN civicrm_case_activity ca ON a.id = ca.activity_id "; } - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts); $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts); @@ -285,7 +274,7 @@ public function &getActivityInfo($clientID, $activityID, $anyActivity = FALSE, $ if ($dao->fetch()) { //if activity type is email get info of all activities. - if ($dao->activity_type_id == CRM_Core_OptionGroup::getValue('activity_type', 'Email', 'name')) { + if ($dao->activity_type_id == CRM_Core_PseudoConstant::getKey('CRM_Activity_DAO_Activity', 'activity_type_id', 'Email')) { $anyActivity = TRUE; } $activityTypes = CRM_Case_PseudoConstant::caseActivityType(FALSE, $anyActivity); @@ -353,7 +342,7 @@ public function &getActivity($clientID, $activityDAO, &$activityTypeInfo) { ); } - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts); $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts); if (!empty($activityDAO->targetID)) { @@ -425,7 +414,7 @@ public function &getActivity($clientID, $activityDAO, &$activityTypeInfo) { 'value' => $this->redact($creator), 'type' => 'String', ); - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts); $source_contact_id = CRM_Activity_BAO_Activity::getActivityContact($activityDAO->id, $sourceID); $reporter = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', @@ -477,9 +466,7 @@ public function &getActivity($clientID, $activityDAO, &$activityTypeInfo) { if ($activityDAO->medium_id) { $activity['fields'][] = array( 'label' => ts('Medium'), - 'value' => CRM_Core_OptionGroup::getLabel('encounter_medium', - $activityDAO->medium_id, FALSE - ), + 'value' => CRM_Core_PseudoConstant::getLabel('CRM_Activity_BAO_Activity', 'medium_id', $activityDAO->medium_id), 'type' => 'String', ); } @@ -512,7 +499,7 @@ public function &getActivity($clientID, $activityDAO, &$activityTypeInfo) { } $activity['fields'][] = array( 'label' => ts('Status'), - 'value' => CRM_Core_OptionGroup::getLabel('activity_status', + 'value' => CRM_Core_PseudoConstant::getLabel('CRM_Activity_DAO_Activity', 'activity_status_id', $activityDAO->status_id ), 'type' => 'String', @@ -520,7 +507,7 @@ public function &getActivity($clientID, $activityDAO, &$activityTypeInfo) { $activity['fields'][] = array( 'label' => ts('Priority'), - 'value' => CRM_Core_OptionGroup::getLabel('priority', + 'value' => CRM_Core_PseudoConstant::getLabel('CRM_Activity_DAO_Activity', 'priority_id', $activityDAO->priority_id ), 'type' => 'String', @@ -558,7 +545,7 @@ public function getCustomData($clientID, $activityDAO, &$activityTypeInfo) { $value = $dao->$columnName; } else { - $value = CRM_Core_BAO_CustomField::displayValue($dao->$columnName, $typeValue['fieldID']); + $value = CRM_Core_BAO_CustomField::displayValue($dao->$columnName, $typeValue['fieldID'], $activityDAO->id); } if ($value) { @@ -572,10 +559,6 @@ public function getCustomData($clientID, $activityDAO, &$activityTypeInfo) { ) { $value = $this->redact($value); } - elseif (CRM_Utils_Array::value('type', $typeValue) == 'File') { - $tableName = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_EntityFile', $typeValue, 'entity_table'); - $value = CRM_Core_BAO_File::attachmentInfo($tableName, $activityDAO->id); - } elseif (CRM_Utils_Array::value('type', $typeValue) == 'Link') { $value = CRM_Utils_System::formatWikiURL($value); } @@ -598,10 +581,11 @@ public function getCustomData($clientID, $activityDAO, &$activityTypeInfo) { /** * @param int $activityTypeID * @param null $dateFormat + * @param bool $onlyActive * * @return mixed */ - public function getActivityTypeCustomSQL($activityTypeID, $dateFormat = NULL) { + public function getActivityTypeCustomSQL($activityTypeID, $dateFormat = NULL, $onlyActive = TRUE) { static $cache = array(); if (is_null($activityTypeID)) { @@ -625,6 +609,9 @@ public function getActivityTypeCustomSQL($activityTypeID, $dateFormat = NULL) { AND cg.extends = 'Activity' AND " . CRM_Core_Permission::customGroupClause(CRM_Core_Permission::VIEW, 'cg.'); + if ($onlyActive) { + $query .= " AND cf.is_active = 1 "; + } if ($activityTypeID) { $query .= "AND ( cg.extends_entity_column_value IS NULL OR cg.extends_entity_column_value LIKE '%" . CRM_Core_DAO::VALUE_SEPARATOR . "%1" . CRM_Core_DAO::VALUE_SEPARATOR . "%' )"; } @@ -666,10 +653,10 @@ public function getActivityTypeCustomSQL($activityTypeID, $dateFormat = NULL) { $query = " SELECT label, value FROM civicrm_option_value - WHERE option_group_id = {$dao->optionGroupID} + WHERE option_group_id = %1 "; - $option = CRM_Core_DAO::executeQuery($query); + $option = CRM_Core_DAO::executeQuery($query, array(1 => array($dao->optionGroupID, 'Positive'))); while ($option->fetch()) { $dataType = $dao->dataType; if ($dataType == 'Int' || $dataType == 'Float') { @@ -688,9 +675,11 @@ public function getActivityTypeCustomSQL($activityTypeID, $dateFormat = NULL) { foreach ($sql as $tableName => $values) { $columnNames = implode(',', $values); + $title = CRM_Core_DAO::escapeString($groupTitle[$tableName]); + $mysqlTableName = CRM_Utils_Type::escape($tableName, 'MysqlColumnNameOrAlias'); $sql[$tableName] = " -SELECT '{$groupTitle[$tableName]}' as groupTitle, $columnNames -FROM $tableName +SELECT '" . $title . "' as groupTitle, $columnNames +FROM $mysqlTableName WHERE entity_id = %1 "; } @@ -811,11 +800,11 @@ public static function getCaseReport($clientID, $caseID, $activitySetName, $para } public static function printCaseReport() { - $caseID = CRM_Utils_Request::retrieve('caseID', 'Positive', CRM_Core_DAO::$_nullObject); - $clientID = CRM_Utils_Request::retrieve('cid', 'Positive', CRM_Core_DAO::$_nullObject); - $activitySetName = CRM_Utils_Request::retrieve('asn', 'String', CRM_Core_DAO::$_nullObject); - $isRedact = CRM_Utils_Request::retrieve('redact', 'Boolean', CRM_Core_DAO::$_nullObject); - $includeActivities = CRM_Utils_Request::retrieve('all', 'Positive', CRM_Core_DAO::$_nullObject); + $caseID = CRM_Utils_Request::retrieve('caseID', 'Positive'); + $clientID = CRM_Utils_Request::retrieve('cid', 'Positive'); + $activitySetName = CRM_Utils_Request::retrieve('asn', 'String'); + $isRedact = CRM_Utils_Request::retrieve('redact', 'Boolean'); + $includeActivities = CRM_Utils_Request::retrieve('all', 'Positive'); $params = $otherRelationships = $globalGroupInfo = array(); $report = new CRM_Case_XMLProcessor_Report($isRedact); if ($includeActivities) { @@ -977,8 +966,8 @@ public static function printCaseReport() { $extends = array('case'); $groupTree = CRM_Core_BAO_CustomGroup::getGroupDetail(NULL, NULL, $extends); $caseCustomFields = array(); - while (list($gid, $group_values) = each($groupTree)) { - while (list($id, $field_values) = each($group_values['fields'])) { + foreach ($groupTree as $gid => $group_values) { + foreach ($group_values['fields'] as $id => $field_values) { if (array_key_exists($id, $customValues)) { $caseCustomFields[$gid]['title'] = $group_values['title']; $caseCustomFields[$gid]['values'][$id] = array( diff --git a/CRM/Case/XMLProcessor/Settings.php b/CRM/Case/XMLProcessor/Settings.php index 368e4e32b10a..dbe1dedb432d 100644 --- a/CRM/Case/XMLProcessor/Settings.php +++ b/CRM/Case/XMLProcessor/Settings.php @@ -1,9 +1,9 @@ */ - protected $xml = array(); + protected $xml = []; /** * @var array|NULL @@ -63,13 +63,20 @@ public static function singleton($fresh = FALSE) { return self::$singleton; } + public function flush() { + $this->xml = []; + $this->hookCache = NULL; + $this->allCaseTypes = NULL; + CRM_Core_DAO::$_dbColumnValueCache = []; + } + /** * Class constructor. * * @param array $allCaseTypes * @param array $xml */ - public function __construct($allCaseTypes = NULL, $xml = array()) { + public function __construct($allCaseTypes = NULL, $xml = []) { $this->allCaseTypes = $allCaseTypes; $this->xml = $xml; } @@ -136,7 +143,9 @@ public function retrieveFile($caseType) { if ($fileName && file_exists($fileName)) { // read xml file $dom = new DomDocument(); - $dom->load($fileName); + $xmlString = file_get_contents($fileName); + $dom->loadXML($xmlString); + $dom->documentURI = $fileName; $dom->xinclude(); $fileXml = simplexml_import_dom($dom); } @@ -167,14 +176,14 @@ public function findXmlFile($caseType) { if (isset($config->customTemplateDir) && $config->customTemplateDir) { // check if the file exists in the custom templates directory $fileName = implode(DIRECTORY_SEPARATOR, - array( + [ $config->customTemplateDir, 'CRM', 'Case', 'xml', 'configuration', "$caseType.xml", - ) + ] ); } } @@ -183,24 +192,24 @@ public function findXmlFile($caseType) { if (!file_exists($fileName)) { // check if file exists locally $fileName = implode(DIRECTORY_SEPARATOR, - array( + [ dirname(__FILE__), 'xml', 'configuration', "$caseType.xml", - ) + ] ); } if (!file_exists($fileName)) { // check if file exists locally $fileName = implode(DIRECTORY_SEPARATOR, - array( + [ dirname(__FILE__), 'xml', 'configuration.sample', "$caseType.xml", - ) + ] ); } } @@ -213,7 +222,7 @@ public function findXmlFile($caseType) { */ public function getCaseTypesViaHook() { if ($this->hookCache === NULL) { - $this->hookCache = array(); + $this->hookCache = []; CRM_Utils_Hook::caseTypes($this->hookCache); } return $this->hookCache; @@ -233,7 +242,7 @@ public function getAllCaseTypes() { * @return array symbolic-names of activity-types */ public function getAllDeclaredActivityTypes() { - $result = array(); + $result = []; $p = new CRM_Case_XMLProcessor_Process(); foreach ($this->getAllCaseTypes() as $caseTypeName) { @@ -250,7 +259,7 @@ public function getAllDeclaredActivityTypes() { * @return array symbolic-names of relationship-types */ public function getAllDeclaredRelationshipTypes() { - $result = array(); + $result = []; $p = new CRM_Case_XMLProcessor_Process(); foreach ($this->getAllCaseTypes() as $caseTypeName) { diff --git a/CRM/Case/xml/Menu/Case.xml b/CRM/Case/xml/Menu/Case.xml index 2d270503cc80..91d5ceb984d2 100644 --- a/CRM/Case/xml/Menu/Case.xml +++ b/CRM/Case/xml/Menu/Case.xml @@ -64,6 +64,14 @@ Case Details CRM_Case_Page_CaseDetails + + civicrm/admin/setting/case + CiviCase Settings + CRM_Admin_Form_Setting_Case + CiviCase + admin/small/36.png + 380 + civicrm/admin/options/case_type Case Types @@ -128,4 +136,8 @@ civicrm/ajax/delcaserole CRM_Case_Page_AJAX::deleteCaseRoles + + civicrm/ajax/get-cases + CRM_Case_Page_AJAX::getCases + diff --git a/CRM/Case/xml/configuration.sample/Settings.xml b/CRM/Case/xml/configuration.sample/Settings.xml index 58dbb7cff983..424eb7afdffe 100644 --- a/CRM/Case/xml/configuration.sample/Settings.xml +++ b/CRM/Case/xml/configuration.sample/Settings.xml @@ -1,16 +1,30 @@ + + + - 0 + + 0 + + 0 + + 0 + diff --git a/CRM/Contact/ActionMapping.php b/CRM/Contact/ActionMapping.php index 69a7dd3ca10b..2f5f7b28d7de 100644 --- a/CRM/Contact/ActionMapping.php +++ b/CRM/Contact/ActionMapping.php @@ -1,9 +1,9 @@ register(CRM_Contact_ActionMapping::create(array( + $registrations->register(CRM_Contact_ActionMapping::create([ 'id' => CRM_Contact_ActionMapping::CONTACT_MAPPING_ID, 'entity' => 'civicrm_contact', 'entity_label' => ts('Contact'), @@ -60,14 +59,14 @@ public static function onRegisterActionMappings(\Civi\ActionSchedule\Event\Mappi 'entity_status' => 'contact_date_reminder_options', 'entity_status_label' => ts('Annual Options'), 'entity_date_start' => 'date_field', - ))); + ])); } - private $contactDateFields = array( + private $contactDateFields = [ 'birth_date', 'created_date', 'modified_date', - ); + ]; /** * Determine whether a schedule based on this mapping is sufficiently @@ -79,7 +78,7 @@ public static function onRegisterActionMappings(\Civi\ActionSchedule\Event\Mappi * List of error messages. */ public function validateSchedule($schedule) { - $errors = array(); + $errors = []; if (CRM_Utils_System::isNull($schedule->entity_value) || $schedule->entity_value === '0') { $errors['entity'] = ts('Please select a specific date field.'); } @@ -119,30 +118,31 @@ public function createQuery($schedule, $phase, $defaultParams) { elseif (in_array($selectedValues[0], $this->contactDateFields)) { $dateDBField = $selectedValues[0]; $query = \CRM_Utils_SQL_Select::from("{$this->entity} e")->param($defaultParams); - $query->param(array( + $query->param([ 'casAddlCheckFrom' => 'civicrm_contact e', 'casContactIdField' => 'e.id', 'casEntityIdField' => 'e.id', 'casContactTableAlias' => 'e', - )); + ]); $query->where('e.is_deleted = 0 AND e.is_deceased = 0'); } else { //custom field - $customFieldParams = array('id' => substr($selectedValues[0], 7)); - $customGroup = $customField = array(); + $customFieldParams = ['id' => substr($selectedValues[0], 7)]; + $customGroup = $customField = []; \CRM_Core_BAO_CustomField::retrieve($customFieldParams, $customField); $dateDBField = $customField['column_name']; - $customGroupParams = array('id' => $customField['custom_group_id'], $customGroup); + $customGroupParams = ['id' => $customField['custom_group_id'], $customGroup]; \CRM_Core_BAO_CustomGroup::retrieve($customGroupParams, $customGroup); $query = \CRM_Utils_SQL_Select::from("{$customGroup['table_name']} e")->param($defaultParams); - $query->param(array( + $query->param([ 'casAddlCheckFrom' => "{$customGroup['table_name']} e", 'casContactIdField' => 'e.entity_id', 'casEntityIdField' => 'e.id', 'casContactTableAlias' => NULL, - )); - $query->where('1'); // possible to have no "where" in this case + ]); + // possible to have no "where" in this case + $query->where('1'); } $query['casDateField'] = 'e.' . $dateDBField; diff --git a/CRM/Contact/BAO/Contact.php b/CRM/Contact/BAO/Contact.php index a38a8272ef72..f1fb6929d82e 100644 --- a/CRM/Contact/BAO/Contact.php +++ b/CRM/Contact/BAO/Contact.php @@ -1,9 +1,9 @@ preferred_communication_method = CRM_Utils_Array::implodePadded($params['preferred_communication_method']); unset($params['preferred_communication_method']); - - CRM_Utils_Array::formatArrayKeys($prefComm); - $prefComm = CRM_Utils_Array::implodePadded($prefComm); } - $contact->preferred_communication_method = $prefComm; + $defaults = ['source' => CRM_Utils_Array::value('contact_source', $params)]; + if ($params['contact_type'] === 'Organization' && isset($params['organization_name'])) { + $defaults['display_name'] = $params['organization_name']; + $defaults['sort_name'] = $params['organization_name']; + } + if ($params['contact_type'] === 'Household' && isset($params['household_name'])) { + $defaults['display_name'] = $params['household_name']; + $defaults['sort_name'] = $params['household_name']; + } + $params = array_merge($defaults, $params); $allNull = $contact->copyValues($params); @@ -168,21 +169,17 @@ public static function add(&$params) { if ($contact->contact_type == 'Individual') { $allNull = FALSE; - + // @todo allow the lines below to be overridden by input or hooks & add tests, + // as has been done for households and organizations. // Format individual fields. CRM_Contact_BAO_Individual::format($params, $contact); } - elseif ($contact->contact_type == 'Household') { - if (isset($params['household_name'])) { - $allNull = FALSE; - $contact->display_name = $contact->sort_name = CRM_Utils_Array::value('household_name', $params, ''); - } + + if (strlen($contact->display_name) > 128) { + $contact->display_name = substr($contact->display_name, 0, 128); } - elseif ($contact->contact_type == 'Organization') { - if (isset($params['organization_name'])) { - $allNull = FALSE; - $contact->display_name = $contact->sort_name = CRM_Utils_Array::value('organization_name', $params, ''); - } + if (strlen($contact->sort_name) > 128) { + $contact->sort_name = substr($contact->sort_name, 0, 128); } $privacy = CRM_Utils_Array::value('privacy', $params); @@ -286,7 +283,7 @@ public static function &create(&$params, $fixAddress = TRUE, $invokeHooks = TRUE } } - $config = CRM_Core_Config::singleton(); + self::ensureGreetingParamsAreSet($params); // CRM-6942: set preferred language to the current language if it’s unset (and we’re creating a contact). if (empty($params['contact_id'])) { @@ -295,15 +292,10 @@ public static function &create(&$params, $fixAddress = TRUE, $invokeHooks = TRUE $params['preferred_language'] = $language; } - // CRM-9739: set greeting & addressee if unset and we’re creating a contact. - foreach (self::$_greetingTypes as $greeting) { - if (empty($params[$greeting . '_id'])) { - if ($defaultGreetingTypeId - = CRM_Contact_BAO_Contact_Utils::defaultGreeting($params['contact_type'], $greeting) - ) { - $params[$greeting . '_id'] = $defaultGreetingTypeId; - } - } + // CRM-21041: set default 'Communication Style' if unset when creating a contact. + if (empty($params['communication_style_id'])) { + $defaultCommunicationStyleId = CRM_Core_OptionGroup::values('communication_style', TRUE, NULL, NULL, 'AND is_default = 1'); + $params['communication_style_id'] = array_pop($defaultCommunicationStyleId); } } @@ -353,12 +345,11 @@ public static function &create(&$params, $fixAddress = TRUE, $invokeHooks = TRUE $skipDelete = TRUE; } - //add website - CRM_Core_BAO_Website::create($params['website'], $contact->id, $skipDelete); + if (isset($params['website'])) { + CRM_Core_BAO_Website::process($params['website'], $contact->id, $skipDelete); + } - //get userID from session - $session = CRM_Core_Session::singleton(); - $userID = $session->get('userID'); + $userID = CRM_Core_Session::singleton()->get('userID'); // add notes if (!empty($params['note'])) { if (is_array($params['note'])) { @@ -384,9 +375,6 @@ public static function &create(&$params, $fixAddress = TRUE, $invokeHooks = TRUE } else { $contactId = $contact->id; - if (isset($note['contact_id'])) { - $contactId = $note['contact_id']; - } //if logged in user, overwrite contactId if ($userID) { $contactId = $userID; @@ -432,13 +420,7 @@ public static function &create(&$params, $fixAddress = TRUE, $invokeHooks = TRUE 'name' ); - if (!$config->doNotResetCache) { - // Note: doNotResetCache flag is currently set by import contact process and merging, - // since resetting and - // rebuilding cache could be expensive (for many contacts). We might come out with better - // approach in future. - CRM_Contact_BAO_Contact_Utils::clearContactCaches($contact->id); - } + CRM_Contact_BAO_Contact_Utils::clearContactCaches(); if ($invokeHooks) { if ($isEdit) { @@ -449,12 +431,64 @@ public static function &create(&$params, $fixAddress = TRUE, $invokeHooks = TRUE } } - // process greetings CRM-4575, cache greetings - self::processGreetings($contact); + // In order to prevent a series of expensive queries in intensive batch processing + // api calls may pass in skip_greeting_processing, probably doing it later via the + // scheduled job. CRM-21551 + if (empty($params['skip_greeting_processing'])) { + self::processGreetings($contact); + } return $contact; } + /** + * Ensure greeting parameters are set. + * + * By always populating greetings here we can be sure they are set if required & avoid a call later. + * (ie. knowing we have definitely tried disambiguates between NULL & not loaded.) + * + * @param array $params + */ + public static function ensureGreetingParamsAreSet(&$params) { + $allGreetingParams = array('addressee' => 'addressee_id', 'postal_greeting' => 'postal_greeting_id', 'email_greeting' => 'email_greeting_id'); + $missingGreetingParams = array(); + + foreach ($allGreetingParams as $greetingIndex => $greetingParam) { + if (empty($params[$greetingParam])) { + $missingGreetingParams[$greetingIndex] = $greetingParam; + } + } + + if (!empty($params['contact_id']) && !empty($missingGreetingParams)) { + $savedGreetings = civicrm_api3('Contact', 'getsingle', array( + 'id' => $params['contact_id'], + 'return' => array_keys($missingGreetingParams), + ) + ); + + foreach (array_keys($missingGreetingParams) as $missingGreetingParam) { + if (!empty($savedGreetings[$missingGreetingParam . '_custom'])) { + $missingGreetingParams[$missingGreetingParam . '_custom'] = $missingGreetingParam . '_custom'; + } + } + // Filter out other fields. + $savedGreetings = array_intersect_key($savedGreetings, array_flip($missingGreetingParams)); + $params = array_merge($params, $savedGreetings); + } + else { + foreach ($missingGreetingParams as $greetingName => $greeting) { + $params[$greeting] = CRM_Contact_BAO_Contact_Utils::defaultGreeting($params['contact_type'], $greetingName); + } + } + + foreach ($allGreetingParams as $greetingIndex => $greetingParam) { + if ($params[$greetingParam] === 'null') { + // If we are setting it to null then null out the display field. + $params[$greetingIndex . '_display'] = 'null'; + } + } + } + /** * Get the display name and image of a contact. * @@ -536,6 +570,103 @@ public static function addBillingNameFieldsIfOtherwiseNotSet(&$params) { } + /** + * Resolve a state province string (UT or Utah) to an ID. + * + * If country has been passed in we should select a state belonging to that country. + * + * Alternatively we should choose from enabled countries, prioritising the default country. + * + * @param array $values + * @param int|NULL $countryID + * + * @return int|null + */ + protected static function resolveStateProvinceID($values, $countryID) { + + if ($countryID) { + $stateProvinceList = CRM_Core_PseudoConstant::stateProvinceForCountry($countryID); + if (CRM_Utils_Array::lookupValue($values, + 'state_province', + $stateProvinceList, + TRUE + )) { + return $values['state_province_id']; + } + $stateProvinceList = CRM_Core_PseudoConstant::stateProvinceForCountry($countryID, 'abbreviation'); + if (CRM_Utils_Array::lookupValue($values, + 'state_province', + $stateProvinceList, + TRUE + )) { + return $values['state_province_id']; + } + return NULL; + } + else { + // The underlying lookupValue function needs some de-fanging. Until that has been unravelled we + // continue to resolve stateprovince lists in descending order of preference & just 'keep trying'. + // prefer matching country.. + $stateProvinceList = CRM_Core_BAO_Address::buildOptions('state_province_id', NULL, array('country_id' => Civi::settings()->get('defaultContactCountry'))); + if (CRM_Utils_Array::lookupValue($values, + 'state_province', + $stateProvinceList, + TRUE + )) { + return $values['state_province_id']; + } + + $stateProvinceList = CRM_Core_PseudoConstant::stateProvince(); + if (CRM_Utils_Array::lookupValue($values, + 'state_province', + $stateProvinceList, + TRUE + )) { + return $values['state_province_id']; + } + + $stateProvinceList = CRM_Core_PseudoConstant::stateProvinceAbbreviationForDefaultCountry(); + if (CRM_Utils_Array::lookupValue($values, + 'state_province', + $stateProvinceList, + TRUE + )) { + return $values['state_province_id']; + } + $stateProvinceList = CRM_Core_PseudoConstant::stateProvinceAbbreviation(); + if (CRM_Utils_Array::lookupValue($values, + 'state_province', + $stateProvinceList, + TRUE + )) { + return $values['state_province_id']; + } + } + + return NULL; + } + + /** + * Get the relevant location entity for the array key. + * + * Based on the field name we determine which location entity + * we are dealing with. Apart from a few specific ones they + * are mostly 'address' (the default). + * + * @param string $fieldName + * + * @return string + */ + protected static function getLocationEntityForKey($fieldName) { + if (in_array($fieldName, ['email', 'phone', 'im', 'openid'])) { + return $fieldName; + } + if ($fieldName === 'phone_ext') { + return 'phone'; + } + return 'address'; + } + /** * Create last viewed link to recently updated contact. * @@ -580,10 +711,15 @@ public function createDefaultCrudLink($crudLinkSpec) { /** * Get the values for pseudoconstants for name->value and reverse. * + * @deprecated + * + * This is called specifically from the contact import parser & should be moved there + * as it is not truly a generic function. + * * @param array $defaults * (reference) the default values, some of which need to be resolved. * @param bool $reverse - * True if we want to resolve the values in the reverse direction (value -> name). + * Always true as this function is only called from one place.. */ public static function resolveDefaults(&$defaults, $reverse = FALSE) { // Hack for birth_date. @@ -639,36 +775,9 @@ public static function resolveDefaults(&$defaults, $reverse = FALSE) { $reverse ); } - - // CRM-7597 - // if we find a country id above, we need to restrict it to that country - // rather than the list of all countries - - if (!empty($values['country_id'])) { - $stateProvinceList = CRM_Core_PseudoConstant::stateProvinceForCountry($values['country_id']); - } - else { - $stateProvinceList = CRM_Core_PseudoConstant::stateProvince(); - } - if (!CRM_Utils_Array::lookupValue($values, - 'state_province', - $stateProvinceList, - $reverse - ) && - $reverse - ) { - - if (!empty($values['country_id'])) { - $stateProvinceList = CRM_Core_PseudoConstant::stateProvinceForCountry($values['country_id'], 'abbreviation'); - } - else { - $stateProvinceList = CRM_Core_PseudoConstant::stateProvinceAbbreviation(); - } - CRM_Utils_Array::lookupValue($values, - 'state_province', - $stateProvinceList, - $reverse - ); + $stateProvinceID = self::resolveStateProvinceID($values, CRM_Utils_Array::value('country_id', $values)); + if ($stateProvinceID) { + $values['state_province_id'] = $stateProvinceID; } if (!empty($values['state_province_id'])) { @@ -785,11 +894,12 @@ public static function displayName($id) { * Whether to actually restore, not delete. * @param bool $skipUndelete * Whether to force contact delete or not. + * @param bool $checkPermissions * * @return bool * Was contact deleted? */ - public static function deleteContact($id, $restore = FALSE, $skipUndelete = FALSE) { + public static function deleteContact($id, $restore = FALSE, $skipUndelete = FALSE, $checkPermissions = TRUE) { if (!$id) { return FALSE; @@ -801,8 +911,8 @@ public static function deleteContact($id, $restore = FALSE, $skipUndelete = FALS // make sure we have edit permission for this contact // before we delete - if (($skipUndelete && !CRM_Core_Permission::check('delete contacts')) || - ($restore && !CRM_Core_Permission::check('access deleted contacts')) + if ($checkPermissions && (($skipUndelete && !CRM_Core_Permission::check('delete contacts')) || + ($restore && !CRM_Core_Permission::check('access deleted contacts'))) ) { return FALSE; } @@ -897,7 +1007,10 @@ public static function deleteContact($id, $restore = FALSE, $skipUndelete = FALS CRM_Utils_Recent::delContact($id); self::updateContactCache($id, empty($restore)); - // delete any dupe cache entry + // delete any prevnext/dupe cache entry + // These two calls are redundant in default deployments, but they're + // meaningful if "prevnext" is memory-backed. + Civi::service('prevnext')->deleteItem($id); CRM_Core_BAO_PrevNextCache::deleteItem($id); $transaction->commit(); @@ -906,10 +1019,6 @@ public static function deleteContact($id, $restore = FALSE, $skipUndelete = FALS CRM_Utils_Hook::post('delete', $contactType, $contact->id, $contact); } - // also reset the DB_DO global array so we can reuse the memory - // http://issues.civicrm.org/jira/browse/CRM-4387 - CRM_Core_DAO::freeResult(); - return TRUE; } @@ -952,11 +1061,12 @@ public static function deleteContactImage($id) { if (!$id) { return FALSE; } - $query = " -UPDATE civicrm_contact -SET image_URL=NULL -WHERE id={$id}; "; - CRM_Core_DAO::executeQuery($query); + + $contact = new self(); + $contact->id = $id; + $contact->image_URL = 'null'; + $contact->save(); + return TRUE; } @@ -1033,7 +1143,7 @@ public static function processImageParams( $statusMsg = ts('Image could not be uploaded due to invalid type extension.'); } if ($opType == 'status') { - CRM_Core_Session::setStatus($statusMsg, 'Sorry', 'error'); + CRM_Core_Session::setStatus($statusMsg, ts('Error'), 'error'); } // FIXME: additional support for fatal, bounce etc could be added. return FALSE; @@ -1088,6 +1198,7 @@ public static function contactTrashRestore($contact, $restore = FALSE) { $contact->copyValues($updateParams); $contact->save(); + CRM_Core_BAO_Log::register($contact->id, 'civicrm_contact', $contact->id); CRM_Utils_Hook::post('update', $contact->contact_type, $contact->id, $contact); @@ -1332,7 +1443,7 @@ public static function importableFields( 'name' => 'tag', ), 'note' => array( - 'title' => ts('Note(s)'), + 'title' => ts('Note'), 'name' => 'note', ), 'communication_style_id' => array( @@ -1379,7 +1490,7 @@ public static function importableFields( * True when used during search, might conflict with export param?. * * @param bool $withMultiRecord - * + * @param bool $checkPermissions * @return array * array of exportable Fields */ @@ -1414,8 +1525,7 @@ public static function &exportableFields($contactType = 'Individual', $status = 'Household', 'Organization', 'All', - ) - )) { + ))) { $fields = array_merge($fields, CRM_Core_OptionValue::getFields('', $contactType)); } // add current employer for individuals @@ -1481,10 +1591,10 @@ public static function &exportableFields($contactType = 'Individual', $status = } else { foreach (array( - 'Individual', - 'Household', - 'Organization', - ) as $type) { + 'Individual', + 'Household', + 'Organization', + ) as $type) { $fields = array_merge($fields, CRM_Core_BAO_CustomField::getFieldsForImport($type, FALSE, FALSE, $search, $checkPermissions, $withMultiRecord) ); @@ -1916,8 +2026,10 @@ public static function createProfileContact( CRM_Contact_BAO_GroupContact::create($params['group'], $contactID, $visibility, $method); } - if (!empty($fields['tag'])) { - CRM_Core_BAO_EntityTag::create($params['tag'], 'civicrm_contact', $contactID); + if (!empty($fields['tag']) && array_key_exists('tag', $params)) { + // Convert comma separated form values from select2 v3 + $tags = is_array($params['tag']) ? $params['tag'] : array_fill_keys(array_filter(explode(',', $params['tag'])), 1); + CRM_Core_BAO_EntityTag::create($tags, 'civicrm_contact', $contactID); } //to add profile in default group @@ -2006,9 +2118,17 @@ public static function formatProfileContactParams( ) { // if profile was used, and had any subtype, we obtain it from there //CRM-13596 - add to existing contact types, rather than overwriting - $data_contact_sub_type_arr = CRM_Utils_Array::explodePadded($data['contact_sub_type']); - if (!in_array($params['contact_sub_type_hidden'], $data_contact_sub_type_arr)) { - $data['contact_sub_type'] .= CRM_Utils_Array::implodePadded($params['contact_sub_type_hidden']); + if (empty($data['contact_sub_type'])) { + // If we don't have a contact ID the $data['contact_sub_type'] will not be defined... + $data['contact_sub_type'] = CRM_Utils_Array::implodePadded($params['contact_sub_type_hidden']); + } + else { + $data_contact_sub_type_arr = CRM_Utils_Array::explodePadded($data['contact_sub_type']); + if (!in_array($params['contact_sub_type_hidden'], $data_contact_sub_type_arr)) { + //CRM-20517 - make sure contact_sub_type gets the correct delimiters + $data['contact_sub_type'] = trim($data['contact_sub_type'], CRM_Core_DAO::VALUE_SEPARATOR); + $data['contact_sub_type'] = CRM_Core_DAO::VALUE_SEPARATOR . $data['contact_sub_type'] . CRM_Utils_Array::implodePadded($params['contact_sub_type_hidden']); + } } } @@ -2083,7 +2203,7 @@ public static function formatProfileContactParams( $loc = CRM_Utils_Array::key($index, $locationType); - $blockName = in_array($fieldName, $blocks) ? $fieldName : 'address'; + $blockName = self::getLocationEntityForKey($fieldName); $data[$blockName][$loc]['location_type_id'] = $locTypeId; @@ -2121,9 +2241,6 @@ public static function formatProfileContactParams( unset($data['phone'][$loc]['is_primary']); } } - elseif ($fieldName == 'phone_ext') { - $data['phone'][$loc]['phone_ext'] = $value; - } elseif ($fieldName == 'email') { $data['email'][$loc]['email'] = $value; if (empty($contactID)) { @@ -2180,7 +2297,7 @@ public static function formatProfileContactParams( $data['address'][$loc][substr($fieldName, 8)] = $value; } else { - $data['address'][$loc][$fieldName] = $value; + $data[$blockName][$loc][$fieldName] = $value; } } } @@ -2270,20 +2387,18 @@ public static function formatProfileContactParams( } } } - elseif (in_array($key, - array( - 'nick_name', - 'job_title', - 'middle_name', - 'birth_date', - 'gender_id', - 'current_employer', - 'prefix_id', - 'suffix_id', - )) && - ($value == '' || !isset($value)) && - ($session->get('authSrc') & (CRM_Core_Permission::AUTH_SRC_CHECKSUM + CRM_Core_Permission::AUTH_SRC_LOGIN)) == 0 || - ($key == 'current_employer' && empty($params['current_employer']))) { + elseif (in_array($key, array( + 'nick_name', + 'job_title', + 'middle_name', + 'birth_date', + 'gender_id', + 'current_employer', + 'prefix_id', + 'suffix_id', + )) && ($value == '' || !isset($value)) && + ($session->get('authSrc') & (CRM_Core_Permission::AUTH_SRC_CHECKSUM + CRM_Core_Permission::AUTH_SRC_LOGIN)) == 0 || + ($key == 'current_employer' && empty($params['current_employer']))) { // CRM-10128: if auth source is not checksum / login && $value is blank, do not fill $data with empty value // to avoid update with empty values continue; @@ -2330,10 +2445,10 @@ public static function formatProfileContactParams( * @param string $ctype * Contact type. * - * @return object + * @return object|null * $dao contact details */ - public static function &matchContactOnEmail($mail, $ctype = NULL) { + public static function matchContactOnEmail($mail, $ctype = NULL) { $strtolower = function_exists('mb_strtolower') ? 'mb_strtolower' : 'strtolower'; $mail = $strtolower(trim($mail)); $query = " @@ -2370,7 +2485,7 @@ public static function &matchContactOnEmail($mail, $ctype = NULL) { if ($dao->fetch()) { return $dao; } - return CRM_Core_DAO::$_nullObject; + return NULL; } /** @@ -2381,10 +2496,10 @@ public static function &matchContactOnEmail($mail, $ctype = NULL) { * @param string $ctype * Contact type. * - * @return object + * @return object|null * $dao contact details */ - public static function &matchContactOnOpenId($openId, $ctype = NULL) { + public static function matchContactOnOpenId($openId, $ctype = NULL) { $strtolower = function_exists('mb_strtolower') ? 'mb_strtolower' : 'strtolower'; $openId = $strtolower(trim($openId)); $query = " @@ -2409,7 +2524,7 @@ public static function &matchContactOnOpenId($openId, $ctype = NULL) { if ($dao->fetch()) { return $dao; } - return CRM_Core_DAO::$_nullObject; + return NULL; } /** @@ -2436,7 +2551,6 @@ public static function getPrimaryEmail($contactID) { if ($dao->fetch()) { $email = $dao->email; } - $dao->free(); return $email; } @@ -2464,7 +2578,6 @@ public static function getPrimaryOpenId($contactID) { if ($dao->fetch()) { $openId = $dao->openid; } - $dao->free(); return $openId; } @@ -2516,6 +2629,7 @@ public static function getValues(&$params, &$values) { ), ); + // @todo This can be figured out from metadata & we can avoid the uncached query. CRM_Core_OptionGroup::lookupValues($temp, $names, FALSE); $values['preferred_communication_method'] = $preffComm; @@ -2539,14 +2653,6 @@ public static function getValues(&$params, &$values) { $values['age']['y'] = CRM_Utils_Array::value('years', $age); $values['age']['m'] = CRM_Utils_Array::value('months', $age); } - - list($values['birth_date']) = CRM_Utils_Date::setDateDefaults($contact->birth_date, 'birth'); - $values['birth_date_display'] = $contact->birth_date; - } - - if ($contact->deceased_date) { - list($values['deceased_date']) = CRM_Utils_Date::setDateDefaults($contact->deceased_date, 'birth'); - $values['deceased_date_display'] = $contact->deceased_date; } $contact->contact_id = $contact->id; @@ -2578,11 +2684,11 @@ public static function getCountComponent($component, $contactId, $tableName = NU case 'rel': $result = CRM_Contact_BAO_Relationship::getRelationship($contactId, CRM_Contact_BAO_Relationship::CURRENT, - 0, 0, 0, + 0, 1, 0, NULL, NULL, TRUE ); - return count($result); + return $result; case 'group': @@ -2640,25 +2746,47 @@ public static function getCountComponent($component, $contactId, $tableName = NU } } + /** + * Update contact greetings if an update has resulted in a custom field change. + * + * @param array $updatedFields + * Array of fields that have been updated e.g array('first_name', 'prefix_id', 'custom_2'); + * @param array $contactParams + * Parameters known about the contact. At minimum array('contact_id' => x). + * Fields in this array will take precedence over DB fields (so far only + * in the case of greeting id fields). + */ + public static function updateGreetingsOnTokenFieldChange($updatedFields, $contactParams) { + $contactID = $contactParams['contact_id']; + CRM_Contact_BAO_Contact::ensureGreetingParamsAreSet($contactParams); + $tokens = CRM_Contact_BAO_Contact_Utils::getTokensRequiredForContactGreetings($contactParams); + if (!empty($tokens['all']['contact'])) { + $affectedTokens = array_intersect_key($updatedFields[$contactID], array_flip($tokens['all']['contact'])); + if (!empty($affectedTokens)) { + // @todo this is still reloading the whole contact -fix to be more selective & use pre-loaded. + $contact = new CRM_Contact_BAO_Contact(); + $contact->id = $contactID; + CRM_Contact_BAO_Contact::processGreetings($contact); + } + } + } + /** * Process greetings and cache. * * @param object $contact * Contact object after save. - * @param bool $useDefaults - * Use default greeting values. */ - public static function processGreetings(&$contact, $useDefaults = FALSE) { - if ($useDefaults) { - //retrieve default greetings - $defaultGreetings = CRM_Core_PseudoConstant::greetingDefaults(); - $contactDefaults = $defaultGreetings[$contact->contact_type]; - } + public static function processGreetings(&$contact) { + + //@todo this function does a lot of unnecessary loading. + // ensureGreetingParamsAreSet now makes sure that the contact is + // loaded and using updateGreetingsOnTokenFieldChange + // allows us the possibility of only doing an update if required. - // note that contact object not always has required greeting related - // fields that are required to calculate greeting and - // also other fields used in tokens etc, - // hence we need to retrieve it again. + // The contact object has not always required the + // fields that are required to calculate greetings + // so we need to retrieve it again. if ($contact->_query !== FALSE) { $contact->find(TRUE); } @@ -2687,14 +2815,7 @@ public static function processGreetings(&$contact, $useDefaults = FALSE) { $updateQueryString[] = " email_greeting_custom = NULL "; } else { - if ($useDefaults) { - reset($contactDefaults['email_greeting']); - $emailGreetingID = key($contactDefaults['email_greeting']); - $emailGreetingString = $contactDefaults['email_greeting'][$emailGreetingID]; - $updateQueryString[] = " email_greeting_id = $emailGreetingID "; - $updateQueryString[] = " email_greeting_custom = NULL "; - } - elseif ($contact->email_greeting_custom) { + if ($contact->email_greeting_custom) { $updateQueryString[] = " email_greeting_display = NULL "; } } @@ -2723,14 +2844,7 @@ public static function processGreetings(&$contact, $useDefaults = FALSE) { $updateQueryString[] = " postal_greeting_custom = NULL "; } else { - if ($useDefaults) { - reset($contactDefaults['postal_greeting']); - $postalGreetingID = key($contactDefaults['postal_greeting']); - $postalGreetingString = $contactDefaults['postal_greeting'][$postalGreetingID]; - $updateQueryString[] = " postal_greeting_id = $postalGreetingID "; - $updateQueryString[] = " postal_greeting_custom = NULL "; - } - elseif ($contact->postal_greeting_custom) { + if ($contact->postal_greeting_custom) { $updateQueryString[] = " postal_greeting_display = NULL "; } } @@ -2760,14 +2874,7 @@ public static function processGreetings(&$contact, $useDefaults = FALSE) { $updateQueryString[] = " addressee_custom = NULL "; } else { - if ($useDefaults) { - reset($contactDefaults['addressee']); - $addresseeID = key($contactDefaults['addressee']); - $addresseeString = $contactDefaults['addressee'][$addresseeID]; - $updateQueryString[] = " addressee_id = $addresseeID "; - $updateQueryString[] = " addressee_custom = NULL "; - } - elseif ($contact->addressee_custom) { + if ($contact->addressee_custom) { $updateQueryString[] = " addressee_display = NULL "; } } @@ -3012,6 +3119,79 @@ public static function contextMenu($contactId = NULL) { ), ); + $menu['otherActions'] = array( + 'print' => array( + 'title' => ts('Print Summary'), + 'description' => ts('Printer-friendly view of this page.'), + 'weight' => 5, + 'ref' => 'crm-contact-print', + 'key' => 'print', + 'tab' => 'print', + 'href' => CRM_Utils_System::url('civicrm/contact/view/print', + "reset=1&print=1" + ), + 'class' => 'print', + 'icon' => 'crm-i fa-print', + ), + 'vcard' => array( + 'title' => ts('vCard'), + 'description' => ts('vCard record for this contact.'), + 'weight' => 10, + 'ref' => 'crm-contact-vcard', + 'key' => 'vcard', + 'tab' => 'vcard', + 'href' => CRM_Utils_System::url('civicrm/contact/view/vcard', + "reset=1" + ), + 'class' => 'vcard', + 'icon' => 'crm-i fa-list-alt', + ), + ); + + if (CRM_Core_Permission::check('access Contact Dashboard')) { + $menu['otherActions']['dashboard'] = array( + 'title' => ts('Contact Dashboard'), + 'description' => ts('Contact Dashboard'), + 'weight' => 15, + 'ref' => 'crm-contact-dashboard', + 'key' => 'dashboard', + 'tab' => 'dashboard', + 'class' => 'dashboard', + // NOTE: As an alternative you can also build url on CMS specific way + // as CRM_Core_Config::singleton()->userSystem->getUserRecordUrl($contactId) + 'href' => CRM_Utils_System::url('civicrm/user', "reset=1&id={$contactId}"), + 'icon' => 'crm-i fa-tachometer', + ); + } + + $uid = CRM_Core_BAO_UFMatch::getUFId($contactId); + if ($uid) { + $menu['otherActions']['user-record'] = array( + 'title' => ts('User Record'), + 'description' => ts('User Record'), + 'weight' => 20, + 'ref' => 'crm-contact-user-record', + 'key' => 'user-record', + 'tab' => 'user-record', + 'class' => 'user-record', + 'href' => CRM_Core_Config::singleton()->userSystem->getUserRecordUrl($contactId), + 'icon' => 'crm-i fa-user', + ); + } + elseif (CRM_Core_Config::singleton()->userSystem->checkPermissionAddUser()) { + $menu['otherActions']['user-add'] = array( + 'title' => ts('Create User Record'), + 'description' => ts('Create User Record'), + 'weight' => 25, + 'ref' => 'crm-contact-user-add', + 'key' => 'user-add', + 'tab' => 'user-add', + 'class' => 'user-add', + 'href' => CRM_Utils_System::url('civicrm/contact/view/useradd', 'reset=1&action=add&cid=' . $contactId), + 'icon' => 'crm-i fa-user-plus', + ); + } + CRM_Utils_Hook::summaryActions($menu, $contactId); //1. check for component is active. //2. check for user permissions. @@ -3029,89 +3209,126 @@ public static function contextMenu($contactId = NULL) { ); $corePermission = CRM_Core_Permission::getPermission(); - $config = CRM_Core_Config::singleton(); - $contextMenu = array(); foreach ($menu as $key => $values) { - $componentName = CRM_Utils_Array::value('component', $values); + if ($key != 'otherActions') { - // if component action - make sure component is enable. - if ($componentName && !in_array($componentName, $config->enableComponents)) { - continue; - } - - // make sure user has all required permissions. - $hasAllPermissions = FALSE; - - $permissions = CRM_Utils_Array::value('permissions', $values); - if (!is_array($permissions) || empty($permissions)) { - $hasAllPermissions = TRUE; - } - - // iterate for required permissions in given permissions array. - if (!$hasAllPermissions) { - $hasPermissions = 0; - foreach ($permissions as $permission) { - if (CRM_Core_Permission::check($permission)) { - $hasPermissions++; - } - } - - if (count($permissions) == $hasPermissions) { - $hasAllPermissions = TRUE; + // user does not have necessary permissions. + if (!self::checkUserMenuPermissions($aclPermissionedTasks, $corePermission, $values)) { + continue; } - - // if still user does not have required permissions, check acl. - if (!$hasAllPermissions && $values['ref'] != 'delete-contact') { - if (in_array($values['ref'], $aclPermissionedTasks) && - $corePermission == CRM_Core_Permission::EDIT - ) { - $hasAllPermissions = TRUE; - } - elseif (in_array($values['ref'], array( - 'new-email', - ))) { - // grant permissions for these tasks. - $hasAllPermissions = TRUE; - } + // build directly accessible action menu. + if (in_array($values['ref'], array( + 'view-contact', + 'edit-contact', + ))) { + $contextMenu['primaryActions'][$key] = array( + 'title' => $values['title'], + 'ref' => $values['ref'], + 'class' => CRM_Utils_Array::value('class', $values), + 'key' => $values['key'], + ); + continue; } - } - // user does not have necessary permissions. - if (!$hasAllPermissions) { - continue; - } - - // build directly accessible action menu. - if (in_array($values['ref'], array( - 'view-contact', - 'edit-contact', - ))) { - $contextMenu['primaryActions'][$key] = array( + // finally get menu item for -more- action widget. + $contextMenu['moreActions'][$values['weight']] = array( 'title' => $values['title'], 'ref' => $values['ref'], + 'href' => CRM_Utils_Array::value('href', $values), + 'tab' => CRM_Utils_Array::value('tab', $values), 'class' => CRM_Utils_Array::value('class', $values), 'key' => $values['key'], ); - continue; } + else { + foreach ($values as $value) { + // user does not have necessary permissions. + if (!self::checkUserMenuPermissions($aclPermissionedTasks, $corePermission, $value)) { + continue; + } - // finally get menu item for -more- action widget. - $contextMenu['moreActions'][$values['weight']] = array( - 'title' => $values['title'], - 'ref' => $values['ref'], - 'href' => CRM_Utils_Array::value('href', $values), - 'tab' => CRM_Utils_Array::value('tab', $values), - 'class' => CRM_Utils_Array::value('class', $values), - 'key' => $values['key'], - ); + // finally get menu item for -more- action widget. + $contextMenu['otherActions'][$value['weight']] = array( + 'title' => $value['title'], + 'ref' => $value['ref'], + 'href' => CRM_Utils_Array::value('href', $value), + 'tab' => CRM_Utils_Array::value('tab', $value), + 'class' => CRM_Utils_Array::value('class', $value), + 'icon' => CRM_Utils_Array::value('icon', $value), + 'key' => $value['key'], + ); + } + } } ksort($contextMenu['moreActions']); + ksort($contextMenu['otherActions']); return $contextMenu; } + /** + * Check if user has permissions to access items in action menu. + * + * @param array $aclPermissionedTasks + * Array containing ACL related tasks. + * @param string $corePermission + * The permission of the user (edit or view or null). + * @param array $menuOptions + * Array containing params of the menu (title, href, etc). + * + * @return bool + * TRUE if user has all permissions, FALSE if otherwise. + */ + public static function checkUserMenuPermissions($aclPermissionedTasks, $corePermission, $menuOptions) { + $componentName = CRM_Utils_Array::value('component', $menuOptions); + + // if component action - make sure component is enable. + if ($componentName && !in_array($componentName, CRM_Core_Config::singleton()->enableComponents)) { + return FALSE; + } + + // make sure user has all required permissions. + $hasAllPermissions = FALSE; + + $permissions = CRM_Utils_Array::value('permissions', $menuOptions); + if (!is_array($permissions) || empty($permissions)) { + $hasAllPermissions = TRUE; + } + + // iterate for required permissions in given permissions array. + if (!$hasAllPermissions) { + $hasPermissions = 0; + foreach ($permissions as $permission) { + if (CRM_Core_Permission::check($permission)) { + $hasPermissions++; + } + } + + if (count($permissions) == $hasPermissions) { + $hasAllPermissions = TRUE; + } + + // if still user does not have required permissions, check acl. + if (!$hasAllPermissions && $menuOptions['ref'] != 'delete-contact') { + if (in_array($menuOptions['ref'], $aclPermissionedTasks) && + $corePermission == CRM_Core_Permission::EDIT + ) { + $hasAllPermissions = TRUE; + } + elseif (in_array($menuOptions['ref'], array( + 'new-email', + ))) { + // grant permissions for these tasks. + $hasAllPermissions = TRUE; + } + } + } + + return $hasAllPermissions; + } + /** * Retrieve display name of contact that address is shared. * @@ -3120,30 +3337,21 @@ public static function contextMenu($contactId = NULL) { * @param int $masterAddressId * Master id. * @param int $contactId - * Contact id. + * Contact id. (deprecated - do not use) * * @return string|null * the found display name or null. */ public static function getMasterDisplayName($masterAddressId = NULL, $contactId = NULL) { $masterDisplayName = NULL; - $sql = NULL; - if (!$masterAddressId && !$contactId) { + if (!$masterAddressId) { return $masterDisplayName; } - if ($masterAddressId) { - $sql = " + $sql = " SELECT display_name from civicrm_contact LEFT JOIN civicrm_address ON ( civicrm_address.contact_id = civicrm_contact.id ) WHERE civicrm_address.id = " . $masterAddressId; - } - elseif ($contactId) { - $sql = " - SELECT display_name from civicrm_contact cc, civicrm_address add1 -LEFT JOIN civicrm_address add2 ON ( add1.master_id = add2.id ) - WHERE cc.id = add2.contact_id AND add1.contact_id = " . $contactId; - } $masterDisplayName = CRM_Core_DAO::singleValueQuery($sql); return $masterDisplayName; @@ -3177,51 +3385,6 @@ public static function getTimestamps($contactId) { } } - /** - * Generate triggers to update the timestamp. - * - * The corresponding civicrm_contact row is updated on insert/update/delete - * to a table that extends civicrm_contact. - * Don't regenerate triggers for all such tables if only asked for one table. - * - * @param array $info - * Reference to the array where generated trigger information is being stored - * @param string|null $reqTableName - * Name of the table for which triggers are being generated, or NULL if all tables - * @param array $relatedTableNames - * Array of all core or all custom table names extending civicrm_contact - * @param string $contactRefColumn - * 'contact_id' if processing core tables, 'entity_id' if processing custom tables - * - * @link https://issues.civicrm.org/jira/browse/CRM-15602 - * @see triggerInfo - */ - public static function generateTimestampTriggers(&$info, $reqTableName, $relatedTableNames, $contactRefColumn) { - // Safety - $contactRefColumn = CRM_Core_DAO::escapeString($contactRefColumn); - // If specific related table requested, just process that one - if (in_array($reqTableName, $relatedTableNames)) { - $relatedTableNames = array($reqTableName); - } - - // If no specific table requested (include all related tables), - // or a specific related table requested (as matched above) - if (empty($reqTableName) || in_array($reqTableName, $relatedTableNames)) { - $info[] = array( - 'table' => $relatedTableNames, - 'when' => 'AFTER', - 'event' => array('INSERT', 'UPDATE'), - 'sql' => "\nUPDATE civicrm_contact SET modified_date = CURRENT_TIMESTAMP WHERE id = NEW.$contactRefColumn;\n", - ); - $info[] = array( - 'table' => $relatedTableNames, - 'when' => 'AFTER', - 'event' => array('DELETE'), - 'sql' => "\nUPDATE civicrm_contact SET modified_date = CURRENT_TIMESTAMP WHERE id = OLD.$contactRefColumn;\n", - ); - } - } - /** * Get a list of triggers for the contact table. * @@ -3243,37 +3406,16 @@ public static function triggerInfo(&$info, $tableName = NULL) { } } - // Update timestamp for civicrm_contact itself - if ($tableName == NULL || $tableName == self::getTableName()) { - $info[] = array( - 'table' => array(self::getTableName()), - 'when' => 'BEFORE', - 'event' => array('INSERT'), - 'sql' => "\nSET NEW.created_date = CURRENT_TIMESTAMP;\n", - ); - } - - // Update timestamp when modifying closely related core tables - $relatedTables = array( - 'civicrm_address', - 'civicrm_email', - 'civicrm_im', - 'civicrm_phone', - 'civicrm_website', - ); - self::generateTimestampTriggers($info, $tableName, $relatedTables, 'contact_id'); - - // Update timestamp when modifying related custom-data tables - $customGroupTables = array(); - $customGroupDAO = CRM_Core_BAO_CustomGroup::getAllCustomGroupsByBaseEntity('Contact'); - $customGroupDAO->is_multiple = 0; - $customGroupDAO->find(); - while ($customGroupDAO->fetch()) { - $customGroupTables[] = $customGroupDAO->table_name; - } - if (!empty($customGroupTables)) { - self::generateTimestampTriggers($info, $tableName, $customGroupTables, 'entity_id'); - } + // Modifications to these records should update the contact timestamps. + \Civi\Core\SqlTrigger\TimestampTriggers::create('civicrm_contact', 'Contact') + ->setRelations(array( + array('table' => 'civicrm_address', 'column' => 'contact_id'), + array('table' => 'civicrm_email', 'column' => 'contact_id'), + array('table' => 'civicrm_im', 'column' => 'contact_id'), + array('table' => 'civicrm_phone', 'column' => 'contact_id'), + array('table' => 'civicrm_website', 'column' => 'contact_id'), + )) + ->alterTriggerInfo($info, $tableName); // Update phone table to populate phone_numeric field if (!$tableName || $tableName == 'civicrm_phone') { @@ -3377,8 +3519,8 @@ public static function buildOptions($fieldName, $context = NULL, $props = array( * Ensures that is_primary gets assigned to another object if available * Also calls pre/post hooks * - * @var object $type - * @var int $id + * @param string $type + * @param int $id * @return bool */ public static function deleteObjectWithPrimary($type, $id) { @@ -3389,8 +3531,9 @@ public static function deleteObjectWithPrimary($type, $id) { $obj = new $daoName(); $obj->id = $id; $obj->find(); + $hookParams = []; if ($obj->fetch()) { - CRM_Utils_Hook::pre('delete', $type, $id, CRM_Core_DAO::$_nullArray); + CRM_Utils_Hook::pre('delete', $type, $id, $hookParams); $contactId = $obj->contact_id; $obj->delete(); } @@ -3411,7 +3554,6 @@ public static function deleteObjectWithPrimary($type, $id) { $dao->save(); } } - $dao->free(); } CRM_Utils_Hook::post('delete', $type, $id, $obj); $obj->free(); @@ -3432,4 +3574,153 @@ public function addSelectWhereClause() { return $clauses; } + /** + * Get any existing duplicate contacts based on the input parameters. + * + * @param array $input + * Input parameters to be matched. + * @param string $contactType + * @param string $rule + * - Supervised + * - Unsupervised + * @param $excludedContactIDs + * An array of ids not to be included in the results. + * @param bool $checkPermissions + * @param int $ruleGroupID + * ID of the rule group to be used if an override is desirable. + * @param array $contextParams + * The context if relevant, eg. ['event_id' => X] + * + * @return array + */ + public static function getDuplicateContacts($input, $contactType, $rule = 'Unsupervised', $excludedContactIDs = [], $checkPermissions = TRUE, $ruleGroupID = NULL, $contextParams = []) { + $dedupeParams = CRM_Dedupe_Finder::formatParams($input, $contactType); + $dedupeParams['check_permission'] = $checkPermissions; + $dedupeParams['contact_type'] = $contactType; + $dedupeParams['rule'] = $rule; + $dedupeParams['rule_group_id'] = $ruleGroupID; + $dedupeParams['excluded_contact_ids'] = $excludedContactIDs; + $dedupeResults['ids'] = []; + $dedupeResults['handled'] = FALSE; + CRM_Utils_Hook::findDuplicates($dedupeParams, $dedupeResults, $contextParams); + if (!$dedupeResults['handled']) { + $dedupeResults['ids'] = CRM_Dedupe_Finder::dupesByParams($dedupeParams, $contactType, $rule, $excludedContactIDs, $ruleGroupID); + } + return $dedupeResults['ids']; + } + + /** + * Get the first duplicate contacts based on the input parameters. + * + * @param array $input + * Input parameters to be matched. + * @param string $contactType + * @param string $rule + * - Supervised + * - Unsupervised + * @param $excludedContactIDs + * An array of ids not to be included in the results. + * @param bool $checkPermissions + * @param int $ruleGroupID + * ID of the rule group to be used if an override is desirable. + * @param array $contextParams + * The context if relevant, eg. ['event_id' => X] + * + * @return int|NULL + */ + public static function getFirstDuplicateContact($input, $contactType, $rule = 'Unsupervised', $excludedContactIDs = [], $checkPermissions = TRUE, $ruleGroupID = NULL, $contextParams = []) { + $ids = self::getDuplicateContacts($input, $contactType, $rule, $excludedContactIDs, $checkPermissions, $ruleGroupID, $contextParams); + if (empty($ids)) { + return NULL; + } + return $ids[0]; + } + + /** + * Check if a field is associated with an entity that has a location type. + * + * (ie. is an address, phone, email etc field). + * + * @param string $fieldTitle + * Title of the field (not the name - create a new function for that if required). + * + * @return bool + */ + public static function isFieldHasLocationType($fieldTitle) { + foreach (CRM_Contact_BAO_Contact::importableFields() as $key => $field) { + if ($field['title'] === $fieldTitle) { + return CRM_Utils_Array::value('hasLocationType', $field); + } + } + return FALSE; + } + + /** + * @param array $appendProfiles + * Name of profile(s) to append to each link. + * + * @return array + */ + public static function getEntityRefCreateLinks($appendProfiles = []) { + // You'd think that "create contacts" would be the permission to check, + // But new contact popups are profile forms and those use their own permissions. + if (!CRM_Core_Permission::check([['profile create', 'profile listings and forms']])) { + return FALSE; + } + $profiles = []; + foreach (CRM_Contact_BAO_ContactType::basicTypes() as $contactType) { + $profiles[] = 'new_' . strtolower($contactType); + } + $retrieved = civicrm_api3('uf_group', 'get', [ + 'name' => ['IN' => array_merge($profiles, (array) $appendProfiles)], + 'is_active' => 1, + ]); + $links = $append = []; + if (!empty($retrieved['values'])) { + $icons = [ + 'individual' => 'fa-user', + 'organization' => 'fa-building', + 'household' => 'fa-home', + ]; + foreach ($retrieved['values'] as $id => $profile) { + if (in_array($profile['name'], $profiles)) { + $links[] = array( + 'label' => $profile['title'], + 'url' => CRM_Utils_System::url('civicrm/profile/create', "reset=1&context=dialog&gid=$id", + NULL, NULL, FALSE, FALSE, TRUE), + 'type' => ucfirst(str_replace('new_', '', $profile['name'])), + 'icon' => CRM_Utils_Array::value(str_replace('new_', '', $profile['name']), $icons), + ); + } + else { + $append[] = $id; + } + } + foreach ($append as $id) { + foreach ($links as &$link) { + $link['url'] .= ",$id"; + } + } + } + return $links; + } + + /** + * @return array + */ + public static function getEntityRefFilters() { + return [ + ['key' => 'contact_type', 'value' => ts('Contact Type')], + ['key' => 'group', 'value' => ts('Group'), 'entity' => 'GroupContact'], + ['key' => 'tag', 'value' => ts('Tag'), 'entity' => 'EntityTag'], + ['key' => 'state_province', 'value' => ts('State/Province'), 'entity' => 'Address'], + ['key' => 'country', 'value' => ts('Country'), 'entity' => 'Address'], + ['key' => 'gender_id', 'value' => ts('Gender'), 'condition' => ['contact_type' => 'Individual']], + ['key' => 'is_deceased', 'value' => ts('Deceased'), 'condition' => ['contact_type' => 'Individual']], + ['key' => 'contact_id', 'value' => ts('Contact ID'), 'type' => 'text'], + ['key' => 'external_identifier', 'value' => ts('External ID'), 'type' => 'text'], + ['key' => 'source', 'value' => ts('Contact Source'), 'type' => 'text'], + ]; + } + } diff --git a/CRM/Contact/BAO/Contact/Location.php b/CRM/Contact/BAO/Contact/Location.php index 44d36c87c904..8e7a12f373c6 100644 --- a/CRM/Contact/BAO/Contact/Location.php +++ b/CRM/Contact/BAO/Contact/Location.php @@ -1,9 +1,9 @@ $locationTypeID, + 'contact_id' => $id, + 'return' => ['contact_id.display_name', 'email', 'location_type_id', 'id'], + ]; if ($isPrimary) { - $primaryClause = " AND civicrm_email.is_primary = 1"; + $params['is_primary'] = 1; } + $emails = civicrm_api3('Email', 'get', $params); - $locationClause = NULL; - if ($locationTypeID) { - $locationClause = " AND civicrm_email.location_type_id = $locationTypeID"; + if ($emails['count'] > 0) { + $email = reset($emails['values']); + return [$email['contact_id.display_name'], $email['email'], $email['location_type_id'], $email['id']]; } - - $sql = " -SELECT civicrm_contact.display_name, - civicrm_email.email, - civicrm_email.location_type_id, - civicrm_email.id -FROM civicrm_contact -LEFT JOIN civicrm_email ON ( civicrm_contact.id = civicrm_email.contact_id {$primaryClause} {$locationClause} ) -WHERE civicrm_contact.id = %1"; - - $params = array(1 => array($id, 'Integer')); - $dao = CRM_Core_DAO::executeQuery($sql, $params); - if ($dao->fetch()) { - return array($dao->display_name, $dao->email, $dao->location_type_id, $dao->id); - } - return array(NULL, NULL, NULL, NULL); + return [NULL, NULL, NULL, NULL]; } /** + * @deprecated Not used anywhere, use the Phone API instead * Get the sms number and display name of a contact. * * @param int $id @@ -84,8 +75,9 @@ public static function getEmailDetails($id, $isPrimary = TRUE, $locationTypeID = * tuple of display_name and sms if found, or (null,null) */ public static function getPhoneDetails($id, $type = NULL) { + CRM_Core_Error::deprecatedFunctionWarning('Phone.get API instead'); if (!$id) { - return array(NULL, NULL); + return [NULL, NULL]; } $cond = NULL; @@ -101,12 +93,12 @@ public static function getPhoneDetails($id, $type = NULL) { $cond AND civicrm_contact.id = %1"; - $params = array(1 => array($id, 'Integer')); + $params = [1 => [$id, 'Integer']]; $dao = CRM_Core_DAO::executeQuery($sql, $params); if ($dao->fetch()) { - return array($dao->display_name, $dao->phone, $dao->do_not_sms); + return [$dao->display_name, $dao->phone, $dao->do_not_sms]; } - return array(NULL, NULL, NULL); + return [NULL, NULL, NULL]; } /** @@ -132,6 +124,7 @@ public static function &getMapInfo($ids, $locationTypeID = NULL, $imageUrlOnly = civicrm_address.street_address as street_address, civicrm_address.supplemental_address_1 as supplemental_address_1, civicrm_address.supplemental_address_2 as supplemental_address_2, + civicrm_address.supplemental_address_3 as supplemental_address_3, civicrm_address.city as city, civicrm_address.postal_code as postal_code, civicrm_address.postal_code_suffix as postal_code_suffix, @@ -149,22 +142,22 @@ public static function &getMapInfo($ids, $locationTypeID = NULL, $imageUrlOnly = AND civicrm_address.geo_code_2 IS NOT NULL AND civicrm_contact.id IN $idString "; - $params = array(); + $params = []; if (!$locationTypeID) { $sql .= " AND civicrm_address.is_primary = 1"; } else { $sql .= " AND civicrm_address.location_type_id = %1"; - $params[1] = array($locationTypeID, 'Integer'); + $params[1] = [$locationTypeID, 'Integer']; } $dao = CRM_Core_DAO::executeQuery($sql, $params); - $locations = array(); + $locations = []; $config = CRM_Core_Config::singleton(); while ($dao->fetch()) { - $location = array(); + $location = []; $location['contactID'] = $dao->contact_id; $location['displayName'] = addslashes($dao->display_name); $location['city'] = $dao->city; @@ -176,18 +169,19 @@ public static function &getMapInfo($ids, $locationTypeID = NULL, $imageUrlOnly = $address = ''; CRM_Utils_String::append($address, '
', - array( + [ $dao->street_address, $dao->supplemental_address_1, $dao->supplemental_address_2, + $dao->supplemental_address_3, $dao->city, - ) + ] ); CRM_Utils_String::append($address, ', ', - array($dao->state, $dao->postal_code) + [$dao->state, $dao->postal_code] ); CRM_Utils_String::append($address, '
', - array($dao->country) + [$dao->country] ); $location['address'] = addslashes($address); $location['displayAddress'] = str_replace('
', ', ', addslashes($address)); diff --git a/CRM/Contact/BAO/Contact/Optimizer.php b/CRM/Contact/BAO/Contact/Optimizer.php deleted file mode 100644 index bce6a44d1447..000000000000 --- a/CRM/Contact/BAO/Contact/Optimizer.php +++ /dev/null @@ -1,190 +0,0 @@ - $value) { - if (!empty($value['url'])) { - $oldEmpty = FALSE; - $old[] = array('website_type_id' => $value['website_type_id'], 'url' => $value['url']); - } - } - - foreach ($newWebsiteValues as $idx => $value) { - if (!empty($value['url'])) { - $newEmpty = FALSE; - $new[] = array('website_type_id' => $value['website_type_id'], 'url' => $value['url']); - } - } - - // if both old and new are empty, we can delete new and avoid a write - if ($oldEmpty && $newEmpty) { - unset($newValues['website']); - } - - // if different number of non-empty entries, return - if (count($new) != count($old)) { - return; - } - - // same number of entries, check if they are exactly the same - foreach ($old as $oldID => $oldValues) { - $found = FALSE; - foreach ($new as $newID => $newValues) { - if ( - $old['website_type_id'] == $new['website_type_id'] && - $old['url'] == $new['url'] - ) { - $found = TRUE; - unset($new[$newID]); - break; - } - if (!$found) { - return; - } - } - } - - // if we've come here, this means old and new are the same - // we can skip saving new and return - unset($newValues['website']); - } - - /** - * @param $newValues - * @param $oldValues - */ - public static function email(&$newValues, &$oldValues) { - $oldEmailValues = CRM_Utils_Array::value('email', $oldValues); - $newEmailValues = CRM_Utils_Array::value('email', $newValues); - - if ($oldEmailValues == NULL || $newEmailValues == NULL) { - return; - } - - // check if we had a value in the old - $oldEmpty = $newEmpty = TRUE; - $old = $new = array(); - - foreach ($oldEmailValues as $idx => $value) { - if (!empty($value['email'])) { - $oldEmpty = FALSE; - $old[] = array( - 'email' => $value['email'], - 'location_type_id' => $value['location_type_id'], - 'on_hold' => $value['on_hold'] ? 1 : 0, - 'is_primary' => $value['is_primary'] ? 1 : 0, - 'is_bulkmail' => $value['is_bulkmail'] ? 1 : 0, - 'signature_text' => $value['signature_text'] ? $value['signature_text'] : '', - 'signature_html' => $value['signature_html'] ? $value['signature_html'] : '', - ); - } - } - - foreach ($newEmailValues as $idx => $value) { - if (!empty($value['email'])) { - $newEmpty = FALSE; - $new[] = array( - 'email' => $value['email'], - 'location_type_id' => $value['location_type_id'], - 'on_hold' => $value['on_hold'] ? 1 : 0, - 'is_primary' => $value['is_primary'] ? 1 : 0, - 'is_bulkmail' => $value['is_bulkmail'] ? 1 : 0, - 'signature_text' => $value['signature_text'] ? $value['signature_text'] : '', - 'signature_html' => $value['signature_html'] ? $value['signature_html'] : '', - ); - } - } - - // if both old and new are empty, we can delete new and avoid a write - if ($oldEmpty && $newEmpty) { - unset($newValues['email']); - } - - // if different number of non-empty entries, return - if (count($new) != count($old)) { - return; - } - - // same number of entries, check if they are exactly the same - foreach ($old as $oldID => $oldValues) { - $found = FALSE; - foreach ($new as $newID => $newValues) { - if ( - $old['email_type_id'] == $new['email_type_id'] && - $old['url'] == $new['url'] - ) { - $found = TRUE; - unset($new[$newID]); - break; - } - if (!$found) { - return; - } - } - } - - // if we've come here, this means old and new are the same - // we can skip saving new and return - unset($newValues['email']); - } - -} diff --git a/CRM/Contact/BAO/Contact/Permission.php b/CRM/Contact/BAO/Contact/Permission.php index 5a3e4710c2d6..731958c98a5f 100644 --- a/CRM/Contact/BAO/Contact/Permission.php +++ b/CRM/Contact/BAO/Contact/Permission.php @@ -1,9 +1,9 @@ fine + return $contact_ids; + } + else { + // if the user CANNOT access deleted contacts, these need to be filtered + $contact_id_list = implode(',', $contact_ids); + $filter_query = "SELECT DISTINCT(id) FROM civicrm_contact WHERE id IN ($contact_id_list) AND is_deleted = 0"; + $query = CRM_Core_DAO::executeQuery($filter_query); + while ($query->fetch()) { + $result_set[(int) $query->id] = TRUE; + } + return array_keys($result_set); + } + } + + // get logged in user + $contactID = CRM_Core_Session::getLoggedInContactID(); + if (empty($contactID)) { + return []; + } + + // make sure the cache is filled + self::cache($contactID, $type); + + // compile query + $operation = ($type == CRM_Core_Permission::VIEW) ? 'View' : 'Edit'; + + // add clause for deleted contacts, if the user doesn't have the permission to access them + $LEFT_JOIN_DELETED = $AND_CAN_ACCESS_DELETED = ''; + if (!CRM_Core_Permission::check('access deleted contacts')) { + $LEFT_JOIN_DELETED = "LEFT JOIN civicrm_contact ON civicrm_contact.id = contact_id"; + $AND_CAN_ACCESS_DELETED = "AND civicrm_contact.is_deleted = 0"; + } + + // RUN the query + $contact_id_list = implode(',', $contact_ids); + $query = " +SELECT contact_id + FROM civicrm_acl_contact_cache + {$LEFT_JOIN_DELETED} +WHERE contact_id IN ({$contact_id_list}) + AND user_id = {$contactID} + AND operation = '{$operation}' + {$AND_CAN_ACCESS_DELETED}"; + $result = CRM_Core_DAO::executeQuery($query); + while ($result->fetch()) { + $result_set[(int) $result->contact_id] = TRUE; + } + + // if some have been rejected, double check for permissions inherited by relationship + if (count($result_set) < count($contact_ids)) { + $rejected_contacts = array_diff_key($contact_ids, $result_set); + // @todo consider storing these to the acl cache for next time, since we have fetched. + $allowed_by_relationship = self::relationshipList($rejected_contacts, $type); + foreach ($allowed_by_relationship as $contact_id) { + $result_set[(int) $contact_id] = TRUE; + } + } + + return array_keys($result_set); + } + /** * Check if the logged in user has permissions for the operation type. * @@ -43,8 +136,15 @@ class CRM_Contact_BAO_Contact_Permission { * true if the user has permission, false otherwise */ public static function allow($id, $type = CRM_Core_Permission::VIEW) { - $tables = array(); - $whereTables = array(); + // get logged in user + $contactID = CRM_Core_Session::getLoggedInContactID(); + + // first: check if contact is trying to view own contact + if ($contactID == $id && ($type == CRM_Core_Permission::VIEW && CRM_Core_Permission::check('view my contact') + || $type == CRM_Core_Permission::EDIT && CRM_Core_Permission::check('edit my contact')) + ) { + return TRUE; + } # FIXME: push this somewhere below, to not give this permission so many rights $isDeleted = (bool) CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $id, 'is_deleted'); @@ -60,22 +160,30 @@ public static function allow($id, $type = CRM_Core_Permission::VIEW) { return TRUE; } - //check permission based on relationship, CRM-2963 - if (self::relationship($id)) { + // check permission based on relationship, CRM-2963 + if (self::relationshipList([$id], $type)) { return TRUE; } - $permission = CRM_ACL_API::whereClause($type, $tables, $whereTables); + // We should probably do a cheap check whether it's in the cache first. + // check permission based on ACL + $tables = []; + $whereTables = []; + $permission = CRM_ACL_API::whereClause($type, $tables, $whereTables, NULL, FALSE, FALSE, TRUE); $from = CRM_Contact_BAO_Query::fromClause($whereTables); $query = " -SELECT count(DISTINCT contact_a.id) +SELECT contact_a.id $from -WHERE contact_a.id = %1 AND $permission"; - $params = array(1 => array($id, 'Integer')); +WHERE contact_a.id = %1 AND $permission + LIMIT 1 +"; - return (CRM_Core_DAO::singleValueQuery($query, $params) > 0) ? TRUE : FALSE; + if (CRM_Core_DAO::singleValueQuery($query, [1 => [$id, 'Integer']])) { + return TRUE; + } + return FALSE; } /** @@ -87,9 +195,18 @@ public static function allow($id, $type = CRM_Core_Permission::VIEW) { * Should we force a recompute. */ public static function cache($userID, $type = CRM_Core_Permission::VIEW, $force = FALSE) { - static $_processed = array(); + // FIXME: maybe find a better way of keeping track of this. @eileen pointed out + // that somebody might flush the cache away from under our feet, + // but the alternative would be a SQL call every time this is called, + // and a complete rebuild if the result was an empty set... + if (!isset(Civi::$statics[__CLASS__]['processed'])) { + Civi::$statics[__CLASS__]['processed'] = [ + CRM_Core_Permission::VIEW => [], + CRM_Core_Permission::EDIT => [], + ]; + } - if ($type = CRM_Core_Permission::VIEW) { + if ($type == CRM_Core_Permission::VIEW) { $operationClause = " operation IN ( 'Edit', 'View' ) "; $operation = 'View'; } @@ -97,87 +214,53 @@ public static function cache($userID, $type = CRM_Core_Permission::VIEW, $force $operationClause = " operation = 'Edit' "; $operation = 'Edit'; } + $queryParams = [1 => [$userID, 'Integer']]; if (!$force) { - if (!empty($_processed[$userID])) { + // skip if already calculated + if (!empty(Civi::$statics[__CLASS__]['processed'][$type][$userID])) { return; } // run a query to see if the cache is filled $sql = " -SELECT count(id) +SELECT count(*) FROM civicrm_acl_contact_cache WHERE user_id = %1 AND $operationClause "; - $params = array(1 => array($userID, 'Integer')); - $count = CRM_Core_DAO::singleValueQuery($sql, $params); + $count = CRM_Core_DAO::singleValueQuery($sql, $queryParams); if ($count > 0) { - $_processed[$userID] = 1; + Civi::$statics[__CLASS__]['processed'][$type][$userID] = 1; return; } } - $tables = array(); - $whereTables = array(); + $tables = []; + $whereTables = []; - $permission = CRM_ACL_API::whereClause($type, $tables, $whereTables, $userID); + $permission = CRM_ACL_API::whereClause($type, $tables, $whereTables, $userID, FALSE, FALSE, TRUE); $from = CRM_Contact_BAO_Query::fromClause($whereTables); - CRM_Core_DAO::executeQuery(" INSERT INTO civicrm_acl_contact_cache ( user_id, contact_id, operation ) -SELECT $userID as user_id, contact_a.id as contact_id, '$operation' as operation +SELECT DISTINCT $userID as user_id, contact_a.id as contact_id, '{$operation}' as operation $from + LEFT JOIN civicrm_acl_contact_cache ac ON ac.user_id = $userID AND ac.contact_id = contact_a.id AND ac.operation = '{$operation}' WHERE $permission -GROUP BY contact_a.id -ON DUPLICATE KEY UPDATE - user_id=VALUES(user_id), - contact_id=VALUES(contact_id), - operation=VALUES(operation)" - ); - - $_processed[$userID] = 1; - } - - /** - * Check if there are any contacts in cache table. - * - * @param int|string $type the type of operation (view|edit) - * @param int $contactID - * Contact id. - * - * @return bool - */ - public static function hasContactsInCache( - $type = CRM_Core_Permission::VIEW, - $contactID = NULL - ) { - if (!$contactID) { - $session = CRM_Core_Session::singleton(); - $contactID = $session->get('userID'); - } - - if ($type = CRM_Core_Permission::VIEW) { - $operationClause = " operation IN ( 'Edit', 'View' ) "; - $operation = 'View'; - } - else { - $operationClause = " operation = 'Edit' "; - $operation = 'Edit'; +AND ac.user_id IS NULL +"); + + // Add in a row for the logged in contact. Do not try to combine with the above query or an ugly OR will appear in + // the permission clause. + if (CRM_Core_Permission::check('edit my contact') || + ($type == CRM_Core_Permission::VIEW && CRM_Core_Permission::check('view my contact'))) { + if (!CRM_Core_DAO::singleValueQuery(" + SELECT count(*) FROM civicrm_acl_contact_cache WHERE user_id = %1 AND contact_id = %1 AND operation = '{$operation}' LIMIT 1", $queryParams)) { + CRM_Core_DAO::executeQuery("INSERT INTO civicrm_acl_contact_cache ( user_id, contact_id, operation ) VALUES(%1, %1, '{$operation}')", $queryParams); + } } - - // fill cache - self::cache($contactID); - - $sql = " -SELECT id -FROM civicrm_acl_contact_cache -WHERE user_id = %1 -AND $operationClause LIMIT 1"; - - $params = array(1 => array($contactID, 'Integer')); - return (bool) CRM_Core_DAO::singleValueQuery($sql, $params); + Civi::$statics[__CLASS__]['processed'][$type][$userID] = 1; } /** @@ -190,16 +273,16 @@ public static function cacheClause($contactAlias = 'contact_a') { CRM_Core_Permission::check('edit all contacts') ) { if (is_array($contactAlias)) { - $wheres = array(); + $wheres = []; foreach ($contactAlias as $alias) { // CRM-6181 $wheres[] = "$alias.is_deleted = 0"; } - return array(NULL, '(' . implode(' AND ', $wheres) . ')'); + return [NULL, '(' . implode(' AND ', $wheres) . ')']; } else { // CRM-6181 - return array(NULL, "$contactAlias.is_deleted = 0"); + return [NULL, "$contactAlias.is_deleted = 0"]; } } @@ -208,7 +291,7 @@ public static function cacheClause($contactAlias = 'contact_a') { if (is_array($contactAlias) && !empty($contactAlias)) { //More than one contact alias - $clauses = array(); + $clauses = []; foreach ($contactAlias as $k => $alias) { $clauses[] = " INNER JOIN civicrm_acl_contact_cache aclContactCache_{$k} ON {$alias}.id = aclContactCache_{$k}.contact_id AND aclContactCache_{$k}.user_id = $contactID "; } @@ -221,16 +304,18 @@ public static function cacheClause($contactAlias = 'contact_a') { $whereClase = " aclContactCache.user_id = $contactID AND $contactAlias.is_deleted = 0"; } - return array($fromClause, $whereClase); + return [$fromClause, $whereClase]; } /** - * Generate acl subquery that can be placed in the WHERE clause of a query or the ON clause of a JOIN + * Generate acl subquery that can be placed in the WHERE clause of a query or the ON clause of a JOIN. + * + * This is specifically for VIEW operations. * * @return string|null */ public static function cacheSubquery() { - if (!CRM_Core_Permission::check(array(array('view all contacts', 'edit all contacts')))) { + if (!CRM_Core_Permission::check([['view all contacts', 'edit all contacts']])) { $contactID = (int) CRM_Core_Session::getLoggedInContactID(); self::cache($contactID); return "IN (SELECT contact_id FROM civicrm_acl_contact_cache WHERE user_id = $contactID)"; @@ -239,100 +324,108 @@ public static function cacheSubquery() { } /** - * Get the permission base on its relationship. + * Filter a list of contact_ids by the ones that the + * currently active user as a permissioned relationship with * - * @param int $selectedContactID - * Contact id of selected contact. - * @param int $contactID - * Contact id of the current contact. + * @param array $contact_ids + * List of contact IDs to be filtered * - * @return bool - * true if logged in user has permission to view - * selected contact record else false + * @param int $type + * access type CRM_Core_Permission::VIEW or CRM_Core_Permission::EDIT + * + * @return array + * List of contact IDs that the user has permissions for */ - public static function relationship($selectedContactID, $contactID = NULL) { - $session = CRM_Core_Session::singleton(); - $config = CRM_Core_Config::singleton(); - if (!$contactID) { - $contactID = $session->get('userID'); - if (!$contactID) { - return FALSE; - } + public static function relationshipList($contact_ids, $type) { + $result_set = []; + + // no processing empty lists (avoid SQL errors as well) + if (empty($contact_ids)) { + return []; } - if ($contactID == $selectedContactID && - (CRM_Core_Permission::check('edit my contact')) - ) { - return TRUE; + + // get the currently logged in user + $contactID = CRM_Core_Session::getLoggedInContactID(); + if (empty($contactID)) { + return []; + } + + // compile a list of queries (later to UNION) + $queries = []; + $contact_id_list = implode(',', $contact_ids); + + // add a select statement for each direction + $directions = [['from' => 'a', 'to' => 'b'], ['from' => 'b', 'to' => 'a']]; + + // CRM_Core_Permission::VIEW is satisfied by either CRM_Contact_BAO_Relationship::VIEW or CRM_Contact_BAO_Relationship::EDIT + if ($type == CRM_Core_Permission::VIEW) { + $is_perm_condition = ' IN ( ' . CRM_Contact_BAO_Relationship::EDIT . ' , ' . CRM_Contact_BAO_Relationship::VIEW . ' ) '; } else { - if ($config->secondDegRelPermissions) { - $query = " -SELECT firstdeg.id -FROM civicrm_relationship firstdeg -LEFT JOIN civicrm_relationship seconddegaa - on firstdeg.contact_id_a = seconddegaa.contact_id_b - and seconddegaa.is_permission_b_a = 1 - and firstdeg.is_permission_b_a = 1 - and seconddegaa.is_active = 1 -LEFT JOIN civicrm_relationship seconddegab - on firstdeg.contact_id_a = seconddegab.contact_id_a - and seconddegab.is_permission_a_b = 1 - and firstdeg.is_permission_b_a = 1 - and seconddegab.is_active = 1 -LEFT JOIN civicrm_relationship seconddegba - on firstdeg.contact_id_b = seconddegba.contact_id_b - and seconddegba.is_permission_b_a = 1 - and firstdeg.is_permission_a_b = 1 - and seconddegba.is_active = 1 -LEFT JOIN civicrm_relationship seconddegbb - on firstdeg.contact_id_b = seconddegbb.contact_id_a - and seconddegbb.is_permission_a_b = 1 - and firstdeg.is_permission_a_b = 1 - and seconddegbb.is_active = 1 -WHERE - ( - ( firstdeg.contact_id_a = %1 AND firstdeg.contact_id_b = %2 AND firstdeg.is_permission_a_b = 1 ) - OR ( firstdeg.contact_id_a = %2 AND firstdeg.contact_id_b = %1 AND firstdeg.is_permission_b_a = 1 ) - OR ( - firstdeg.contact_id_a = %1 AND seconddegba.contact_id_a = %2 - AND (seconddegba.contact_id_a NOT IN (SELECT id FROM civicrm_contact WHERE is_deleted = 1)) - ) - OR ( - firstdeg.contact_id_a = %1 AND seconddegbb.contact_id_b = %2 - AND (seconddegbb.contact_id_b NOT IN (SELECT id FROM civicrm_contact WHERE is_deleted = 1)) - ) - OR ( - firstdeg.contact_id_b = %1 AND seconddegab.contact_id_b = %2 - AND (seconddegab.contact_id_b NOT IN (SELECT id FROM civicrm_contact WHERE is_deleted = 1)) - ) - OR ( - firstdeg.contact_id_b = %1 AND seconddegaa.contact_id_a = %2 AND (seconddegaa.contact_id_a NOT IN (SELECT id FROM civicrm_contact WHERE is_deleted = 1)) - ) - ) - AND (firstdeg.contact_id_a NOT IN (SELECT id FROM civicrm_contact WHERE is_deleted = 1)) - AND (firstdeg.contact_id_b NOT IN (SELECT id FROM civicrm_contact WHERE is_deleted = 1)) - AND ( firstdeg.is_active = 1) - "; + $is_perm_condition = ' = ' . CRM_Contact_BAO_Relationship::EDIT; + } + + // NORMAL/SINGLE DEGREE RELATIONSHIPS + foreach ($directions as $direction) { + $user_id_column = "contact_id_{$direction['from']}"; + $contact_id_column = "contact_id_{$direction['to']}"; + + // add clause for deleted contacts, if the user doesn't have the permission to access them + $LEFT_JOIN_DELETED = $AND_CAN_ACCESS_DELETED = ''; + if (!CRM_Core_Permission::check('access deleted contacts')) { + $LEFT_JOIN_DELETED = "LEFT JOIN civicrm_contact ON civicrm_contact.id = {$contact_id_column} "; + $AND_CAN_ACCESS_DELETED = "AND civicrm_contact.is_deleted = 0"; } - else { - $query = " -SELECT id -FROM civicrm_relationship -WHERE (( contact_id_a = %1 AND contact_id_b = %2 AND is_permission_a_b = 1 ) OR - ( contact_id_a = %2 AND contact_id_b = %1 AND is_permission_b_a = 1 )) AND - (contact_id_a NOT IN (SELECT id FROM civicrm_contact WHERE is_deleted = 1)) AND - (contact_id_b NOT IN (SELECT id FROM civicrm_contact WHERE is_deleted = 1)) - AND ( civicrm_relationship.is_active = 1 ) -"; + + $queries[] = " +SELECT civicrm_relationship.{$contact_id_column} AS contact_id + FROM civicrm_relationship + {$LEFT_JOIN_DELETED} + WHERE civicrm_relationship.{$user_id_column} = {$contactID} + AND civicrm_relationship.{$contact_id_column} IN ({$contact_id_list}) + AND civicrm_relationship.is_active = 1 + AND civicrm_relationship.is_permission_{$direction['from']}_{$direction['to']} {$is_perm_condition} + $AND_CAN_ACCESS_DELETED"; + } + + // FIXME: secondDegRelPermissions should be a setting + $config = CRM_Core_Config::singleton(); + if ($config->secondDegRelPermissions) { + foreach ($directions as $first_direction) { + foreach ($directions as $second_direction) { + // add clause for deleted contacts, if the user doesn't have the permission to access them + $LEFT_JOIN_DELETED = $AND_CAN_ACCESS_DELETED = ''; + if (!CRM_Core_Permission::check('access deleted contacts')) { + $LEFT_JOIN_DELETED = "LEFT JOIN civicrm_contact first_degree_contact ON first_degree_contact.id = second_degree_relationship.contact_id_{$second_direction['from']}\n"; + $LEFT_JOIN_DELETED .= "LEFT JOIN civicrm_contact second_degree_contact ON second_degree_contact.id = second_degree_relationship.contact_id_{$second_direction['to']} "; + $AND_CAN_ACCESS_DELETED = "AND first_degree_contact.is_deleted = 0\n"; + $AND_CAN_ACCESS_DELETED .= "AND second_degree_contact.is_deleted = 0 "; + } + + $queries[] = " +SELECT second_degree_relationship.contact_id_{$second_direction['to']} AS contact_id + FROM civicrm_relationship first_degree_relationship + LEFT JOIN civicrm_relationship second_degree_relationship ON first_degree_relationship.contact_id_{$first_direction['to']} = second_degree_relationship.contact_id_{$second_direction['from']} + {$LEFT_JOIN_DELETED} + WHERE first_degree_relationship.contact_id_{$first_direction['from']} = {$contactID} + AND second_degree_relationship.contact_id_{$second_direction['to']} IN ({$contact_id_list}) + AND first_degree_relationship.is_active = 1 + AND first_degree_relationship.is_permission_{$first_direction['from']}_{$first_direction['to']} {$is_perm_condition} + AND second_degree_relationship.is_active = 1 + AND second_degree_relationship.is_permission_{$second_direction['from']}_{$second_direction['to']} {$is_perm_condition} + $AND_CAN_ACCESS_DELETED"; + } } - $params = array( - 1 => array($contactID, 'Integer'), - 2 => array($selectedContactID, 'Integer'), - ); - return CRM_Core_DAO::singleValueQuery($query, $params); } - } + // finally UNION the queries and call + $query = "(" . implode(")\nUNION DISTINCT (", $queries) . ")"; + $result = CRM_Core_DAO::executeQuery($query); + while ($result->fetch()) { + $result_set[(int) $result->contact_id] = TRUE; + } + return array_keys($result_set); + } /** * @param int $contactID @@ -367,7 +460,7 @@ public static function validateOnlyChecksum($contactID, &$form, $redirect = TRUE // so here the contact is posing as $contactID, lets set the logging contact ID variable // CRM-8965 CRM_Core_DAO::executeQuery('SET @civicrm_user_id = %1', - array(1 => array($contactID, 'Integer')) + [1 => [$contactID, 'Integer']] ); return TRUE; diff --git a/CRM/Contact/BAO/Contact/Utils.php b/CRM/Contact/BAO/Contact/Utils.php index 3339de232ab4..2b3f0879133a 100644 --- a/CRM/Contact/BAO/Contact/Utils.php +++ b/CRM/Contact/BAO/Contact/Utils.php @@ -1,9 +1,9 @@ $contactType); + $typeInfo = []; + $params = ['name' => $contactType]; CRM_Contact_BAO_ContactType::retrieve($params, $typeInfo); if (!empty($typeInfo['image_URL'])) { @@ -229,7 +229,7 @@ public static function validChecksum($contactID, $inputCheck) { $check = self::generateChecksum($contactID, $inputTS, $inputLF); - if ($check != $inputCheck) { + if (!hash_equals($check, $inputCheck)) { return FALSE; } @@ -253,7 +253,7 @@ public static function validChecksum($contactID, $inputCheck) { * max locations for the contact */ public static function maxLocations($contactId) { - $contactLocations = array(); + $contactLocations = []; // find number of location blocks for this contact and adjust value accordinly // get location type from email @@ -284,11 +284,7 @@ public static function maxLocations($contactId) { public static function createCurrentEmployerRelationship($contactID, $organization, $previousEmployerID = NULL, $newContact = FALSE) { //if organization name is passed. CRM-15368,CRM-15547 if ($organization && !is_numeric($organization)) { - $organizationParams['organization_name'] = $organization; - $dedupeParams = CRM_Dedupe_Finder::formatParams($organizationParams, 'Organization'); - - $dedupeParams['check_permission'] = FALSE; - $dupeIDs = CRM_Dedupe_Finder::dupesByParams($dedupeParams, 'Organization', 'Unsupervised'); + $dupeIDs = CRM_Contact_BAO_Contact::getDuplicateContacts(['organization_name' => $organization], 'Organization', 'Unsupervised', [], FALSE); if (is_array($dupeIDs) && !empty($dupeIDs)) { // we should create relationship only w/ first org CRM-4193 @@ -299,17 +295,17 @@ public static function createCurrentEmployerRelationship($contactID, $organizati } else { //create new organization - $newOrg = array( + $newOrg = [ 'contact_type' => 'Organization', 'organization_name' => $organization, - ); + ]; $org = CRM_Contact_BAO_Contact::create($newOrg); $organization = $org->id; } } if ($organization && is_numeric($organization)) { - $cid = array('contact' => $contactID); + $cid = ['contact' => $contactID]; // get the relationship type id of "Employee of" $relTypeId = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_RelationshipType', 'Employee of', 'id', 'name_a_b'); @@ -318,11 +314,11 @@ public static function createCurrentEmployerRelationship($contactID, $organizati } // create employee of relationship - $relationshipParams = array( + $relationshipParams = [ 'is_active' => TRUE, 'relationship_type_id' => $relTypeId . '_a_b', - 'contact_check' => array($organization => TRUE), - ); + 'contact_check' => [$organization => TRUE], + ]; list($valid, $invalid, $duplicate, $saved, $relationshipIds) = CRM_Contact_BAO_Relationship::legacyCreateMultiple($relationshipParams, $cid); @@ -337,7 +333,7 @@ public static function createCurrentEmployerRelationship($contactID, $organizati } // set current employer - self::setCurrentEmployer(array($contactID => $organization)); + self::setCurrentEmployer([$contactID => $organization]); $relationshipParams['relationship_ids'] = $relationshipIds; // Handle related memberships. CRM-3792 @@ -362,7 +358,7 @@ public static function createCurrentEmployerRelationship($contactID, $organizati * @throws CiviCRM_API3_Exception */ public static function currentEmployerRelatedMembership($contactID, $employerID, $relationshipParams, $duplicate = FALSE, $previousEmpID = NULL) { - $ids = array(); + $ids = []; $action = CRM_Core_Action::ADD; //we do not know that triggered relationship record is active. @@ -457,7 +453,7 @@ public static function clearCurrentEmployer($contactId, $employerId = NULL) { if ($relationship->find(TRUE)) { CRM_Contact_BAO_Relationship::setIsActive($relationship->id, FALSE); CRM_Contact_BAO_Relationship::relatedMemberships($contactId, $relMembershipParams, - $ids = array(), + $ids = [], CRM_Core_Action::DELETE ); } @@ -503,7 +499,7 @@ public static function buildOnBehalfForm(&$form, $contactType, $countryID, $stat default: // individual $form->addElement('select', 'prefix_id', ts('Prefix'), - array('' => ts('- prefix -')) + CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'prefix_id') + ['' => ts('- prefix -')] + CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'prefix_id') ); $form->addElement('text', 'first_name', ts('First Name'), $attributes['first_name'] @@ -515,7 +511,7 @@ public static function buildOnBehalfForm(&$form, $contactType, $countryID, $stat $attributes['last_name'] ); $form->addElement('select', 'suffix_id', ts('Suffix'), - array('' => ts('- suffix -')) + CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'suffix_id') + ['' => ts('- suffix -')] + CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'suffix_id') ); } @@ -573,19 +569,19 @@ public static function clearAllEmployee($employerId) { * returns array with links to contact view */ public static function formatContactIDSToLinks($contactIDs, $addViewLink = TRUE, $addEditLink = TRUE, $originalId = NULL) { - $contactLinks = array(); + $contactLinks = []; if (!is_array($contactIDs) || empty($contactIDs)) { return $contactLinks; } // does contact has sufficient permissions. - $permissions = array( + $permissions = [ 'view' => 'view all contacts', 'edit' => 'edit all contacts', 'merge' => 'merge duplicate contacts', - ); + ]; - $permissionedContactIds = array(); + $permissionedContactIds = []; foreach ($permissions as $task => $permission) { // give permission. if (CRM_Core_Permission::check($permission)) { @@ -596,10 +592,10 @@ public static function formatContactIDSToLinks($contactIDs, $addViewLink = TRUE, } // check permission on acl basis. - if (in_array($task, array( + if (in_array($task, [ 'view', 'edit', - ))) { + ])) { $aclPermission = CRM_Core_Permission::VIEW; if ($task == 'edit') { $aclPermission = CRM_Core_Permission::EDIT; @@ -680,10 +676,10 @@ public static function formatContactIDSToLinks($contactIDs, $addViewLink = TRUE, * @return array * array of contact info. */ - public static function contactDetails($componentIds, $componentName, $returnProperties = array()) { - $contactDetails = array(); + public static function contactDetails($componentIds, $componentName, $returnProperties = []) { + $contactDetails = []; if (empty($componentIds) || - !in_array($componentName, array('CiviContribute', 'CiviMember', 'CiviEvent', 'Activity')) + !in_array($componentName, ['CiviContribute', 'CiviMember', 'CiviEvent', 'Activity', 'CiviCase']) ) { return $contactDetails; } @@ -692,7 +688,7 @@ public static function contactDetails($componentIds, $componentName, $returnProp $autocompleteContactSearch = CRM_Core_BAO_Setting::valueOptions(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'contact_autocomplete_options' ); - $returnProperties = array_fill_keys(array_merge(array('sort_name'), + $returnProperties = array_fill_keys(array_merge(['sort_name'], array_keys($autocompleteContactSearch) ), 1); } @@ -706,18 +702,22 @@ public static function contactDetails($componentIds, $componentName, $returnProp } elseif ($componentName == 'Activity') { $compTable = 'civicrm_activity'; - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); + } + elseif ($componentName == 'CiviCase') { + $compTable = 'civicrm_case'; } else { $compTable = 'civicrm_participant'; } - $select = $from = array(); + $select = $from = []; foreach ($returnProperties as $property => $ignore) { - $value = (in_array($property, array( + $value = (in_array($property, [ 'city', 'street_address', - ))) ? 'address' : $property; + 'postal_code', + ])) ? 'address' : $property; switch ($property) { case 'sort_name': if ($componentName == 'Activity') { @@ -726,6 +726,12 @@ public static function contactDetails($componentIds, $componentName, $returnProp $from[$value] = " INNER JOIN civicrm_activity_contact acs ON (acs.activity_id = {$compTable}.id AND acs.record_type_id = {$sourceID}) INNER JOIN civicrm_contact contact ON ( contact.id = acs.contact_id )"; + } + elseif ($componentName == 'CiviCase') { + $select[] = "contact.$property as $property"; + $from[$value] = " +INNER JOIN civicrm_case_contact ccs ON (ccs.case_id = {$compTable}.id) +INNER JOIN civicrm_contact contact ON ( contact.id = ccs.contact_id )"; } else { $select[] = "$property as $property"; @@ -745,6 +751,7 @@ public static function contactDetails($componentIds, $componentName, $returnProp case 'phone': case 'city': case 'street_address': + case 'postal_code': $select[] = "$property as $property"; // Grab target contact properties if this is for activity if ($componentName == 'Activity') { @@ -777,7 +784,7 @@ public static function contactDetails($componentIds, $componentName, $returnProp $fromClause = implode(' ', $from); $selectClause = implode(', ', $select); $whereClause = "{$compTable}.id IN (" . implode(',', $componentIds) . ')'; - $groupBy = CRM_Contact_BAO_Query::getGroupByFromSelectColumns($select, array("{$compTable}.id", 'contact.id')); + $groupBy = CRM_Contact_BAO_Query::getGroupByFromSelectColumns($select, ["{$compTable}.id", 'contact.id']); $query = " SELECT contact.id as contactId, $compTable.id as componentId, $selectClause @@ -802,7 +809,7 @@ public static function contactDetails($componentIds, $componentName, $returnProp * Function handles shared contact address processing. * In this function we just modify submitted values so that new address created for the user * has same address as shared contact address. We copy the address so that search etc will be - * much efficient. + * much more efficient. * * @param array $address * This is associated array which contains submitted form values. @@ -812,23 +819,26 @@ public static function processSharedAddress(&$address) { return; } - // Sharing contact address during create mode is pretty straight forward. - // In update mode we should check following: - // - We should check if user has uncheck shared contact address - // - If yes then unset the master_id or may be just delete the address that copied master - // Normal update process will automatically create new address with submitted values + // In create mode sharing a contact's address is pretty straight forward. + // In update mode we should check if the user stops sharing. If yes: + // - Set the master_id to an empty value + // Normal update process will automatically create new address with submitted values - // 1. loop through entire subnitted address array - $masterAddress = array(); - $skipFields = array('is_primary', 'location_type_id', 'is_billing', 'master_id'); + // 1. loop through entire submitted address array + $skipFields = ['is_primary', 'location_type_id', 'is_billing', 'master_id', 'update_current_employer']; foreach ($address as & $values) { - // 2. check if master id exists, if not continue - if (empty($values['master_id']) || empty($values['use_shared_address'])) { - // we should unset master id when use uncheck share address for existing address - $values['master_id'] = 'null'; + // 2. check if "Use another contact's address" is checked, if not continue + // Additionally, if master_id is set (address was shared), set master_id to empty value. + if (empty($values['use_shared_address'])) { + if (!empty($values['master_id'])) { + $values['master_id'] = ''; + } continue; } + // Set update_current_employer checkbox value + $values['update_current_employer'] = !empty($values['update_current_employer']); + // 3. get the address details for master_id $masterAddress = new CRM_Core_BAO_Address(); $masterAddress->id = CRM_Utils_Array::value('master_id', $values); @@ -866,9 +876,9 @@ public static function processSharedAddress(&$address) { * associated array of contact names */ public static function getAddressShareContactNames(&$addresses) { - $contactNames = array(); + $contactNames = []; // get the list of master id's for address - $masterAddressIds = array(); + $masterAddressIds = []; foreach ($addresses as $key => $addressValue) { if (!empty($addressValue['master_id'])) { $masterAddressIds[] = $addressValue['master_id']; @@ -884,11 +894,11 @@ public static function getAddressShareContactNames(&$addresses) { while ($dao->fetch()) { $contactViewUrl = CRM_Utils_System::url('civicrm/contact/view', "reset=1&cid={$dao->cid}"); - $contactNames[$dao->id] = array( + $contactNames[$dao->id] = [ 'name' => "{$dao->display_name}", 'is_deleted' => $dao->is_deleted, 'contact_id' => $dao->cid, - ); + ]; } } return $contactNames; @@ -899,18 +909,24 @@ public static function getAddressShareContactNames(&$addresses) { * caches, but are backing off from this with every release. Compromise between ease of coding versus * performance versus being accurate at that very instant * - * @param $contactID - * The contactID that was edited / deleted. + * @param bool $isEmptyPrevNextTable + * Should the civicrm_prev_next table be cleared of any contact entries. + * This is currently done from import but not other places and would + * likely affect user experience in unexpected ways. Existing behaviour retained + * ... reluctantly. */ - public static function clearContactCaches($contactID = NULL) { - // clear acl cache if any. - CRM_ACL_BAO_Cache::resetCache(); - - if (empty($contactID)) { - // also clear prev/next dedupe cache - if no contactID passed in + public static function clearContactCaches($isEmptyPrevNextTable = FALSE) { + if (!CRM_Core_Config::isPermitCacheFlushMode()) { + return; + } + if ($isEmptyPrevNextTable) { + // These two calls are redundant in default deployments, but they're + // meaningful if "prevnext" is memory-backed. + Civi::service('prevnext')->deleteItem(); CRM_Core_BAO_PrevNextCache::deleteItem(); } - + // clear acl cache if any. + CRM_ACL_BAO_Cache::resetCache(); CRM_Contact_BAO_GroupContactCache::opportunisticCacheFlush(); } @@ -931,21 +947,21 @@ public static function updateGreeting($params) { $valueID = $id = self::defaultGreeting($contactType, $greeting); } - $filter = array( + $filter = [ 'contact_type' => $contactType, 'greeting_type' => $greeting, - ); + ]; $allGreetings = CRM_Core_PseudoConstant::greeting($filter); $originalGreetingString = $greetingString = CRM_Utils_Array::value($valueID, $allGreetings); if (!$greetingString) { - CRM_Core_Error::fatal(ts('Incorrect greeting value id %1, or no default greeting for this contact type and greeting type.', array(1 => $valueID))); + CRM_Core_Error::fatal(ts('Incorrect greeting value id %1, or no default greeting for this contact type and greeting type.', [1 => $valueID])); } // build return properties based on tokens $greetingTokens = CRM_Utils_Token::getTokens($greetingString); $tokens = CRM_Utils_Array::value('contact', $greetingTokens); - $greetingsReturnProperties = array(); + $greetingsReturnProperties = []; if (is_array($tokens)) { $greetingsReturnProperties = array_fill_keys(array_values($tokens), 1); } @@ -960,7 +976,7 @@ public static function updateGreeting($params) { } //FIXME : apiQuery should handle these clause. - $filterContactFldIds = $filterIds = array(); + $filterContactFldIds = $filterIds = []; $idFldName = $displayFldName = NULL; if (in_array($greeting, CRM_Contact_BAO_Contact::$_greetingTypes)) { $idFldName = $greeting . '_id'; @@ -968,7 +984,7 @@ public static function updateGreeting($params) { } if ($idFldName) { - $queryParams = array(1 => array($contactType, 'String')); + $queryParams = [1 => [$contactType, 'String']]; // if $force == 1 then update all contacts else only // those with NULL greeting or addressee value CRM-9476 @@ -986,7 +1002,7 @@ public static function updateGreeting($params) { if ($limit) { $sql .= " LIMIT 0, %2"; - $queryParams += array(2 => array($limit, 'Integer')); + $queryParams += [2 => [$limit, 'Integer']]; } $dao = CRM_Core_DAO::executeQuery($sql, $queryParams); @@ -1004,14 +1020,14 @@ public static function updateGreeting($params) { } // retrieve only required contact information - $extraParams[] = array('contact_type', '=', $contactType, 0, 0); + $extraParams[] = ['contact_type', '=', $contactType, 0, 0]; // we do token replacement in the replaceGreetingTokens hook list($greetingDetails) = CRM_Utils_Token::getTokenDetails(array_keys($filterContactFldIds), $greetingsReturnProperties, FALSE, FALSE, $extraParams ); // perform token replacement and build update SQL - $contactIds = array(); + $contactIds = []; $cacheFieldQuery = "UPDATE civicrm_contact SET {$greeting}_display = CASE id "; foreach ($greetingDetails as $contactID => $contactDetails) { if (!$processAll && @@ -1076,11 +1092,11 @@ public static function updateGreeting($params) { * @return int|NULL */ public static function defaultGreeting($contactType, $greetingType) { - $contactTypeFilters = array( + $contactTypeFilters = [ 'Individual' => 1, 'Household' => 2, 'Organization' => 3, - ); + ]; if (!isset($contactTypeFilters[$contactType])) { return NULL; } @@ -1095,6 +1111,34 @@ public static function defaultGreeting($contactType, $greetingType) { } } + /** + * Get the tokens that will need to be resolved to populate the contact's greetings. + * + * @param array $contactParams + * + * @return array + * Array of tokens. The ALL ke + */ + public static function getTokensRequiredForContactGreetings($contactParams) { + $tokens = []; + foreach (['addressee', 'email_greeting', 'postal_greeting'] as $greeting) { + $string = ''; + if (!empty($contactParams[$greeting . '_id'])) { + $string = CRM_Core_PseudoConstant::getLabel('CRM_Contact_BAO_Contact', $greeting . '_id', $contactParams[$greeting . '_id']); + } + $string = isset($contactParams[$greeting . '_custom']) ? $contactParams[$greeting . '_custom'] : $string; + if (empty($string)) { + $tokens[$greeting] = []; + } + else { + $tokens[$greeting] = CRM_Utils_Token::getTokens($string); + } + } + $allTokens = array_merge_recursive($tokens['addressee'], $tokens['email_greeting'], $tokens['postal_greeting']); + $tokens['all'] = $allTokens; + return $tokens; + } + /** * Process a greeting template string to produce the individualised greeting text. * diff --git a/CRM/Contact/BAO/ContactType.php b/CRM/Contact/BAO/ContactType.php index a8d318f1ca81..897fcbc6f886 100644 --- a/CRM/Contact/BAO/ContactType.php +++ b/CRM/Contact/BAO/ContactType.php @@ -1,9 +1,9 @@ fetch()) { - $value = array(); + $value = []; CRM_Core_DAO::storeValues($dao, $value); $_cache[$argString][$dao->name] = $value; } @@ -133,7 +134,7 @@ public static function basicTypes($all = FALSE) { public static function basicTypePairs($all = FALSE, $key = 'name') { $subtypes = self::basicTypeInfo($all); - $pairs = array(); + $pairs = []; foreach ($subtypes as $name => $info) { $index = ($key == 'name') ? $name : $info[$key]; $pairs[$index] = $info['label']; @@ -161,10 +162,10 @@ public static function subTypeInfo($contactType = NULL, $all = FALSE, $ignoreCac } if ($_cache === NULL) { - $_cache = array(); + $_cache = []; } if ($contactType && !is_array($contactType)) { - $contactType = array($contactType); + $contactType = [$contactType]; } $argString = $all ? 'CRM_CT_STI_1_' : 'CRM_CT_STI_0_'; @@ -176,7 +177,7 @@ public static function subTypeInfo($contactType = NULL, $all = FALSE, $ignoreCac $cache = CRM_Utils_Cache::singleton(); $_cache[$argString] = $cache->get($argString); if (!$_cache[$argString] || $ignoreCache) { - $_cache[$argString] = array(); + $_cache[$argString] = []; $ctWHERE = ''; if (!empty($contactType)) { @@ -192,11 +193,11 @@ public static function subTypeInfo($contactType = NULL, $all = FALSE, $ignoreCac if ($all === FALSE) { $sql .= " AND subtype.is_active = 1 AND parent.is_active = 1 ORDER BY parent.id"; } - $dao = CRM_Core_DAO::executeQuery($sql, array(), + $dao = CRM_Core_DAO::executeQuery($sql, [], FALSE, 'CRM_Contact_DAO_ContactType' ); while ($dao->fetch()) { - $value = array(); + $value = []; CRM_Core_DAO::storeValues($dao, $value); $value['parent'] = $dao->parent; $value['parent_label'] = $dao->parent_label; @@ -247,7 +248,7 @@ public static function subTypes($contactType = NULL, $all = FALSE, $columnName = public static function subTypePairs($contactType = NULL, $all = FALSE, $labelPrefix = '- ', $ignoreCache = FALSE) { $subtypes = self::subTypeInfo($contactType, $all, $ignoreCache); - $pairs = array(); + $pairs = []; foreach ($subtypes as $name => $info) { $pairs[$name] = $labelPrefix . $info['label']; } @@ -284,7 +285,7 @@ public static function contactTypeInfo($all = FALSE, $reset = FALSE) { } if ($_cache === NULL) { - $_cache = array(); + $_cache = []; } $argString = $all ? 'CRM_CT_CTI_1' : 'CRM_CT_CTI_0'; @@ -292,7 +293,7 @@ public static function contactTypeInfo($all = FALSE, $reset = FALSE) { $cache = CRM_Utils_Cache::singleton(); $_cache[$argString] = $cache->get($argString); if (!$_cache[$argString]) { - $_cache[$argString] = array(); + $_cache[$argString] = []; $sql = " SELECT type.*, parent.name as parent, parent.label as parent_label @@ -305,12 +306,12 @@ public static function contactTypeInfo($all = FALSE, $reset = FALSE) { } $dao = CRM_Core_DAO::executeQuery($sql, - array(), + [], FALSE, 'CRM_Contact_DAO_ContactType' ); while ($dao->fetch()) { - $value = array(); + $value = []; CRM_Core_DAO::storeValues($dao, $value); if (array_key_exists('parent_id', $value)) { $value['parent'] = $dao->parent; @@ -343,7 +344,7 @@ public static function contactTypePairs($all = FALSE, $typeName = NULL, $delimit $typeName = explode(CRM_Core_DAO::VALUE_SEPARATOR, trim($typeName, CRM_Core_DAO::VALUE_SEPARATOR)); } - $pairs = array(); + $pairs = []; if ($typeName) { foreach ($typeName as $type) { if (array_key_exists($type, $types)) { @@ -380,18 +381,19 @@ public static function getSelectElements( static $_cache = NULL; if ($_cache === NULL) { - $_cache = array(); + $_cache = []; } $argString = $all ? 'CRM_CT_GSE_1' : 'CRM_CT_GSE_0'; $argString .= $isSeparator ? '_1' : '_0'; $argString .= $separator; + $argString = CRM_Core_BAO_Cache::cleanKey($argString); if (!array_key_exists($argString, $_cache)) { $cache = CRM_Utils_Cache::singleton(); $_cache[$argString] = $cache->get($argString); if (!$_cache[$argString]) { - $_cache[$argString] = array(); + $_cache[$argString] = []; $sql = " SELECT c.name as child_name , c.label as child_label , c.id as child_id, @@ -409,7 +411,7 @@ public static function getSelectElements( } $sql .= " ORDER BY c.id"; - $values = array(); + $values = []; $dao = CRM_Core_DAO::executeQuery($sql); while ($dao->fetch()) { if (!empty($dao->parent_id)) { @@ -424,12 +426,12 @@ public static function getSelectElements( } if (!isset($values[$pName])) { - $values[$pName] = array(); + $values[$pName] = []; } - $values[$pName][] = array('key' => $key, 'label' => $label); + $values[$pName][] = ['key' => $key, 'label' => $label]; } - $selectElements = array(); + $selectElements = []; foreach ($values as $pName => $elements) { foreach ($elements as $element) { $selectElements[$element['key']] = $element['label']; @@ -460,24 +462,25 @@ public static function isaSubType($subType, $ignoreCache = FALSE) { /** * Retrieve the basic contact type associated with given subType. * - * @param array /string $subType contact subType. - * @return array/string of basicTypes. + * @param array|string $subType contact subType. + * @return array|string + * basicTypes. */ public static function getBasicType($subType) { static $_cache = NULL; if ($_cache === NULL) { - $_cache = array(); + $_cache = []; } $isArray = TRUE; if ($subType && !is_array($subType)) { - $subType = array($subType); + $subType = [$subType]; $isArray = FALSE; } $argString = implode('_', $subType); if (!array_key_exists($argString, $_cache)) { - $_cache[$argString] = array(); + $_cache[$argString] = []; $sql = " SELECT subtype.name as contact_subtype, type.name as contact_type @@ -538,7 +541,7 @@ public static function isExtendsContactType($subType, $contactType, $ignoreCache * of contactTypes */ public static function getCreateNewList() { - $shortCuts = array(); + $shortCuts = []; //@todo FIXME - using the CRM_Core_DAO::VALUE_SEPARATOR creates invalid html - if you can find the form // this is loaded onto then replace with something like '__' & test $separator = CRM_Core_DAO::VALUE_SEPARATOR; @@ -551,12 +554,12 @@ public static function getCreateNewList() { if ($csType = CRM_Utils_Array::value('1', $typeValue)) { $typeUrl .= "&cst=$csType"; } - $shortCut = array( + $shortCut = [ 'path' => 'civicrm/contact/add', 'query' => "$typeUrl&reset=1", 'ref' => "new-$value", 'title' => $value, - ); + ]; if ($csType = CRM_Utils_Array::value('1', $typeValue)) { $shortCuts[$cType]['shortCuts'][] = $shortCut; } @@ -582,7 +585,7 @@ public static function del($contactTypeId) { return FALSE; } - $params = array('id' => $contactTypeId); + $params = ['id' => $contactTypeId]; self::retrieve($params, $typeInfo); $name = $typeInfo['name']; // check if any custom group @@ -613,7 +616,7 @@ public static function del($contactTypeId) { DELETE FROM civicrm_navigation WHERE name = %1"; - $params = array(1 => array("New $name", 'String')); + $params = [1 => ["New $name", 'String']]; $dao = CRM_Core_DAO::executeQuery($sql, $params); CRM_Core_BAO_Navigation::resetNavigation(); } @@ -653,28 +656,27 @@ public static function add(&$params) { } if (!empty($params['id'])) { - $params = array('name' => "New $contactName"); - $newParams = array( + $newParams = [ 'label' => "New $contact", 'is_active' => $active, - ); - CRM_Core_BAO_Navigation::processUpdate($params, $newParams); + ]; + CRM_Core_BAO_Navigation::processUpdate(['name' => "New $contactName"], $newParams); } else { $name = self::getBasicType($contactName); if (!$name) { - return; + return NULL; } - $value = array('name' => "New $name"); + $value = ['name' => "New $name"]; CRM_Core_BAO_Navigation::retrieve($value, $navinfo); - $navigation = array( + $navigation = [ 'label' => "New $contact", 'name' => "New $contactName", 'url' => "civicrm/contact/add?ct=$name&cst=$contactName&reset=1", 'permission' => 'add contacts', 'parent_id' => $navinfo['id'], 'is_active' => $active, - ); + ]; CRM_Core_BAO_Navigation::add($navigation); } CRM_Core_BAO_Navigation::resetNavigation(); @@ -693,14 +695,14 @@ public static function add(&$params) { * @param bool $is_active * Value we want to set the is_active field. * - * @return Object - * DAO object on success, null otherwise + * @return bool + * true if we found and updated the object, else false */ public static function setIsActive($id, $is_active) { - $params = array('id' => $id); + $params = ['id' => $id]; self::retrieve($params, $contactinfo); - $params = array('name' => "New $contactinfo[name]"); - $newParams = array('is_active' => $is_active); + $params = ['name' => "New $contactinfo[name]"]; + $newParams = ['is_active' => $is_active]; CRM_Core_BAO_Navigation::processUpdate($params, $newParams); CRM_Core_BAO_Navigation::resetNavigation(); return CRM_Core_DAO::setFieldValue('CRM_Contact_DAO_ContactType', $id, @@ -833,21 +835,21 @@ public static function hasRelationships($contactId, $contactType) { * * @return array */ - public static function getSubtypeCustomPair($contactType, $subtypeSet = array()) { + public static function getSubtypeCustomPair($contactType, $subtypeSet = []) { if (empty($subtypeSet)) { return $subtypeSet; } - $customSet = $subTypeClause = array(); + $customSet = $subTypeClause = []; foreach ($subtypeSet as $subtype) { $subtype = CRM_Utils_Type::escape($subtype, 'String'); - $subType = CRM_Core_DAO::VALUE_SEPARATOR . $subtype . CRM_Core_DAO::VALUE_SEPARATOR; + $subtype = CRM_Core_DAO::VALUE_SEPARATOR . $subtype . CRM_Core_DAO::VALUE_SEPARATOR; $subTypeClause[] = "extends_entity_column_value LIKE '%{$subtype}%' "; } $query = "SELECT table_name FROM civicrm_custom_group WHERE extends = %1 AND " . implode(" OR ", $subTypeClause); - $dao = CRM_Core_DAO::executeQuery($query, array(1 => array($contactType, 'String'))); + $dao = CRM_Core_DAO::executeQuery($query, [1 => [$contactType, 'String']]); while ($dao->fetch()) { $customSet[] = $dao->table_name; } @@ -868,8 +870,8 @@ public static function getSubtypeCustomPair($contactType, $subtypeSet = array()) public static function deleteCustomSetForSubtypeMigration( $contactID, $contactType, - $oldSubtypeSet = array(), - $newSubtypeSet = array() + $oldSubtypeSet = [], + $newSubtypeSet = [] ) { $oldCustomSet = self::getSubtypeCustomPair($contactType, $oldSubtypeSet); $newCustomSet = self::getSubtypeCustomPair($contactType, $newSubtypeSet); @@ -890,10 +892,11 @@ public static function deleteCustomSetForSubtypeMigration( * Custom group id. * @param array $subtypes * List of subtypes related to which entry is to be removed. + * @param array $subtypesToPreserve * * @return bool */ - public static function deleteCustomRowsOfSubtype($gID, $subtypes = array(), $subtypesToPreserve = array()) { + public static function deleteCustomRowsOfSubtype($gID, $subtypes = [], $subtypesToPreserve = []) { if (!$gID or empty($subtypes)) { return FALSE; } @@ -909,7 +912,7 @@ public static function deleteCustomRowsOfSubtype($gID, $subtypes = array(), $sub } $subtypesToPreserveClause = implode(' AND ', $subtypesToPreserveClause); - $subtypeClause = array(); + $subtypeClause = []; foreach ($subtypes as $subtype) { $subtype = CRM_Utils_Type::escape($subtype, 'String'); $subtypeClause[] = "( civicrm_contact.contact_sub_type LIKE '%" . CRM_Core_DAO::VALUE_SEPARATOR . $subtype . CRM_Core_DAO::VALUE_SEPARATOR . "%'" @@ -941,7 +944,7 @@ public static function deleteCustomRowsOfSubtype($gID, $subtypes = array(), $sub public static function deleteCustomRowsForEntityID($customTable, $entityID) { $customTable = CRM_Utils_Type::escape($customTable, 'String'); $query = "DELETE FROM {$customTable} WHERE entity_id = %1"; - return CRM_Core_DAO::singleValueQuery($query, array(1 => array($entityID, 'Integer'))); + return CRM_Core_DAO::singleValueQuery($query, [1 => [$entityID, 'Integer']]); } } diff --git a/CRM/Contact/BAO/DashboardContact.php b/CRM/Contact/BAO/DashboardContact.php index 15146a1e09a1..f2b3e0ac0f45 100644 --- a/CRM/Contact/BAO/DashboardContact.php +++ b/CRM/Contact/BAO/DashboardContact.php @@ -1,9 +1,9 @@ delete(); // make all the 'add_to_group_id' field of 'civicrm_uf_group table', pointing to this group, as null - $params = array(1 => array($id, 'Integer')); + $params = [1 => [$id, 'Integer']]; $query = "UPDATE civicrm_uf_group SET `add_to_group_id`= NULL WHERE `add_to_group_id` = %1"; CRM_Core_DAO::executeQuery($query, $params); @@ -117,10 +117,10 @@ public static function discard($id) { CRM_Utils_Hook::post('delete', 'Group', $id, $group); // delete the recently created Group - $groupRecent = array( + $groupRecent = [ 'id' => $id, 'type' => 'Group', - ); + ]; CRM_Utils_Recent::del($groupRecent); } @@ -130,8 +130,8 @@ public static function discard($id) { * @param int $id */ public static function getGroupContacts($id) { - $params = array(array('group', 'IN', array(1 => $id), 0, 0)); - list($contacts, $_) = CRM_Contact_BAO_Query::apiQuery($params, array('contact_id')); + $params = [['group', 'IN', [1 => $id], 0, 0]]; + list($contacts, $_) = CRM_Contact_BAO_Query::apiQuery($params, ['contact_id']); return $contacts; } @@ -149,7 +149,7 @@ public static function getGroupContacts($id) { */ public static function memberCount($id, $status = 'Added', $countChildGroups = FALSE) { $groupContact = new CRM_Contact_DAO_GroupContact(); - $groupIds = array($id); + $groupIds = [$id]; if ($countChildGroups) { $groupIds = CRM_Contact_BAO_GroupNesting::getDescendentGroupIds($groupIds); } @@ -195,11 +195,11 @@ public static function memberCount($id, $status = 'Added', $countChildGroups = F * this array contains the list of members for this group id */ public static function getMember($groupID, $useCache = TRUE, $limit = 0) { - $params = array(array('group', '=', $groupID, 0, 0)); - $returnProperties = array('contact_id'); + $params = [['group', '=', $groupID, 0, 0]]; + $returnProperties = ['contact_id']; list($contacts) = CRM_Contact_BAO_Query::apiQuery($params, $returnProperties, NULL, NULL, 0, $limit, $useCache); - $aMembers = array(); + $aMembers = []; foreach ($contacts as $contact) { $aMembers[$contact['contact_id']] = 1; } @@ -278,7 +278,7 @@ public static function getGroups( $flag = $returnProperties && in_array('member_count', $returnProperties) ? 1 : 0; - $groups = array(); + $groups = []; while ($dao->fetch()) { $group = new CRM_Contact_DAO_Group(); if ($flag) { @@ -350,22 +350,33 @@ public static function create(&$params) { CRM_Utils_Hook::pre('create', 'Group', NULL, $params); } + // dev/core#287 Disable child groups if all parents are disabled. + if (!empty($params['id'])) { + $allChildGroupIds = self::getChildGroupIds($params['id']); + foreach ($allChildGroupIds as $childKey => $childValue) { + $parentIds = CRM_Contact_BAO_GroupNesting::getParentGroupIds($childValue); + $activeParentsCount = civicrm_api3('Group', 'getcount', [ + 'id' => ['IN' => $parentIds], + 'is_active' => 1, + ]); + if (count($parentIds) >= 1 && $activeParentsCount <= 1) { + $setDisable = self::setIsActive($childValue, CRM_Utils_Array::value('is_active', $params, 1)); + } + } + } // form the name only if missing: CRM-627 $nameParam = CRM_Utils_Array::value('name', $params, NULL); if (!$nameParam && empty($params['id'])) { $params['name'] = CRM_Utils_String::titleToVar($params['title']); } + if (!empty($params['parents'])) { + $params['parents'] = CRM_Utils_Array::convertCheckboxFormatToArray((array) $params['parents']); + } + // convert params if array type if (isset($params['group_type'])) { - if (is_array($params['group_type'])) { - $params['group_type'] = CRM_Core_DAO::VALUE_SEPARATOR . implode(CRM_Core_DAO::VALUE_SEPARATOR, - $params['group_type'] - ) . CRM_Core_DAO::VALUE_SEPARATOR; - } - else { - $params['group_type'] = CRM_Core_DAO::VALUE_SEPARATOR . $params['group_type'] . CRM_Core_DAO::VALUE_SEPARATOR; - } + $params['group_type'] = CRM_Utils_Array::convertCheckboxFormatToArray((array) $params['group_type']); } else { $params['group_type'] = NULL; @@ -391,17 +402,8 @@ public static function create(&$params) { } } $group = new CRM_Contact_BAO_Group(); - $group->copyValues($params); - //@todo very hacky fix for the fact this function wants to receive 'parents' as an array further down but - // needs it as a separated string for the DB. Preferred approaches are having the copyParams or save fn - // use metadata to translate the array to the appropriate DB type or altering the param in the api layer, - // or at least altering the param in same section as 'group_type' rather than repeating here. However, further down - // we need the $params one to be in it's original form & we are not sure what test coverage we have on that - if (isset($group->parents) && is_array($group->parents)) { - $group->parents = CRM_Core_DAO::VALUE_SEPARATOR . implode(CRM_Core_DAO::VALUE_SEPARATOR, - array_keys($group->parents) - ) . CRM_Core_DAO::VALUE_SEPARATOR; - } + $group->copyValues($params, TRUE); + if (empty($params['id']) && !$nameParam ) { @@ -437,14 +439,11 @@ public static function create(&$params) { ) { // if no parent present and the group doesn't already have any parents, // make sure site group goes as parent - $params['parents'] = array($domainGroupID => 1); - } - elseif (array_key_exists('parents', $params) && !is_array($params['parents'])) { - $params['parents'] = array($params['parents'] => 1); + $params['parents'] = [$domainGroupID]; } if (!empty($params['parents'])) { - foreach ($params['parents'] as $parentId => $dnc) { + foreach ($params['parents'] as $parentId) { if ($parentId && !CRM_Contact_BAO_GroupNesting::isParentChild($parentId, $group->id)) { CRM_Contact_BAO_GroupNesting::add($parentId, $group->id); } @@ -463,12 +462,15 @@ public static function create(&$params) { } if (!empty($params['organization_id'])) { - $groupOrg = $params; - $groupOrg['group_id'] = $group->id; + // dev/core#382 Keeping the id here can cause db errors as it tries to update the wrong record in the Organization table + $groupOrg = [ + 'group_id' => $group->id, + 'organization_id' => $params['organization_id'], + ]; CRM_Contact_BAO_GroupOrganization::add($groupOrg); } - CRM_Utils_System::flushCache(); + self::flushCaches(); CRM_Contact_BAO_GroupContactCache::add($group->id); if (!empty($params['id'])) { @@ -478,7 +480,7 @@ public static function create(&$params) { CRM_Utils_Hook::post('create', 'Group', $group->id, $group); } - $recentOther = array(); + $recentOther = []; if (CRM_Core_Permission::check('edit groups')) { $recentOther['editUrl'] = CRM_Utils_System::url('civicrm/group', 'reset=1&action=update&id=' . $group->id); // currently same permission we are using for delete a group @@ -504,10 +506,10 @@ public static function create(&$params) { * and store it for future use */ public function buildClause() { - $params = array(array('group', 'IN', array($this->id), 0, 0)); + $params = [['group', 'IN', [$this->id], 0, 0]]; if (!empty($params)) { - $tables = $whereTables = array(); + $tables = $whereTables = []; $this->where_clause = CRM_Contact_BAO_Query::getWhereClause($params, NULL, $tables, $whereTables); if (!empty($tables)) { $this->select_tables = serialize($tables); @@ -554,8 +556,8 @@ public static function createSmartGroup(&$params) { * @param bool $isActive * Value we want to set the is_active field. * - * @return CRM_Core_DAO|null - * DAO object on success, NULL otherwise + * @return bool + * true if we found and updated the object, else false */ public static function setIsActive($id, $isActive) { return CRM_Core_DAO::setFieldValue('CRM_Contact_DAO_Group', $id, 'is_active', $isActive); @@ -602,16 +604,11 @@ public static function groupTypeCondition($groupType = NULL, $excludeHidden = TR /** * Get permission relevant clauses. - * CRM-12209 - * - * @param bool $force * * @return array */ - public static function getPermissionClause($force = FALSE) { - static $clause = 1; - static $retrieved = FALSE; - if (!$retrieved || $force) { + public static function getPermissionClause() { + if (!isset(Civi::$statics[__CLASS__]['permission_clause'])) { if (CRM_Core_Permission::check('view all contacts') || CRM_Core_Permission::check('edit all contacts')) { $clause = 1; } @@ -626,9 +623,29 @@ public static function getPermissionClause($force = FALSE) { $clause = '1 = 0'; } } + Civi::$statics[__CLASS__]['permission_clause'] = $clause; + } + return Civi::$statics[__CLASS__]['permission_clause']; + } + + /** + * Flush caches that hold group data. + * + * (Actually probably some overkill at the moment.) + */ + protected static function flushCaches() { + CRM_Utils_System::flushCache(); + $staticCaches = [ + 'CRM_Core_PseudoConstant' => 'groups', + 'CRM_ACL_API' => 'group_permission', + 'CRM_ACL_BAO_ACL' => 'permissioned_groups', + 'CRM_Contact_BAO_Group' => 'permission_clause', + ]; + foreach ($staticCaches as $class => $key) { + if (isset(Civi::$statics[$class][$key])) { + unset(Civi::$statics[$class][$key]); + } } - $retrieved = TRUE; - return $clause; } /** @@ -657,9 +674,10 @@ public static function createHiddenSmartGroup($params) { //save the mapping for search builder if (!$ssId) { //save record in mapping table - $temp = array(); - $mappingParams = array('mapping_type' => 'Search Builder'); - $mapping = CRM_Core_BAO_Mapping::add($mappingParams, $temp); + $mappingParams = [ + 'mapping_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_Mapping', 'mapping_type_id', 'Search Builder'), + ]; + $mapping = CRM_Core_BAO_Mapping::add($mappingParams); $mappingId = $mapping->id; } else { @@ -677,7 +695,7 @@ public static function createHiddenSmartGroup($params) { //create/update saved search record. $savedSearch = new CRM_Contact_BAO_SavedSearch(); $savedSearch->id = $ssId; - $savedSearch->form_values = serialize($params['form_values']); + $savedSearch->form_values = serialize(CRM_Contact_BAO_Query::convertFormValues($params['form_values'])); $savedSearch->mapping_id = $mappingId; $savedSearch->search_custom_id = CRM_Utils_Array::value('search_custom_id', $params); $savedSearch->save(); @@ -693,20 +711,31 @@ public static function createHiddenSmartGroup($params) { } else { //create group only when new saved search. - $groupParams = array( + $groupParams = [ 'title' => "Hidden Smart Group {$ssId}", 'is_active' => CRM_Utils_Array::value('is_active', $params, 1), 'is_hidden' => CRM_Utils_Array::value('is_hidden', $params, 1), 'group_type' => CRM_Utils_Array::value('group_type', $params), 'visibility' => CRM_Utils_Array::value('visibility', $params), 'saved_search_id' => $ssId, - ); + ]; $smartGroup = self::create($groupParams); $smartGroupId = $smartGroup->id; } - return array($smartGroupId, $ssId); + // Update mapping with the name and description of the hidden smart group. + if ($mappingId) { + $mappingParams = [ + 'id' => $mappingId, + 'name' => CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Group', $smartGroupId, 'name', 'id'), + 'description' => CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Group', $smartGroupId, 'description', 'id'), + 'mapping_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_Mapping', 'mapping_type_id', 'Search Builder'), + ]; + CRM_Core_BAO_Mapping::add($mappingParams); + } + + return [$smartGroupId, $ssId]; } /** @@ -722,7 +751,7 @@ public static function createHiddenSmartGroup($params) { * @todo there seems little reason for the small number of functions that call this to pass in * params that then need to be translated in this function since they are coding them when calling */ - static public function getGroupListSelector(&$params) { + public static function getGroupListSelector(&$params) { // format the params $params['offset'] = ($params['page'] - 1) * $params['rp']; $params['rowCount'] = $params['rp']; @@ -741,9 +770,9 @@ static public function getGroupListSelector(&$params) { } // format params and add links - $groupList = array(); + $groupList = []; foreach ($groups as $id => $value) { - $group = array(); + $group = []; $group['group_id'] = $value['id']; $group['count'] = $value['count']; $group['title'] = $value['title']; @@ -752,12 +781,12 @@ static public function getGroupListSelector(&$params) { if (empty($params['parent_id']) && !empty($value['parents'])) { $group['parent_id'] = $value['parents']; $groupIds = explode(',', $value['parents']); - $title = array(); + $title = []; foreach ($groupIds as $gId) { $title[] = $allGroups[$gId]; } $group['title'] .= '
' . ts('Child of') . ': ' . implode(', ', $title) . '
'; - $value['class'] = array_diff($value['class'], array('crm-row-parent')); + $value['class'] = array_diff($value['class'], ['crm-row-parent']); } $group['DT_RowId'] = 'row_' . $value['id']; if (empty($params['parentsOnly'])) { @@ -768,7 +797,7 @@ static public function getGroupListSelector(&$params) { } } $group['DT_RowClass'] = 'crm-entity ' . implode(' ', $value['class']); - $group['DT_RowAttr'] = array(); + $group['DT_RowAttr'] = []; $group['DT_RowAttr']['data-id'] = $value['id']; $group['DT_RowAttr']['data-entity'] = 'group'; @@ -791,10 +820,10 @@ static public function getGroupListSelector(&$params) { array_push($groupList, $group); } - $groupsDT = array(); + $groupsDT = []; $groupsDT['data'] = $groupList; - $groupsDT['recordsTotal'] = $params['total']; - $groupsDT['recordsFiltered'] = $params['total']; + $groupsDT['recordsTotal'] = !empty($params['total']) ? $params['total'] : NULL; + $groupsDT['recordsFiltered'] = !empty($params['total']) ? $params['total'] : NULL; return $groupsDT; } @@ -808,12 +837,9 @@ static public function getGroupListSelector(&$params) { * @return array */ public static function getGroupList(&$params) { - $config = CRM_Core_Config::singleton(); - $whereClause = self::whereClause($params, FALSE); - //$this->pagerAToZ( $whereClause, $params ); - + $limit = ""; if (!empty($params['rowCount']) && $params['rowCount'] > 0 ) { @@ -842,7 +868,7 @@ public static function getGroupList(&$params) { ON contact.id = gOrg.organization_id "; //get the Organization ID - $orgID = CRM_Utils_Request::retrieve('oid', 'Positive', CRM_Core_DAO::$_nullObject); + $orgID = CRM_Utils_Request::retrieve('oid', 'Positive'); if ($orgID) { $where = " AND gOrg.organization_id = {$orgID}"; } @@ -864,7 +890,7 @@ public static function getGroupList(&$params) { //FIXME CRM-4418, now we are handling delete separately //if we introduce 'delete for group' make sure to handle here. - $groupPermissions = array(CRM_Core_Permission::VIEW); + $groupPermissions = [CRM_Core_Permission::VIEW]; if (CRM_Core_Permission::check('edit groups')) { $groupPermissions[] = CRM_Core_Permission::EDIT; $groupPermissions[] = CRM_Core_Permission::DELETE; @@ -873,136 +899,128 @@ public static function getGroupList(&$params) { // CRM-9936 $reservedPermission = CRM_Core_Permission::check('administer reserved groups'); - $links = self::actionLinks(); + $links = self::actionLinks($params); $allTypes = CRM_Core_OptionGroup::values('group_type'); - $values = $groupsToCount = array(); + $values = []; $visibility = CRM_Core_SelectValues::ufVisibility(); while ($object->fetch()) { - $permission = CRM_Contact_BAO_Group::checkPermission($object->id, $object->title); - //@todo CRM-12209 introduced an ACL check in the whereClause function - // it may be that this checking is now obsolete - or that what remains - // should be removed to the whereClause (which is also accessed by getCount) - - if ($permission) { - $newLinks = $links; - $values[$object->id] = array( - 'class' => array(), - 'count' => '0', + $newLinks = $links; + $values[$object->id] = [ + 'class' => [], + 'count' => '0', + ]; + CRM_Core_DAO::storeValues($object, $values[$object->id]); + + if ($object->saved_search_id) { + $values[$object->id]['title'] .= ' (' . ts('Smart Group') . ')'; + // check if custom search, if so fix view link + $customSearchID = CRM_Core_DAO::getFieldValue( + 'CRM_Contact_DAO_SavedSearch', + $object->saved_search_id, + 'search_custom_id' ); - CRM_Core_DAO::storeValues($object, $values[$object->id]); - - if ($object->saved_search_id) { - $values[$object->id]['title'] .= ' (' . ts('Smart Group') . ')'; - // check if custom search, if so fix view link - $customSearchID = CRM_Core_DAO::getFieldValue( - 'CRM_Contact_DAO_SavedSearch', - $object->saved_search_id, - 'search_custom_id' - ); - - if ($customSearchID) { - $newLinks[CRM_Core_Action::VIEW]['url'] = 'civicrm/contact/search/custom'; - $newLinks[CRM_Core_Action::VIEW]['qs'] = "reset=1&force=1&ssID={$object->saved_search_id}"; - } + + if ($customSearchID) { + $newLinks[CRM_Core_Action::VIEW]['url'] = 'civicrm/contact/search/custom'; + $newLinks[CRM_Core_Action::VIEW]['qs'] = "reset=1&force=1&ssID={$object->saved_search_id}"; } + } - $action = array_sum(array_keys($newLinks)); + $action = array_sum(array_keys($newLinks)); - // CRM-9936 - if (array_key_exists('is_reserved', $object)) { - //if group is reserved and I don't have reserved permission, suppress delete/edit - if ($object->is_reserved && !$reservedPermission) { - $action -= CRM_Core_Action::DELETE; - $action -= CRM_Core_Action::UPDATE; - $action -= CRM_Core_Action::DISABLE; - } + // CRM-9936 + if (array_key_exists('is_reserved', $object)) { + //if group is reserved and I don't have reserved permission, suppress delete/edit + if ($object->is_reserved && !$reservedPermission) { + $action -= CRM_Core_Action::DELETE; + $action -= CRM_Core_Action::UPDATE; + $action -= CRM_Core_Action::DISABLE; } + } - if (array_key_exists('is_active', $object)) { - if ($object->is_active) { - $action -= CRM_Core_Action::ENABLE; - } - else { - $values[$object->id]['class'][] = 'disabled'; - $action -= CRM_Core_Action::VIEW; - $action -= CRM_Core_Action::DISABLE; - } + if (array_key_exists('is_active', $object)) { + if ($object->is_active) { + $action -= CRM_Core_Action::ENABLE; } + else { + $values[$object->id]['class'][] = 'disabled'; + $action -= CRM_Core_Action::VIEW; + $action -= CRM_Core_Action::DISABLE; + } + } - $action = $action & CRM_Core_Action::mask($groupPermissions); - - $values[$object->id]['visibility'] = $visibility[$values[$object->id]['visibility']]; + $action = $action & CRM_Core_Action::mask($groupPermissions); - $groupsToCount[$object->saved_search_id ? 'civicrm_group_contact_cache' : 'civicrm_group_contact'][] = $object->id; + $values[$object->id]['visibility'] = $visibility[$values[$object->id]['visibility']]; - if (isset($values[$object->id]['group_type'])) { - $groupTypes = explode(CRM_Core_DAO::VALUE_SEPARATOR, - substr($values[$object->id]['group_type'], 1, -1) - ); - $types = array(); - foreach ($groupTypes as $type) { - $types[] = CRM_Utils_Array::value($type, $allTypes); - } - $values[$object->id]['group_type'] = implode(', ', $types); + if (isset($values[$object->id]['group_type'])) { + $groupTypes = explode(CRM_Core_DAO::VALUE_SEPARATOR, + substr($values[$object->id]['group_type'], 1, -1) + ); + $types = []; + foreach ($groupTypes as $type) { + $types[] = CRM_Utils_Array::value($type, $allTypes); } + $values[$object->id]['group_type'] = implode(', ', $types); + } + if ($action) { $values[$object->id]['action'] = CRM_Core_Action::formLink($newLinks, $action, - array( + [ 'id' => $object->id, 'ssid' => $object->saved_search_id, - ), + ], ts('more'), FALSE, 'group.selector.row', 'Group', $object->id ); + } - // If group has children, add class for link to view children - $values[$object->id]['is_parent'] = FALSE; - if (array_key_exists('children', $values[$object->id])) { - $values[$object->id]['class'][] = "crm-group-parent"; - $values[$object->id]['is_parent'] = TRUE; - } + // If group has children, add class for link to view children + $values[$object->id]['is_parent'] = FALSE; + if (array_key_exists('children', $values[$object->id])) { + $values[$object->id]['class'][] = "crm-group-parent"; + $values[$object->id]['is_parent'] = TRUE; + } - // If group is a child, add child class - if (array_key_exists('parents', $values[$object->id])) { - $values[$object->id]['class'][] = "crm-group-child"; - } + // If group is a child, add child class + if (array_key_exists('parents', $values[$object->id])) { + $values[$object->id]['class'][] = "crm-group-child"; + } - if ($groupOrg) { - if ($object->org_id) { - $contactUrl = CRM_Utils_System::url('civicrm/contact/view', "reset=1&cid={$object->org_id}"); - $values[$object->id]['org_info'] = "{$object->org_name}"; - } - else { - $values[$object->id]['org_info'] = ''; // Empty cell - } + if ($groupOrg) { + if ($object->org_id) { + $contactUrl = CRM_Utils_System::url('civicrm/contact/view', "reset=1&cid={$object->org_id}"); + $values[$object->id]['org_info'] = "{$object->org_name}"; } else { - $values[$object->id]['org_info'] = NULL; // Collapsed column if all cells are NULL - } - if ($object->created_id) { - $contactUrl = CRM_Utils_System::url('civicrm/contact/view', "reset=1&cid={$object->created_id}"); - $values[$object->id]['created_by'] = "{$object->created_by}"; + // Empty cell + $values[$object->id]['org_info'] = ''; } } - } + else { + // Collapsed column if all cells are NULL + $values[$object->id]['org_info'] = NULL; + } + if ($object->created_id) { + $contactUrl = CRM_Utils_System::url('civicrm/contact/view', "reset=1&cid={$object->created_id}"); + $values[$object->id]['created_by'] = "{$object->created_by}"; + } - // Get group counts - executes one query for regular groups and another for smart groups - foreach ($groupsToCount as $table => $groups) { - $where = "g.group_id IN (" . implode(',', $groups) . ")"; - if ($table == 'civicrm_group_contact') { - $where .= " AND g.status = 'Added'"; + // By default, we try to get a count of the contacts in each group + // to display to the user on the Manage Group page. However, if + // that will result in the cache being regenerated, then dipslay + // "unknown" instead to avoid a long wait for the user. + if (CRM_Contact_BAO_GroupContactCache::shouldGroupBeRefreshed($object->id)) { + $values[$object->id]['count'] = ts('unknown'); } - // Exclude deleted contacts - $where .= " and c.id = g.contact_id AND c.is_deleted = 0"; - $dao = CRM_Core_DAO::executeQuery("SELECT g.group_id, COUNT(g.id) as `count` FROM $table g, civicrm_contact c WHERE $where GROUP BY g.group_id"); - while ($dao->fetch()) { - $values[$dao->group_id]['count'] = $dao->count; + else { + $values[$object->id]['count'] = civicrm_api3('Contact', 'getcount', ['group' => $object->id]); } } @@ -1039,7 +1057,7 @@ public static function getGroupsHierarchy( $titleOnly = FALSE ) { if (empty($groupIDs)) { - return array(); + return []; } $groupIdString = '(' . implode(',', array_keys($groupIDs)) . ')'; @@ -1051,18 +1069,18 @@ public static function getGroupsHierarchy( // separators in front of the name to give it a visual offset. // Instead of recursively making mysql queries, we'll make one big // query and build the hierarchy with the algorithm below. - $groups = array(); - $args = array(1 => array($groupIdString, 'String')); + $groups = []; + $args = [1 => [$groupIdString, 'String']]; $query = " SELECT id, title, description, visibility, parents FROM civicrm_group WHERE id IN $groupIdString "; if ($parents) { - // group can have > 1 parent so parents may be comma separated list (eg. '1,2,5'). We just grab and match on 1st parent. + // group can have > 1 parent so parents may be comma separated list (eg. '1,2,5'). $parentArray = explode(',', $parents); - $parent = $parentArray[0]; - $args[2] = array($parent, 'Integer'); + $parent = self::filterActiveGroups($parentArray); + $args[2] = [$parent, 'Integer']; $query .= " AND SUBSTRING_INDEX(parents, ',', 1) = %2"; } $query .= " ORDER BY title"; @@ -1071,88 +1089,74 @@ public static function getGroupsHierarchy( // Sort the groups into the correct storage by the parent // $roots represent the current leaf nodes that need to be checked for // children. $rows represent the unplaced nodes - $roots = $rows = $allGroups = array(); + // $tree contains the child nodes based on their parent_id. + $roots = []; + $tree = []; while ($dao->fetch()) { - $allGroups[$dao->id] = array( - 'title' => $dao->title, - 'visibility' => $dao->visibility, - 'description' => $dao->description, - ); - - if ($dao->parents == $parents) { - $roots[] = array( + if ($dao->parents) { + $parentArray = explode(',', $dao->parents); + $parent = self::filterActiveGroups($parentArray); + $tree[$parent][] = [ 'id' => $dao->id, - 'prefix' => '', 'title' => $dao->title, - ); + 'visibility' => $dao->visibility, + 'description' => $dao->description, + ]; } else { - // group can have > 1 parent so $dao->parents may be comma separated list (eg. '1,2,5'). Grab and match on 1st parent. - $parentArray = explode(',', $dao->parents); - $parent = $parentArray[0]; - $rows[] = array( + $roots[] = [ 'id' => $dao->id, - 'prefix' => '', 'title' => $dao->title, - 'parents' => $parent, - ); + 'visibility' => $dao->visibility, + 'description' => $dao->description, + ]; } } $dao->free(); - // While we have nodes left to build, shift the first (alphabetically) - // node of the list, place it in our groups list and loop through the - // list of unplaced nodes to find its children. We make a copy to - // iterate through because we must modify the unplaced nodes list - // during the loop. - while (count($roots)) { - $new_roots = array(); - $current_rows = $rows; - $root = array_shift($roots); - $groups[$root['id']] = array($root['prefix'], $root['title']); - - // As you find the children, append them to the end of the new set - // of roots (maintain alphabetical ordering). Also remove the node - // from the set of unplaced nodes. - if (is_array($current_rows)) { - foreach ($current_rows as $key => $row) { - if ($row['parents'] == $root['id']) { - $new_roots[] = array( - 'id' => $row['id'], - 'prefix' => $groups[$root['id']][0] . $spacer, - 'title' => $row['title'], - ); - unset($rows[$key]); - } - } - } - //As a group, insert the new roots into the beginning of the roots - //list. This maintains the hierarchical ordering of the tags. - $roots = array_merge($new_roots, $roots); + $hierarchy = []; + for ($i = 0; $i < count($roots); $i++) { + self::buildGroupHierarchy($hierarchy, $roots[$i], $tree, $titleOnly, $spacer, 0); } + return $hierarchy; + } + + /** + * Build a list with groups on alphabetical order and child groups after the parent group. + * + * This is a recursive function filling the $hierarchy parameter. + * + * @param $hierarchy + * @param $group + * @param $tree + * @param $titleOnly + * @param $spacer + * @param $level + */ + private static function buildGroupHierarchy(&$hierarchy, $group, $tree, $titleOnly, $spacer, $level) { + $spaces = str_repeat($spacer, $level); - // below is the redundant looping to ensure child groups are populated in the case where user does not have - // access to parent groups ( esp. using ACL permissions and logged in user can assess only child groups ) - foreach ($rows as $value) { - $groups[$value['id']] = array($value['prefix'], $value['title']); + if ($titleOnly) { + $hierarchy[$group['id']] = $spaces . $group['title']; } - // Prefix titles with the calcuated spacing to give the visual - // appearance of ordering when transformed into HTML in the form layer. Add description and visibility. - $groupsReturn = array(); - foreach ($groups as $key => $value) { - if ($titleOnly) { - $groupsReturn[$key] = $value[0] . $value[1]; - } - else { - $groupsReturn[$key] = array( - 'title' => $value[0] . $value[1], - 'description' => $allGroups[$key]['description'], - 'visibility' => $allGroups[$key]['visibility'], - ); + else { + $hierarchy[$group['id']] = [ + 'title' => $spaces . $group['title'], + 'description' => $group['description'], + 'visibility' => $group['visibility'], + ]; + } + + // For performance reasons we use a for loop rather than a foreach. + // Metrics for performance in an installation with 2867 groups a foreach + // caused the function getGroupsHierarchy with a foreach execution takes + // around 2.2 seoonds (2,200 ms). + // Changing to a for loop execustion takes around 0.02 seconds (20 ms). + if (isset($tree[$group['id']]) && is_array($tree[$group['id']])) { + for ($i = 0; $i < count($tree[$group['id']]); $i++) { + self::buildGroupHierarchy($hierarchy, $tree[$group['id']][$i], $tree, $titleOnly, $spacer, $level + 1); } } - - return $groupsReturn; } /** @@ -1183,15 +1187,15 @@ public static function getGroupCount(&$params) { * @return string */ public static function whereClause(&$params, $sortBy = TRUE, $excludeHidden = TRUE) { - $values = array(); + $values = []; $title = CRM_Utils_Array::value('title', $params); if ($title) { $clauses[] = "groups.title LIKE %1"; if (strpos($title, '%') !== FALSE) { - $params[1] = array($title, 'String', FALSE); + $params[1] = [$title, 'String', FALSE]; } else { - $params[1] = array($title, 'String', TRUE); + $params[1] = [$title, 'String', TRUE]; } } @@ -1201,14 +1205,14 @@ public static function whereClause(&$params, $sortBy = TRUE, $excludeHidden = TR if (!empty($types)) { $clauses[] = 'groups.group_type LIKE %2'; $typeString = CRM_Core_DAO::VALUE_SEPARATOR . implode(CRM_Core_DAO::VALUE_SEPARATOR, $types) . CRM_Core_DAO::VALUE_SEPARATOR; - $params[2] = array($typeString, 'String', TRUE); + $params[2] = [$typeString, 'String', TRUE]; } } $visibility = CRM_Utils_Array::value('visibility', $params); if ($visibility) { $clauses[] = 'groups.visibility = %3'; - $params[3] = array($visibility, 'String'); + $params[3] = [$visibility, 'String']; } $groupStatus = CRM_Utils_Array::value('status', $params); @@ -1216,12 +1220,12 @@ public static function whereClause(&$params, $sortBy = TRUE, $excludeHidden = TR switch ($groupStatus) { case 1: $clauses[] = 'groups.is_active = 1'; - $params[4] = array($groupStatus, 'Integer'); + $params[4] = [$groupStatus, 'Integer']; break; case 2: $clauses[] = 'groups.is_active = 0'; - $params[4] = array($groupStatus, 'Integer'); + $params[4] = [$groupStatus, 'Integer']; break; case 3: @@ -1239,16 +1243,16 @@ public static function whereClause(&$params, $sortBy = TRUE, $excludeHidden = TR $parent_id = CRM_Utils_Array::value('parent_id', $params); if ($parent_id) { $clauses[] = 'groups.id IN (SELECT child_group_id FROM civicrm_group_nesting WHERE parent_group_id = %5)'; - $params[5] = array($parent_id, 'Integer'); + $params[5] = [$parent_id, 'Integer']; } if ($createdBy = CRM_Utils_Array::value('created_by', $params)) { $clauses[] = "createdBy.sort_name LIKE %6"; if (strpos($createdBy, '%') !== FALSE) { - $params[6] = array($createdBy, 'String', FALSE); + $params[6] = [$createdBy, 'String', FALSE]; } else { - $params[6] = array($createdBy, 'String', TRUE); + $params[6] = [$createdBy, 'String', TRUE]; } } @@ -1271,37 +1275,42 @@ public static function whereClause(&$params, $sortBy = TRUE, $excludeHidden = TR * @return array * array of action links */ - public static function actionLinks() { - $links = array( - CRM_Core_Action::VIEW => array( - 'name' => ts('Contacts'), + public static function actionLinks($params) { + // If component_mode is set we change the "View" link to match the requested component type + if (!isset($params['component_mode'])) { + $params['component_mode'] = CRM_Contact_BAO_Query::MODE_CONTACTS; + } + $modeValue = CRM_Contact_Form_Search::getModeValue($params['component_mode']); + $links = [ + CRM_Core_Action::VIEW => [ + 'name' => $modeValue['selectorLabel'], 'url' => 'civicrm/group/search', - 'qs' => 'reset=1&force=1&context=smog&gid=%%id%%', + 'qs' => 'reset=1&force=1&context=smog&gid=%%id%%&component_mode=' . $params['component_mode'], 'title' => ts('Group Contacts'), - ), - CRM_Core_Action::UPDATE => array( + ], + CRM_Core_Action::UPDATE => [ 'name' => ts('Settings'), 'url' => 'civicrm/group', 'qs' => 'reset=1&action=update&id=%%id%%', 'title' => ts('Edit Group'), - ), - CRM_Core_Action::DISABLE => array( + ], + CRM_Core_Action::DISABLE => [ 'name' => ts('Disable'), 'ref' => 'crm-enable-disable', 'title' => ts('Disable Group'), - ), - CRM_Core_Action::ENABLE => array( + ], + CRM_Core_Action::ENABLE => [ 'name' => ts('Enable'), 'ref' => 'crm-enable-disable', 'title' => ts('Enable Group'), - ), - CRM_Core_Action::DELETE => array( + ], + CRM_Core_Action::DELETE => [ 'name' => ts('Delete'), 'url' => 'civicrm/group', 'qs' => 'reset=1&action=delete&id=%%id%%', 'title' => ts('Delete Group'), - ), - ); + ], + ]; return $links; } @@ -1340,4 +1349,68 @@ protected function assignTestValue($fieldName, &$fieldDef, $counter) { } } + /** + * Get child group ids + * + * @param array $regularGroupIDs + * Parent Group IDs + * + * @return array + */ + public static function getChildGroupIds($regularGroupIDs) { + $childGroupIDs = []; + + foreach ((array) $regularGroupIDs as $regularGroupID) { + // temporary store the child group ID(s) of regular group identified by $id, + // later merge with main child group array + $tempChildGroupIDs = []; + // check that the regular group has any child group, if not then continue + if ($childrenFound = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Group', $regularGroupID, 'children')) { + $tempChildGroupIDs[] = $childrenFound; + } + else { + continue; + } + // as civicrm_group.children stores multiple group IDs in comma imploded string format, + // so we need to convert it into array of child group IDs first + $tempChildGroupIDs = explode(',', implode(',', $tempChildGroupIDs)); + $childGroupIDs = array_merge($childGroupIDs, $tempChildGroupIDs); + // recursively fetch the child group IDs + while (count($tempChildGroupIDs)) { + $tempChildGroupIDs = self::getChildGroupIds($tempChildGroupIDs); + if (count($tempChildGroupIDs)) { + $childGroupIDs = array_merge($childGroupIDs, $tempChildGroupIDs); + } + } + } + + return $childGroupIDs; + } + + /** + * Check parent groups and filter out the disabled ones. + * + * @param array $parentArray + * Array of group Ids. + * + * @return int + */ + public static function filterActiveGroups($parentArray) { + if (count($parentArray) > 1) { + $result = civicrm_api3('Group', 'get', [ + 'id' => ['IN' => $parentArray], + 'is_active' => TRUE, + 'return' => 'id', + ]); + $activeParentGroupIDs = CRM_Utils_Array::collect('id', $result['values']); + foreach ($parentArray as $key => $groupID) { + if (!array_key_exists($groupID, $activeParentGroupIDs)) { + unset($parentArray[$key]); + } + } + } + + return reset($parentArray); + } + } diff --git a/CRM/Contact/BAO/GroupContact.php b/CRM/Contact/BAO/GroupContact.php index c25192a2b1fd..c4540d79d53f 100644 --- a/CRM/Contact/BAO/GroupContact.php +++ b/CRM/Contact/BAO/GroupContact.php @@ -1,9 +1,9 @@ copyValues($params); - CRM_Contact_BAO_SubscriptionHistory::create($params); $groupContact->save(); + + // Lookup existing info for the sake of subscription history + if (!empty($params['id'])) { + $groupContact->find(TRUE); + $params = $groupContact->toArray(); + } + CRM_Contact_BAO_SubscriptionHistory::create($params); + + CRM_Utils_Hook::post($hook, 'GroupContact', $groupContact->id, $groupContact); + return $groupContact; } @@ -74,12 +84,7 @@ public static function add(&$params) { * @return bool */ public static function dataExists(&$params) { - // return if no data present - if ($params['group_id'] == 0) { - return FALSE; - } - - return TRUE; + return (!empty($params['id']) || (!empty($params['group_id']) && !empty($params['contact_id']))); } /** @@ -134,25 +139,20 @@ public static function addContactsToGroup( $status = 'Added', $tracking = NULL ) { + if (empty($contactIds) || empty($groupId)) { + return []; + } CRM_Utils_Hook::pre('create', 'GroupContact', $groupId, $contactIds); list($numContactsAdded, $numContactsNotAdded) = self::bulkAddContactsToGroup($contactIds, $groupId, $method, $status, $tracking); - // also reset the acl cache - $config = CRM_Core_Config::singleton(); - if (!$config->doNotResetCache) { - CRM_ACL_BAO_Cache::resetCache(); - } - - // reset the group contact cache for all group(s) - // if this group is being used as a smart group - CRM_Contact_BAO_GroupContactCache::opportunisticCacheFlush(); + CRM_Contact_BAO_Contact_Utils::clearContactCaches(); CRM_Utils_Hook::post('create', 'GroupContact', $groupId, $contactIds); - return array(count($contactIds), $numContactsAdded, $numContactsNotAdded); + return [count($contactIds), $numContactsAdded, $numContactsNotAdded]; } /** @@ -178,7 +178,7 @@ public static function removeContactsFromGroup( $tracking = NULL ) { if (!is_array($contactIds)) { - return array(0, 0, 0); + return [0, 0, 0]; } if ($status == 'Removed' || $status == 'Deleted') { @@ -202,14 +202,14 @@ public static function removeContactsFromGroup( if ($status == 'Deleted') { $query = "DELETE FROM civicrm_group_contact WHERE contact_id=$contactId AND group_id=$groupId"; $dao = CRM_Core_DAO::executeQuery($query); - $historyParams = array( + $historyParams = [ 'group_id' => $groupId, 'contact_id' => $contactId, 'status' => $status, 'method' => $method, 'date' => $date, 'tracking' => $tracking, - ); + ]; CRM_Contact_BAO_SubscriptionHistory::create($historyParams); } else { @@ -228,39 +228,25 @@ public static function removeContactsFromGroup( } //now we grant the negative membership to contact if not member. CRM-3711 - $historyParams = array( + $historyParams = [ 'group_id' => $groupId, 'contact_id' => $contactId, 'status' => $status, 'method' => $method, 'date' => $date, 'tracking' => $tracking, - ); + ]; CRM_Contact_BAO_SubscriptionHistory::create($historyParams); $groupContact->status = $status; $groupContact->save(); } } - // also reset the acl cache - $config = CRM_Core_Config::singleton(); - if (!$config->doNotResetCache) { - CRM_ACL_BAO_Cache::resetCache(); - } - - // reset the group contact cache for all group(s) - // if this group is being used as a smart group - // @todo consider what to do here - it feels like we should either - // 1) just invalidate the specific group's cache(& perhaps any parents) & let cron do it's thing or - // possibly clear this specific groups cache, or just call opportunisticCacheFlush() - which would have the - // same effect as the remove call. The reservation about that is that it is no more aggressive for the group that - // we know is altered than for all the others, or perhaps, more the point with it's parents & groups that use it in - // their criteria. - CRM_Contact_BAO_GroupContactCache::remove(); + CRM_Contact_BAO_Contact_Utils::clearContactCaches(); CRM_Utils_Hook::post($op, 'GroupContact', $groupId, $contactIds); - return array(count($contactIds), $numContactsRemoved, $numContactsNotRemoved); + return [count($contactIds), $numContactsRemoved, $numContactsNotRemoved]; } /** @@ -299,7 +285,7 @@ public static function getGroupList($contactId = 0, $visibility = FALSE) { $group->query($sql); - $values = array(); + $values = []; while ($group->fetch()) { $values[$group->id] = $group->title; } @@ -329,7 +315,10 @@ public static function getGroupList($contactId = 0, $visibility = FALSE) { * * @param int $groupId * - * @return array|int $values + * @param bool $includeSmartGroups + * Include or Exclude Smart Group(s) + * + * @return array|int * the relevant data object values for the contact or the total count when $count is TRUE */ public static function getContactGroup( @@ -340,7 +329,8 @@ public static function getContactGroup( $ignorePermission = FALSE, $onlyPublicGroups = FALSE, $excludeHidden = TRUE, - $groupId = NULL + $groupId = NULL, + $includeSmartGroups = FALSE ) { if ($count) { $select = 'SELECT count(DISTINCT civicrm_group_contact.id)'; @@ -357,27 +347,28 @@ public static function getContactGroup( civicrm_subscription_history.method as method'; } - $where = " WHERE contact_a.id = %1 AND civicrm_group.is_active = 1 AND saved_search_id IS NULL"; - + $where = " WHERE contact_a.id = %1 AND civicrm_group.is_active = 1"; + if (!$includeSmartGroups) { + $where .= " AND saved_search_id IS NULL"; + } if ($excludeHidden) { $where .= " AND civicrm_group.is_hidden = 0 "; } - - $params = array(1 => array($contactId, 'Integer')); + $params = [1 => [$contactId, 'Integer']]; if (!empty($status)) { $where .= ' AND civicrm_group_contact.status = %2'; - $params[2] = array($status, 'String'); + $params[2] = [$status, 'String']; } if (!empty($groupId)) { $where .= " AND civicrm_group.id = %3 "; - $params[3] = array($groupId, 'Integer'); + $params[3] = [$groupId, 'Integer']; } - $tables = array( + $tables = [ 'civicrm_group_contact' => 1, 'civicrm_group' => 1, 'civicrm_subscription_history' => 1, - ); - $whereTables = array(); + ]; + $whereTables = []; if ($ignorePermission) { $permission = ' ( 1 ) '; } @@ -410,7 +401,7 @@ public static function getContactGroup( } else { $dao = CRM_Core_DAO::executeQuery($sql, $params); - $values = array(); + $values = []; while ($dao->fetch()) { $id = $dao->civicrm_group_contact_id; $values[$id]['id'] = $id; @@ -479,10 +470,10 @@ public static function getMembershipDetail($contactId, $groupID, $method = 'Emai $orderBy "; - $params = array( - 1 => array($contactId, 'Integer'), - 2 => array($groupID, 'Integer'), - ); + $params = [ + 1 => [$contactId, 'Integer'], + 2 => [$groupID, 'Integer'], + ]; $dao = CRM_Core_DAO::executeQuery($query, $params); $dao->fetch(); return $dao; @@ -518,7 +509,7 @@ public static function getGroupId($groupContactID) { * @param string $method */ public static function create(&$params, $contactId, $visibility = FALSE, $method = 'Admin') { - $contactIds = array(); + $contactIds = []; $contactIds[] = $contactId; //if $visibility is true we are coming in via profile mean $method = 'Web' @@ -544,12 +535,12 @@ public static function create(&$params, $contactId, $visibility = FALSE, $method // this fix is done to prevent warning generated by array_key_exits incase of empty array is given as input if (!is_array($params)) { - $params = array(); + $params = []; } // this fix is done to prevent warning generated by array_key_exits incase of empty array is given as input if (!isset($contactGroup) || !is_array($contactGroup)) { - $contactGroup = array(); + $contactGroup = []; } // check which values has to be add/remove contact from group @@ -578,11 +569,11 @@ public static function isContactInGroup($contactID, $groupID) { return FALSE; } - $params = array( - array('group', 'IN', array($groupID), 0, 0), - array('contact_id', '=', $contactID, 0, 0), - ); - list($contacts, $_) = CRM_Contact_BAO_Query::apiQuery($params, array('contact_id')); + $params = [ + ['group', 'IN', [$groupID], 0, 0], + ['contact_id', '=', $contactID, 0, 0], + ]; + list($contacts, $_) = CRM_Contact_BAO_Query::apiQuery($params, ['contact_id']); if (!empty($contacts)) { return TRUE; @@ -604,10 +595,10 @@ public static function isContactInGroup($contactID, $groupID) { * TODO: use the 3rd $sqls param to append sql statements rather than executing them here */ public static function mergeGroupContact($mainContactId, $otherContactId) { - $params = array( - 1 => array($mainContactId, 'Integer'), - 2 => array($otherContactId, 'Integer'), - ); + $params = [ + 1 => [$mainContactId, 'Integer'], + 2 => [$otherContactId, 'Integer'], + ]; // find all groups that are in otherContactID but not in mainContactID, copy them over $sql = " @@ -619,7 +610,7 @@ public static function mergeGroupContact($mainContactId, $otherContactId) { "; $dao = CRM_Core_DAO::executeQuery($sql, $params); - $otherGroupIDs = array(); + $otherGroupIDs = []; while ($dao->fetch()) { $otherGroupIDs[] = $dao->group_id; } @@ -654,7 +645,7 @@ public static function mergeGroupContact($mainContactId, $otherContactId) { "; $dao = CRM_Core_DAO::executeQuery($sql, $params); - $groupIDs = array(); + $groupIDs = []; while ($dao->fetch()) { // only copy it over if it has added status and migrate the history if ($dao->group_status == 'Added') { @@ -747,19 +738,19 @@ public static function bulkAddContactsToGroup( AND status = %2 AND contact_id IN ( $contactStr ) "; - $params = array( - 1 => array($groupID, 'Integer'), - 2 => array($status, 'String'), - ); + $params = [ + 1 => [$groupID, 'Integer'], + 2 => [$status, 'String'], + ]; - $presentIDs = array(); + $presentIDs = []; $dao = CRM_Core_DAO::executeQuery($sql, $params); if ($dao->fetch()) { $presentIDs = explode(',', $dao->contactStr); $presentIDs = array_flip($presentIDs); } - $gcValues = $shValues = array(); + $gcValues = $shValues = []; foreach ($input as $cid) { if (isset($presentIDs[$cid])) { $numContactsNotAdded++; @@ -780,7 +771,7 @@ public static function bulkAddContactsToGroup( } } - return array($numContactsAdded, $numContactsNotAdded); + return [$numContactsAdded, $numContactsNotAdded]; } /** @@ -795,10 +786,9 @@ public static function bulkAddContactsToGroup( * * @return array|bool */ - public static function buildOptions($fieldName, $context = NULL, $props = array()) { - $params = array(); + public static function buildOptions($fieldName, $context = NULL, $props = []) { - $options = CRM_Core_PseudoConstant::get(__CLASS__, $fieldName, $params, $context); + $options = CRM_Core_PseudoConstant::get(__CLASS__, $fieldName, $props, $context); // Sort group list by hierarchy // TODO: This will only work when api.entity is "group_contact". What about others? diff --git a/CRM/Contact/BAO/GroupContactCache.php b/CRM/Contact/BAO/GroupContactCache.php index 2ba71e2d77e7..a54dcdeb43fc 100644 --- a/CRM/Contact/BAO/GroupContactCache.php +++ b/CRM/Contact/BAO/GroupContactCache.php @@ -1,9 +1,9 @@ ts('Opportunistic Flush'), // Flush expired caches via background cron jobs. 'deterministic' => ts('Cron Flush'), - ); + ]; } /** @@ -122,7 +122,7 @@ public static function groupRefreshedClause($groupIDClause = NULL, $includeHidde */ public static function shouldGroupBeRefreshed($groupID, $includeHiddenGroups = FALSE) { $query = self::groupRefreshedClause("g.id = %1", $includeHiddenGroups); - $params = array(1 => array($groupID, 'Integer')); + $params = [1 => [$groupID, 'Integer']]; // if the query returns the group ID, it means the group is a valid candidate for refreshing return CRM_Core_DAO::singleValueQuery($query, $params); @@ -146,11 +146,11 @@ public static function loadAll($groupIDs = NULL, $limit = 0) { // this function is expensive and should be sparingly used if groupIDs is empty if (empty($groupIDs)) { $groupIDClause = NULL; - $groupIDs = array(); + $groupIDs = []; } else { if (!is_array($groupIDs)) { - $groupIDs = array($groupIDs); + $groupIDs = [$groupIDs]; } // note escapeString is a must here and we can't send the imploded value as second argument to @@ -175,7 +175,7 @@ public static function loadAll($groupIDs = NULL, $limit = 0) { "; $dao = CRM_Core_DAO::executeQuery($query); - $processGroupIDs = array(); + $processGroupIDs = []; $refreshGroupIDs = $groupIDs; while ($dao->fetch()) { $processGroupIDs[] = $dao->id; @@ -211,22 +211,19 @@ public static function loadAll($groupIDs = NULL, $limit = 0) { } /** - * Build the smart group cache for a given group. + * Build the smart group cache for given groups. * - * @param int $groupID + * @param array $groupIDs */ - public static function add($groupID) { - // first delete the current cache - self::remove($groupID); - if (!is_array($groupID)) { - $groupID = array($groupID); - } + public static function add($groupIDs) { + $groupIDs = (array) $groupIDs; - $returnProperties = array('contact_id'); - foreach ($groupID as $gid) { - $params = array(array('group', 'IN', array($gid), 0, 0)); + foreach ($groupIDs as $groupID) { + // first delete the current cache + self::clearGroupContactCache($groupID); + $params = [['group', 'IN', [$groupID], 0, 0]]; // the below call updates the cache table as a byproduct of the query - CRM_Contact_BAO_Query::apiQuery($params, $returnProperties, NULL, NULL, 0, 0, FALSE); + CRM_Contact_BAO_Query::apiQuery($params, ['contact_id'], NULL, NULL, 0, 0, FALSE); } } @@ -236,10 +233,10 @@ public static function add($groupID) { * @todo review use of INSERT IGNORE. This function appears to be slower that inserting * with a left join. Also, 200 at once seems too little. * - * @param int $groupID + * @param array $groupID * @param array $values */ - public static function store(&$groupID, &$values) { + public static function store($groupID, &$values) { $processed = FALSE; // sort the values so we put group IDs in front and hence optimize @@ -286,130 +283,51 @@ public static function updateCacheTime($groupID, $processed) { } /** - * Removes all the cache entries pertaining to a specific group. - * - * If no groupID is passed in, removes cache entries for all groups - * Has an optimization to bypass repeated invocations of this function. - * Note that this function is an advisory, i.e. the removal respects the - * cache date, i.e. the removal is not done if the group was recently - * loaded into the cache. + * @deprecated function - the best function to call is + * CRM_Contact_BAO_Contact::updateContactCache at the moment, or api job.group_cache_flush + * to really force a flush. * - * In fact it turned out there is little overlap between the code when group is passed in - * and group is not so it makes more sense as separate functions. + * Remove this function altogether by mid 2018. * - * @todo remove last call to this function from outside the class then make function protected, - * enforce groupID as an array & remove non group handling. + * However, if updating code outside core to use this (or any BAO function) it is recommended that + * you add an api call to lock in into our contract. Currently there is not really a supported + * method for non core functions. + */ + public static function remove() { + Civi::log() + ->warning('Deprecated code. This function should not be called without groupIDs. Extensions can use the api job.group_cache_flush for a hard flush or add an api option for soft flush', ['civi.tag' => 'deprecated']); + CRM_Contact_BAO_GroupContactCache::opportunisticCacheFlush(); + } + + /** + * Function to clear group contact cache and reset the corresponding + * group's cache and refresh date * * @param int $groupID - * the groupID to delete cache entries, NULL for all groups. - * @param bool $onceOnly - * run the function exactly once for all groups. + * */ - public static function remove($groupID = NULL, $onceOnly = TRUE) { - static $invoked = FALSE; - - // typically this needs to happy only once per instance - // this is especially TRUE in import, where we don't need - // to do this all the time - // this optimization is done only when no groupID is passed - // i.e. cache is reset for all groups - if ( - $onceOnly && - $invoked && - $groupID == NULL - ) { - return; - } - - if ($groupID == NULL) { - $invoked = TRUE; - } - elseif (is_array($groupID)) { - foreach ($groupID as $gid) { - unset(self::$_alreadyLoaded[$gid]); - } - } - elseif ($groupID && array_key_exists($groupID, self::$_alreadyLoaded)) { - unset(self::$_alreadyLoaded[$groupID]); - } + public static function clearGroupContactCache($groupID) { + $transaction = new CRM_Core_Transaction(); + $query = " + DELETE g + FROM civicrm_group_contact_cache g + WHERE g.group_id = %1 "; - $refresh = NULL; - $smartGroupCacheTimeout = self::smartGroupCacheTimeout(); - $params = array( - 1 => array(self::getCacheInvalidDateTime(), 'String'), - 2 => array(self::getRefreshDateTime(), 'String'), - ); + $update = " + UPDATE civicrm_group g + SET cache_date = null, refresh_date = null + WHERE id = %1 "; - if (!isset($groupID)) { - if ($smartGroupCacheTimeout == 0) { - $query = " -DELETE FROM civicrm_group_contact_cache -"; - $update = " -UPDATE civicrm_group g -SET cache_date = null, - refresh_date = null -"; - } - else { - - $query = " -DELETE gc -FROM civicrm_group_contact_cache gc -INNER JOIN civicrm_group g ON g.id = gc.group_id -WHERE g.cache_date <= %1 -"; - $update = " -UPDATE civicrm_group g -SET cache_date = null, - refresh_date = null -WHERE g.cache_date <= %1 -"; - $refresh = " -UPDATE civicrm_group g -SET refresh_date = %2 -WHERE g.cache_date < %1 -AND refresh_date IS NULL -"; - } - } - elseif (is_array($groupID)) { - $groupIDs = implode(', ', $groupID); - $query = " -DELETE g -FROM civicrm_group_contact_cache g -WHERE g.group_id IN ( $groupIDs ) -"; - $update = " -UPDATE civicrm_group g -SET cache_date = null, - refresh_date = null -WHERE id IN ( $groupIDs ) -"; - } - else { - $query = " -DELETE g -FROM civicrm_group_contact_cache g -WHERE g.group_id = %1 -"; - $update = " -UPDATE civicrm_group g -SET cache_date = null, - refresh_date = null -WHERE id = %1 -"; - $params = array(1 => array($groupID, 'Integer')); - } + $params = [ + 1 => [$groupID, 'Integer'], + ]; CRM_Core_DAO::executeQuery($query, $params); - - if ($refresh) { - CRM_Core_DAO::executeQuery($refresh, $params); - } - // also update the cache_date for these groups CRM_Core_DAO::executeQuery($update, $params); + unset(self::$_alreadyLoaded[$groupID]); + + $transaction->commit(); } /** @@ -428,7 +346,7 @@ protected static function flushCaches() { // Someone else is kindly doing the refresh for us right now. return; } - $params = array(1 => array(self::getCacheInvalidDateTime(), 'String')); + $params = [1 => [self::getCacheInvalidDateTime(), 'String']]; // @todo this is consistent with previous behaviour but as the first query could take several seconds the second // could become inaccurate. It seems to make more sense to fetch them first & delete from an array (which would // also reduce joins). If we do this we should also consider how best to iterate the groups. If we do them one at @@ -475,8 +393,8 @@ protected static function flushCaches() { * @throws \CRM_Core_Exception */ protected static function getLockForRefresh() { - if (!isset(Civi::$statics[__CLASS__])) { - Civi::$statics[__CLASS__] = array('is_refresh_init' => FALSE); + if (!isset(Civi::$statics[__CLASS__]['is_refresh_init'])) { + Civi::$statics[__CLASS__] = ['is_refresh_init' => FALSE]; } if (Civi::$statics[__CLASS__]['is_refresh_init']) { @@ -493,8 +411,9 @@ protected static function getLockForRefresh() { /** * Do an opportunistic cache refresh if the site is configured for these. * - * Sites that do not run the smart group clearing cron job should refresh the caches under an opportunistic mode, akin - * to a poor man's cron. The user session will be forced to wait on this so it is less desirable. + * Sites that do not run the smart group clearing cron job should refresh the + * caches on demand. The user session will be forced to wait so it is less + * ideal. */ public static function opportunisticCacheFlush() { if (Civi::settings()->get('smart_group_cache_refresh_mode') == 'opportunistic') { @@ -529,7 +448,7 @@ public static function deterministicCacheFlush() { * TRUE if successful. */ public static function removeContact($cid, $groupId = NULL) { - $cids = array(); + $cids = []; // sanitize input foreach ((array) $cid as $c) { $cids[] = CRM_Utils_Type::escape($c, 'Integer'); @@ -595,7 +514,7 @@ public static function load(&$group, $force = FALSE) { CRM_Contact_BAO_ProximityQuery::fixInputParams($ssParams); } - $returnProperties = array(); + $returnProperties = []; if (CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_SavedSearch', $savedSearchID, 'mapping_id')) { $fv = CRM_Contact_BAO_SavedSearch::getFormValues($savedSearchID); $returnProperties = CRM_Core_BAO_Mapping::returnProperties($fv); @@ -636,15 +555,12 @@ public static function load(&$group, $force = FALSE) { ); $query->_useDistinct = FALSE; $query->_useGroupBy = FALSE; - $searchSQL - = $query->searchQuery( + $sqlParts = $query->getSearchSQLParts( 0, 0, NULL, FALSE, FALSE, - FALSE, TRUE, - TRUE, - NULL, NULL, NULL, - TRUE + FALSE, TRUE ); + $searchSQL = "{$sqlParts['select']} {$sqlParts['from']} {$sqlParts['where']} {$sqlParts['having']} {$sqlParts['group_by']}"; } $groupID = CRM_Utils_Type::escape($groupID, 'Integer'); $sql = $searchSQL . " AND contact_a.id NOT IN ( @@ -665,11 +581,11 @@ public static function load(&$group, $force = FALSE) { WHERE civicrm_group_contact.status = 'Added' AND civicrm_group_contact.group_id = $groupID "; - $groupIDs = array($groupID); - self::remove($groupIDs); + self::clearGroupContactCache($groupID); + $processed = FALSE; $tempTable = 'civicrm_temp_group_contact_cache' . rand(0, 2000); - foreach (array($sql, $sqlB) as $selectSql) { + foreach ([$sql, $sqlB] as $selectSql) { if (!$selectSql) { continue; } @@ -683,7 +599,7 @@ public static function load(&$group, $force = FALSE) { CRM_Core_DAO::executeQuery(" DROP TEMPORARY TABLE $tempTable"); } - self::updateCacheTime($groupIDs, $processed); + self::updateCacheTime([$groupID], $processed); if ($group->children) { @@ -694,7 +610,7 @@ public static function load(&$group, $force = FALSE) { WHERE civicrm_group_contact.status = 'Removed' AND civicrm_group_contact.group_id = $groupID "; $dao = CRM_Core_DAO::executeQuery($sql); - $removed_contacts = array(); + $removed_contacts = []; while ($dao->fetch()) { $removed_contacts[] = $dao->contact_id; } @@ -706,12 +622,12 @@ public static function load(&$group, $force = FALSE) { foreach ($removed_contacts as $removed_contact) { unset($contactIDs[$removed_contact]); } - $values = array(); + $values = []; foreach ($contactIDs as $contactID => $dontCare) { $values[] = "({$groupID},{$contactID})"; } - self::store($groupIDs, $values); + self::store([$groupID], $values); } } @@ -763,7 +679,7 @@ public static function contactGroup($contactID, $showHidden = FALSE) { $contactIDs = $contactID; } else { - $contactIDs = array($contactID); + $contactIDs = [$contactID]; } self::loadAll(); @@ -784,7 +700,7 @@ public static function contactGroup($contactID, $showHidden = FALSE) { "; $dao = CRM_Core_DAO::executeQuery($sql); - $contactGroup = array(); + $contactGroup = []; $prevContactID = NULL; while ($dao->fetch()) { if ( @@ -796,16 +712,16 @@ public static function contactGroup($contactID, $showHidden = FALSE) { $prevContactID = $dao->contact_id; if (!array_key_exists($dao->contact_id, $contactGroup)) { $contactGroup[$dao->contact_id] - = array('group' => array(), 'groupTitle' => array()); + = ['group' => [], 'groupTitle' => []]; } $contactGroup[$dao->contact_id]['group'][] - = array( + = [ 'id' => $dao->group_id, 'title' => $dao->title, 'description' => $dao->description, 'children' => $dao->children, - ); + ]; $contactGroup[$dao->contact_id]['groupTitle'][] = $dao->title; } diff --git a/CRM/Contact/BAO/GroupNesting.php b/CRM/Contact/BAO/GroupNesting.php index 284aa3a1bf40..dae7b4a3fd2e 100644 --- a/CRM/Contact/BAO/GroupNesting.php +++ b/CRM/Contact/BAO/GroupNesting.php @@ -1,7 +1,7 @@ copyValues($params); + if (empty($params['id'])) { + $dao->find(TRUE); + } + $dao->save(); + CRM_Utils_Hook::post($hook, 'GroupNesting', $dao->id, $dao); + return $dao; + } + /** * Adds a new group nesting record. * @@ -124,12 +146,12 @@ public static function hasParentGroups($groupId) { */ public static function getChildGroupIds($groupIds) { if (!is_array($groupIds)) { - $groupIds = array($groupIds); + $groupIds = [$groupIds]; } $dao = new CRM_Contact_DAO_GroupNesting(); $query = "SELECT child_group_id FROM civicrm_group_nesting WHERE parent_group_id IN (" . implode(',', $groupIds) . ")"; $dao->query($query); - $childGroupIds = array(); + $childGroupIds = []; while ($dao->fetch()) { $childGroupIds[] = $dao->child_group_id; } @@ -147,12 +169,12 @@ public static function getChildGroupIds($groupIds) { */ public static function getParentGroupIds($groupIds) { if (!is_array($groupIds)) { - $groupIds = array($groupIds); + $groupIds = [$groupIds]; } $dao = new CRM_Contact_DAO_GroupNesting(); $query = "SELECT parent_group_id FROM civicrm_group_nesting WHERE child_group_id IN (" . implode(',', $groupIds) . ")"; $dao->query($query); - $parentGroupIds = array(); + $parentGroupIds = []; while ($dao->fetch()) { $parentGroupIds[] = $dao->parent_group_id; } @@ -172,13 +194,13 @@ public static function getParentGroupIds($groupIds) { */ public static function getDescendentGroupIds($groupIds, $includeSelf = TRUE) { if (!is_array($groupIds)) { - $groupIds = array($groupIds); + $groupIds = [$groupIds]; } $dao = new CRM_Contact_DAO_GroupNesting(); $query = "SELECT child_group_id, parent_group_id FROM civicrm_group_nesting WHERE parent_group_id IN (" . implode(',', $groupIds) . ")"; $dao->query($query); - $tmpGroupIds = array(); - $childGroupIds = array(); + $tmpGroupIds = []; + $childGroupIds = []; if ($includeSelf) { $childGroupIds = $groupIds; } diff --git a/CRM/Contact/BAO/GroupNestingCache.php b/CRM/Contact/BAO/GroupNestingCache.php index 4e0351e3d9e4..e48d988b9023 100644 --- a/CRM/Contact/BAO/GroupNestingCache.php +++ b/CRM/Contact/BAO/GroupNestingCache.php @@ -1,9 +1,9 @@ fetch()) { if (!array_key_exists($dao->child, $tree)) { - $tree[$dao->child] = array( - 'children' => array(), - 'parents' => array(), - ); + $tree[$dao->child] = [ + 'children' => [], + 'parents' => [], + ]; } if (!array_key_exists($dao->parent, $tree)) { - $tree[$dao->parent] = array( - 'children' => array(), - 'parents' => array(), - ); + $tree[$dao->parent] = [ + 'children' => [], + 'parents' => [], + ]; } $tree[$dao->child]['parents'][] = $dao->parent; @@ -84,7 +84,7 @@ static public function update() { "; CRM_Core_DAO::executeQuery($sql); - $values = array(); + $values = []; foreach (array_keys($tree) as $id) { $parents = implode(',', $tree[$id]['parents']); $children = implode(',', $tree[$id]['children']); @@ -129,7 +129,7 @@ public static function checkCyclicGraph(&$tree) { * @return bool */ public static function isCyclic(&$tree, $id) { - $parents = $children = array(); + $parents = $children = []; self::getAll($parent, $tree, $id, 'parents'); self::getAll($child, $tree, $id, 'children'); @@ -232,7 +232,7 @@ public static function json() { foreach ($groups as $id => $name) { $string = "id:'$id', name:'$name'"; if (isset($tree[$id])) { - $children = array(); + $children = []; if (!empty($tree[$id]['children'])) { foreach ($tree[$id]['children'] as $child) { $children[] = "{_reference:'$child'}"; diff --git a/CRM/Contact/BAO/GroupOrganization.php b/CRM/Contact/BAO/GroupOrganization.php index a22f77810335..f57814da249c 100644 --- a/CRM/Contact/BAO/GroupOrganization.php +++ b/CRM/Contact/BAO/GroupOrganization.php @@ -1,9 +1,9 @@ copyValues($formattedValues); - // we have ensured we have group_id & organization_id so we can do a find knowing that - // this can only find a matching record - $groupOrganization->find(TRUE); + $groupOrganization->copyValues($params); + if (!isset($params['id'])) { + // we have ensured we have group_id & organization_id so we can do a find knowing that + // this can only find a matching record + $groupOrganization->find(TRUE); + } $groupOrganization->save(); return $groupOrganization; } - /** - * Format the params. - * - * @param array $params - * (reference ) an assoc array of name/value pairs. - * @param array $formatedValues - * (reference ) an assoc array of name/value pairs. - */ - public static function formatValues(&$params, &$formatedValues) { - if (!empty($params['group_organization'])) { - $formatedValues['id'] = $params['group_organization']; - } - - if (!empty($params['group_id'])) { - $formatedValues['group_id'] = $params['group_id']; - } - - if (!empty($params['organization_id'])) { - $formatedValues['organization_id'] = $params['organization_id']; - } - } - /** * Check if there is data to create the object. * diff --git a/CRM/Contact/BAO/Household.php b/CRM/Contact/BAO/Household.php index 3246b84d7a9e..523849e72327 100644 --- a/CRM/Contact/BAO/Household.php +++ b/CRM/Contact/BAO/Household.php @@ -1,9 +1,9 @@ $dbName; @@ -105,7 +105,7 @@ public static function format(&$params, &$contact) { } } - foreach (array('prefix', 'suffix') as $name) { + foreach (['prefix', 'suffix'] as $name) { $dbName = "{$name}_id"; $value = $individual->$dbName; if ($value && !empty($params['preserveDBName'])) { @@ -122,7 +122,7 @@ public static function format(&$params, &$contact) { //2. lets get value from param if exists. //3. if not in params, lets get from db. - foreach (array('last', 'middle', 'first', 'nick') as $name) { + foreach (['last', 'middle', 'first', 'nick'] as $name) { $phpName = "{$name}Name"; $dbName = "{$name}_name"; $value = $individual->$dbName; @@ -139,7 +139,7 @@ public static function format(&$params, &$contact) { } } - foreach (array('prefix', 'suffix') as $name) { + foreach (['prefix', 'suffix'] as $name) { $dbName = "{$name}_id"; $value = $individual->$dbName; @@ -179,14 +179,14 @@ public static function format(&$params, &$contact) { } //first trim before further processing. - foreach (array('lastName', 'firstName', 'middleName') as $fld) { + foreach (['lastName', 'firstName', 'middleName'] as $fld) { $$fld = trim($$fld); } if ($lastName || $firstName || $middleName) { // make sure we have values for all the name fields. $formatted = $params; - $nameParams = array( + $nameParams = [ 'first_name' => $firstName, 'middle_name' => $middleName, 'last_name' => $lastName, @@ -196,7 +196,7 @@ public static function format(&$params, &$contact) { 'prefix_id' => $prefix_id, 'suffix_id' => $suffix_id, 'formal_title' => $formalTitle, - ); + ]; // make sure we have all the name fields. foreach ($nameParams as $name => $value) { if (empty($formatted[$name]) && $value) { @@ -204,9 +204,9 @@ public static function format(&$params, &$contact) { } } - $tokens = array(); + $tokens = []; CRM_Utils_Hook::tokens($tokens); - $tokenFields = array(); + $tokenFields = []; foreach ($tokens as $catTokens) { foreach ($catTokens as $token => $label) { $tokenFields[] = $token; @@ -216,14 +216,14 @@ public static function format(&$params, &$contact) { //build the sort name. $format = Civi::settings()->get('sort_name_format'); $sortName = CRM_Utils_Address::format($formatted, $format, - FALSE, FALSE, TRUE, $tokenFields + FALSE, FALSE, $tokenFields ); $sortName = trim($sortName); //build the display name. $format = Civi::settings()->get('display_name_format'); $displayName = CRM_Utils_Address::format($formatted, $format, - FALSE, FALSE, TRUE, $tokenFields + FALSE, FALSE, $tokenFields ); $displayName = trim($displayName); } @@ -248,7 +248,7 @@ public static function format(&$params, &$contact) { } //now set the names. - $names = array('displayName' => 'display_name', 'sortName' => 'sort_name'); + $names = ['displayName' => 'display_name', 'sortName' => 'sort_name']; foreach ($names as $value => $name) { if (empty($$value)) { if ($email) { @@ -273,29 +273,29 @@ public static function format(&$params, &$contact) { $format = CRM_Utils_Date::getDateFormat('birth'); if ($date = CRM_Utils_Array::value('birth_date', $params)) { - if (in_array($format, array( + if (in_array($format, [ 'dd-mm', 'mm/dd', - ))) { + ])) { $separator = '/'; if ($format == 'dd-mm') { $separator = '-'; } $date = $date . $separator . '1902'; } - elseif (in_array($format, array( + elseif (in_array($format, [ 'yy-mm', - ))) { + ])) { $date = $date . '-01'; } - elseif (in_array($format, array( + elseif (in_array($format, [ 'M yy', - ))) { + ])) { $date = $date . '-01'; } - elseif (in_array($format, array( + elseif (in_array($format, [ 'yy', - ))) { + ])) { $date = $date . '-01-01'; } $contact->birth_date = CRM_Utils_Date::processDate($date); @@ -305,29 +305,29 @@ public static function format(&$params, &$contact) { } if ($date = CRM_Utils_Array::value('deceased_date', $params)) { - if (in_array($format, array( + if (in_array($format, [ 'dd-mm', 'mm/dd', - ))) { + ])) { $separator = '/'; if ($format == 'dd-mm') { $separator = '-'; } $date = $date . $separator . '1902'; } - elseif (in_array($format, array( + elseif (in_array($format, [ 'yy-mm', - ))) { + ])) { $date = $date . '-01'; } - elseif (in_array($format, array( + elseif (in_array($format, [ 'M yy', - ))) { + ])) { $date = $date . '-01'; } - elseif (in_array($format, array( + elseif (in_array($format, [ 'yy', - ))) { + ])) { $date = $date . '-01-01'; } diff --git a/CRM/Contact/BAO/ProximityQuery.php b/CRM/Contact/BAO/ProximityQuery.php index 9f0a1d920f8b..c1e72783b872 100644 --- a/CRM/Contact/BAO/ProximityQuery.php +++ b/CRM/Contact/BAO/ProximityQuery.php @@ -1,9 +1,9 @@ = $minLatitude "; } @@ -264,7 +267,7 @@ public static function process(&$query, &$values) { list($name, $op, $distance, $grouping, $wildcard) = $values; // also get values array for all address related info - $proximityVars = array( + $proximityVars = [ 'street_address' => 1, 'city' => 1, 'postal_code' => 1, @@ -273,10 +276,12 @@ public static function process(&$query, &$values) { 'state_province' => 0, 'country' => 0, 'distance_unit' => 0, - ); + 'geo_code_1' => 0, + 'geo_code_2' => 0, + ]; - $proximityAddress = array(); - $qill = array(); + $proximityAddress = []; + $qill = []; foreach ($proximityVars as $var => $recordQill) { $proximityValues = $query->getWhereValues("prox_{$var}", $grouping); if (!empty($proximityValues) && @@ -327,21 +332,20 @@ public static function process(&$query, &$values) { } $qill = ts('Proximity search to a distance of %1 from %2', - array( + [ 1 => $qillUnits, 2 => implode(', ', $qill), - ) + ] ); - $fnName = isset($config->geocodeMethod) ? $config->geocodeMethod : NULL; - if (empty($fnName)) { - CRM_Core_Error::fatal(ts('Proximity searching requires you to set a valid geocoding provider')); - } - $query->_tables['civicrm_address'] = $query->_whereTables['civicrm_address'] = 1; - require_once str_replace('_', DIRECTORY_SEPARATOR, $fnName) . '.php'; - $fnName::format($proximityAddress); + if (empty($proximityAddress['geo_code_1']) || empty($proximityAddress['geo_code_2'])) { + if (!CRM_Core_BAO_Address::addGeocoderData($proximityAddress)) { + throw new CRM_Core_Exception(ts('Proximity searching requires you to set a valid geocoding provider')); + } + } + if ( !is_numeric(CRM_Utils_Array::value('geo_code_1', $proximityAddress)) || !is_numeric(CRM_Utils_Array::value('geo_code_2', $proximityAddress)) @@ -373,7 +377,7 @@ public static function fixInputParams(&$input) { foreach ($input as $param) { if (CRM_Utils_Array::value('0', $param) == 'prox_distance') { // add prox_ prefix to these - $param_alter = array('street_address', 'city', 'postal_code', 'state_province', 'country'); + $param_alter = ['street_address', 'city', 'postal_code', 'state_province', 'country']; foreach ($input as $key => $_param) { if (in_array($_param[0], $param_alter)) { diff --git a/CRM/Contact/BAO/Query.php b/CRM/Contact/BAO/Query.php index 62bf3381c4df..e5f9b4c6048b 100644 --- a/CRM/Contact/BAO/Query.php +++ b/CRM/Contact/BAO/Query.php @@ -1,9 +1,9 @@ 1, 'civicrm_country' => 1, 'civicrm_county' => 1, 'civicrm_address' => 1, 'civicrm_location_type' => 1, - ); + ]; /** * List of location specific fields. + * @var array */ - static $_locationSpecificFields = array( + public static $_locationSpecificFields = [ 'street_address', 'street_number', 'street_name', 'street_unit', 'supplemental_address_1', 'supplemental_address_2', + 'supplemental_address_3', 'city', 'postal_code', 'postal_code_suffix', @@ -370,13 +393,14 @@ class CRM_Contact_BAO_Query { 'im', 'address_name', 'master_id', - ); + ]; /** * Remember if we handle either end of a number or date range * so we can skip the other + * @var array */ - protected $_rangeCache = array(); + protected $_rangeCache = []; /** * Set to true when $this->relationship is run to avoid adding twice * @var Boolean @@ -387,9 +411,12 @@ class CRM_Contact_BAO_Query { * Set to the name of the temp table if one has been created * @var String */ - static $_relationshipTempTable = NULL; + public static $_relationshipTempTable = NULL; - public $_pseudoConstantsSelect = array(); + public $_pseudoConstantsSelect = []; + + public $_groupUniqueKey = NULL; + public $_groupKeys = []; /** * Class constructor which also does all the work. @@ -407,8 +434,7 @@ class CRM_Contact_BAO_Query { * @param null $displayRelationshipType * @param string $operator * @param string $apiEntity - * - * @return \CRM_Contact_BAO_Query + * @param bool|NULL $primaryLocationOnly */ public function __construct( $params = NULL, $returnProperties = NULL, $fields = NULL, @@ -416,15 +442,20 @@ public function __construct( $skipPermission = FALSE, $searchDescendentGroups = TRUE, $smartGroupCache = TRUE, $displayRelationshipType = NULL, $operator = 'AND', - $apiEntity = NULL + $apiEntity = NULL, + $primaryLocationOnly = NULL ) { + if ($primaryLocationOnly === NULL) { + $primaryLocationOnly = Civi::settings()->get('searchPrimaryDetailsOnly'); + } + $this->_primaryLocation = $primaryLocationOnly; $this->_params = &$params; if ($this->_params == NULL) { - $this->_params = array(); + $this->_params = []; } if ($returnProperties === self::NO_RETURN_PROPERTIES) { - $this->_returnProperties = array(); + $this->_returnProperties = []; } elseif (empty($returnProperties)) { $this->_returnProperties = self::defaultReturnProperties($mode); @@ -468,18 +499,22 @@ public function __construct( /** * Function which actually does all the work for the constructor. + * + * @param string $apiEntity + * The api entity being called. + * This sort-of duplicates $mode in a confusing way. Probably not by design. */ public function initialize($apiEntity = NULL) { - $this->_select = array(); - $this->_element = array(); - $this->_tables = array(); - $this->_whereTables = array(); - $this->_where = array(); - $this->_qill = array(); - $this->_options = array(); - $this->_cfIDs = array(); - $this->_paramLookup = array(); - $this->_having = array(); + $this->_select = []; + $this->_element = []; + $this->_tables = []; + $this->_whereTables = []; + $this->_where = []; + $this->_qill = []; + $this->_options = []; + $this->_cfIDs = []; + $this->_paramLookup = []; + $this->_having = []; $this->_customQuery = NULL; @@ -507,10 +542,11 @@ public function initialize($apiEntity = NULL) { $component = 'membership'; } if (isset($component)) { + // @todo should be if (isset($component && !$this->_skipPermission) CRM_Financial_BAO_FinancialType::buildPermissionedClause($this->_whereClause, $component); } - $this->_fromClause = self::fromClause($this->_tables, NULL, NULL, $this->_primaryLocation, $this->_mode); + $this->_fromClause = self::fromClause($this->_tables, NULL, NULL, $this->_primaryLocation, $this->_mode, $apiEntity); $this->_simpleFromClause = self::fromClause($this->_whereTables, NULL, NULL, $this->_primaryLocation, $this->_mode); $this->openedSearchPanes(TRUE); @@ -525,7 +561,7 @@ public function initialize($apiEntity = NULL) { * versus search builder. * * The direction we are going is having the form convert values to a standardised format & - * moving away from wierd & wonderful where clause switches. + * moving away from weird & wonderful where clause switches. * * Fix and handle contact deletion nicely. * @@ -535,7 +571,7 @@ public function initialize($apiEntity = NULL) { */ public function buildParamsLookup() { $trashParamExists = FALSE; - $paramByGroup = array(); + $paramByGroup = []; foreach ($this->_params as $k => $param) { if (!empty($param[0]) && $param[0] == 'contact_is_deleted') { $trashParamExists = TRUE; @@ -551,16 +587,16 @@ public function buildParamsLookup() { //cycle through group sets and explicitly add trash param if not set foreach ($paramByGroup as $setID => $set) { if ( - !in_array(array('contact_is_deleted', '=', '1', $setID, '0'), $this->_params) && - !in_array(array('contact_is_deleted', '=', '0', $setID, '0'), $this->_params) + !in_array(['contact_is_deleted', '=', '1', $setID, '0'], $this->_params) && + !in_array(['contact_is_deleted', '=', '0', $setID, '0'], $this->_params) ) { - $this->_params[] = array( + $this->_params[] = [ 'contact_is_deleted', '=', '0', $setID, '0', - ); + ]; } } } @@ -572,7 +608,7 @@ public function buildParamsLookup() { $cfID = CRM_Core_BAO_CustomField::getKeyID($value[0]); if ($cfID) { if (!array_key_exists($cfID, $this->_cfIDs)) { - $this->_cfIDs[$cfID] = array(); + $this->_cfIDs[$cfID] = []; } // Set wildcard value based on "and/or" selection foreach ($this->_params as $key => $param) { @@ -585,10 +621,10 @@ public function buildParamsLookup() { } if (!array_key_exists($value[0], $this->_paramLookup)) { - $this->_paramLookup[$value[0]] = array(); + $this->_paramLookup[$value[0]] = []; } if ($value[0] !== 'group') { - // Just trying to unravel how group interacts here! This whole function is wieid. + // Just trying to unravel how group interacts here! This whole function is weird. $this->_paramLookup[$value[0]][] = $value; } } @@ -596,9 +632,13 @@ public function buildParamsLookup() { /** * Some composite fields do not appear in the fields array hack to make them part of the query. + * + * @param $apiEntity + * The api entity being called. + * This sort-of duplicates $mode in a confusing way. Probably not by design. */ public function addSpecialFields($apiEntity) { - static $special = array('contact_type', 'contact_sub_type', 'sort_name', 'display_name'); + static $special = ['contact_type', 'contact_sub_type', 'sort_name', 'display_name']; // if get called via Contact.get API having address_id as return parameter if ($apiEntity == 'Contact') { $special[] = 'address_id'; @@ -624,22 +664,28 @@ public function addSpecialFields($apiEntity) { * clauses. Note that since the where clause introduces new * tables, the initial attempt also retrieves all variables used * in the params list + * + * @param string $apiEntity + * The api entity being called. + * This sort-of duplicates $mode in a confusing way. Probably not by design. */ public function selectClause($apiEntity = NULL) { + // @todo Tidy up this. This arises because 1) we are ignoring the $mode & adding a new + // param ($apiEntity) instead - presumably an oversight & 2 because + // contact is not implemented as a component. $this->addSpecialFields($apiEntity); foreach ($this->_fields as $name => $field) { // skip component fields // there are done by the alter query below // and need not be done on every field + // @todo remove these & handle using metadata - only obscure fields + // that are hack-added should need to be excluded from the main loop. if ( (substr($name, 0, 12) == 'participant_') || (substr($name, 0, 7) == 'pledge_') || - (substr($name, 0, 5) == 'case_') || - (substr($name, 0, 13) == 'contribution_' && - (strpos($name, 'source') !== FALSE && strpos($name, 'recur') !== FALSE)) || - (substr($name, 0, 8) == 'payment_') + (substr($name, 0, 5) == 'case_') ) { continue; } @@ -650,7 +696,6 @@ public function selectClause($apiEntity = NULL) { ($name == 'parent_id') ) { CRM_Activity_BAO_Query::select($this); - continue; } // if this is a hierarchical name, we ignore it @@ -663,16 +708,19 @@ public function selectClause($apiEntity = NULL) { $makeException = FALSE; //special handling for groups/tags - if (in_array($name, array('groups', 'tags', 'notes')) + if (in_array($name, ['groups', 'tags', 'notes']) && isset($this->_returnProperties[substr($name, 0, -1)]) ) { + // @todo instead of setting make exception to get us into + // an if clause that has handling for these fields buried with in it + // move the handling to here. $makeException = TRUE; } // since note has 3 different options we need special handling // note / note_subject / note_body if ($name == 'notes') { - foreach (array('note', 'note_subject', 'note_body') as $noteField) { + foreach (['note', 'note_subject', 'note_body'] as $noteField) { if (isset($this->_returnProperties[$noteField])) { $makeException = TRUE; break; @@ -680,20 +728,17 @@ public function selectClause($apiEntity = NULL) { } } - if (in_array($name, array('prefix_id', 'suffix_id', 'gender_id', 'communication_style_id'))) { - if (CRM_Utils_Array::value($field['pseudoconstant']['optionGroupName'], $this->_returnProperties)) { - $makeException = TRUE; - } - } - $cfID = CRM_Core_BAO_CustomField::getKeyID($name); - if (!empty($this->_paramLookup[$name]) || !empty($this->_returnProperties[$name]) || - $makeException + if ( + !empty($this->_paramLookup[$name]) + || !empty($this->_returnProperties[$name]) + || $this->pseudoConstantNameIsInReturnProperties($field, $name) + || $makeException ) { if ($cfID) { // add to cfIDs array if not present if (!array_key_exists($cfID, $this->_cfIDs)) { - $this->_cfIDs[$cfID] = array(); + $this->_cfIDs[$cfID] = []; } } elseif (isset($field['where'])) { @@ -713,26 +758,29 @@ public function selectClause($apiEntity = NULL) { } if (in_array($tableName, - array('email_greeting', 'postal_greeting', 'addressee'))) { + ['email_greeting', 'postal_greeting', 'addressee'])) { $this->_element["{$name}_id"] = 1; $this->_select["{$name}_id"] = "contact_a.{$name}_id as {$name}_id"; - $this->_pseudoConstantsSelect[$name] = array('pseudoField' => $tableName, 'idCol' => "{$name}_id"); + $this->_pseudoConstantsSelect[$name] = ['pseudoField' => $tableName, 'idCol' => "{$name}_id"]; $this->_pseudoConstantsSelect[$name]['select'] = "{$name}.{$fieldName} as $name"; $this->_pseudoConstantsSelect[$name]['element'] = $name; if ($tableName == 'email_greeting') { + // @todo bad join. $this->_pseudoConstantsSelect[$name]['join'] = " LEFT JOIN civicrm_option_group option_group_email_greeting ON (option_group_email_greeting.name = 'email_greeting')"; $this->_pseudoConstantsSelect[$name]['join'] .= " LEFT JOIN civicrm_option_value email_greeting ON (contact_a.email_greeting_id = email_greeting.value AND option_group_email_greeting.id = email_greeting.option_group_id ) "; } elseif ($tableName == 'postal_greeting') { + // @todo bad join. $this->_pseudoConstantsSelect[$name]['join'] = " LEFT JOIN civicrm_option_group option_group_postal_greeting ON (option_group_postal_greeting.name = 'postal_greeting')"; $this->_pseudoConstantsSelect[$name]['join'] .= " LEFT JOIN civicrm_option_value postal_greeting ON (contact_a.postal_greeting_id = postal_greeting.value AND option_group_postal_greeting.id = postal_greeting.option_group_id ) "; } elseif ($tableName == 'addressee') { + // @todo bad join. $this->_pseudoConstantsSelect[$name]['join'] = " LEFT JOIN civicrm_option_group option_group_addressee ON (option_group_addressee.name = 'addressee')"; $this->_pseudoConstantsSelect[$name]['join'] .= @@ -751,37 +799,37 @@ public function selectClause($apiEntity = NULL) { } } else { - if (!in_array($tableName, array('civicrm_state_province', 'civicrm_country', 'civicrm_county'))) { + if (!in_array($tableName, ['civicrm_state_province', 'civicrm_country', 'civicrm_county'])) { $this->_tables[$tableName] = 1; } // also get the id of the tableName $tName = substr($tableName, 8); - if (in_array($tName, array('country', 'state_province', 'county'))) { + if (in_array($tName, ['country', 'state_province', 'county'])) { if ($tName == 'state_province') { - $this->_pseudoConstantsSelect['state_province_name'] = array( + $this->_pseudoConstantsSelect['state_province_name'] = [ 'pseudoField' => "{$tName}", 'idCol' => "{$tName}_id", 'bao' => 'CRM_Core_BAO_Address', 'table' => "civicrm_{$tName}", 'join' => " LEFT JOIN civicrm_{$tName} ON civicrm_address.{$tName}_id = civicrm_{$tName}.id ", - ); + ]; - $this->_pseudoConstantsSelect[$tName] = array( + $this->_pseudoConstantsSelect[$tName] = [ 'pseudoField' => 'state_province_abbreviation', 'idCol' => "{$tName}_id", 'table' => "civicrm_{$tName}", 'join' => " LEFT JOIN civicrm_{$tName} ON civicrm_address.{$tName}_id = civicrm_{$tName}.id ", - ); + ]; } else { - $this->_pseudoConstantsSelect[$name] = array( + $this->_pseudoConstantsSelect[$name] = [ 'pseudoField' => "{$tName}_id", 'idCol' => "{$tName}_id", 'bao' => 'CRM_Core_BAO_Address', 'table' => "civicrm_{$tName}", 'join' => " LEFT JOIN civicrm_{$tName} ON civicrm_address.{$tName}_id = civicrm_{$tName}.id ", - ); + ]; } $this->_select["{$tName}_id"] = "civicrm_address.{$tName}_id as {$tName}_id"; @@ -805,44 +853,14 @@ public function selectClause($apiEntity = NULL) { $this->_element['provider_id'] = 1; } - if ($tName == 'contact') { + if ($tName == 'contact' && $fieldName == 'organization_name') { // special case, when current employer is set for Individual contact - if ($fieldName == 'organization_name') { - $this->_select[$name] = "IF ( contact_a.contact_type = 'Individual', NULL, contact_a.organization_name ) as organization_name"; - } - elseif ($fieldName != 'id') { - if ($fieldName == 'prefix_id') { - $this->_pseudoConstantsSelect['individual_prefix'] = array( - 'pseudoField' => 'prefix_id', - 'idCol' => "prefix_id", - 'bao' => 'CRM_Contact_BAO_Contact', - ); - } - if ($fieldName == 'suffix_id') { - $this->_pseudoConstantsSelect['individual_suffix'] = array( - 'pseudoField' => 'suffix_id', - 'idCol' => "suffix_id", - 'bao' => 'CRM_Contact_BAO_Contact', - ); - } - if ($fieldName == 'gender_id') { - $this->_pseudoConstantsSelect['gender'] = array( - 'pseudoField' => 'gender_id', - 'idCol' => "gender_id", - 'bao' => 'CRM_Contact_BAO_Contact', - ); - } - if ($name == 'communication_style_id') { - $this->_pseudoConstantsSelect['communication_style'] = array( - 'pseudoField' => 'communication_style_id', - 'idCol' => "communication_style_id", - 'bao' => 'CRM_Contact_BAO_Contact', - ); - } - $this->_select[$name] = "contact_a.{$fieldName} as `$name`"; - } + $this->_select[$name] = "IF ( contact_a.contact_type = 'Individual', NULL, contact_a.organization_name ) as organization_name"; + } + elseif ($tName == 'contact' && $fieldName === 'id') { + // Handled elsewhere, explicitly ignore. Possibly for all tables... } - elseif (in_array($tName, array('country', 'county'))) { + elseif (in_array($tName, ['country', 'county'])) { $this->_pseudoConstantsSelect[$name]['select'] = "{$field['where']} as `$name`"; $this->_pseudoConstantsSelect[$name]['element'] = $name; } @@ -855,16 +873,20 @@ public function selectClause($apiEntity = NULL) { $this->_select[$name] = "{$field['where']} as `$name`"; } } + elseif ($this->pseudoConstantNameIsInReturnProperties($field, $name)) { + $this->addPseudoconstantFieldToSelect($name); + } else { - $this->_select[$name] = "{$field['where']} as `$name`"; + $this->_select[$name] = str_replace('civicrm_contact.', 'contact_a.', "{$field['where']} as `$name`"); } - if (!in_array($tName, array('state_province', 'country', 'county'))) { + if (!in_array($tName, ['state_province', 'country', 'county'])) { $this->_element[$name] = 1; } } } } elseif ($name === 'tags') { + //@todo move this handling outside the big IF & ditch $makeException $this->_useGroupBy = TRUE; $this->_select[$name] = "GROUP_CONCAT(DISTINCT(civicrm_tag.name)) as tags"; $this->_element[$name] = 1; @@ -872,6 +894,7 @@ public function selectClause($apiEntity = NULL) { $this->_tables['civicrm_entity_tag'] = 1; } elseif ($name === 'groups') { + //@todo move this handling outside the big IF & ditch $makeException $this->_useGroupBy = TRUE; // Duplicates will be created here but better to sort them out in php land. $this->_select[$name] = " @@ -883,12 +906,13 @@ public function selectClause($apiEntity = NULL) { $this->_element[$name] = 1; $this->_tables['civicrm_group_contact'] = 1; $this->_tables['civicrm_group_contact_cache'] = 1; - $this->_pseudoConstantsSelect["{$name}"] = array( + $this->_pseudoConstantsSelect["{$name}"] = [ 'pseudoField' => "groups", 'idCol' => "groups", - ); + ]; } elseif ($name === 'notes') { + //@todo move this handling outside the big IF & ditch $makeException // if note field is subject then return subject else body of the note $noteColumn = 'note'; if (isset($noteField) && $noteField == 'note_subject') { @@ -910,7 +934,7 @@ public function selectClause($apiEntity = NULL) { // this is a custom field with range search enabled, so we better check for two/from values if (!empty($this->_paramLookup[$name . '_from'])) { if (!array_key_exists($cfID, $this->_cfIDs)) { - $this->_cfIDs[$cfID] = array(); + $this->_cfIDs[$cfID] = []; } foreach ($this->_paramLookup[$name . '_from'] as $pID => $p) { // search in the cdID array for the same grouping @@ -922,14 +946,14 @@ public function selectClause($apiEntity = NULL) { } } if (!$fnd) { - $p[2] = array('from' => $p[2]); + $p[2] = ['from' => $p[2]]; $this->_cfIDs[$cfID][] = $p; } } } if (!empty($this->_paramLookup[$name . '_to'])) { if (!array_key_exists($cfID, $this->_cfIDs)) { - $this->_cfIDs[$cfID] = array(); + $this->_cfIDs[$cfID] = []; } foreach ($this->_paramLookup[$name . '_to'] as $pID => $p) { // search in the cdID array for the same grouping @@ -941,7 +965,7 @@ public function selectClause($apiEntity = NULL) { } } if (!$fnd) { - $p[2] = array('to' => $p[2]); + $p[2] = ['to' => $p[2]]; $this->_cfIDs[$cfID][] = $p; } } @@ -984,18 +1008,18 @@ public function addHierarchicalElements() { return; } - $locationTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id'); - $processed = array(); + $locationTypes = CRM_Core_DAO_Address::buildOptions('location_type_id', 'validate'); + $processed = []; $index = 0; $addressCustomFields = CRM_Core_BAO_CustomField::getFieldsForImport('Address'); - $addressCustomFieldIds = array(); + $addressCustomFieldIds = []; foreach ($this->_returnProperties['location'] as $name => $elements) { $lCond = self::getPrimaryCondition($name); + $locationTypeId = is_numeric($name) ? NULL : array_search($name, $locationTypes); if (!$lCond) { - $locationTypeId = array_search($name, $locationTypes); if ($locationTypeId === FALSE) { continue; } @@ -1007,7 +1031,6 @@ public function addHierarchicalElements() { } $name = str_replace(' ', '_', $name); - $tName = "$name-location_type"; $ltName = "`$name-location_type`"; $this->_select["{$tName}_id"] = "`$tName`.id as `{$tName}_id`"; @@ -1016,9 +1039,8 @@ public function addHierarchicalElements() { $this->_element["{$tName}"] = 1; $locationTypeName = $tName; - $locationTypeJoin = array(); + $locationTypeJoin = []; - $addAddress = FALSE; $addWhereCount = 0; foreach ($elements as $elementFullName => $dontCare) { $index++; @@ -1033,20 +1055,14 @@ public function addHierarchicalElements() { $addressCustomFieldIds[$cfID][$name] = 1; } } - //add address table only once + // add address table - doesn't matter if we do it mutliple times - it's the same data + // @todo ditch the double processing of addressJoin if ((in_array($elementCmpName, self::$_locationSpecificFields) || !empty($addressCustomFieldIds)) - && !$addAddress - && !in_array($elementCmpName, array('email', 'phone', 'im', 'openid')) + && !in_array($elementCmpName, ['email', 'phone', 'im', 'openid']) ) { - $tName = "$name-address"; - $aName = "`$name-address`"; - $this->_select["{$tName}_id"] = "`$tName`.id as `{$tName}_id`"; - $this->_element["{$tName}_id"] = 1; - $addressJoin = "\nLEFT JOIN civicrm_address $aName ON ($aName.contact_id = contact_a.id AND $aName.$lCond)"; - $this->_tables[$tName] = $addressJoin; + list($aName, $addressJoin) = $this->addAddressTable($name, $lCond); $locationTypeJoin[$tName] = " ( $aName.location_type_id = $ltName.id ) "; $processed[$aName] = 1; - $addAddress = TRUE; } $cond = $elementType = ''; @@ -1087,7 +1103,7 @@ public function addHierarchicalElements() { } elseif (is_numeric($name)) { //this for phone type to work - if (in_array($elementName, array('phone', 'phone_ext'))) { + if (in_array($elementName, ['phone', 'phone_ext'])) { $field = CRM_Utils_Array::value($elementName . "-Primary" . $elementType, $this->_fields); } else { @@ -1096,7 +1112,7 @@ public function addHierarchicalElements() { } else { //this is for phone type to work for profile edit - if (in_array($elementName, array('phone', 'phone_ext'))) { + if (in_array($elementName, ['phone', 'phone_ext'])) { $field = CRM_Utils_Array::value($elementName . "-$locationTypeId$elementType", $this->_fields); } else { @@ -1118,7 +1134,7 @@ public function addHierarchicalElements() { foreach ($this->_params as $id => $values) { if ((is_array($values) && $values[0] == $nm) || - (in_array($elementName, array('phone', 'im')) + (in_array($elementName, ['phone', 'im']) && (strpos($values[0], $nm) !== FALSE) ) ) { @@ -1147,18 +1163,18 @@ public function addHierarchicalElements() { $a = Civi::settings()->get('address_format'); if (substr_count($a, 'state_province_name') > 0) { - $this->_pseudoConstantsSelect["{$name}-{$elementFullName}"] = array( + $this->_pseudoConstantsSelect["{$name}-{$elementFullName}"] = [ 'pseudoField' => "{$pf}_id", 'idCol' => "{$tName}_id", 'bao' => 'CRM_Core_BAO_Address', - ); + ]; $this->_pseudoConstantsSelect["{$name}-{$elementFullName}"]['select'] = "`$tName`.name as `{$name}-{$elementFullName}`"; } else { - $this->_pseudoConstantsSelect["{$name}-{$elementFullName}"] = array( + $this->_pseudoConstantsSelect["{$name}-{$elementFullName}"] = [ 'pseudoField' => 'state_province_abbreviation', 'idCol' => "{$tName}_id", - ); + ]; $this->_pseudoConstantsSelect["{$name}-{$elementFullName}"]['select'] = "`$tName`.abbreviation as `{$name}-{$elementFullName}`"; } } @@ -1169,11 +1185,11 @@ public function addHierarchicalElements() { $this->_element[$provider] = 1; } if ($pf == 'country' || $pf == 'county') { - $this->_pseudoConstantsSelect["{$name}-{$elementFullName}"] = array( + $this->_pseudoConstantsSelect["{$name}-{$elementFullName}"] = [ 'pseudoField' => "{$pf}_id", 'idCol' => "{$tName}_id", 'bao' => 'CRM_Core_BAO_Address', - ); + ]; $this->_pseudoConstantsSelect["{$name}-{$elementFullName}"]['select'] = "`$tName`.$fieldName as `{$name}-{$elementFullName}`"; } else { @@ -1181,7 +1197,7 @@ public function addHierarchicalElements() { } } - if (in_array($pf, array('state_province', 'country', 'county'))) { + if (in_array($pf, ['state_province', 'country', 'county'])) { $this->_pseudoConstantsSelect["{$name}-{$elementFullName}"]['element'] = "{$name}-{$elementFullName}"; } else { @@ -1271,7 +1287,7 @@ public function addHierarchicalElements() { // table should be present in $this->_whereTables, // to add its condition in location type join, CRM-3939. if ($addWhereCount) { - $locClause = array(); + $locClause = []; foreach ($this->_whereTables as $tableName => $clause) { if (!empty($locationTypeJoin[$tableName])) { $locClause[] = $locationTypeJoin[$tableName]; @@ -1288,7 +1304,7 @@ public function addHierarchicalElements() { $customQuery = new CRM_Core_BAO_CustomQuery($addressCustomFieldIds); foreach ($addressCustomFieldIds as $cfID => $locTypeName) { foreach ($locTypeName as $name => $dnc) { - $this->_locationSpecificCustomFields[$cfID] = array($name, array_search($name, $locationTypes)); + $this->_locationSpecificCustomFields[$cfID] = [$name, array_search($name, $locationTypes)]; $fieldName = "$name-custom_{$cfID}"; $tName = "$name-address-custom-{$cfID}"; $aName = "`$name-address-custom-{$cfID}`"; @@ -1363,7 +1379,9 @@ public function query($count = FALSE, $sortByChar = FALSE, $groupContacts = FALS } } elseif ($sortByChar) { - $select = 'SELECT DISTINCT UPPER(LEFT(contact_a.sort_name, 1)) as sort_name'; + // @fixme add the deprecated warning back in (it breaks CRM_Contact_SelectorTest::testSelectorQuery) + // CRM_Core_Error::deprecatedFunctionWarning('sort by char is deprecated - use alphabetQuery method'); + $select = 'SELECT DISTINCT LEFT(contact_a.sort_name, 1) as sort_name'; $from = $this->_simpleFromClause; } elseif ($groupContacts) { @@ -1398,13 +1416,13 @@ public function query($count = FALSE, $sortByChar = FALSE, $groupContacts = FALS $group->find(TRUE); if (!isset($group->saved_search_id)) { - $tbName = "`civicrm_group_contact-{$groupId}`"; + $tbName = "civicrm_group_contact"; // CRM-17254 don't retrieve extra fields if contact_id is specifically requested // as this will add load to an intentionally light query. // ideally this code would be removed as it appears to be to support CRM-1203 // and passing in the required returnProperties from the url would // make more sense that globally applying the requirements of one form. - if (($this->_returnProperties != array('contact_id'))) { + if (($this->_returnProperties != ['contact_id'])) { $this->_select['group_contact_id'] = "$tbName.id as group_contact_id"; $this->_element['group_contact_id'] = 1; $this->_select['status'] = "$tbName.status as status"; @@ -1423,11 +1441,7 @@ public function query($count = FALSE, $sortByChar = FALSE, $groupContacts = FALS } } - $select = "SELECT "; - if (isset($this->_distinctComponentClause)) { - $select .= "{$this->_distinctComponentClause}, "; - } - $select .= implode(', ', $this->_select); + $select = $this->getSelect(); $from = $this->_fromClause; } @@ -1461,7 +1475,7 @@ public function query($count = FALSE, $sortByChar = FALSE, $groupContacts = FALS $this->filterRelatedContacts($from, $where, $having); } - return array($select, $from, $where, $having); + return [$select, $from, $where, $having]; } /** @@ -1530,16 +1544,51 @@ public static function fixDateValues($relative, &$from, &$to) { * @return array */ public static function convertFormValues(&$formValues, $wildcard = 0, $useEquals = FALSE, $apiEntity = NULL, - $entityReferenceFields = array()) { - $params = array(); + $entityReferenceFields = []) { + $params = []; if (empty($formValues)) { return $params; } self::filterCountryFromValuesIfStateExists($formValues); - foreach ($formValues as $id => $values) { + // Handle relative dates first + foreach (array_keys($formValues) as $id) { + if (preg_match('/_date_relative$/', $id) || + $id == 'event_relative' || + $id == 'case_from_relative' || + $id == 'case_to_relative' || + $id == 'participant_relative' + ) { + if ($id == 'event_relative') { + $fromRange = 'event_start_date_low'; + $toRange = 'event_end_date_high'; + } + elseif ($id == 'participant_relative') { + $fromRange = 'participant_register_date_low'; + $toRange = 'participant_register_date_high'; + } + elseif ($id == 'case_from_relative') { + $fromRange = 'case_from_start_date_low'; + $toRange = 'case_from_start_date_high'; + } + elseif ($id == 'case_to_relative') { + $fromRange = 'case_to_end_date_low'; + $toRange = 'case_to_end_date_high'; + } + else { + $dateComponent = explode('_date_relative', $id); + $fromRange = "{$dateComponent[0]}_date_low"; + $toRange = "{$dateComponent[0]}_date_high"; + } + + if (array_key_exists($fromRange, $formValues) && array_key_exists($toRange, $formValues)) { + CRM_Contact_BAO_Query::fixDateValues($formValues[$id], $formValues[$fromRange], $formValues[$toRange]); + } + } + } + foreach ($formValues as $id => $values) { if (self::isAlreadyProcessedForQueryFormat($values)) { $params[] = $values; continue; @@ -1549,19 +1598,19 @@ public static function convertFormValues(&$formValues, $wildcard = 0, $useEquals // The form uses 1 field to represent two db fields if ($id == 'contact_type' && $values && (!is_array($values) || !array_intersect(array_keys($values), CRM_Core_DAO::acceptedSQLOperators()))) { - $contactType = array(); - $subType = array(); + $contactType = []; + $subType = []; foreach ((array) $values as $key => $type) { - $types = explode('__', is_numeric($type) ? $key : $type); + $types = explode('__', is_numeric($type) ? $key : $type, 2); $contactType[$types[0]] = $types[0]; // Add sub-type if specified if (!empty($types[1])) { $subType[$types[1]] = $types[1]; } } - $params[] = array('contact_type', 'IN', $contactType, 0, 0); + $params[] = ['contact_type', 'IN', $contactType, 0, 0]; if ($subType) { - $params[] = array('contact_sub_type', 'IN', $subType, 0, 0); + $params[] = ['contact_sub_type', 'IN', $subType, 0, 0]; } } elseif ($id == 'privacy') { @@ -1569,14 +1618,22 @@ public static function convertFormValues(&$formValues, $wildcard = 0, $useEquals $op = !empty($formValues['privacy']['do_not_toggle']) ? '=' : '!='; foreach ($formValues['privacy'] as $key => $value) { if ($value) { - $params[] = array($key, $op, $value, 0, 0); + $params[] = [$key, $op, $value, 0, 0]; } } } } elseif ($id == 'email_on_hold') { - if ($formValues['email_on_hold']['on_hold']) { - $params[] = array('on_hold', '=', $formValues['email_on_hold']['on_hold'], 0, 0); + if ($onHoldValue = CRM_Utils_Array::value('email_on_hold', $formValues)) { + // onHoldValue should be 0 or 1 or an array. Some legacy groups may hold '' + // so in 5.11 we have an extra if that should become redundant over time. + // https://lab.civicrm.org/dev/core/issues/745 + // @todo this renaming of email_on_hold to on_hold needs revisiting + // it preceeds recent changes but causes the default not to reload. + $onHoldValue = array_filter((array) $onHoldValue, 'is_numeric'); + if (!empty($onHoldValue)) { + $params[] = ['on_hold', 'IN', $onHoldValue, 0, 0]; + } } } elseif (substr($id, 0, 7) == 'custom_' @@ -1594,36 +1651,12 @@ public static function convertFormValues(&$formValues, $wildcard = 0, $useEquals $id == 'case_to_relative' || $id == 'participant_relative' ) { - if ($id == 'event_relative') { - $fromRange = 'event_start_date_low'; - $toRange = 'event_end_date_high'; - } - elseif ($id == 'participant_relative') { - $fromRange = 'participant_register_date_low'; - $toRange = 'participant_register_date_high'; - } - elseif ($id == 'case_from_relative') { - $fromRange = 'case_from_start_date_low'; - $toRange = 'case_from_start_date_high'; - } - elseif ($id == 'case_to_relative') { - $fromRange = 'case_to_end_date_low'; - $toRange = 'case_to_end_date_high'; - } - else { - $dateComponent = explode('_date_relative', $id); - $fromRange = "{$dateComponent[0]}_date_low"; - $toRange = "{$dateComponent[0]}_date_high"; - } - - if (array_key_exists($fromRange, $formValues) && array_key_exists($toRange, $formValues)) { - CRM_Contact_BAO_Query::fixDateValues($formValues[$id], $formValues[$fromRange], $formValues[$toRange]); - continue; - } + // Already handled in previous loop + continue; } elseif (in_array($id, $entityReferenceFields) && !empty($values) && is_string($values) && (strpos($values, ',') != FALSE)) { - $params[] = array($id, 'IN', explode(',', $values), 0, 0); + $params[] = [$id, 'IN', explode(',', $values), 0, 0]; } else { $values = CRM_Contact_BAO_Query::fixWhereValues($id, $values, $wildcard, $useEquals, $apiEntity); @@ -1645,14 +1678,14 @@ public static function convertFormValues(&$formValues, $wildcard = 0, $useEquals * */ public static function legacyConvertFormValues($id, &$values) { - $legacyElements = array( + $legacyElements = [ 'group', 'tag', 'contact_tags', 'contact_type', 'membership_type_id', 'membership_status_id', - ); + ]; if (in_array($id, $legacyElements) && is_array($values)) { // prior to 4.7, formValues for some attributes (e.g. group, tag) are stored in array(id1 => 1, id2 => 1), // as per the recent Search fixes $values need to be in standard array(id1, id2) format @@ -1687,7 +1720,7 @@ public static function fixWhereValues($id, &$values, $wildcard = 0, $useEquals = } if (!$skipWhere) { - $skipWhere = array( + $skipWhere = [ 'task', 'radio_ts', 'uf_group_id', @@ -1695,7 +1728,7 @@ public static function fixWhereValues($id, &$values, $wildcard = 0, $useEquals = 'qfKey', 'operator', 'display_relationship_type', - ); + ]; } if (in_array($id, $skipWhere) || @@ -1714,7 +1747,7 @@ public static function fixWhereValues($id, &$values, $wildcard = 0, $useEquals = } if (!$likeNames) { - $likeNames = array('sort_name', 'email', 'note', 'display_name'); + $likeNames = ['sort_name', 'email', 'note', 'display_name']; } // email comes in via advanced search @@ -1724,18 +1757,18 @@ public static function fixWhereValues($id, &$values, $wildcard = 0, $useEquals = } if (!$useEquals && in_array($id, $likeNames)) { - $result = array($id, 'LIKE', $values, 0, 1); + $result = [$id, 'LIKE', $values, 0, 1]; } elseif (is_string($values) && strpos($values, '%') !== FALSE) { - $result = array($id, 'LIKE', $values, 0, 0); + $result = [$id, 'LIKE', $values, 0, 0]; } elseif ($id == 'contact_type' || (!empty($values) && is_array($values) && !in_array(key($values), CRM_Core_DAO::acceptedSQLOperators(), TRUE)) ) { - $result = array($id, 'IN', $values, 0, $wildcard); + $result = [$id, 'IN', $values, 0, $wildcard]; } else { - $result = array($id, '=', $values, 0, $wildcard); + $result = [$id, '=', $values, 0, $wildcard]; } return $result; @@ -1748,6 +1781,11 @@ public static function fixWhereValues($id, &$values, $wildcard = 0, $useEquals = * @param string $apiEntity */ public function whereClauseSingle(&$values, $apiEntity = NULL) { + if ($this->isARelativeDateField($values[0])) { + $this->buildRelativeDateQuery($values); + return; + } + // do not process custom fields or prefixed contact ids or component params if (CRM_Core_BAO_CustomField::getKeyID($values[0]) || (substr($values[0], 0, CRM_Core_Form::CB_PREFIX_LEN) == CRM_Core_Form::CB_PREFIX) || @@ -1781,18 +1819,7 @@ public function whereClauseSingle(&$values, $apiEntity = NULL) { return; case 'group': - $this->group($values); - return; - case 'group_type': - // so we resolve this into a list of groups & proceed as if they had been - // handed in - list($name, $op, $value, $grouping, $wildcard) = $values; - $values[0] = 'group'; - $values[1] = 'IN'; - $this->_paramLookup['group'][0][0] = 'group'; - $this->_paramLookup['group'][0][1] = 'IN'; - $this->_paramLookup['group'][0][2] = $values[2] = $this->getGroupsFromTypeCriteria($value); $this->group($values); return; @@ -1881,13 +1908,18 @@ public function whereClauseSingle(&$values, $apiEntity = NULL) { case 'activity_date': case 'activity_date_low': case 'activity_date_high': + case 'activity_date_time_low': + case 'activity_date_time_high': case 'activity_role': case 'activity_status_id': case 'activity_status': + case 'activity_priority': + case 'activity_priority_id': case 'followup_parent_id': case 'parent_id': case 'source_contact_id': - case 'activity_subject': + case 'activity_text': + case 'activity_option': case 'test_activities': case 'activity_type_id': case 'activity_type': @@ -1952,6 +1984,8 @@ public function whereClauseSingle(&$values, $apiEntity = NULL) { case 'relation_start_date_low': case 'relation_end_date_high': case 'relation_end_date_low': + case 'relation_active_period_date_high': + case 'relation_active_period_date_low': case 'relation_target_name': case 'relation_status': case 'relation_date_low': @@ -1977,6 +2011,8 @@ public function whereClauseSingle(&$values, $apiEntity = NULL) { case 'prox_postal_code': case 'prox_state_province_id': case 'prox_country_id': + case 'prox_geo_code_1': + case 'prox_geo_code_2': // handled by the proximity_distance clause return; @@ -1994,8 +2030,8 @@ public function whereClauseSingle(&$values, $apiEntity = NULL) { * @return string */ public function whereClause($apiEntity = NULL) { - $this->_where[0] = array(); - $this->_qill[0] = array(); + $this->_where[0] = []; + $this->_qill[0] = []; $this->includeContactIds(); if (!empty($this->_params)) { @@ -2006,6 +2042,18 @@ public function whereClause($apiEntity = NULL) { // check for both id and contact_id if ($this->_params[$id][0] == 'id' || $this->_params[$id][0] == 'contact_id') { $this->_where[0][] = self::buildClause("contact_a.id", $this->_params[$id][1], $this->_params[$id][2]); + $field = CRM_Utils_Array::value('id', $this->_fields); + list($qillop, $qillVal) = CRM_Contact_BAO_Query::buildQillForFieldValue( + 'CRM_Contact_BAO_Contact', + "contact_a.id", + $this->_params[$id][2], + $this->_params[$id][1] + ); + $this->_qill[0][] = ts("%1 %2 %3", [ + 1 => $field['title'], + 2 => $qillop, + 3 => $qillVal, + ]); } else { $this->whereClauseSingle($this->_params[$id], $apiEntity); @@ -2027,8 +2075,8 @@ public function whereClause($apiEntity = NULL) { $this->_qill = CRM_Utils_Array::crmArrayMerge($this->_qill, $this->_customQuery->_qill); } - $clauses = array(); - $andClauses = array(); + $clauses = []; + $andClauses = []; $validClauses = 0; if (!empty($this->_where)) { @@ -2069,10 +2117,10 @@ public function restWhere(&$values) { $wildcard = CRM_Utils_Array::value(4, $values); if (isset($grouping) && empty($this->_where[$grouping])) { - $this->_where[$grouping] = array(); + $this->_where[$grouping] = []; } - $multipleFields = array('url'); + $multipleFields = ['url']; //check if the location type exists for fields $lType = ''; @@ -2097,8 +2145,7 @@ public function restWhere(&$values) { $setTables = TRUE; - $strtolower = function_exists('mb_strtolower') ? 'mb_strtolower' : 'strtolower'; - $locationType = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id'); + $locationType = CRM_Core_DAO_Address::buildOptions('location_type_id', 'validate'); if (isset($locType[1]) && is_numeric($locType[1])) { $lType = $locationType[$locType[1]]; } @@ -2117,8 +2164,9 @@ public function restWhere(&$values) { } $this->_where[$grouping][] = self::buildClause($where, $op, $value); + $this->_tables[$aName] = $this->_whereTables[$aName] = 1; list($qillop, $qillVal) = self::buildQillForFieldValue('CRM_Core_DAO_Address', "state_province_id", $value, $op); - $this->_qill[$grouping][] = ts("%1 %2 %3", array(1 => $field['title'], 2 => $qillop, 3 => $qillVal)); + $this->_qill[$grouping][] = ts("%1 %2 %3", [1 => $field['title'], 2 => $qillop, 3 => $qillVal]); } elseif (!empty($field['pseudoconstant'])) { $this->optionValueQuery( @@ -2145,9 +2193,10 @@ public function restWhere(&$values) { } $this->_where[$grouping][] = self::buildClause($where, $op, $value, 'Positive'); + $this->_tables[$aName] = $this->_whereTables[$aName] = 1; list($qillop, $qillVal) = CRM_Contact_BAO_Query::buildQillForFieldValue(NULL, $name, $value, $op); - $this->_qill[$grouping][] = ts("%1 %2 %3", array(1 => $field['title'], 2 => $qillop, 3 => $qillVal)); + $this->_qill[$grouping][] = ts("%1 %2 %3", [1 => $field['title'], 2 => $qillop, 3 => $qillVal]); } elseif ($name === 'world_region') { $this->optionValueQuery( @@ -2178,23 +2227,23 @@ public function restWhere(&$values) { } } elseif ($name === 'name') { - $value = $strtolower(CRM_Core_DAO::escapeString($value)); + $value = CRM_Core_DAO::escapeString($value); if ($wildcard) { $op = 'LIKE'; $value = self::getWildCardedValue($wildcard, $op, $value); } - $wc = self::caseImportant($op) ? "LOWER({$field['where']})" : "{$field['where']}"; - $this->_where[$grouping][] = self::buildClause($wc, $op, "'$value'"); + CRM_Core_Error::deprecatedFunctionWarning('Untested code path'); + // @todo it's likely this code path is obsolete / never called. It is definitely not + // passed through in our test suite. + $this->_where[$grouping][] = self::buildClause($field['where'], $op, "'$value'"); $this->_qill[$grouping][] = "$field[title] $op \"$value\""; } elseif ($name === 'current_employer') { - $value = $strtolower(CRM_Core_DAO::escapeString($value)); if ($wildcard) { $op = 'LIKE'; $value = self::getWildCardedValue($wildcard, $op, $value); } - $wc = self::caseImportant($op) ? "LOWER(contact_a.organization_name)" : "contact_a.organization_name"; - $ceWhereClause = self::buildClause($wc, $op, + $ceWhereClause = self::buildClause("contact_a.organization_name", $op, $value ); $ceWhereClause .= " AND contact_a.contact_type = 'Individual'"; @@ -2202,7 +2251,8 @@ public function restWhere(&$values) { $this->_qill[$grouping][] = "$field[title] $op \"$value\""; } elseif ($name === 'email_greeting') { - $filterCondition = array('greeting_type' => 'email_greeting'); + CRM_Core_Error::deprecatedFunctionWarning('pass in email_greeting_id or email_greeting_display'); + $filterCondition = ['greeting_type' => 'email_greeting']; $this->optionValueQuery( $name, $op, $value, $grouping, CRM_Core_PseudoConstant::greeting($filterCondition), @@ -2211,7 +2261,8 @@ public function restWhere(&$values) { ); } elseif ($name === 'postal_greeting') { - $filterCondition = array('greeting_type' => 'postal_greeting'); + CRM_Core_Error::deprecatedFunctionWarning('pass in postal_greeting_id or postal_greeting_display'); + $filterCondition = ['greeting_type' => 'postal_greeting']; $this->optionValueQuery( $name, $op, $value, $grouping, CRM_Core_PseudoConstant::greeting($filterCondition), @@ -2220,7 +2271,8 @@ public function restWhere(&$values) { ); } elseif ($name === 'addressee') { - $filterCondition = array('greeting_type' => 'addressee'); + CRM_Core_Error::deprecatedFunctionWarning('pass in addressee_id or addressee_display'); + $filterCondition = ['greeting_type' => 'addressee']; $this->optionValueQuery( $name, $op, $value, $grouping, CRM_Core_PseudoConstant::greeting($filterCondition), @@ -2231,7 +2283,7 @@ public function restWhere(&$values) { elseif (substr($name, 0, 4) === 'url-') { $tName = 'civicrm_website'; $this->_whereTables[$tName] = $this->_tables[$tName] = "\nLEFT JOIN civicrm_website ON ( civicrm_website.contact_id = contact_a.id )"; - $value = $strtolower(CRM_Core_DAO::escapeString($value)); + $value = CRM_Core_DAO::escapeString($value); if ($wildcard) { $op = 'LIKE'; $value = self::getWildCardedValue($wildcard, $op, $value); @@ -2258,8 +2310,7 @@ public function restWhere(&$values) { //get the location name list($tName, $fldName) = self::getLocationTableName($field['where'], $locType); - - $fieldName = "LOWER(`$tName`.$fldName)"; + $fieldName = "`$tName`.$fldName"; // we set both _tables & whereTables because whereTables doesn't seem to do what the name implies it should $this->_tables[$tName] = $this->_whereTables[$tName] = 1; @@ -2267,23 +2318,19 @@ public function restWhere(&$values) { } else { if ($tableName == 'civicrm_contact') { - $fieldName = "LOWER(contact_a.{$fieldName})"; + $fieldName = "contact_a.{$fieldName}"; } else { - if ($op != 'IN' && !is_numeric($value) && !is_array($value)) { - $fieldName = "LOWER({$field['where']})"; - } - else { - $fieldName = "{$field['where']}"; - } + $fieldName = $field['where']; } } list($qillop, $qillVal) = self::buildQillForFieldValue(NULL, $field['title'], $value, $op); - $this->_qill[$grouping][] = ts("%1 %2 %3", array( + $this->_qill[$grouping][] = ts("%1 %2 %3", [ 1 => $field['title'], 2 => $qillop, - 3 => (strpos($op, 'NULL') !== FALSE || strpos($op, 'EMPTY') !== FALSE) ? $qillVal : "'$qillVal'")); + 3 => (strpos($op, 'NULL') !== FALSE || strpos($op, 'EMPTY') !== FALSE) ? $qillVal : "'$qillVal'", + ]); if (is_array($value)) { // traditionally an array being passed has been a fatal error. We can take advantage of this to add support @@ -2296,20 +2343,17 @@ public function restWhere(&$values) { //Via Contact get api value is not in array(operator => array(values)) format ONLY for IN/NOT IN operators //so this condition will satisfy the search for now if (strpos($op, 'IN') !== FALSE) { - $value = array($op => $value); + $value = [$op => $value]; } // we don't know when this might happen else { - CRM_Core_Error::fatal(ts("%1 is not a valid operator", array(1 => $operator))); + CRM_Core_Error::fatal(ts("%1 is not a valid operator", [1 => $operator])); } } } $this->_where[$grouping][] = CRM_Core_DAO::createSQLFilter($fieldName, $value, $type); } else { - if (!strpos($op, 'IN')) { - $value = $strtolower($value); - } if ($wildcard) { $op = 'LIKE'; $value = self::getWildCardedValue($wildcard, $op, $value); @@ -2328,7 +2372,6 @@ public function restWhere(&$values) { } } - /** * @param $where * @param $locType @@ -2341,8 +2384,8 @@ public static function getLocationTableName(&$where, &$locType) { list($tbName, $fldName) = explode(".", $where); //get the location name - $locationType = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id'); - $specialFields = array('email', 'im', 'phone', 'openid', 'phone_ext'); + $locationType = CRM_Core_DAO_Address::buildOptions('location_type_id', 'validate'); + $specialFields = ['email', 'im', 'phone', 'openid', 'phone_ext']; if (in_array($locType[0], $specialFields)) { //hack to fix / special handing for phone_ext if ($locType[0] == 'phone_ext') { @@ -2356,7 +2399,7 @@ public static function getLocationTableName(&$where, &$locType) { } } elseif (in_array($locType[0], - array( + [ 'address_name', 'street_address', 'street_name', @@ -2364,24 +2407,25 @@ public static function getLocationTableName(&$where, &$locType) { 'street_unit', 'supplemental_address_1', 'supplemental_address_2', + 'supplemental_address_3', 'city', 'postal_code', 'postal_code_suffix', 'geo_code_1', 'geo_code_2', 'master_id', - ) + ] )) { //fix for search by profile with address fields. $tName = "{$locationType[$locType[1]]}-address"; } elseif (in_array($locType[0], - array( + [ 'on_hold', 'signature_html', 'signature_text', 'is_bulkmail', - ) + ] )) { $tName = "{$locationType[$locType[1]]}-email"; } @@ -2395,7 +2439,7 @@ public static function getLocationTableName(&$where, &$locType) { $tName = "{$locationType[$locType[1]]}-{$locType[0]}"; } $tName = str_replace(' ', '_', $tName); - return array($tName, $fldName); + return [$tName, $fldName]; } CRM_Core_Error::fatal(); } @@ -2409,7 +2453,7 @@ public static function getLocationTableName(&$where, &$locType) { * values for this query */ public function store($dao) { - $value = array(); + $value = []; foreach ($this->_element as $key => $dontCare) { if (property_exists($dao, $key)) { @@ -2421,7 +2465,7 @@ public function store($dao) { $count = 1; foreach ($values as $v) { if (!array_key_exists($v, $current)) { - $current[$v] = array(); + $current[$v] = []; } //bad hack for im_provider if ($lastElement == 'provider_id') { @@ -2458,9 +2502,14 @@ public function tables() { } /** - * Where tables is sometimes used to create the from clause, but, not reliably, set this AND set tables - * It's unclear the intent - there is a 'simpleFrom' clause which takes whereTables into account & a fromClause which doesn't - * logic may have eroded + * Sometimes used to create the from clause, but, not reliably, set + * this AND set tables. + * + * It's unclear the intent - there is a 'simpleFrom' clause which + * takes whereTables into account & a fromClause which doesn't. + * + * logic may have eroded? + * * @return array */ public function whereTables() { @@ -2493,20 +2542,30 @@ public static function getWhereClause($params, $fields, &$tables, &$whereTables, * Create the from clause. * * @param array $tables - * Tables that need to be included in this from clause. - * if null, return mimimal from clause (i.e. civicrm_contact) + * Tables that need to be included in this from clause. If null, + * return mimimal from clause (i.e. civicrm_contact). * @param array $inner * Tables that should be inner-joined. * @param array $right * Tables that should be right-joined. - * * @param bool $primaryLocation + * Search on primary location. See note below. * @param int $mode + * Determines search mode based on bitwise MODE_* constants. + * @param string|NULL $apiEntity + * Determines search mode based on entity by string. + * + * The $primaryLocation flag only seems to be used when + * locationType() has been called. This may be a search option + * exposed, or perhaps it's a "search all details" approach which + * predates decoupling of location types and primary fields? + * + * @see https://issues.civicrm.org/jira/browse/CRM-19967 * * @return string * the from clause */ - public static function fromClause(&$tables, $inner = NULL, $right = NULL, $primaryLocation = TRUE, $mode = 1) { + public static function fromClause(&$tables, $inner = NULL, $right = NULL, $primaryLocation = TRUE, $mode = 1, $apiEntity = NULL) { $from = ' FROM civicrm_contact contact_a'; if (empty($tables)) { @@ -2514,24 +2573,22 @@ public static function fromClause(&$tables, $inner = NULL, $right = NULL, $prima } if (!empty($tables['civicrm_worldregion'])) { - $tables = array_merge(array('civicrm_country' => 1), $tables); + $tables = array_merge(['civicrm_country' => 1], $tables); } if ((!empty($tables['civicrm_state_province']) || !empty($tables['civicrm_country']) || - CRM_Utils_Array::value('civicrm_county', $tables) - ) && empty($tables['civicrm_address']) - ) { - $tables = array_merge(array('civicrm_address' => 1), + CRM_Utils_Array::value('civicrm_county', $tables)) && empty($tables['civicrm_address'])) { + $tables = array_merge(['civicrm_address' => 1], $tables ); } // add group_contact and group table is subscription history is present if (!empty($tables['civicrm_subscription_history']) && empty($tables['civicrm_group'])) { - $tables = array_merge(array( - 'civicrm_group' => 1, - 'civicrm_group_contact' => 1, - ), + $tables = array_merge([ + 'civicrm_group' => 1, + 'civicrm_group_contact' => 1, + ], $tables ); } @@ -2565,7 +2622,7 @@ public static function fromClause(&$tables, $inner = NULL, $right = NULL, $prima $tempTable[$k . ".$key"] = $key; } ksort($tempTable); - $newTables = array(); + $newTables = []; foreach ($tempTable as $key) { $newTables[$key] = $tables[$key]; } @@ -2597,158 +2654,181 @@ public static function fromClause(&$tables, $inner = NULL, $right = NULL, $prima } continue; } - switch ($name) { - case 'civicrm_address': - if ($primaryLocation) { - $from .= " $side JOIN civicrm_address ON ( contact_a.id = civicrm_address.contact_id AND civicrm_address.is_primary = 1 )"; - } - else { - //CRM-14263 further handling of address joins further down... - $from .= " $side JOIN civicrm_address ON ( contact_a.id = civicrm_address.contact_id ) "; - } - continue; - case 'civicrm_phone': - $from .= " $side JOIN civicrm_phone ON (contact_a.id = civicrm_phone.contact_id AND civicrm_phone.is_primary = 1) "; - continue; + $from .= self::getEntitySpecificJoins($name, $mode, $side, $primaryLocation); + } + return $from; + } - case 'civicrm_email': - $from .= " $side JOIN civicrm_email ON (contact_a.id = civicrm_email.contact_id AND civicrm_email.is_primary = 1) "; - continue; + /** + * Get join statements for the from clause depending on entity type + * + * @param string $name + * @param int $mode + * @param string $side + * @param string $primaryLocation + * @return string + */ + protected static function getEntitySpecificJoins($name, $mode, $side, $primaryLocation) { + $limitToPrimaryClause = $primaryLocation ? "AND {$name}.is_primary = 1" : ''; + switch ($name) { + case 'civicrm_address': + //CRM-14263 further handling of address joins further down... + return " $side JOIN civicrm_address ON ( contact_a.id = civicrm_address.contact_id {$limitToPrimaryClause} )"; - case 'civicrm_im': - $from .= " $side JOIN civicrm_im ON (contact_a.id = civicrm_im.contact_id AND civicrm_im.is_primary = 1) "; - continue; + case 'civicrm_state_province': + // This is encountered when doing an export after having applied a 'sort' - it pretty much implies primary + // but that will have been implied-in by the calling function. + // test cover in testContactIDQuery + return " $side JOIN civicrm_state_province ON ( civicrm_address.state_province_id = civicrm_state_province.id )"; - case 'im_provider': - $from .= " $side JOIN civicrm_im ON (contact_a.id = civicrm_im.contact_id) "; - $from .= " $side JOIN civicrm_option_group option_group_imProvider ON option_group_imProvider.name = 'instant_messenger_service'"; - $from .= " $side JOIN civicrm_option_value im_provider ON (civicrm_im.provider_id = im_provider.value AND option_group_imProvider.id = im_provider.option_group_id)"; - continue; + case 'civicrm_country': + // This is encountered when doing an export after having applied a 'sort' - it pretty much implies primary + // but that will have been implied-in by the calling function. + // test cover in testContactIDQuery + return " $side JOIN civicrm_country ON ( civicrm_address.country_id = civicrm_country.id )"; - case 'civicrm_openid': - $from .= " $side JOIN civicrm_openid ON ( civicrm_openid.contact_id = contact_a.id AND civicrm_openid.is_primary = 1 )"; - continue; + case 'civicrm_phone': + return " $side JOIN civicrm_phone ON (contact_a.id = civicrm_phone.contact_id {$limitToPrimaryClause}) "; - case 'civicrm_worldregion': - $from .= " $side JOIN civicrm_country ON civicrm_address.country_id = civicrm_country.id "; - $from .= " $side JOIN civicrm_worldregion ON civicrm_country.region_id = civicrm_worldregion.id "; - continue; + case 'civicrm_email': + return " $side JOIN civicrm_email ON (contact_a.id = civicrm_email.contact_id {$limitToPrimaryClause})"; - case 'civicrm_location_type': - $from .= " $side JOIN civicrm_location_type ON civicrm_address.location_type_id = civicrm_location_type.id "; - continue; + case 'civicrm_im': + return " $side JOIN civicrm_im ON (contact_a.id = civicrm_im.contact_id {$limitToPrimaryClause}) "; - case 'civicrm_group': - $from .= " $side JOIN civicrm_group ON civicrm_group.id = civicrm_group_contact.group_id "; - continue; + case 'im_provider': + $from = " $side JOIN civicrm_im ON (contact_a.id = civicrm_im.contact_id) "; + $from .= " $side JOIN civicrm_option_group option_group_imProvider ON option_group_imProvider.name = 'instant_messenger_service'"; + $from .= " $side JOIN civicrm_option_value im_provider ON (civicrm_im.provider_id = im_provider.value AND option_group_imProvider.id = im_provider.option_group_id)"; + return $from; - case 'civicrm_group_contact': - $from .= " $side JOIN civicrm_group_contact ON contact_a.id = civicrm_group_contact.contact_id "; - continue; + case 'civicrm_openid': + return " $side JOIN civicrm_openid ON ( civicrm_openid.contact_id = contact_a.id {$limitToPrimaryClause} )"; - case 'civicrm_group_contact_cache': - $from .= " $side JOIN civicrm_group_contact_cache ON contact_a.id = civicrm_group_contact_cache.contact_id "; - continue; + case 'civicrm_worldregion': + // We can be sure from the calling function that country will already be joined in. + // we really don't need world_region - we could use a pseudoconstant for it. + return "$side JOIN civicrm_worldregion ON civicrm_country.region_id = civicrm_worldregion.id "; - case 'civicrm_activity': - case 'civicrm_activity_tag': - case 'activity_type': - case 'activity_status': - case 'parent_id': - case 'civicrm_activity_contact': - case 'source_contact': - $from .= CRM_Activity_BAO_Query::from($name, $mode, $side); - continue; + case 'civicrm_location_type': + return " $side JOIN civicrm_location_type ON civicrm_address.location_type_id = civicrm_location_type.id "; - case 'civicrm_entity_tag': - $from .= " $side JOIN civicrm_entity_tag ON ( civicrm_entity_tag.entity_table = 'civicrm_contact' AND - civicrm_entity_tag.entity_id = contact_a.id ) "; - continue; + case 'civicrm_group': + return " $side JOIN civicrm_group ON civicrm_group.id = civicrm_group_contact.group_id "; - case 'civicrm_note': - $from .= " $side JOIN civicrm_note ON ( civicrm_note.entity_table = 'civicrm_contact' AND - contact_a.id = civicrm_note.entity_id ) "; - continue; + case 'civicrm_group_contact': + return " $side JOIN civicrm_group_contact ON contact_a.id = civicrm_group_contact.contact_id "; - case 'civicrm_subscription_history': - $from .= " $side JOIN civicrm_subscription_history - ON civicrm_group_contact.contact_id = civicrm_subscription_history.contact_id - AND civicrm_group_contact.group_id = civicrm_subscription_history.group_id"; - continue; + case 'civicrm_group_contact_cache': + return " $side JOIN civicrm_group_contact_cache ON contact_a.id = civicrm_group_contact_cache.contact_id "; - case 'civicrm_relationship': - if (self::$_relType == 'reciprocal') { - if (self::$_relationshipTempTable) { - // we have a temptable to join on - $tbl = self::$_relationshipTempTable; - $from .= " INNER JOIN {$tbl} civicrm_relationship ON civicrm_relationship.contact_id = contact_a.id"; - } - else { - $from .= " $side JOIN civicrm_relationship ON (civicrm_relationship.contact_id_b = contact_a.id OR civicrm_relationship.contact_id_a = contact_a.id)"; - $from .= " $side JOIN civicrm_contact contact_b ON (civicrm_relationship.contact_id_a = contact_b.id OR civicrm_relationship.contact_id_b = contact_b.id)"; - } - } - elseif (self::$_relType == 'b') { - $from .= " $side JOIN civicrm_relationship ON (civicrm_relationship.contact_id_b = contact_a.id )"; - $from .= " $side JOIN civicrm_contact contact_b ON (civicrm_relationship.contact_id_a = contact_b.id )"; + case 'civicrm_activity': + case 'civicrm_activity_tag': + case 'activity_type': + case 'activity_status': + case 'parent_id': + case 'civicrm_activity_contact': + case 'source_contact': + case 'activity_priority': + return CRM_Activity_BAO_Query::from($name, $mode, $side); + + case 'civicrm_entity_tag': + $from = " $side JOIN civicrm_entity_tag ON ( civicrm_entity_tag.entity_table = 'civicrm_contact'"; + return "$from AND civicrm_entity_tag.entity_id = contact_a.id ) "; + + case 'civicrm_note': + $from = " $side JOIN civicrm_note ON ( civicrm_note.entity_table = 'civicrm_contact'"; + return "$from AND contact_a.id = civicrm_note.entity_id ) "; + + case 'civicrm_subscription_history': + $from = " $side JOIN civicrm_subscription_history"; + $from .= " ON civicrm_group_contact.contact_id = civicrm_subscription_history.contact_id"; + return "$from AND civicrm_group_contact.group_id = civicrm_subscription_history.group_id"; + + case 'civicrm_relationship': + if (self::$_relType == 'reciprocal') { + if (self::$_relationshipTempTable) { + // we have a temptable to join on + $tbl = self::$_relationshipTempTable; + return " INNER JOIN {$tbl} civicrm_relationship ON civicrm_relationship.contact_id = contact_a.id"; } else { - $from .= " $side JOIN civicrm_relationship ON (civicrm_relationship.contact_id_a = contact_a.id )"; - $from .= " $side JOIN civicrm_contact contact_b ON (civicrm_relationship.contact_id_b = contact_b.id )"; + $from = " $side JOIN civicrm_relationship ON (civicrm_relationship.contact_id_b = contact_a.id OR civicrm_relationship.contact_id_a = contact_a.id)"; + $from .= " $side JOIN civicrm_contact contact_b ON (civicrm_relationship.contact_id_a = contact_b.id OR civicrm_relationship.contact_id_b = contact_b.id)"; + return $from; } - continue; + } + elseif (self::$_relType == 'b') { + $from = " $side JOIN civicrm_relationship ON (civicrm_relationship.contact_id_b = contact_a.id )"; + return "$from $side JOIN civicrm_contact contact_b ON (civicrm_relationship.contact_id_a = contact_b.id )"; + } + else { + $from = " $side JOIN civicrm_relationship ON (civicrm_relationship.contact_id_a = contact_a.id )"; + return "$from $side JOIN civicrm_contact contact_b ON (civicrm_relationship.contact_id_b = contact_b.id )"; + } - case 'civicrm_log': - $from .= " INNER JOIN civicrm_log ON (civicrm_log.entity_id = contact_a.id AND civicrm_log.entity_table = 'civicrm_contact')"; - $from .= " INNER JOIN civicrm_contact contact_b_log ON (civicrm_log.modified_id = contact_b_log.id)"; - continue; + case 'civicrm_log': + $from = " INNER JOIN civicrm_log ON (civicrm_log.entity_id = contact_a.id AND civicrm_log.entity_table = 'civicrm_contact')"; + return "$from INNER JOIN civicrm_contact contact_b_log ON (civicrm_log.modified_id = contact_b_log.id)"; - case 'civicrm_tag': - $from .= " $side JOIN civicrm_tag ON civicrm_entity_tag.tag_id = civicrm_tag.id "; - continue; + case 'civicrm_tag': + return " $side JOIN civicrm_tag ON civicrm_entity_tag.tag_id = civicrm_tag.id "; - case 'civicrm_grant': - $from .= CRM_Grant_BAO_Query::from($name, $mode, $side); - continue; + case 'civicrm_grant': + return CRM_Grant_BAO_Query::from($name, $mode, $side); - case 'civicrm_website': - $from .= " $side JOIN civicrm_website ON contact_a.id = civicrm_website.contact_id "; - continue; + case 'civicrm_website': + return " $side JOIN civicrm_website ON contact_a.id = civicrm_website.contact_id "; - default: - $locationTypeName = ''; - if (strpos($name, '-address') != 0) { - $locationTypeName = 'address'; - } - elseif (strpos($name, '-phone') != 0) { - $locationTypeName = 'phone'; - } - elseif (strpos($name, '-email') != 0) { - $locationTypeName = 'email'; - } - elseif (strpos($name, '-im') != 0) { - $locationTypeName = 'im'; - } - elseif (strpos($name, '-openid') != 0) { - $locationTypeName = 'openid'; - } + case 'civicrm_campaign': + //Move to default case if not in either mode. + if ($mode & CRM_Contact_BAO_Query::MODE_CONTRIBUTE) { + return CRM_Contribute_BAO_Query::from($name, $mode, $side); + } + elseif ($mode & CRM_Contact_BAO_Query::MODE_MAILING) { + return CRM_Mailing_BAO_Query::from($name, $mode, $side); + } + elseif ($mode & CRM_Contact_BAO_Query::MODE_CAMPAIGN) { + return CRM_Campaign_BAO_Query::from($name, $mode, $side); + } - if ($locationTypeName) { - //we have a join on an location table - possibly in conjunction with search builder - CRM-14263 - $parts = explode('-', $name); - $locationID = array_search($parts[0], CRM_Core_BAO_Address::buildOptions('location_type_id', 'get', array('name' => $parts[0]))); - $from .= " $side JOIN civicrm_{$locationTypeName} `{$name}` ON ( contact_a.id = `{$name}`.contact_id ) and `{$name}`.location_type_id = $locationID "; - } - else { - $from .= CRM_Core_Component::from($name, $mode, $side); + default: + $locationTypeName = ''; + if (strpos($name, '-address') != 0) { + $locationTypeName = 'address'; + } + elseif (strpos($name, '-phone') != 0) { + $locationTypeName = 'phone'; + } + elseif (strpos($name, '-email') != 0) { + $locationTypeName = 'email'; + } + elseif (strpos($name, '-im') != 0) { + $locationTypeName = 'im'; + } + elseif (strpos($name, '-openid') != 0) { + $locationTypeName = 'openid'; + } + + if ($locationTypeName) { + //we have a join on an location table - possibly in conjunction with search builder - CRM-14263 + $parts = explode('-', $name); + $locationTypes = CRM_Core_DAO_Address::buildOptions('location_type_id', 'validate'); + foreach ($locationTypes as $locationTypeID => $locationType) { + if ($parts[0] == str_replace(' ', '_', $locationType)) { + $locationID = $locationTypeID; + } } - $from .= CRM_Contact_BAO_Query_Hook::singleton()->buildSearchfrom($name, $mode, $side); + $from = " $side JOIN civicrm_{$locationTypeName} `{$name}` ON ( contact_a.id = `{$name}`.contact_id ) and `{$name}`.location_type_id = $locationID "; + } + else { + $from = CRM_Core_Component::from($name, $mode, $side); + } + $from .= CRM_Contact_BAO_Query_Hook::singleton()->buildSearchfrom($name, $mode, $side); - continue; - } + return $from; } - return $from; } /** @@ -2772,8 +2852,8 @@ public function deletedContacts($values) { public function contactType(&$values) { list($name, $op, $value, $grouping, $wildcard) = $values; - $subTypes = array(); - $clause = array(); + $subTypes = []; + $clause = []; // account for search builder mapping multiple values if (!is_array($value)) { @@ -2830,9 +2910,9 @@ public function contactType(&$values) { } /** - * Where / qill clause for contact_sub_type + * Where / qill clause for contact_sub_type. * - * @param $values + * @param array $values */ public function contactSubType(&$values) { list($name, $op, $value, $grouping, $wildcard) = $values; @@ -2851,7 +2931,7 @@ public function includeContactSubTypes($value, $grouping, $op = 'LIKE') { $value = $value[$op]; } - $clause = array(); + $clause = []; $alias = "contact_a.contact_sub_type"; $qillOperators = CRM_Core_SelectValues::getSearchBuilderOperators(); @@ -2864,9 +2944,7 @@ public function includeContactSubTypes($value, $grouping, $op = 'LIKE') { } elseif (is_array($value)) { foreach ($value as $k => $v) { - if (!empty($k)) { - $clause[$k] = "($alias $op '%" . CRM_Core_DAO::VALUE_SEPARATOR . CRM_Utils_Type::escape($v, 'String') . CRM_Core_DAO::VALUE_SEPARATOR . "%')"; - } + $clause[$k] = "($alias $op '%" . CRM_Core_DAO::VALUE_SEPARATOR . CRM_Utils_Type::escape($v, 'String') . CRM_Core_DAO::VALUE_SEPARATOR . "%')"; } } else { @@ -2876,7 +2954,7 @@ public function includeContactSubTypes($value, $grouping, $op = 'LIKE') { if (!empty($clause)) { $this->_where[$grouping][] = "( " . implode(' OR ', $clause) . " )"; } - $this->_qill[$grouping][] = ts('Contact Subtype %1 ', array(1 => $qillOperators[$op])) . implode(' ' . ts('or') . ' ', array_keys($clause)); + $this->_qill[$grouping][] = ts('Contact Subtype %1 ', [1 => $qillOperators[$op]]) . implode(' ' . ts('or') . ' ', array_keys($clause)); } /** @@ -2900,9 +2978,9 @@ public function group($values) { $value = NULL; } - if (count($value) > 1) { + if (is_array($value) && count($value) > 1) { if (strpos($op, 'IN') === FALSE && strpos($op, 'NULL') === FALSE) { - CRM_Core_Error::fatal(ts("%1 is not a valid operator", array(1 => $op))); + CRM_Core_Error::fatal(ts("%1 is not a valid operator", [1 => $op])); } $this->_useDistinct = TRUE; } @@ -2911,11 +2989,23 @@ public function group($values) { $value = CRM_Utils_Array::value($op, $value, $value); } - $groupIds = NULL; + if ($name == 'group_type') { + $value = array_keys($this->getGroupsFromTypeCriteria($value)); + } + + $regularGroupIDs = $smartGroupIDs = []; + foreach ((array) $value as $id) { + if (CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Group', $id, 'saved_search_id')) { + $smartGroupIDs[] = $id; + } + else { + $regularGroupIDs[] = trim($id); + } + } $isNotOp = ($op == 'NOT IN' || $op == '!='); - $statii = array(); + $statii = []; $gcsValues = $this->getWhereValues('group_contact_status', $grouping); if ($gcsValues && is_array($gcsValues[2]) @@ -2927,61 +3017,98 @@ public function group($values) { } } else { - $statii[] = '"Added"'; - } + $statii[] = "'Added'"; + } + $groupClause = []; + if (count($regularGroupIDs) || empty($value)) { + // include child groups IDs if any + $childGroupIds = (array) CRM_Contact_BAO_Group::getChildGroupIds($regularGroupIDs); + foreach ($childGroupIds as $key => $id) { + if (CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Group', $id, 'saved_search_id')) { + $smartGroupIDs[] = $id; + unset($childGroupIds[$key]); + } + } + if (count($childGroupIds)) { + $regularGroupIDs = array_merge($regularGroupIDs, $childGroupIds); + } - $ssClause = $this->addGroupContactCache($value, NULL, "contact_a", $op); - $isSmart = (!$ssClause) ? FALSE : TRUE; - if (!is_array($value) && - count($statii) == 1 && - $statii[0] == '"Added"' && - !$isNotOp - ) { - if (!empty($value) && CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Group', $value, 'saved_search_id')) { - $isSmart = TRUE; + if (empty($regularGroupIDs)) { + $regularGroupIDs = [0]; } - } - $groupClause = NULL; - if (!$isSmart) { - $groupIds = implode(',', (array) $value); - $gcTable = "`civicrm_group_contact-{$groupIds}`"; - $joinClause = array("contact_a.id = {$gcTable}.contact_id"); - if ($statii) { - $joinClause[] = "{$gcTable}.status IN (" . implode(', ', $statii) . ")"; + // if $regularGroupIDs is populated with regular child group IDs + // then change the mysql operator to desired + if (count($regularGroupIDs) > 1) { + $op = strpos($op, 'IN') ? $op : ($op == '!=') ? 'NOT IN' : 'IN'; } - $this->_tables[$gcTable] = $this->_whereTables[$gcTable] = " LEFT JOIN civicrm_group_contact {$gcTable} ON (" . implode(' AND ', $joinClause) . ")"; + $groupIds = ''; + if (!empty($regularGroupIDs)) { + $groupIds = CRM_Utils_Type::validate( + implode(',', (array) $regularGroupIDs), + 'CommaSeparatedIntegers' + ); + } + $gcTable = "`civicrm_group_contact-" . uniqid() . "`"; + $joinClause = ["contact_a.id = {$gcTable}.contact_id"]; + if (strpos($op, 'IN') !== FALSE) { - $groupClause = "{$gcTable}.group_id $op ( $groupIds )"; + $clause = "{$gcTable}.group_id $op ( $groupIds ) "; } elseif ($op == '!=') { - $groupClause = "{$gcTable}.contact_id NOT IN (SELECT contact_id FROM civicrm_group_contact cgc WHERE cgc.group_id = $groupIds)"; + $clause = "{$gcTable}.contact_id NOT IN (SELECT contact_id FROM civicrm_group_contact cgc WHERE cgc.group_id = $groupIds )"; } else { - $groupClause = "{$gcTable}.group_id $op $groupIds"; + $clause = "{$gcTable}.group_id $op $groupIds "; } - } + $groupClause[] = "( {$clause} )"; - if ($ssClause) { - $and = ($op == 'IS NULL') ? 'AND' : 'OR'; - if ($groupClause) { - $groupClause = "( ( $groupClause ) $and ( $ssClause ) )"; + if ($statii) { + $joinClause[] = "{$gcTable}.status IN (" . implode(', ', $statii) . ")"; } - else { - $groupClause = $ssClause; + $this->_tables[$gcTable] = $this->_whereTables[$gcTable] = " LEFT JOIN civicrm_group_contact {$gcTable} ON (" . implode(' AND ', $joinClause) . ")"; + } + + //CRM-19589: contact(s) removed from a Smart Group, resides in civicrm_group_contact table + $groupContactCacheClause = ''; + if (count($smartGroupIDs) || empty($value)) { + $this->_groupUniqueKey = uniqid(); + $this->_groupKeys[] = $this->_groupUniqueKey; + $gccTableAlias = "civicrm_group_contact_cache_{$this->_groupUniqueKey}"; + $groupContactCacheClause = $this->addGroupContactCache($smartGroupIDs, $gccTableAlias, "contact_a", $op); + if (!empty($groupContactCacheClause)) { + if ($isNotOp) { + $groupIds = implode(',', (array) $smartGroupIDs); + $gcTable = "civicrm_group_contact_{$this->_groupUniqueKey}"; + $joinClause = ["contact_a.id = {$gcTable}.contact_id"]; + $this->_tables[$gcTable] = $this->_whereTables[$gcTable] = " LEFT JOIN civicrm_group_contact {$gcTable} ON (" . implode(' AND ', $joinClause) . ")"; + if (strpos($op, 'IN') !== FALSE) { + $groupClause[] = "{$gcTable}.group_id $op ( $groupIds ) AND {$gccTableAlias}.group_id IS NULL"; + } + else { + $groupClause[] = "{$gcTable}.group_id $op $groupIds AND {$gccTableAlias}.group_id IS NULL"; + } + } + $groupClause[] = " ( {$groupContactCacheClause} ) "; } } + $and = ($op == 'IS NULL') ? ' AND ' : ' OR '; + if (!empty($groupClause)) { + $this->_where[$grouping][] = ' ( ' . implode($and, $groupClause) . ' ) '; + } + list($qillop, $qillVal) = CRM_Contact_BAO_Query::buildQillForFieldValue('CRM_Contact_DAO_Group', 'id', $value, $op); - $this->_qill[$grouping][] = ts("Group(s) %1 %2", array(1 => $qillop, 2 => $qillVal)); + $this->_qill[$grouping][] = ts("Group(s) %1 %2", [1 => $qillop, 2 => $qillVal]); if (strpos($op, 'NULL') === FALSE) { - $this->_qill[$grouping][] = ts("Group Status %1", array(1 => implode(' ' . ts('or') . ' ', $statii))); - } - if ($groupClause) { - $this->_where[$grouping][] = $groupClause; + $this->_qill[$grouping][] = ts("Group Status %1", [1 => implode(' ' . ts('or') . ' ', $statii)]); } } + public function getGroupCacheTableKeys() { + return $this->_groupKeys; + } + /** * Function translates selection of group type into a list of groups. * @param $value @@ -2989,7 +3116,7 @@ public function group($values) { * @return array */ public function getGroupsFromTypeCriteria($value) { - $groupIds = array(); + $groupIds = []; foreach ((array) $value as $groupTypeValue) { $groupList = CRM_Core_PseudoConstant::group($groupTypeValue); $groupIds = ($groupIds + $groupList); @@ -2998,24 +3125,33 @@ public function getGroupsFromTypeCriteria($value) { } /** - * @param array $groups - * @param string $tableAlias - * @param string $joinTable - * @param string $op + * Prime smart group cache for smart groups in the search, and join + * civicrm_group_contact_cache table into the query. + * + * @param array $groups IDs of groups specified in search criteria. + * @param string $tableAlias Alias to use for civicrm_group_contact_cache table. + * @param string $joinTable Table on which to join civicrm_group_contact_cache + * @param string $op SQL comparison operator (NULL, IN, !=, IS NULL, etc.) + * @param string $joinColumn Column in $joinTable on which to join civicrm_group_contact_cache.contact_id * - * @return null|string + * @return string WHERE clause component for smart group criteria. */ - public function addGroupContactCache($groups, $tableAlias = NULL, $joinTable = "contact_a", $op) { + public function addGroupContactCache($groups, $tableAlias, $joinTable = "contact_a", $op, $joinColumn = 'id') { $isNullOp = (strpos($op, 'NULL') !== FALSE); $groupsIds = $groups; + + $operator = ['=' => 'IN', '!=' => 'NOT IN']; + if (!empty($operator[$op]) && is_array($groups)) { + $op = $operator[$op]; + } if (!$isNullOp && !$groups) { return NULL; } elseif (strpos($op, 'IN') !== FALSE) { - $groups = array($op => $groups); + $groups = [$op => $groups]; } elseif (is_array($groups) && count($groups)) { - $groups = array('IN' => $groups); + $groups = ['IN' => $groups]; } // Find all the groups that are part of a saved search. @@ -3037,16 +3173,15 @@ public function addGroupContactCache($groups, $tableAlias = NULL, $joinTable = " CRM_Contact_BAO_GroupContactCache::load($group); } } - if ($group->N == 0) { + if ($group->N == 0 && $op != 'NOT IN') { return NULL; } - if (!$tableAlias) { - $tableAlias = "`civicrm_group_contact_cache_"; - $tableAlias .= ($isNullOp) ? "a`" : implode(',', (array) $groupsIds) . "`"; - } + $this->_tables[$tableAlias] = $this->_whereTables[$tableAlias] = " LEFT JOIN civicrm_group_contact_cache {$tableAlias} ON {$joinTable}.{$joinColumn} = {$tableAlias}.contact_id "; - $this->_tables[$tableAlias] = $this->_whereTables[$tableAlias] = " LEFT JOIN civicrm_group_contact_cache {$tableAlias} ON {$joinTable}.id = {$tableAlias}.contact_id "; + if ($op == 'NOT IN') { + return "{$tableAlias}.contact_id NOT IN (SELECT contact_id FROM civicrm_group_contact_cache cgcc WHERE cgcc.group_id IN ( " . implode(',', (array) $groupsIds) . " ) )"; + } return self::buildClause("{$tableAlias}.group_id", $op, $groups, 'Int'); } @@ -3081,12 +3216,13 @@ public function tagSearch(&$values) { $op = "LIKE"; $value = "%{$value}%"; + $escapedValue = CRM_Utils_Type::escape("%{$value}%", 'String'); $useAllTagTypes = $this->getWhereValues('all_tag_types', $grouping); $tagTypesText = $this->getWhereValues('tag_types_text', $grouping); - $etTable = "`civicrm_entity_tag-" . $value . "`"; - $tTable = "`civicrm_tag-" . $value . "`"; + $etTable = "`civicrm_entity_tag-" . uniqid() . "`"; + $tTable = "`civicrm_tag-" . uniqid() . "`"; if ($useAllTagTypes[2]) { $this->_tables[$etTable] = $this->_whereTables[$etTable] @@ -3094,8 +3230,8 @@ public function tagSearch(&$values) { LEFT JOIN civicrm_tag {$tTable} ON ( {$etTable}.tag_id = {$tTable}.id )"; // search tag in cases - $etCaseTable = "`civicrm_entity_case_tag-" . $value . "`"; - $tCaseTable = "`civicrm_case_tag-" . $value . "`"; + $etCaseTable = "`civicrm_entity_case_tag-" . uniqid() . "`"; + $tCaseTable = "`civicrm_case_tag-" . uniqid() . "`"; $this->_tables[$etCaseTable] = $this->_whereTables[$etCaseTable] = " LEFT JOIN civicrm_case_contact ON civicrm_case_contact.contact_id = contact_a.id LEFT JOIN civicrm_case @@ -3104,9 +3240,9 @@ public function tagSearch(&$values) { LEFT JOIN civicrm_entity_tag {$etCaseTable} ON ( {$etCaseTable}.entity_table = 'civicrm_case' AND {$etCaseTable}.entity_id = civicrm_case.id ) LEFT JOIN civicrm_tag {$tCaseTable} ON ( {$etCaseTable}.tag_id = {$tCaseTable}.id )"; // search tag in activities - $etActTable = "`civicrm_entity_act_tag-" . $value . "`"; - $tActTable = "`civicrm_act_tag-" . $value . "`"; - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $etActTable = "`civicrm_entity_act_tag-" . uniqid() . "`"; + $tActTable = "`civicrm_act_tag-" . uniqid() . "`"; + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts); $this->_tables[$etActTable] = $this->_whereTables[$etActTable] @@ -3118,30 +3254,30 @@ public function tagSearch(&$values) { LEFT JOIN civicrm_entity_tag as {$etActTable} ON ( {$etActTable}.entity_table = 'civicrm_activity' AND {$etActTable}.entity_id = civicrm_activity.id ) LEFT JOIN civicrm_tag {$tActTable} ON ( {$etActTable}.tag_id = {$tActTable}.id )"; - $this->_where[$grouping][] = "({$tTable}.name $op '" . $value . "' OR {$tCaseTable}.name $op '" . $value . "' OR {$tActTable}.name $op '" . $value . "')"; - $this->_qill[$grouping][] = ts('Tag %1 %2', array(1 => $tagTypesText[2], 2 => $op)) . ' ' . $value; + $this->_where[$grouping][] = "({$tTable}.name $op '" . $escapedValue . "' OR {$tCaseTable}.name $op '" . $escapedValue . "' OR {$tActTable}.name $op '" . $escapedValue . "')"; + $this->_qill[$grouping][] = ts('Tag %1 %2', [1 => $tagTypesText[2], 2 => $op]) . ' ' . $value; } else { - $etTable = "`civicrm_entity_tag-" . $value . "`"; - $tTable = "`civicrm_tag-" . $value . "`"; + $etTable = "`civicrm_entity_tag-" . uniqid() . "`"; + $tTable = "`civicrm_tag-" . uniqid() . "`"; $this->_tables[$etTable] = $this->_whereTables[$etTable] = " LEFT JOIN civicrm_entity_tag {$etTable} ON ( {$etTable}.entity_id = contact_a.id AND {$etTable}.entity_table = 'civicrm_contact' ) LEFT JOIN civicrm_tag {$tTable} ON ( {$etTable}.tag_id = {$tTable}.id ) "; $this->_where[$grouping][] = self::buildClause("{$tTable}.name", $op, $value, 'String'); - $this->_qill[$grouping][] = ts('Tagged %1', array(1 => $op)) . ' ' . $value; + $this->_qill[$grouping][] = ts('Tagged %1', [1 => $op]) . ' ' . $value; } } /** - * Where / qill clause for tag + * Where / qill clause for tag. * * @param array $values */ public function tag(&$values) { list($name, $op, $value, $grouping, $wildcard) = $values; - list($qillop, $qillVal) = self::buildQillForFieldValue('CRM_Core_DAO_EntityTag', "tag_id", $value, $op, array('onlyActive' => FALSE)); + list($qillop, $qillVal) = self::buildQillForFieldValue('CRM_Core_DAO_EntityTag', "tag_id", $value, $op, ['onlyActive' => FALSE]); // API/Search Builder format array(operator => array(values)) if (is_array($value)) { if (in_array(key($value), CRM_Core_DAO::acceptedSQLOperators(), TRUE)) { @@ -3151,21 +3287,29 @@ public function tag(&$values) { if (count($value) > 1) { $this->_useDistinct = TRUE; } - $value = implode(',', (array) $value); + } + + // implode array, then remove all spaces + $value = str_replace(' ', '', implode(',', (array) $value)); + if (!empty($value)) { + $value = CRM_Utils_Type::validate( + $value, + 'CommaSeparatedIntegers' + ); } $useAllTagTypes = $this->getWhereValues('all_tag_types', $grouping); $tagTypesText = $this->getWhereValues('tag_types_text', $grouping); - $etTable = "`civicrm_entity_tag-" . $value . "`"; + $etTable = "`civicrm_entity_tag-" . uniqid() . "`"; if ($useAllTagTypes[2]) { $this->_tables[$etTable] = $this->_whereTables[$etTable] = " LEFT JOIN civicrm_entity_tag {$etTable} ON ( {$etTable}.entity_id = contact_a.id AND {$etTable}.entity_table = 'civicrm_contact') "; // search tag in cases - $etCaseTable = "`civicrm_entity_case_tag-" . $value . "`"; - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $etCaseTable = "`civicrm_entity_case_tag-" . uniqid() . "`"; + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts); $this->_tables[$etCaseTable] = $this->_whereTables[$etCaseTable] @@ -3175,7 +3319,7 @@ public function tag(&$values) { AND civicrm_case.is_deleted = 0 ) LEFT JOIN civicrm_entity_tag {$etCaseTable} ON ( {$etCaseTable}.entity_table = 'civicrm_case' AND {$etCaseTable}.entity_id = civicrm_case.id ) "; // search tag in activities - $etActTable = "`civicrm_entity_act_tag-" . $value . "`"; + $etActTable = "`civicrm_entity_act_tag-" . uniqid() . "`"; $this->_tables[$etActTable] = $this->_whereTables[$etActTable] = " LEFT JOIN civicrm_activity_contact ON ( civicrm_activity_contact.contact_id = contact_a.id AND civicrm_activity_contact.record_type_id = {$targetID} ) @@ -3185,7 +3329,7 @@ public function tag(&$values) { LEFT JOIN civicrm_entity_tag as {$etActTable} ON ( {$etActTable}.entity_table = 'civicrm_activity' AND {$etActTable}.entity_id = civicrm_activity.id ) "; // CRM-10338 - if (in_array($op, array('IS NULL', 'IS NOT NULL', 'IS EMPTY', 'IS NOT EMPTY'))) { + if (in_array($op, ['IS NULL', 'IS NOT NULL', 'IS EMPTY', 'IS NOT EMPTY'])) { $this->_where[$grouping][] = "({$etTable}.tag_id $op OR {$etCaseTable}.tag_id $op OR {$etActTable}.tag_id $op)"; } else { @@ -3197,7 +3341,7 @@ public function tag(&$values) { = " LEFT JOIN civicrm_entity_tag {$etTable} ON ( {$etTable}.entity_id = contact_a.id AND {$etTable}.entity_table = 'civicrm_contact') "; // CRM-10338 - if (in_array($op, array('IS NULL', 'IS NOT NULL', 'IS EMPTY', 'IS NOT EMPTY'))) { + if (in_array($op, ['IS NULL', 'IS NOT NULL', 'IS EMPTY', 'IS NOT EMPTY'])) { // this converts IS (NOT)? EMPTY to IS (NOT)? NULL $op = str_replace('EMPTY', 'NULL', $op); $this->_where[$grouping][] = "{$etTable}.tag_id $op"; @@ -3211,7 +3355,7 @@ public function tag(&$values) { $this->_where[$grouping][] = "{$etTable}.tag_id $op ( $value )"; } } - $this->_qill[$grouping][] = ts('Tagged %1 %2', array(1 => $qillop, 2 => $qillVal)); + $this->_qill[$grouping][] = ts('Tagged %1 %2', [1 => $qillop, 2 => $qillVal]); } /** @@ -3231,9 +3375,8 @@ public function notes(&$values) { $this->_tables['civicrm_note'] = $this->_whereTables['civicrm_note'] = " LEFT JOIN civicrm_note ON ( civicrm_note.entity_table = 'civicrm_contact' AND contact_a.id = civicrm_note.entity_id ) "; - $strtolower = function_exists('mb_strtolower') ? 'mb_strtolower' : 'strtolower'; $n = trim($value); - $value = $strtolower(CRM_Core_DAO::escapeString($n)); + $value = CRM_Core_DAO::escapeString($n); if ($wildcard) { if (strpos($value, '%') === FALSE) { $value = "%$value%"; @@ -3245,7 +3388,7 @@ public function notes(&$values) { } $label = NULL; - $clauses = array(); + $clauses = []; if ($noteOption % 2 == 0) { $clauses[] = self::buildClause('civicrm_note.note', $op, $value, 'String'); $label = ts('Note: Body Only'); @@ -3256,7 +3399,7 @@ public function notes(&$values) { } $this->_where[$grouping][] = "( " . implode(' OR ', $clauses) . " )"; list($qillOp, $qillVal) = self::buildQillForFieldValue(NULL, $name, $n, $op); - $this->_qill[$grouping][] = ts("%1 %2 %3", array(1 => $label, 2 => $qillOp, 3 => $qillVal)); + $this->_qill[$grouping][] = ts("%1 %2 %3", [1 => $label, 2 => $qillOp, 3 => $qillVal]); } /** @@ -3310,16 +3453,14 @@ public function sortName(&$values) { $config = CRM_Core_Config::singleton(); - $sub = array(); + $sub = []; //By default, $sub elements should be joined together with OR statements (don't change this variable). $subGlue = ' OR '; - $strtolower = function_exists('mb_strtolower') ? 'mb_strtolower' : 'strtolower'; - $firstChar = substr($value, 0, 1); $lastChar = substr($value, -1, 1); - $quotes = array("'", '"'); + $quotes = ["'", '"']; // If string is quoted, strip quotes and otherwise don't alter it if ((strlen($value) > 2) && in_array($firstChar, $quotes) && in_array($lastChar, $quotes)) { $value = trim($value, implode('', $quotes)); @@ -3329,19 +3470,19 @@ public function sortName(&$values) { elseif ($op == 'LIKE' && strpos($value, ',') === FALSE) { $value = str_replace(' ', '%', $value); } - $value = $strtolower(CRM_Core_DAO::escapeString(trim($value))); + $value = CRM_Core_DAO::escapeString(trim($value)); if (strlen($value)) { - $fieldsub = array(); + $fieldsub = []; $value = "'" . self::getWildCardedValue($wildcard, $op, $value) . "'"; if ($fieldName == 'sort_name') { - $wc = self::caseImportant($op) ? "LOWER(contact_a.sort_name)" : "contact_a.sort_name"; + $wc = "contact_a.sort_name"; } else { - $wc = self::caseImportant($op) ? "LOWER(contact_a.display_name)" : "contact_a.display_name"; + $wc = "contact_a.display_name"; } $fieldsub[] = " ( $wc $op $value )"; if ($config->includeNickNameInName) { - $wc = self::caseImportant($op) ? "LOWER(contact_a.nick_name)" : "contact_a.nick_name"; + $wc = "contact_a.nick_name"; $fieldsub[] = " ( $wc $op $value )"; } if ($config->includeEmailInName) { @@ -3372,7 +3513,7 @@ public function greetings(&$values) { $name .= '_display'; list($qillop, $qillVal) = CRM_Contact_BAO_Query::buildQillForFieldValue(NULL, $name, $value, $op); - $this->_qill[$grouping][] = ts('Greeting %1 %2', array(1 => $qillop, 2 => $qillVal)); + $this->_qill[$grouping][] = ts('Greeting %1 %2', [1 => $qillop, 2 => $qillVal]); $this->_where[$grouping][] = self::buildClause("contact_a.{$name}", $op, $value, 'String'); } @@ -3400,7 +3541,7 @@ protected function email(&$values, $apiEntity) { return; } - $n = strtolower(trim($value)); + $n = trim($value); if ($n) { if (substr($n, 0, 1) == '"' && substr($n, -1, 1) == '"' @@ -3462,7 +3603,7 @@ public function phone_option_group($values) { * @param array $values */ public function street_address(&$values) { - list($name, $op, $value, $grouping, $wildcard) = $values; + list($name, $op, $value, $grouping) = $values; if (!$op) { $op = 'LIKE'; @@ -3471,13 +3612,12 @@ public function street_address(&$values) { $n = trim($value); if ($n) { - $value = strtolower($n); if (strpos($value, '%') === FALSE) { // only add wild card if not there $value = "%{$value}%"; } $op = 'LIKE'; - $this->_where[$grouping][] = self::buildClause('LOWER(civicrm_address.street_address)', $op, $value, 'String'); + $this->_where[$grouping][] = self::buildClause('civicrm_address.street_address', $op, $value, 'String'); $this->_qill[$grouping][] = ts('Street') . " $op '$n'"; } else { @@ -3511,9 +3651,8 @@ public function street_number(&$values) { $this->_qill[$grouping][] = ts('Street Number is even'); } else { - $value = strtolower($n); - - $this->_where[$grouping][] = self::buildClause('LOWER(civicrm_address.street_number)', $op, $value, 'String'); + $value = $n; + $this->_where[$grouping][] = self::buildClause('civicrm_address.street_number', $op, $value, 'String'); $this->_qill[$grouping][] = ts('Street Number') . " $op '$n'"; } @@ -3529,9 +3668,9 @@ public function sortByCharacter(&$values) { list($name, $op, $value, $grouping, $wildcard) = $values; $name = trim($value); - $cond = " contact_a.sort_name LIKE '" . strtolower(CRM_Core_DAO::escapeWildCardString($name)) . "%'"; + $cond = " contact_a.sort_name LIKE '" . CRM_Core_DAO::escapeWildCardString($name) . "%'"; $this->_where[$grouping][] = $cond; - $this->_qill[$grouping][] = ts('Showing only Contacts starting with: \'%1\'', array(1 => $name)); + $this->_qill[$grouping][] = ts('Showing only Contacts starting with: \'%1\'', [1 => $name]); } /** @@ -3542,12 +3681,13 @@ public function includeContactIDs() { return; } - $contactIds = array(); + $contactIds = []; foreach ($this->_params as $id => $values) { if (substr($values[0], 0, CRM_Core_Form::CB_PREFIX_LEN) == CRM_Core_Form::CB_PREFIX) { $contactIds[] = substr($values[0], CRM_Core_Form::CB_PREFIX_LEN); } } + CRM_Utils_Type::validateAll($contactIds, 'Positive'); if (!empty($contactIds)) { $this->_where[0][] = " ( contact_a.id IN (" . implode(',', $contactIds) . " ) ) "; } @@ -3568,7 +3708,7 @@ public function postalCode(&$values) { // Handle numeric postal code range searches properly by casting the column as numeric if (is_numeric($value)) { - $field = "IF (civicrm_address.postal_code REGEXP '^[0-9]+$', CAST(civicrm_address.postal_code AS UNSIGNED), 0)"; + $field = "IF (civicrm_address.postal_code REGEXP '^[0-9]{1,10}$', CAST(civicrm_address.postal_code AS UNSIGNED), 0)"; $val = CRM_Utils_Type::escape($value, 'Integer'); } else { @@ -3591,11 +3731,11 @@ public function postalCode(&$values) { } elseif ($name == 'postal_code_low') { $this->_where[$grouping][] = " ( $field >= '$val' ) "; - $this->_qill[$grouping][] = ts('Postal code greater than or equal to \'%1\'', array(1 => $value)); + $this->_qill[$grouping][] = ts('Postal code greater than or equal to \'%1\'', [1 => $value]); } elseif ($name == 'postal_code_high') { $this->_where[$grouping][] = " ( $field <= '$val' ) "; - $this->_qill[$grouping][] = ts('Postal code less than or equal to \'%1\'', array(1 => $value)); + $this->_qill[$grouping][] = ts('Postal code less than or equal to \'%1\'', [1 => $value]); } } @@ -3616,7 +3756,7 @@ public function locationType(&$values, $status = NULL) { $this->_whereTables['civicrm_address'] = 1; $locationType = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id'); - $names = array(); + $names = []; foreach ($value as $id) { $names[] = $locationType[$id]; } @@ -3651,13 +3791,13 @@ public function country(&$values, $fromStateProvince = TRUE) { } $countryClause = $countryQill = NULL; - if ($values && !empty($value)) { + if (in_array($op, ['IS NULL', 'IS NOT NULL', 'IS EMPTY', 'IS NOT EMPTY']) || ($values && !empty($value))) { $this->_tables['civicrm_address'] = 1; $this->_whereTables['civicrm_address'] = 1; $countryClause = self::buildClause('civicrm_address.country_id', $op, $value, 'Positive'); list($qillop, $qillVal) = CRM_Contact_BAO_Query::buildQillForFieldValue(NULL, 'country_id', $value, $op); - $countryQill = ts("%1 %2 %3", array(1 => 'Country', 2 => $qillop, 3 => $qillVal)); + $countryQill = ts("%1 %2 %3", [1 => 'Country', 2 => $qillop, 3 => $qillVal]); if (!$fromStateProvince) { $this->_where[$grouping][] = $countryClause; @@ -3667,13 +3807,13 @@ public function country(&$values, $fromStateProvince = TRUE) { if ($fromStateProvince) { if (!empty($countryClause)) { - return array( + return [ $countryClause, " ...AND... " . $countryQill, - ); + ]; } else { - return array(NULL, NULL); + return [NULL, NULL]; } } } @@ -3691,7 +3831,7 @@ public function county(&$values, $status = NULL) { if (!is_array($value)) { // force the county to be an array - $value = array($value); + $value = [$value]; } // check if the values are ids OR names of the counties @@ -3702,7 +3842,7 @@ public function county(&$values, $status = NULL) { break; } } - $names = array(); + $names = []; if ($op == '=') { $op = 'IN'; } @@ -3714,7 +3854,7 @@ public function county(&$values, $status = NULL) { $op = str_replace('EMPTY', 'NULL', $op); } - if (in_array($op, array('IS NULL', 'IS NOT NULL', 'IS EMPTY', 'IS NOT EMPTY'))) { + if (in_array($op, ['IS NULL', 'IS NOT NULL', 'IS EMPTY', 'IS NOT EMPTY'])) { $clause = "civicrm_address.county_id $op"; } elseif ($inputFormat == 'id') { @@ -3726,7 +3866,7 @@ public function county(&$values, $status = NULL) { } } else { - $inputClause = array(); + $inputClause = []; $county = CRM_Core_PseudoConstant::county(); foreach ($value as $name) { $name = trim($name); @@ -3774,7 +3914,7 @@ public function stateProvince(&$values, $status = NULL) { $this->_where[$grouping][] = $clause; list($qillop, $qillVal) = self::buildQillForFieldValue('CRM_Core_DAO_Address', "state_province_id", $value, $op); if (!$status) { - $this->_qill[$grouping][] = ts("State/Province %1 %2 %3", array(1 => $qillop, 2 => $qillVal, 3 => $countryQill)); + $this->_qill[$grouping][] = ts("State/Province %1 %2 %3", [1 => $qillop, 2 => $qillVal, 3 => $countryQill]); } else { return implode(' ' . ts('or') . ' ', $qillVal) . $countryQill; @@ -3795,11 +3935,25 @@ public function changeLog(&$values) { } $name = trim($targetName[2]); - $name = strtolower(CRM_Core_DAO::escapeString($name)); + $name = CRM_Core_DAO::escapeString($name); $name = $targetName[4] ? "%$name%" : $name; $this->_where[$grouping][] = "contact_b_log.sort_name LIKE '%$name%'"; $this->_tables['civicrm_log'] = $this->_whereTables['civicrm_log'] = 1; - $this->_qill[$grouping][] = ts('Modified By') . " $name"; + $fieldTitle = ts('Added By'); + foreach ($this->_params as $params) { + if ($params[0] == 'log_date') { + if ($params[2] == 2) { + $fieldTitle = ts('Modified By'); + } + break; + } + } + list($qillop, $qillVal) = self::buildQillForFieldValue(NULL, 'changed_by', $name, 'LIKE'); + $this->_qill[$grouping][] = ts("%1 %2 '%3'", [ + 1 => $fieldTitle, + 2 => $qillop, + 3 => $qillVal, + ]); } /** @@ -3856,7 +4010,7 @@ public function demographics(&$values) { * @param $values */ public function privacy(&$values) { - list($name, $op, $value, $grouping, $wildcard) = $values; + list($name, $op, $value, $grouping) = $values; //fixed for profile search listing CRM-4633 if (strpbrk($value, "[")) { $value = "'{$value}'"; @@ -3888,6 +4042,7 @@ public function privacyOptions($values) { if ($opValues && strtolower($opValues[2] == 'AND') ) { + // @todo this line is logially unreachable $operator = 'AND'; } @@ -3899,8 +4054,8 @@ public function privacyOptions($values) { $compareOP = ''; } - $clauses = array(); - $qill = array(); + $clauses = []; + $qill = []; foreach ($value as $dontCare => $pOption) { $clauses[] = " ( contact_a.{$pOption} = 1 ) "; $field = CRM_Utils_Array::value($pOption, $this->_fields); @@ -3919,7 +4074,7 @@ public function preferredCommunication(&$values) { list($name, $op, $value, $grouping, $wildcard) = $values; if (!is_array($value)) { - $value = str_replace(array('(', ')'), '', explode(",", $value)); + $value = str_replace(['(', ')'], '', explode(",", $value)); } elseif (in_array(key($value), CRM_Core_DAO::acceptedSQLOperators(), TRUE)) { $op = key($value); @@ -3934,7 +4089,7 @@ public function preferredCommunication(&$values) { } $this->_where[$grouping][] = self::buildClause("contact_a.preferred_communication_method", $op, $value); - $this->_qill[$grouping][] = ts('Preferred Communication Method %1 %2', array(1 => $qillop, 2 => $qillVal)); + $this->_qill[$grouping][] = ts('Preferred Communication Method %1 %2', [1 => $qillop, 2 => $qillVal]); } /** @@ -3952,7 +4107,6 @@ public function relationship(&$values) { $relationType = $this->getWhereValues('relation_type_id', $grouping); $targetName = $this->getWhereValues('relation_target_name', $grouping); $relStatus = $this->getWhereValues('relation_status', $grouping); - $relPermission = $this->getWhereValues('relation_permission', $grouping); $targetGroup = $this->getWhereValues('relation_target_group', $grouping); $nameClause = $name = NULL; @@ -3962,48 +4116,65 @@ public function relationship(&$values) { substr($name, -1, 1) == '"' ) { $name = substr($name, 1, -1); - $name = strtolower(CRM_Core_DAO::escapeString($name)); + $name = CRM_Core_DAO::escapeString($name); $nameClause = "= '$name'"; } else { - $name = strtolower(CRM_Core_DAO::escapeString($name)); + $name = CRM_Core_DAO::escapeString($name); $nameClause = "LIKE '%{$name}%'"; } } - $rTypeValues = array(); + $relTypes = $relTypesIds = []; if (!empty($relationType)) { - $rel = explode('_', $relationType[2]); - self::$_relType = $rel[1]; - $params = array('id' => $rel[0]); - $rType = CRM_Contact_BAO_RelationshipType::retrieve($params, $rTypeValues); - } - if (!empty($rTypeValues) && $rTypeValues['name_a_b'] == $rTypeValues['name_b_a']) { - // if we don't know which end of the relationship we are dealing with we'll create a temp table - //@todo unless we are dealing with a target group - self::$_relType = 'reciprocal'; + $relationType[2] = (array) $relationType[2]; + foreach ($relationType[2] as $relType) { + $rel = explode('_', $relType); + self::$_relType = $rel[1]; + $params = ['id' => $rel[0]]; + $typeValues = []; + $rTypeValue = CRM_Contact_BAO_RelationshipType::retrieve($params, $typeValues); + if (!empty($rTypeValue)) { + if ($rTypeValue->name_a_b == $rTypeValue->name_b_a) { + // if we don't know which end of the relationship we are dealing with we'll create a temp table + self::$_relType = 'reciprocal'; + } + $relTypesIds[] = $rel[0]; + $relTypes[] = $relType; + } + } } + // if we are creating a temp table we build our own where for the relationship table $relationshipTempTable = NULL; - if (self::$_relType == 'reciprocal' && empty($targetGroup)) { - $where = array(); + if (self::$_relType == 'reciprocal') { + $where = []; self::$_relationshipTempTable = $relationshipTempTable = CRM_Core_DAO::createTempTableName('civicrm_rel'); if ($nameClause) { $where[$grouping][] = " sort_name $nameClause "; } + $groupJoinTable = "civicrm_relationship"; + $groupJoinColumn = "contact_id_alt"; } else { $where = &$this->_where; if ($nameClause) { $where[$grouping][] = "( contact_b.sort_name $nameClause AND contact_b.id != contact_a.id )"; } + $groupJoinTable = "contact_b"; + $groupJoinColumn = "id"; } - $allRelationshipType = CRM_Contact_BAO_Relationship::getContactRelationshipType(NULL, 'null', NULL, NULL, TRUE); - if ($nameClause || !$targetGroup) { if (!empty($relationType)) { - $this->_qill[$grouping][] = $allRelationshipType[$relationType[2]] . " $name"; + $relQill = ''; + foreach ($relTypes as $rel) { + if (!empty($relQill)) { + $relQill .= ' OR '; + } + $relQill .= $allRelationshipType[$rel]; + } + $this->_qill[$grouping][] = 'Relationship Type(s) ' . $relQill . " $name"; } else { $this->_qill[$grouping][] = $name; @@ -4014,12 +4185,12 @@ public function relationship(&$values) { if ($targetGroup) { //add contacts from static groups $this->_tables['civicrm_relationship_group_contact'] = $this->_whereTables['civicrm_relationship_group_contact'] - = " LEFT JOIN civicrm_group_contact civicrm_relationship_group_contact ON civicrm_relationship_group_contact.contact_id = contact_b.id AND civicrm_relationship_group_contact.status = 'Added'"; + = " LEFT JOIN civicrm_group_contact civicrm_relationship_group_contact ON civicrm_relationship_group_contact.contact_id = {$groupJoinTable}.{$groupJoinColumn} AND civicrm_relationship_group_contact.status = 'Added'"; $groupWhere[] = "( civicrm_relationship_group_contact.group_id IN (" . implode(",", $targetGroup[2]) . ") ) "; //add contacts from saved searches - $ssWhere = $this->addGroupContactCache($targetGroup[2], "civicrm_relationship_group_contact_cache", "contact_b", $op); + $ssWhere = $this->addGroupContactCache($targetGroup[2], "civicrm_relationship_group_contact_cache", $groupJoinTable, $op, $groupJoinColumn); //set the group where clause if ($ssWhere) { @@ -4029,14 +4200,21 @@ public function relationship(&$values) { //Get the names of the target groups for the qill $groupNames = CRM_Core_PseudoConstant::group(); - $qillNames = array(); + $qillNames = []; foreach ($targetGroup[2] as $groupId) { if (array_key_exists($groupId, $groupNames)) { $qillNames[] = $groupNames[$groupId]; } } if (!empty($relationType)) { - $this->_qill[$grouping][] = $allRelationshipType[$relationType[2]] . " ( " . implode(", ", $qillNames) . " )"; + $relQill = ''; + foreach ($relTypes as $rel) { + if (!empty($relQill)) { + $relQill .= ' OR '; + } + $relQill .= CRM_Utils_Array::value($rel, $allRelationshipType); + } + $this->_qill[$grouping][] = 'Relationship Type(s) ' . $relQill . " ( " . implode(", ", $qillNames) . " )"; } else { $this->_qill[$grouping][] = implode(", ", $qillNames); @@ -4064,38 +4242,22 @@ public function relationship(&$values) { } $onlyDeleted = 0; - if (in_array(array('deleted_contacts', '=', '1', '0', '0'), $this->_params)) { + if (in_array(['deleted_contacts', '=', '1', '0', '0'], $this->_params)) { $onlyDeleted = 1; } $where[$grouping][] = "(contact_b.is_deleted = {$onlyDeleted})"; - //check for permissioned, non-permissioned and all permissioned relations - if ($relPermission[2] == 1) { - $where[$grouping][] = "( -civicrm_relationship.is_permission_a_b = 1 -)"; - $this->_qill[$grouping][] = ts('Relationship - Permissioned'); - } - elseif ($relPermission[2] == 2) { - //non-allowed permission relationship. - $where[$grouping][] = "( -civicrm_relationship.is_permission_a_b = 0 -)"; - $this->_qill[$grouping][] = ts('Relationship - Non-permissioned'); - } - + $this->addRelationshipPermissionClauses($grouping, $where); $this->addRelationshipDateClauses($grouping, $where); - if (!empty($relationType) && !empty($rType) && isset($rType->id)) { - $where[$grouping][] = 'civicrm_relationship.relationship_type_id = ' . $rType->id; + $this->addRelationshipActivePeriodClauses($grouping, $where); + if (!empty($relTypes)) { + $where[$grouping][] = 'civicrm_relationship.relationship_type_id IN (' . implode(',', $relTypesIds) . ')'; } $this->_tables['civicrm_relationship'] = $this->_whereTables['civicrm_relationship'] = 1; $this->_useDistinct = TRUE; $this->_relationshipValuesAdded = TRUE; // it could be a or b, using an OR creates an unindexed join - better to create a temp table & // join on that, - // @todo creating a temp table could be expanded to group filter - // as even creating a temp table of all relationships is much much more efficient than - // an OR in the join if ($relationshipTempTable) { $whereClause = ''; if (!empty($where[$grouping])) { @@ -4104,19 +4266,47 @@ public function relationship(&$values) { } $sql = " CREATE TEMPORARY TABLE {$relationshipTempTable} - (SELECT contact_id_b as contact_id, civicrm_relationship.id + ( + `contact_id` int(10) unsigned NOT NULL DEFAULT '0', + `contact_id_alt` int(10) unsigned NOT NULL DEFAULT '0', + KEY `contact_id` (`contact_id`), + KEY `contact_id_alt` (`contact_id_alt`) + ) + (SELECT contact_id_b as contact_id, contact_id_a as contact_id_alt, civicrm_relationship.id FROM civicrm_relationship INNER JOIN civicrm_contact c ON civicrm_relationship.contact_id_a = c.id $whereClause ) UNION - (SELECT contact_id_a as contact_id, civicrm_relationship.id + (SELECT contact_id_a as contact_id, contact_id_b as contact_id_alt, civicrm_relationship.id FROM civicrm_relationship INNER JOIN civicrm_contact c ON civicrm_relationship.contact_id_b = c.id $whereClause ) "; CRM_Core_DAO::executeQuery($sql); } + } + /** + * Add relationship permission criteria to where clause. + * + * @param string $grouping + * @param array $where Array to add "where" criteria to, in case you are generating a temp table. + * Not the main query. + */ + public function addRelationshipPermissionClauses($grouping, &$where) { + $relPermission = $this->getWhereValues('relation_permission', $grouping); + if ($relPermission) { + if (!is_array($relPermission[2])) { + // this form value was scalar in previous versions of Civi + $relPermission[2] = [$relPermission[2]]; + } + $where[$grouping][] = "(civicrm_relationship.is_permission_a_b IN (" . implode(",", $relPermission[2]) . "))"; + + $allRelationshipPermissions = CRM_Contact_BAO_Relationship::buildOptions('is_permission_a_b'); + + $relPermNames = array_intersect_key($allRelationshipPermissions, array_flip($relPermission[2])); + $this->_qill[$grouping][] = ts('Permissioned Relationships') . ' - ' . implode(' OR ', $relPermNames); + } } /** @@ -4127,11 +4317,11 @@ public function relationship(&$values) { * not the main query. */ public function addRelationshipDateClauses($grouping, &$where) { - $dateValues = array(); - $dateTypes = array( + $dateValues = []; + $dateTypes = [ 'start_date', 'end_date', - ); + ]; foreach ($dateTypes as $dateField) { $dateValueLow = $this->getWhereValues('relation_' . $dateField . '_low', $grouping); @@ -4149,6 +4339,64 @@ public function addRelationshipDateClauses($grouping, &$where) { } } + /** + * Add start & end active period criteria in + * @param string $grouping + * @param array $where + * = array to add where clauses to, in case you are generating a temp table. + * not the main query. + */ + public function addRelationshipActivePeriodClauses($grouping, &$where) { + $dateValues = []; + $dateField = 'active_period_date'; + + $dateValueLow = $this->getWhereValues('relation_active_period_date_low', $grouping); + $dateValueHigh = $this->getWhereValues('relation_active_period_date_high', $grouping); + $dateValueLowFormated = $dateValueHighFormated = NULL; + if (!empty($dateValueLow) && !empty($dateValueHigh)) { + $dateValueLowFormated = date('Ymd', strtotime($dateValueLow[2])); + $dateValueHighFormated = date('Ymd', strtotime($dateValueHigh[2])); + $this->_qill[$grouping][] = (ts('Relationship was active between')) . " " . CRM_Utils_Date::customFormat($dateValueLowFormated) . " and " . CRM_Utils_Date::customFormat($dateValueHighFormated); + } + elseif (!empty($dateValueLow)) { + $dateValueLowFormated = date('Ymd', strtotime($dateValueLow[2])); + $this->_qill[$grouping][] = (ts('Relationship was active after')) . " " . CRM_Utils_Date::customFormat($dateValueLowFormated); + } + elseif (!empty($dateValueHigh)) { + $dateValueHighFormated = date('Ymd', strtotime($dateValueHigh[2])); + $this->_qill[$grouping][] = (ts('Relationship was active before')) . " " . CRM_Utils_Date::customFormat($dateValueHighFormated); + } + + if ($activePeriodClauses = self::getRelationshipActivePeriodClauses($dateValueLowFormated, $dateValueHighFormated, TRUE)) { + $where[$grouping][] = $activePeriodClauses; + } + } + + /** + * Get start & end active period criteria + */ + public static function getRelationshipActivePeriodClauses($from, $to, $forceTableName) { + $tableName = $forceTableName ? 'civicrm_relationship.' : ''; + if (!is_null($from) && !is_null($to)) { + return '(((' . $tableName . 'start_date >= ' . $from . ' AND ' . $tableName . 'start_date <= ' . $to . ') OR + (' . $tableName . 'end_date >= ' . $from . ' AND ' . $tableName . 'end_date <= ' . $to . ') OR + (' . $tableName . 'start_date <= ' . $from . ' AND ' . $tableName . 'end_date >= ' . $to . ' )) OR + (' . $tableName . 'start_date IS NULL AND ' . $tableName . 'end_date IS NULL) OR + (' . $tableName . 'start_date IS NULL AND ' . $tableName . 'end_date >= ' . $from . ') OR + (' . $tableName . 'end_date IS NULL AND ' . $tableName . 'start_date <= ' . $to . '))'; + } + elseif (!is_null($from)) { + return '((' . $tableName . 'start_date >= ' . $from . ') OR + (' . $tableName . 'start_date IS NULL AND ' . $tableName . 'end_date IS NULL) OR + (' . $tableName . 'start_date IS NULL AND ' . $tableName . 'end_date >= ' . $from . '))'; + } + elseif (!is_null($to)) { + return '((' . $tableName . 'start_date <= ' . $to . ') OR + (' . $tableName . 'start_date IS NULL AND ' . $tableName . 'end_date IS NULL) OR + (' . $tableName . 'end_date IS NULL AND ' . $tableName . 'start_date <= ' . $to . '))'; + } + } + /** * Default set of return properties. * @@ -4159,7 +4407,7 @@ public function addRelationshipDateClauses($grouping, &$where) { */ public static function &defaultReturnProperties($mode = 1) { if (!isset(self::$_defaultReturnProperties)) { - self::$_defaultReturnProperties = array(); + self::$_defaultReturnProperties = []; } if (!isset(self::$_defaultReturnProperties[$mode])) { @@ -4172,7 +4420,7 @@ public static function &defaultReturnProperties($mode = 1) { } if (empty(self::$_defaultReturnProperties[$mode])) { - self::$_defaultReturnProperties[$mode] = array( + self::$_defaultReturnProperties[$mode] = [ 'home_URL' => 1, 'image_URL' => 1, 'legal_identifier' => 1, @@ -4195,6 +4443,7 @@ public static function &defaultReturnProperties($mode = 1) { 'street_address' => 1, 'supplemental_address_1' => 1, 'supplemental_address_2' => 1, + 'supplemental_address_3' => 1, 'city' => 1, 'postal_code' => 1, 'postal_code_suffix' => 1, @@ -4225,7 +4474,7 @@ public static function &defaultReturnProperties($mode = 1) { 'contact_is_deleted' => 1, 'preferred_communication_method' => 1, 'preferred_language' => 1, - ); + ]; } } return self::$_defaultReturnProperties[$mode]; @@ -4260,7 +4509,8 @@ public static function getQuery($params = NULL, $returnProperties = NULL, $count list($select, $from, $where, $having) = $query->query(); $groupBy = ($query->_useGroupBy) ? 'GROUP BY contact_a.id' : ''; - return "$select $from $where $groupBy $having"; + $query = "$select $from $where $groupBy $having"; + return $query; } /** @@ -4286,7 +4536,11 @@ public static function getQuery($params = NULL, $returnProperties = NULL, $count * Should permissions be ignored or should the logged in user's permissions be applied. * @param int $mode * This basically correlates to the component. + * @param string $apiEntity + * The api entity being called. + * This sort-of duplicates $mode in a confusing way. Probably not by design. * + * @param bool|null $primaryLocationOnly * @return array */ public static function apiQuery( @@ -4299,8 +4553,9 @@ public static function apiQuery( $smartGroupCache = TRUE, $count = FALSE, $skipPermissions = TRUE, - $mode = 1, - $apiEntity = NULL + $mode = CRM_Contact_BAO_Query::MODE_CONTACTS, + $apiEntity = NULL, + $primaryLocationOnly = NULL ) { $query = new CRM_Contact_BAO_Query( @@ -4309,7 +4564,7 @@ public static function apiQuery( $skipPermissions, TRUE, $smartGroupCache, NULL, 'AND', - $apiEntity + $apiEntity, $primaryLocationOnly ); //this should add a check for view deleted if permissions are enabled @@ -4335,8 +4590,9 @@ public static function apiQuery( $sql = "$select $from $where $having"; - // add group by - if ($query->_useGroupBy) { + // add group by only when API action is not getcount + // otherwise query fetches incorrect count + if ($query->_useGroupBy && !$count) { $sql .= self::getGroupByFromSelectColumns($query->_select, 'contact_a.id'); } if (!empty($sort)) { @@ -4354,23 +4610,21 @@ public static function apiQuery( // @todo derive this from the component class rather than hard-code two options. $entityIDField = ($mode == CRM_Contact_BAO_Query::MODE_CONTRIBUTE) ? 'contribution_id' : 'contact_id'; - $values = array(); + $values = []; while ($dao->fetch()) { if ($count) { $noRows = $dao->rowCount; - $dao->free(); - return array($noRows, NULL); + return [$noRows, NULL]; } $val = $query->store($dao); - $convertedVals = $query->convertToPseudoNames($dao, TRUE); + $convertedVals = $query->convertToPseudoNames($dao, TRUE, TRUE); if (!empty($convertedVals)) { $val = array_replace_recursive($val, $convertedVals); } $values[$dao->$entityIDField] = $val; } - $dao->free(); - return array($values, $options); + return [$values, $options]; } /** @@ -4441,22 +4695,22 @@ protected static function convertCustomRelativeFields(&$formValues, &$params, $v if ($from) { if ($to) { - $relativeFunction = array('BETWEEN' => array($from, $to)); + $relativeFunction = ['BETWEEN' => [$from, $to]]; } else { - $relativeFunction = array('>=' => $from); + $relativeFunction = ['>=' => $from]; } } else { - $relativeFunction = array('<=' => $to); + $relativeFunction = ['<=' => $to]; } - $params[] = array( + $params[] = [ $customFieldName, '=', $relativeFunction, 0, 0, - ); + ]; } /** @@ -4470,7 +4724,7 @@ public static function isCustomDateField($fieldName) { if (($customFieldID = CRM_Core_BAO_CustomField::getKeyID($fieldName)) == FALSE) { return FALSE; } - if ('Date' == civicrm_api3('CustomField', 'getvalue', array('id' => $customFieldID, 'return' => 'data_type'))) { + if ('Date' == civicrm_api3('CustomField', 'getvalue', ['id' => $customFieldID, 'return' => 'data_type'])) { return TRUE; } return FALSE; @@ -4530,27 +4784,21 @@ public static function filterCountryFromValuesIfStateExists(&$formValues) { * * @param array $selectClauses * @param array $groupBy - Columns already included in GROUP By clause. + * @param string $aggregateFunction * * @return string */ - public static function appendAnyValueToSelect($selectClauses, $groupBy) { - $mysqlVersion = CRM_Core_DAO::singleValueQuery('SELECT VERSION()'); - $sqlMode = explode(',', CRM_Core_DAO::singleValueQuery('SELECT @@sql_mode')); - - // Disable only_full_group_by mode for lower sql versions. - if (version_compare($mysqlVersion, '5.7', '<') || (!empty($sqlMode) && !in_array('ONLY_FULL_GROUP_BY', $sqlMode))) { - $key = array_search('ONLY_FULL_GROUP_BY', $sqlMode); - unset($sqlMode[$key]); - CRM_Core_DAO::executeQuery("SET SESSION sql_mode = '" . implode(',', $sqlMode) . "'"); - } - else { + public static function appendAnyValueToSelect($selectClauses, $groupBy, $aggregateFunction = 'ANY_VALUE') { + if (!CRM_Utils_SQL::disableFullGroupByMode()) { $groupBy = array_map('trim', (array) $groupBy); - $aggregateFunctions = '/(ROUND|AVG|COUNT|GROUP_CONCAT|SUM|MAX|MIN)\(/i'; + $aggregateFunctions = '/(ROUND|AVG|COUNT|GROUP_CONCAT|SUM|MAX|MIN|IF)[[:blank:]]*\(/i'; foreach ($selectClauses as $key => &$val) { list($selectColumn, $alias) = array_pad(preg_split('/ as /i', $val), 2, NULL); // append ANY_VALUE() keyword if (!in_array($selectColumn, $groupBy) && preg_match($aggregateFunctions, trim($selectColumn)) !== 1) { - $val = str_replace($selectColumn, "ANY_VALUE({$selectColumn})", $val); + $val = ($aggregateFunction == 'GROUP_CONCAT') ? + str_replace($selectColumn, "$aggregateFunction(DISTINCT {$selectColumn})", $val) : + str_replace($selectColumn, "$aggregateFunction({$selectColumn})", $val); } } } @@ -4558,6 +4806,28 @@ public static function appendAnyValueToSelect($selectClauses, $groupBy) { return "SELECT " . implode(', ', $selectClauses) . " "; } + /** + * For some special cases, where if non-aggregate ORDER BY columns are not present in GROUP BY + * on full_group_by mode, then append the those missing columns to GROUP BY clause + * keyword to select fields not present in groupBy + * + * @param string $groupBy - GROUP BY clause where missing ORDER BY columns will be appended if not present + * @param array $orderBys - ORDER BY sub-clauses + * + */ + public static function getGroupByFromOrderBy(&$groupBy, $orderBys) { + if (!CRM_Utils_SQL::disableFullGroupByMode()) { + foreach ($orderBys as $orderBy) { + // remove sort syntax from ORDER BY clauses if present + $orderBy = str_ireplace([' DESC', ' ASC', '`'], '', $orderBy); + // if ORDER BY column is not present in GROUP BY then append it to end + if (preg_match('/(MAX|MIN)\(/i', trim($orderBy)) !== 1 && !strstr($groupBy, $orderBy)) { + $groupBy .= ", {$orderBy}"; + } + } + } + } + /** * Include Select columns in groupBy clause. * @@ -4572,8 +4842,8 @@ public static function getGroupByFromSelectColumns($selectClauses, $groupBy = NU $sqlMode = CRM_Core_DAO::singleValueQuery('SELECT @@sql_mode'); //return if ONLY_FULL_GROUP_BY is not enabled. - if (!version_compare($mysqlVersion, '5.7', '<') && !empty($sqlMode) && in_array('ONLY_FULL_GROUP_BY', explode(',', $sqlMode))) { - $regexToExclude = '/(ROUND|AVG|COUNT|GROUP_CONCAT|SUM|MAX|MIN)\(/i'; + if (CRM_Utils_SQL::supportsFullGroupBy() && !empty($sqlMode) && in_array('ONLY_FULL_GROUP_BY', explode(',', $sqlMode))) { + $regexToExclude = '/(ROUND|AVG|COUNT|GROUP_CONCAT|SUM|MAX|MIN|IF)[[:blank:]]*\(/i'; foreach ($selectClauses as $key => $val) { $aliasArray = preg_split('/ as /i', $val); // if more than 1 alias we need to split by ','. @@ -4649,89 +4919,7 @@ public function searchQuery( $additionalFromClause = NULL, $skipOrderAndLimit = FALSE ) { - if ($includeContactIds) { - $this->_includeContactIds = TRUE; - $this->_whereClause = $this->whereClause(); - } - - $onlyDeleted = in_array(array('deleted_contacts', '=', '1', '0', '0'), $this->_params); - - // if we’re explicitly looking for a certain contact’s contribs, events, etc. - // and that contact happens to be deleted, set $onlyDeleted to true - foreach ($this->_params as $values) { - $name = CRM_Utils_Array::value(0, $values); - $op = CRM_Utils_Array::value(1, $values); - $value = CRM_Utils_Array::value(2, $values); - if ($name == 'contact_id' and $op == '=') { - if (CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $value, 'is_deleted')) { - $onlyDeleted = TRUE; - } - break; - } - } - - // building the query string - $groupBy = $groupByCol = NULL; - if (!$count) { - if (isset($this->_groupByComponentClause)) { - $groupBy = $this->_groupByComponentClause; - $groupCols = preg_replace('/^GROUP BY /', '', trim($this->_groupByComponentClause)); - $groupByCol = explode(', ', $groupCols); - } - elseif ($this->_useGroupBy) { - $groupByCol = 'contact_a.id'; - $groupBy = ' GROUP BY contact_a.id'; - } - } - if ($this->_mode & CRM_Contact_BAO_Query::MODE_ACTIVITY && (!$count)) { - $groupByCol = 'civicrm_activity.id'; - $groupBy = 'GROUP BY civicrm_activity.id '; - } - - $order = $orderBy = $limit = ''; - if (!$count) { - list($order, $additionalFromClause) = $this->prepareOrderBy($sort, $sortByChar, $sortOrder, $additionalFromClause); - - if ($rowCount > 0 && $offset >= 0) { - $offset = CRM_Utils_Type::escape($offset, 'Int'); - $rowCount = CRM_Utils_Type::escape($rowCount, 'Int'); - $limit = " LIMIT $offset, $rowCount "; - } - } - - // CRM-15231 - $this->_sort = $sort; - - //CRM-15967 - $this->includePseudoFieldsJoin($sort); - - list($select, $from, $where, $having) = $this->query($count, $sortByChar, $groupContacts, $onlyDeleted); - - if (!empty($groupByCol)) { - $groupBy = self::getGroupByFromSelectColumns($this->_select, $groupByCol); - } - - if ($additionalWhereClause) { - $where = $where . ' AND ' . $additionalWhereClause; - } - - //additional from clause should be w/ proper joins. - if ($additionalFromClause) { - $from .= "\n" . $additionalFromClause; - } - - // if we are doing a transform, do it here - // use the $from, $where and $having to get the contact ID - if ($this->_displayRelationshipType) { - $this->filterRelatedContacts($from, $where, $having); - } - - if ($skipOrderAndLimit) { - $query = "$select $from $where $having $groupBy"; - } - else { - $query = "$select $from $where $having $groupBy $order $limit"; - } + $query = $this->getSearchSQL($offset, $rowCount, $sort, $count, $includeContactIds, $sortByChar, $groupContacts, $additionalWhereClause, $sortOrder, $additionalFromClause, $skipOrderAndLimit); if ($returnQuery) { return $query; @@ -4741,12 +4929,15 @@ public function searchQuery( } $dao = CRM_Core_DAO::executeQuery($query); + + // We can always call this - it will only re-enable if it was originally enabled. + CRM_Core_DAO::reenableFullGroupByMode(); + if ($groupContacts) { - $ids = array(); + $ids = []; while ($dao->fetch()) { $ids[] = $dao->id; } - $dao->free(); return implode(',', $ids); } @@ -4754,28 +4945,64 @@ public function searchQuery( } /** - * Fetch a list of contacts from the prev/next cache for displaying a search results page + * Create and query the db for the list of all first letters used by contacts * - * @param string $cacheKey - * @param int $offset - * @param int $rowCount + * @return CRM_Core_DAO + */ + public function alphabetQuery() { + $sqlParts = $this->getSearchSQLParts(NULL, NULL, NULL, FALSE, FALSE, TRUE); + $query = "SELECT DISTINCT LEFT(contact_a.sort_name, 1) as sort_name + {$this->_simpleFromClause} + {$sqlParts['where']} + {$sqlParts['having']} + GROUP BY sort_name + ORDER BY sort_name asc"; + $dao = CRM_Core_DAO::executeQuery($query); + return $dao; + } + + /** + * Fetch a list of contacts for displaying a search results page + * + * @param array $cids + * List of contact IDs * @param bool $includeContactIds * @return CRM_Core_DAO */ - public function getCachedContacts($cacheKey, $offset, $rowCount, $includeContactIds) { + public function getCachedContacts($cids, $includeContactIds) { + CRM_Utils_Type::validateAll($cids, 'Positive'); $this->_includeContactIds = $includeContactIds; - $onlyDeleted = in_array(array('deleted_contacts', '=', '1', '0', '0'), $this->_params); + $onlyDeleted = in_array(['deleted_contacts', '=', '1', '0', '0'], $this->_params); list($select, $from, $where) = $this->query(FALSE, FALSE, FALSE, $onlyDeleted); - $from = " FROM civicrm_prevnext_cache pnc INNER JOIN civicrm_contact contact_a ON contact_a.id = pnc.entity_id1 AND pnc.cacheKey = '$cacheKey' " . substr($from, 31); - $order = " ORDER BY pnc.id"; - $groupByCol = array('contact_a.id', 'pnc.id'); - $groupBy = self::getGroupByFromSelectColumns($this->_select, $groupByCol); - $limit = " LIMIT $offset, $rowCount"; + $select .= sprintf(", (%s) AS _wgt", $this->createSqlCase('contact_a.id', $cids)); + $where .= sprintf(' AND contact_a.id IN (%s)', implode(',', $cids)); + $order = 'ORDER BY _wgt'; + $groupBy = $this->_useGroupBy ? ' GROUP BY contact_a.id' : ''; + $limit = ''; $query = "$select $from $where $groupBy $order $limit"; return CRM_Core_DAO::executeQuery($query); } + /** + * Construct a SQL CASE expression. + * + * @param string $idCol + * The name of a column with ID's (eg 'contact_a.id'). + * @param array $cids + * Array(int $weight => int $id). + * @return string + * CASE WHEN id=123 THEN 1 WHEN id=456 THEN 2 END + */ + private function createSqlCase($idCol, $cids) { + $buf = "CASE\n"; + foreach ($cids as $weight => $cid) { + $buf .= " WHEN $idCol = $cid THEN $weight \n"; + } + $buf .= "END\n"; + return $buf; + } + /** * Populate $this->_permissionWhereClause with permission related clause and update other * query related properties. @@ -4841,159 +5068,49 @@ public function setSkipPermission($val) { * * @return array */ - public function &summaryContribution($context = NULL) { + public function summaryContribution($context = NULL) { list($innerselect, $from, $where, $having) = $this->query(TRUE); - - // hack $select - $select = " -SELECT COUNT( conts.total_amount ) as total_count, - SUM( conts.total_amount ) as total_amount, - AVG( conts.total_amount ) as total_avg, - conts.currency as currency"; if ($this->_permissionWhereClause) { $where .= " AND " . $this->_permissionWhereClause; } if ($context == 'search') { $where .= " AND contact_a.is_deleted = 0 "; } - CRM_Financial_BAO_FinancialType::getAvailableFinancialTypes($financialTypes); - if (!empty($financialTypes)) { - $where .= " AND civicrm_contribution.financial_type_id IN (" . implode(',', array_keys($financialTypes)) . ") AND li.id IS NULL"; - $from .= " LEFT JOIN civicrm_line_item li - ON civicrm_contribution.id = li.contribution_id AND - li.entity_table = 'civicrm_contribution' AND li.financial_type_id NOT IN (" . implode(',', array_keys($financialTypes)) . ") "; - } - else { - $where .= " AND civicrm_contribution.financial_type_id IN (0)"; - } - - // make sure contribution is completed - CRM-4989 - $completedWhere = $where . " AND civicrm_contribution.contribution_status_id = 1 "; - - $summary = array(); - $summary['total'] = array(); - $summary['total']['count'] = $summary['total']['amount'] = $summary['total']['avg'] = "n/a"; - $innerQuery = "SELECT civicrm_contribution.total_amount, COUNT(civicrm_contribution.total_amount) as civicrm_contribution_total_amount_count, - civicrm_contribution.currency $from $completedWhere"; - $query = "$select FROM ( - $innerQuery GROUP BY civicrm_contribution.id - ) as conts - GROUP BY currency"; - - $dao = CRM_Core_DAO::executeQuery($query); - - $summary['total']['count'] = 0; - $summary['total']['amount'] = $summary['total']['avg'] = array(); - while ($dao->fetch()) { - $summary['total']['count'] += $dao->total_count; - $summary['total']['amount'][] = CRM_Utils_Money::format($dao->total_amount, $dao->currency); - $summary['total']['avg'][] = CRM_Utils_Money::format($dao->total_avg, $dao->currency); - } - - $orderBy = 'ORDER BY civicrm_contribution_total_amount_count DESC'; - $groupBy = 'GROUP BY currency, civicrm_contribution.total_amount'; - $modeSQL = "$select, SUBSTRING_INDEX(GROUP_CONCAT(conts.total_amount - ORDER BY conts.civicrm_contribution_total_amount_count DESC SEPARATOR ';'), ';', 1) as amount, - MAX(conts.civicrm_contribution_total_amount_count) as civicrm_contribution_total_amount_count - FROM ($innerQuery - $groupBy $orderBy) as conts - GROUP BY currency"; - - $summary['total']['mode'] = CRM_Contribute_BAO_Contribution::computeStats('mode', $modeSQL); - $medianSQL = "{$from} {$completedWhere}"; - $summary['total']['median'] = CRM_Contribute_BAO_Contribution::computeStats('median', $medianSQL, 'civicrm_contribution'); - $summary['total']['currencyCount'] = count($summary['total']['median']); + $this->appendFinancialTypeWhereAndFromToQueryStrings($where, $from); - if (!empty($summary['total']['amount'])) { - $summary['total']['amount'] = implode(', ', $summary['total']['amount']); - $summary['total']['avg'] = implode(', ', $summary['total']['avg']); - $summary['total']['mode'] = implode(', ', $summary['total']['mode']); - $summary['total']['median'] = implode(', ', $summary['total']['median']); - } - else { - $summary['total']['amount'] = $summary['total']['avg'] = $summary['total']['median'] = 0; - } + $summary = ['total' => []]; + $this->addBasicStatsToSummary($summary, $where, $from); - // soft credit summary if (CRM_Contribute_BAO_Query::isSoftCreditOptionEnabled()) { - $softCreditWhere = "{$completedWhere} AND civicrm_contribution_soft.id IS NOT NULL"; - $query = " - $select FROM ( - SELECT civicrm_contribution_soft.amount as total_amount, civicrm_contribution_soft.currency $from $softCreditWhere - GROUP BY civicrm_contribution_soft.id - ) as conts - GROUP BY currency"; - $dao = CRM_Core_DAO::executeQuery($query); - $summary['soft_credit']['count'] = 0; - $summary['soft_credit']['amount'] = $summary['soft_credit']['avg'] = array(); - while ($dao->fetch()) { - $summary['soft_credit']['count'] += $dao->total_count; - $summary['soft_credit']['amount'][] = CRM_Utils_Money::format($dao->total_amount, $dao->currency); - $summary['soft_credit']['avg'][] = CRM_Utils_Money::format($dao->total_avg, $dao->currency); - } - if (!empty($summary['soft_credit']['amount'])) { - $summary['soft_credit']['amount'] = implode(', ', $summary['soft_credit']['amount']); - $summary['soft_credit']['avg'] = implode(', ', $summary['soft_credit']['avg']); - } - else { - $summary['soft_credit']['amount'] = $summary['soft_credit']['avg'] = 0; - } - } - - // hack $select - //@todo - this could be one query using the IF in mysql - eg - // SELECT sum(total_completed), sum(count_completed), sum(count_cancelled), sum(total_cancelled) FROM ( - // SELECT civicrm_contribution.total_amount, civicrm_contribution.currency , - // IF(civicrm_contribution.contribution_status_id = 1, 1, 0 ) as count_completed, - // IF(civicrm_contribution.contribution_status_id = 1, total_amount, 0 ) as total_completed, - // IF(civicrm_contribution.cancel_date IS NOT NULL = 1, 1, 0 ) as count_cancelled, - // IF(civicrm_contribution.cancel_date IS NOT NULL = 1, total_amount, 0 ) as total_cancelled - // FROM civicrm_contact contact_a - // LEFT JOIN civicrm_contribution ON civicrm_contribution.contact_id = contact_a.id - // WHERE ( ... where clause.... - // AND (civicrm_contribution.cancel_date IS NOT NULL OR civicrm_contribution.contribution_status_id = 1) - // ) as conts - - $select = " -SELECT COUNT( conts.total_amount ) as cancel_count, - SUM( conts.total_amount ) as cancel_amount, - AVG( conts.total_amount ) as cancel_avg, - conts.currency as currency"; - - $where .= " AND civicrm_contribution.cancel_date IS NOT NULL "; - if ($context == 'search') { - $where .= " AND contact_a.is_deleted = 0 "; + $this->addBasicSoftCreditStatsToStats($summary, $where, $from); } - $query = "$select FROM ( - SELECT civicrm_contribution.total_amount, civicrm_contribution.currency $from $where - GROUP BY civicrm_contribution.id - ) as conts - GROUP BY currency"; + $this->addBasicCancelStatsToSummary($summary, $where, $from); - $dao = CRM_Core_DAO::executeQuery($query); + return $summary; + } - if ($dao->N <= 1) { - if ($dao->fetch()) { - $summary['cancel']['count'] = $dao->cancel_count; - $summary['cancel']['amount'] = CRM_Utils_Money::format($dao->cancel_amount, $dao->currency); - $summary['cancel']['avg'] = CRM_Utils_Money::format($dao->cancel_avg, $dao->currency); - } + /** + * Append financial ACL limits to the query from & where clauses, if applicable. + * + * @param string $where + * @param string $from + */ + public function appendFinancialTypeWhereAndFromToQueryStrings(&$where, &$from) { + if (!CRM_Financial_BAO_FinancialType::isACLFinancialTypeStatus()) { + return; + } + CRM_Financial_BAO_FinancialType::getAvailableFinancialTypes($financialTypes); + if (!empty($financialTypes)) { + $where .= " AND civicrm_contribution.financial_type_id IN (" . implode(',', array_keys($financialTypes)) . ") AND li.id IS NULL"; + $from .= " LEFT JOIN civicrm_line_item li + ON civicrm_contribution.id = li.contribution_id AND + li.entity_table = 'civicrm_contribution' AND li.financial_type_id NOT IN (" . implode(',', array_keys($financialTypes)) . ") "; } else { - $summary['cancel']['count'] = 0; - $summary['cancel']['amount'] = $summary['cancel']['avg'] = array(); - while ($dao->fetch()) { - $summary['cancel']['count'] += $dao->cancel_count; - $summary['cancel']['amount'][] = CRM_Utils_Money::format($dao->cancel_amount, $dao->currency); - $summary['cancel']['avg'][] = CRM_Utils_Money::format($dao->cancel_avg, $dao->currency); - } - $summary['cancel']['amount'] = implode(', ', $summary['cancel']['amount']); - $summary['cancel']['avg'] = implode(', ', $summary['cancel']['avg']); + $where .= " AND civicrm_contribution.financial_type_id IN (0)"; } - - return $summary; } /** @@ -5012,7 +5129,7 @@ public function qill() { */ public static function &defaultHierReturnProperties() { if (!isset(self::$_defaultHierReturnProperties)) { - self::$_defaultHierReturnProperties = array( + self::$_defaultHierReturnProperties = [ 'home_URL' => 1, 'image_URL' => 1, 'legal_identifier' => 1, @@ -5040,8 +5157,8 @@ public static function &defaultHierReturnProperties() { 'do_not_mail' => 1, 'do_not_sms' => 1, 'do_not_trade' => 1, - 'location' => array( - '1' => array( + 'location' => [ + '1' => [ 'location_type' => 1, 'street_address' => 1, 'city' => 1, @@ -5061,8 +5178,8 @@ public static function &defaultHierReturnProperties() { 'email-1' => 1, 'email-2' => 1, 'email-3' => 1, - ), - '2' => array( + ], + '2' => [ 'location_type' => 1, 'street_address' => 1, 'city' => 1, @@ -5081,9 +5198,9 @@ public static function &defaultHierReturnProperties() { 'email-1' => 1, 'email-2' => 1, 'email-3' => 1, - ), - ), - ); + ], + ], + ]; } return self::$_defaultHierReturnProperties; } @@ -5097,11 +5214,13 @@ public static function &defaultHierReturnProperties() { * @param string $dbFieldName * @param string $fieldTitle * @param bool $appendTimeStamp + * @param string $dateFormat */ public function dateQueryBuilder( &$values, $tableName, $fieldName, $dbFieldName, $fieldTitle, - $appendTimeStamp = TRUE + $appendTimeStamp = TRUE, + $dateFormat = 'YmdHis' ) { list($name, $op, $value, $grouping, $wildcard) = $values; @@ -5118,7 +5237,7 @@ public function dateQueryBuilder( if ($name == $fieldName . '_low') { $firstOP = '>='; $firstPhrase = ts('greater than or equal to'); - $firstDate = CRM_Utils_Date::processDate($value); + $firstDate = CRM_Utils_Date::processDate($value, NULL, FALSE, $dateFormat); $secondValues = $this->getWhereValues("{$fieldName}_high", $grouping); if (!empty($secondValues) && $secondValues[2]) { @@ -5129,7 +5248,7 @@ public function dateQueryBuilder( if ($appendTimeStamp && strlen($secondValue) == 10) { $secondValue .= ' 23:59:59'; } - $secondDate = CRM_Utils_Date::processDate($secondValue); + $secondDate = CRM_Utils_Date::processDate($secondValue, NULL, FALSE, $dateFormat); } } elseif ($name == $fieldName . '_high') { @@ -5139,14 +5258,14 @@ public function dateQueryBuilder( if ($appendTimeStamp && strlen($value) == 10) { $value .= ' 23:59:59'; } - $firstDate = CRM_Utils_Date::processDate($value); + $firstDate = CRM_Utils_Date::processDate($value, NULL, FALSE, $dateFormat); $secondValues = $this->getWhereValues("{$fieldName}_low", $grouping); if (!empty($secondValues) && $secondValues[2]) { $secondOP = '>='; $secondPhrase = ts('greater than or equal to'); $secondValue = $secondValues[2]; - $secondDate = CRM_Utils_Date::processDate($secondValue); + $secondDate = CRM_Utils_Date::processDate($secondValue, NULL, FALSE, $dateFormat); } } @@ -5185,9 +5304,9 @@ public function dateQueryBuilder( $date = $format = NULL; if (strstr($op, 'IN')) { - $format = array(); + $format = []; foreach ($value as &$date) { - $date = CRM_Utils_Date::processDate($date); + $date = CRM_Utils_Date::processDate($date, NULL, FALSE, $dateFormat); if (!$appendTimeStamp) { $date = substr($date, 0, 8); } @@ -5197,7 +5316,7 @@ public function dateQueryBuilder( $format = implode(', ', $format); } elseif ($value && (!strstr($op, 'NULL') && !strstr($op, 'EMPTY'))) { - $date = CRM_Utils_Date::processDate($value); + $date = CRM_Utils_Date::processDate($value, NULL, FALSE, $dateFormat); if (!$appendTimeStamp) { $date = substr($date, 0, 8); } @@ -5301,7 +5420,6 @@ public function numberRangeBuilder( } } - /** * @param $values * @param string $tableName @@ -5319,7 +5437,8 @@ public function ageRangeQueryBuilder( list($name, $op, $value, $grouping, $wildcard) = $values; $asofDateValues = $this->getWhereValues("{$fieldName}_asof_date", $grouping); - $asofDate = NULL; // will be treated as current day + // will be treated as current day + $asofDate = NULL; if ($asofDateValues) { $asofDate = CRM_Utils_Date::processDate($asofDateValues[2]); $asofDateFormat = CRM_Utils_Date::customFormat(substr($asofDate, 0, 8)); @@ -5435,22 +5554,25 @@ public static function buildClause($field, $op, $value = NULL, $dataType = NULL) return $clause; case 'IS EMPTY': - $clause = " (NULLIF($field, '') IS NULL) "; + $clause = ($dataType == 'Date') ? " $field IS NULL " : " (NULLIF($field, '') IS NULL) "; return $clause; case 'IS NOT EMPTY': - $clause = " (NULLIF($field, '') IS NOT NULL) "; + $clause = ($dataType == 'Date') ? " $field IS NOT NULL " : " (NULLIF($field, '') IS NOT NULL) "; return $clause; + case 'RLIKE': + return " {$clause} BINARY '{$value}' "; + case 'IN': case 'NOT IN': // I feel like this would be escaped properly if passed through $queryString = CRM_Core_DAO::createSqlFilter. if (!empty($value) && (!is_array($value) || !array_key_exists($op, $value))) { - $value = array($op => (array) $value); + $value = [$op => (array) $value]; } default: - if (empty($dataType)) { + if (empty($dataType) || $dataType == 'Date') { $dataType = 'String'; } if (is_array($value)) { @@ -5461,32 +5583,19 @@ public static function buildClause($field, $op, $value = NULL, $dataType = NULL) return $queryString; } - - // This is the here-be-dragons zone. We have no other hopes left for an array so lets assume it 'should' be array('IN' => array(2,5)) - // but we got only array(2,5) from the form. - // We could get away with keeping this in 4.6 if we make it such that it throws an enotice in 4.7 so - // people have to de-slopify it. - if (!empty($value[0])) { - if ($op != 'BETWEEN') { - $dragonPlace = $iAmAnIntentionalENoticeThatWarnsOfAProblemYouShouldReport; - } - if (($queryString = CRM_Core_DAO::createSqlFilter($field, array($op => $value), $dataType)) != FALSE) { - return $queryString; - } - } - else { - $op = 'IN'; - $dragonPlace = $iAmAnIntentionalENoticeThatWarnsOfAProblemYouShouldReportUsingOldFormat; - if (($queryString = CRM_Core_DAO::createSqlFilter($field, array($op => array_keys($value)), $dataType)) != FALSE) { + if (!empty($value[0]) && $op === 'BETWEEN') { + CRM_Core_Error::deprecatedFunctionWarning('Fix search input params'); + if (($queryString = CRM_Core_DAO::createSqlFilter($field, [$op => $value], $dataType)) != FALSE) { return $queryString; } } + throw new CRM_Core_Exception(ts('Failed to interpret input for search')); } $value = CRM_Utils_Type::escape($value, $dataType); // if we don't have a dataType we should assume if ($dataType == 'String' || $dataType == 'Text') { - $value = "'" . strtolower($value) . "'"; + $value = "'" . $value . "'"; } return "$clause $value"; } @@ -5503,7 +5612,7 @@ public function openedSearchPanes($reset = FALSE) { } // pane name to table mapper - $panesMapper = array( + $panesMapper = [ ts('Contributions') => 'civicrm_contribution', ts('Memberships') => 'civicrm_membership', ts('Events') => 'civicrm_participant', @@ -5516,7 +5625,7 @@ public function openedSearchPanes($reset = FALSE) { ts('Notes') => 'civicrm_note', ts('Change Log') => 'civicrm_log', ts('Mailings') => 'civicrm_mailing', - ); + ]; CRM_Contact_BAO_Query_Hook::singleton()->getPanesMapper($panesMapper); foreach (array_keys($this->_whereTables) as $table) { @@ -5532,7 +5641,7 @@ public function openedSearchPanes($reset = FALSE) { * @param $operator */ public function setOperator($operator) { - $validOperators = array('AND', 'OR'); + $validOperators = ['AND', 'OR']; if (!in_array($operator, $validOperators)) { $operator = 'AND'; } @@ -5552,15 +5661,28 @@ public function getOperator() { * @param $having */ public function filterRelatedContacts(&$from, &$where, &$having) { - static $_rTypeProcessed = NULL; - static $_rTypeFrom = NULL; - static $_rTypeWhere = NULL; - - if (!$_rTypeProcessed) { - $_rTypeProcessed = TRUE; - + if (!isset(Civi::$statics[__CLASS__]['related_contacts_filter'])) { + Civi::$statics[__CLASS__]['related_contacts_filter'] = []; + } + $_rTempCache =& Civi::$statics[__CLASS__]['related_contacts_filter']; + // since there only can be one instance of this filter in every query + // skip if filter has already applied + foreach ($_rTempCache as $acache) { + foreach ($acache['queries'] as $aqcache) { + if (strpos($from, $aqcache['from']) !== FALSE) { + $having = NULL; + return; + } + } + } + $arg_sig = sha1("$from $where $having"); + if (isset($_rTempCache[$arg_sig])) { + $cache = $_rTempCache[$arg_sig]; + } + else { // create temp table with contact ids $tableName = CRM_Core_DAO::createTempTableName('civicrm_transform', TRUE); + $sql = "CREATE TEMPORARY TABLE $tableName ( contact_id int primary key) ENGINE=HEAP"; CRM_Core_DAO::executeQuery($sql); @@ -5573,16 +5695,27 @@ public function filterRelatedContacts(&$from, &$where, &$having) { "; CRM_Core_DAO::executeQuery($sql); - $qillMessage = ts('Contacts with a Relationship Type of: '); + $cache = ['tableName' => $tableName, 'queries' => []]; + $_rTempCache[$arg_sig] = $cache; + } + // upsert the query depending on relationship type + if (isset($cache['queries'][$this->_displayRelationshipType])) { + $qcache = $cache['queries'][$this->_displayRelationshipType]; + } + else { + $tableName = $cache['tableName']; + $qcache = [ + "from" => "", + "where" => "", + ]; $rTypes = CRM_Core_PseudoConstant::relationshipType(); - if (is_numeric($this->_displayRelationshipType)) { $relationshipTypeLabel = $rTypes[$this->_displayRelationshipType]['label_a_b']; - $_rTypeFrom = " + $qcache['from'] = " INNER JOIN civicrm_relationship displayRelType ON ( displayRelType.contact_id_a = contact_a.id OR displayRelType.contact_id_b = contact_a.id ) INNER JOIN $tableName transform_temp ON ( transform_temp.contact_id = displayRelType.contact_id_a OR transform_temp.contact_id = displayRelType.contact_id_b ) "; - $_rTypeWhere = " + $qcache['where'] = " WHERE displayRelType.relationship_type_id = {$this->_displayRelationshipType} AND displayRelType.is_active = 1 "; @@ -5591,49 +5724,54 @@ public function filterRelatedContacts(&$from, &$where, &$having) { list($relType, $dirOne, $dirTwo) = explode('_', $this->_displayRelationshipType); if ($dirOne == 'a') { $relationshipTypeLabel = $rTypes[$relType]['label_a_b']; - $_rTypeFrom .= " + $qcache['from'] .= " INNER JOIN civicrm_relationship displayRelType ON ( displayRelType.contact_id_a = contact_a.id ) INNER JOIN $tableName transform_temp ON ( transform_temp.contact_id = displayRelType.contact_id_b ) "; } else { $relationshipTypeLabel = $rTypes[$relType]['label_b_a']; - $_rTypeFrom .= " + $qcache['from'] .= " INNER JOIN civicrm_relationship displayRelType ON ( displayRelType.contact_id_b = contact_a.id ) INNER JOIN $tableName transform_temp ON ( transform_temp.contact_id = displayRelType.contact_id_a ) "; } - $_rTypeWhere = " + $qcache['where'] = " WHERE displayRelType.relationship_type_id = $relType AND displayRelType.is_active = 1 "; } - $this->_qill[0][] = $qillMessage . "'" . $relationshipTypeLabel . "'"; + $qcache['relTypeLabel'] = $relationshipTypeLabel; + $_rTempCache[$arg_sig]['queries'][$this->_displayRelationshipType] = $qcache; } - - if (!empty($this->_permissionWhereClause)) { - $_rTypeWhere .= "AND $this->_permissionWhereClause"; + $qillMessage = ts('Contacts with a Relationship Type of: '); + $iqill = $qillMessage . "'" . $qcache['relTypeLabel'] . "'"; + if (!is_array($this->_qill[0]) || !in_array($iqill, $this->_qill[0])) { + $this->_qill[0][] = $iqill; } - - if (strpos($from, $_rTypeFrom) === FALSE) { + if (strpos($from, $qcache['from']) === FALSE) { // lets replace all the INNER JOIN's in the $from so we dont exclude other data // this happens when we have an event_type in the quert (CRM-7969) $from = str_replace("INNER JOIN", "LEFT JOIN", $from); - $from .= $_rTypeFrom; - $where = $_rTypeWhere; + $from .= $qcache['from']; + $where = $qcache['where']; + if (!empty($this->_permissionWhereClause)) { + $where .= "AND $this->_permissionWhereClause"; + } } $having = NULL; } /** + * See CRM-19811 for why this is database hurty without apparent benefit. + * * @param $op * * @return bool */ public static function caseImportant($op) { - return - in_array($op, array('LIKE', 'IS NULL', 'IS NOT NULL', 'IS EMPTY', 'IS NOT EMPTY')) ? FALSE : TRUE; + return in_array($op, ['LIKE', 'IS NULL', 'IS NOT NULL', 'IS EMPTY', 'IS NOT EMPTY']) ? FALSE : TRUE; } /** @@ -5684,7 +5822,7 @@ public function optionValueQuery( $useIDsOnly = FALSE ) { - $pseudoFields = array( + $pseudoFields = [ 'email_greeting', 'postal_greeting', 'addressee', @@ -5692,7 +5830,7 @@ public function optionValueQuery( 'prefix_id', 'suffix_id', 'communication_style_id', - ); + ]; if ($useIDsOnly) { list($tableName, $fieldName) = explode('.', $field['where'], 2); @@ -5700,14 +5838,23 @@ public function optionValueQuery( $wc = "contact_a.$fieldName"; } else { - $wc = "$tableName.id"; + // Special handling for on_hold, so that we actually use the 'where' + // property in order to limit the query by the on_hold status of the email, + // instead of using email.id which would be nonsensical. + if ($field['name'] == 'on_hold') { + $wc = "{$field['where']}"; + } + else { + $wc = "$tableName.id"; + } } } else { - $wc = self::caseImportant($op) ? "LOWER({$field['where']})" : "{$field['where']}"; + CRM_Core_Error::deprecatedFunctionWarning('pass $ids to this method'); + $wc = "{$field['where']}"; } if (in_array($name, $pseudoFields)) { - if (!in_array($name, array('gender_id', 'prefix_id', 'suffix_id', 'communication_style_id'))) { + if (!in_array($name, ['gender_id', 'prefix_id', 'suffix_id', 'communication_style_id'])) { $wc = "contact_a.{$name}_id"; } $dataType = 'Positive'; @@ -5718,7 +5865,7 @@ public function optionValueQuery( } list($qillop, $qillVal) = CRM_Contact_BAO_Query::buildQillForFieldValue($daoName, $field['name'], $value, $op); - $this->_qill[$grouping][] = ts("%1 %2 %3", array(1 => $label, 2 => $qillop, 3 => $qillVal)); + $this->_qill[$grouping][] = ts("%1 %2 %3", [1 => $label, 2 => $qillop, 3 => $qillVal]); $this->_where[$grouping][] = self::buildClause($wc, $op, $value, $dataType); } @@ -5738,7 +5885,7 @@ public function optionValueQuery( public static function parseSearchBuilderString($string, $dataType = 'Integer') { $string = trim($string); if (substr($string, 0, 1) != '(' || substr($string, -1, 1) != ')') { - Return FALSE; + return FALSE; } $string = substr($string, 1, -1); @@ -5747,7 +5894,7 @@ public static function parseSearchBuilderString($string, $dataType = 'Integer') return FALSE; } - $returnValues = array(); + $returnValues = []; foreach ($values as $v) { if ($dataType == 'Integer' && !is_numeric($v)) { return FALSE; @@ -5778,7 +5925,7 @@ public function convertToPseudoNames(&$dao, $return = FALSE, $usedForAPI = FALSE if (empty($this->_pseudoConstantsSelect)) { return NULL; } - $values = array(); + $values = []; foreach ($this->_pseudoConstantsSelect as $key => $value) { if (!empty($this->_pseudoConstantsSelect[$key]['sorting'])) { continue; @@ -5788,19 +5935,47 @@ public function convertToPseudoNames(&$dao, $return = FALSE, $usedForAPI = FALSE $val = $dao->{$value['idCol']}; if ($key == 'groups') { $dao->groups = $this->convertGroupIDStringToLabelString($dao, $val); - return; + continue; } if (CRM_Utils_System::isNull($val)) { $dao->$key = NULL; } - elseif ($baoName = CRM_Utils_Array::value('bao', $value, NULL)) { - //preserve id value + elseif (!empty($value['pseudoconstant'])) { + // If pseudoconstant is set that is kind of defacto for 'we have a bit more info about this' + // and we can use the metadata to figure it out. + // ideally this bit of IF will absorb & replace all the rest in time as we move to + // more metadata based choices. + if (strpos($val, CRM_Core_DAO::VALUE_SEPARATOR) !== FALSE) { + $dbValues = explode(CRM_Core_DAO::VALUE_SEPARATOR, trim($val, CRM_Core_DAO::VALUE_SEPARATOR)); + foreach ($dbValues as $pseudoValue) { + $convertedValues[] = CRM_Core_PseudoConstant::getLabel($value['bao'], $value['idCol'], $pseudoValue); + } + + $dao->$key = ($usedForAPI) ? $convertedValues : implode(', ', $convertedValues); + $realFieldName = CRM_Utils_Array::value('field_name', $this->_pseudoConstantsSelect[$key]); + if ($usedForAPI && $realFieldName) { + // normally we would see 2 fields returned for pseudoConstants. An exception is + // preferred_communication_method where there is no id-variant. + // For the api we prioritise getting the real data returned. + // over the resolved version + $dao->$realFieldName = $dbValues; + } + + } + else { + // This is basically the same as the default but since we have the bao we can use + // a cached function. + $dao->$key = CRM_Core_PseudoConstant::getLabel($value['bao'], $value['idCol'], $val); + } + } + elseif ($baoName = CRM_Utils_Array::value('bao', $value, NULL)) { + //preserve id value $idColumn = "{$key}_id"; $dao->$idColumn = $val; if ($key == 'state_province_name') { - $dao->{$value['pseudoField']} = $dao->$key = CRM_Core_PseudoConstant::stateProvinceAbbreviation($val); + $dao->{$value['pseudoField']} = $dao->$key = CRM_Core_PseudoConstant::stateProvince($val); } else { $dao->{$value['pseudoField']} = $dao->$key = CRM_Core_PseudoConstant::getLabel($baoName, $value['pseudoField'], $val); @@ -5809,9 +5984,9 @@ public function convertToPseudoNames(&$dao, $return = FALSE, $usedForAPI = FALSE elseif ($value['pseudoField'] == 'state_province_abbreviation') { $dao->$key = CRM_Core_PseudoConstant::stateProvinceAbbreviation($val); } - // FIX ME: we should potentially move this to component Query and write a wrapper function that - // handles pseudoconstant fixes for all component - elseif (in_array($value['pseudoField'], array('participant_role_id', 'participant_role'))) { + // @todo handle this in the section above for pseudoconstants. + elseif (in_array($value['pseudoField'], ['participant_role_id', 'participant_role'])) { + // @todo define bao on this & merge into the above condition. $viewValues = explode(CRM_Core_DAO::VALUE_SEPARATOR, $val); if ($value['pseudoField'] == 'participant_role') { @@ -5835,7 +6010,7 @@ public function convertToPseudoNames(&$dao, $return = FALSE, $usedForAPI = FALSE $lastElement = array_pop($keyVal); foreach ($keyVal as $v) { if (!array_key_exists($v, $current)) { - $current[$v] = array(); + $current[$v] = []; } $current = &$current[$v]; } @@ -5847,6 +6022,21 @@ public function convertToPseudoNames(&$dao, $return = FALSE, $usedForAPI = FALSE } } } + if (!$usedForAPI) { + foreach ([ + 'gender_id' => 'gender', + 'prefix_id' => 'individual_prefix', + 'suffix_id' => 'individual_suffix', + 'communication_style_id' => 'communication_style', + ] as $realField => $labelField) { + // This is a temporary routine for handling these fields while + // we figure out how to handled them based on metadata in + /// export and search builder. CRM-19815, CRM-19830. + if (isset($dao->$realField) && is_numeric($dao->$realField) && isset($dao->$labelField)) { + $dao->$realField = $dao->$labelField; + } + } + } return $values; } @@ -5861,7 +6051,7 @@ public function includePseudoFieldsJoin($sort) { return NULL; } $sort = is_string($sort) ? $sort : $sort->orderBy(); - $present = array(); + $present = []; foreach ($this->_pseudoConstantsSelect as $name => $value) { if (!empty($value['table'])) { @@ -5898,7 +6088,7 @@ public function includePseudoFieldsJoin($sort) { $this->_fromClause = $this->_fromClause . $presentClause; $this->_simpleFromClause = $this->_simpleFromClause . $presentSimpleFromClause; - return array($presentClause, $presentSimpleFromClause); + return [$presentClause, $presentSimpleFromClause]; } /** @@ -5921,14 +6111,21 @@ public static function buildQillForFieldValue( $fieldName, $fieldValue, $op, - $pseudoExtraParam = array(), + $pseudoExtraParam = [], $type = CRM_Utils_Type::T_STRING ) { $qillOperators = CRM_Core_SelectValues::getSearchBuilderOperators(); + //API usually have fieldValue format as array(operator => array(values)), + //so we need to separate operator out of fieldValue param + if (is_array($fieldValue) && in_array(key($fieldValue), CRM_Core_DAO::acceptedSQLOperators(), TRUE)) { + $op = key($fieldValue); + $fieldValue = $fieldValue[$op]; + } + // if Operator chosen is NULL/EMPTY then if (strpos($op, 'NULL') !== FALSE || strpos($op, 'EMPTY') !== FALSE) { - return array(CRM_Utils_Array::value($op, $qillOperators, $op), ''); + return [CRM_Utils_Array::value($op, $qillOperators, $op), '']; } if ($fieldName == 'activity_type_id') { @@ -5944,7 +6141,8 @@ public static function buildQillForFieldValue( $pseudoOptions = CRM_Core_PseudoConstant::worldRegion(); } elseif ($daoName == 'CRM_Event_DAO_Event' && $fieldName == 'id') { - $pseudoOptions = CRM_Event_BAO_Event::getEvents(0, $fieldValue, TRUE, TRUE, TRUE); + $checkPermission = CRM_Utils_Array::value('check_permission', $pseudoExtraParam, TRUE); + $pseudoOptions = CRM_Event_BAO_Event::getEvents(0, $fieldValue, TRUE, $checkPermission, TRUE); } elseif ($fieldName == 'contribution_product_id') { $pseudoOptions = CRM_Contribute_PseudoConstant::products(); @@ -5952,19 +6150,15 @@ public static function buildQillForFieldValue( elseif ($daoName == 'CRM_Contact_DAO_Group' && $fieldName == 'id') { $pseudoOptions = CRM_Core_PseudoConstant::group(); } + elseif ($daoName == 'CRM_Batch_BAO_EntityBatch' && $fieldName == 'batch_id') { + $pseudoOptions = CRM_Contribute_PseudoConstant::batch(); + } elseif ($daoName) { $pseudoOptions = CRM_Core_PseudoConstant::get($daoName, $fieldName, $pseudoExtraParam); } - //API usually have fieldValue format as array(operator => array(values)), - //so we need to separate operator out of fieldValue param - if (is_array($fieldValue) && in_array(key($fieldValue), CRM_Core_DAO::acceptedSQLOperators(), TRUE)) { - $op = key($fieldValue); - $fieldValue = $fieldValue[$op]; - } - if (is_array($fieldValue)) { - $qillString = array(); + $qillString = []; if (!empty($pseudoOptions)) { foreach ((array) $fieldValue as $val) { $qillString[] = CRM_Utils_Array::value($val, $pseudoOptions, $val); @@ -5994,7 +6188,7 @@ public static function buildQillForFieldValue( $fieldValue = CRM_Utils_Date::customFormat($fieldValue); } - return array(CRM_Utils_Array::value($op, $qillOperators, $op), $fieldValue); + return [CRM_Utils_Array::value($op, $qillOperators, $op), $fieldValue]; } /** @@ -6036,7 +6230,10 @@ public static function getWildCardedValue($wildcard, $op, $value) { * @param array $changeNames * Array of fields whose name should be changed */ - public static function processSpecialFormValue(&$formValues, $specialFields, $changeNames = array()) { + public static function processSpecialFormValue(&$formValues, $specialFields, $changeNames = []) { + // Array of special fields whose value are considered only for NULL or EMPTY operators + $nullableFields = ['contribution_batch_id']; + foreach ($specialFields as $element) { $value = CRM_Utils_Array::value($element, $formValues); if ($value) { @@ -6045,12 +6242,15 @@ public static function processSpecialFormValue(&$formValues, $specialFields, $ch unset($formValues[$element]); $element = $changeNames[$element]; } - $formValues[$element] = array('IN' => $value); + $formValues[$element] = ['IN' => $value]; } - else { + elseif (in_array($value, ['IS NULL', 'IS NOT NULL', 'IS EMPTY', 'IS NOT EMPTY'])) { + $formValues[$element] = [$value => 1]; + } + elseif (!in_array($element, $nullableFields)) { // if wildcard is already present return searchString as it is OR append and/or prepend with wildcard $isWilcard = strstr($value, '%') ? FALSE : CRM_Core_Config::singleton()->includeWildCardInName; - $formValues[$element] = array('LIKE' => self::getWildCardedValue($isWilcard, 'LIKE', $value)); + $formValues[$element] = ['LIKE' => self::getWildCardedValue($isWilcard, 'LIKE', $value)]; } } } @@ -6067,19 +6267,17 @@ public static function processSpecialFormValue(&$formValues, $specialFields, $ch * * @param string|CRM_Utils_Sort $sort * The order by string. - * @param bool $sortByChar - * If true returns the distinct array of first characters for search results. * @param null $sortOrder * Who knows? Hu knows. He who knows Hu knows who. - * @param string $additionalFromClause - * Should be clause with proper joins, effective to reduce where clause load. + * * @return array * list(string $orderByClause, string $additionalFromClause). */ - protected function prepareOrderBy($sort, $sortByChar, $sortOrder, $additionalFromClause) { - $order = NULL; - $config = CRM_Core_Config::singleton(); - if ($config->includeOrderByClause || + protected function prepareOrderBy($sort, $sortOrder) { + $orderByArray = []; + $orderBy = ''; + + if (CRM_Core_Config::singleton()->includeOrderByClause || isset($this->_distinctComponentClause) ) { if ($sort) { @@ -6100,76 +6298,101 @@ protected function prepareOrderBy($sort, $sortByChar, $sortOrder, $additionalFro $orderBy = str_replace('sort_name', 'contact_a.sort_name', $orderBy); } - $order = " ORDER BY $orderBy"; - if ($sortOrder) { - $order .= " $sortOrder"; + $orderBy .= " $sortOrder"; } // always add contact_a.id to the ORDER clause // so the order is deterministic - if (strpos('contact_a.id', $order) === FALSE) { - $order .= ", contact_a.id"; + if (strpos('contact_a.id', $orderBy) === FALSE) { + $orderBy .= ", contact_a.id"; } } } - elseif ($sortByChar) { - $order = " ORDER BY UPPER(LEFT(contact_a.sort_name, 1)) asc"; - } else { - $order = " ORDER BY contact_a.sort_name asc, contact_a.id"; + $orderBy = " contact_a.sort_name ASC, contact_a.id"; } } + if (!$orderBy) { + return NULL; + } + // Remove this here & add it at the end for simplicity. + $order = trim($orderBy); + $orderByArray = explode(',', $order); - // hack for order clause - if ($order) { - $fieldStr = trim(str_replace('ORDER BY', '', $order)); - $fieldOrder = explode(' ', $fieldStr); - $field = $fieldOrder[0]; - - if ($field) { - switch ($field) { - case 'city': - case 'postal_code': - $this->_tables["civicrm_address"] = $this->_whereTables["civicrm_address"] = 1; - $order = str_replace($field, "civicrm_address.{$field}", $order); - break; + foreach ($orderByArray as $orderByClause) { + $orderByClauseParts = explode(' ', trim($orderByClause)); + $field = $orderByClauseParts[0]; + $direction = isset($orderByClauseParts[1]) ? $orderByClauseParts[1] : 'asc'; + $fieldSpec = $this->getMetadataForRealField($field); - case 'country': - case 'state_province': - $this->_tables["civicrm_{$field}"] = $this->_whereTables["civicrm_{$field}"] = 1; - if (is_array($this->_returnProperties) && empty($this->_returnProperties)) { - $additionalFromClause .= " LEFT JOIN civicrm_{$field} ON civicrm_{$field}.id = civicrm_address.{$field}_id"; - } - $order = str_replace($field, "civicrm_{$field}.name", $order); - break; + // This is a hacky add-in for primary address joins. Feel free to iterate as it is unit tested. + // @todo much more cleanup on location handling in addHierarchical elements. Potentially + // add keys to $this->fields to represent the actual keys for locations. + if (empty($fieldSpec) && substr($field, 0, 2) === '1-') { + $fieldSpec = $this->getMetadataForField(substr($field, 2)); + $this->addAddressTable('1-' . str_replace('civicrm_', '', $fieldSpec['table_name']), 'is_primary = 1'); + } - case 'email': - $this->_tables["civicrm_email"] = $this->_whereTables["civicrm_email"] = 1; - $order = str_replace($field, "civicrm_email.{$field}", $order); - break; + if ($this->_returnProperties === []) { + if (!empty($fieldSpec['table_name']) && !isset($this->_tables[$fieldSpec['table_name']])) { + $this->_tables[$fieldSpec['table_name']] = 1; + $order = $fieldSpec['where'] . ' ' . $direction; + } - default: - //CRM-12565 add "`" around $field if it is a pseudo constant - foreach ($this->_pseudoConstantsSelect as $key => $value) { - if (!empty($value['element']) && $value['element'] == $field) { - $order = str_replace($field, "`{$field}`", $order); - } - } + } + $cfID = CRM_Core_BAO_CustomField::getKeyID($field); + // add to cfIDs array if not present + if (!empty($cfID) && !array_key_exists($cfID, $this->_cfIDs)) { + $this->_cfIDs[$cfID] = []; + $this->_customQuery = new CRM_Core_BAO_CustomQuery($this->_cfIDs, TRUE, $this->_locationSpecificCustomFields); + $this->_customQuery->query(); + $this->_select = array_merge($this->_select, $this->_customQuery->_select); + $this->_tables = array_merge($this->_tables, $this->_customQuery->_tables); + } + + // By replacing the join to the option value table with the mysql construct + // ORDER BY field('contribution_status_id', 2,1,4) + // we can remove a join. In the case of the option value join it is + /// a join known to cause slow queries. + // @todo cover other pseudoconstant types. Limited to option group ones & Foreign keys + // matching an id+name parrern in the + // first instance for scope reasons. They require slightly different handling as the column (label) + // is not declared for them. + // @todo so far only integer fields are being handled. If we add string fields we need to look at + // escaping. + $pseudoConstantMetadata = CRM_Utils_Array::value('pseudoconstant', $fieldSpec, FALSE); + if (!empty($pseudoConstantMetadata) + ) { + if (!empty($pseudoConstantMetadata['optionGroupName']) + || $this->isPseudoFieldAnFK($fieldSpec) + ) { + $sortedOptions = $fieldSpec['bao']::buildOptions($fieldSpec['name'], NULL, [ + 'orderColumn' => CRM_Utils_Array::value('labelColumn', $pseudoConstantMetadata, 'label'), + ]); + $fieldIDsInOrder = implode(',', array_keys($sortedOptions)); + // Pretty sure this validation ALSO happens in the order clause & this can't be reached but... + // this might give some early warning. + CRM_Utils_Type::validate($fieldIDsInOrder, 'CommaSeparatedIntegers'); + $order = str_replace("$field", "field({$fieldSpec['name']},$fieldIDsInOrder)", $order); + } + //CRM-12565 add "`" around $field if it is a pseudo constant + // This appears to be for 'special' fields like locations with appended numbers or hyphens .. maybe. + if (!empty($pseudoConstantMetadata['element']) && $pseudoConstantMetadata['element'] == $field) { + $order = str_replace($field, "`{$field}`", $order); } - $this->_fromClause = self::fromClause($this->_tables, NULL, NULL, $this->_primaryLocation, $this->_mode); - $this->_simpleFromClause = self::fromClause($this->_whereTables, NULL, NULL, $this->_primaryLocation, $this->_mode); } } + $this->_fromClause = self::fromClause($this->_tables, NULL, NULL, $this->_primaryLocation, $this->_mode); + $this->_simpleFromClause = self::fromClause($this->_whereTables, NULL, NULL, $this->_primaryLocation, $this->_mode); + // The above code relies on crazy brittle string manipulation of a peculiarly-encoded ORDER BY // clause. But this magic helper which forgivingly reescapes ORDER BY. - // Note: $sortByChar implies that $order was hard-coded/trusted, so it can do funky things. - if ($order && !$sortByChar) { - $order = ' ORDER BY ' . CRM_Utils_Type::escape(preg_replace('/^\s*ORDER BY\s*/', '', $order), 'MysqlOrderBy'); - return array($order, $additionalFromClause); + if ($order) { + $order = CRM_Utils_Type::escape($order, 'MysqlOrderBy'); + return ' ORDER BY ' . $order; } - return array($order, $additionalFromClause); } /** @@ -6185,14 +6408,8 @@ protected function prepareOrderBy($sort, $sortByChar, $sortOrder, $additionalFro */ public function convertGroupIDStringToLabelString(&$dao, $val) { $groupIDs = explode(',', $val); - - // The pseudoConstant function does not actually cache. - static $allGroups; - if (!$allGroups) { - $allGroups = CRM_Core_PseudoConstant::group(); - } // Note that groups that the user does not have permission to will be excluded (good). - $groups = array_intersect_key($allGroups, array_flip($groupIDs)); + $groups = array_intersect_key(CRM_Core_PseudoConstant::group(), array_flip($groupIDs)); return implode(', ', $groups); } @@ -6212,11 +6429,549 @@ public function convertGroupIDStringToLabelString(&$dao, $val) { public function setQillAndWhere($name, $op, $value, $grouping, $field) { $this->_where[$grouping][] = self::buildClause("contact_a.{$name}", $op, $value); list($qillop, $qillVal) = CRM_Contact_BAO_Query::buildQillForFieldValue(NULL, $name, $value, $op); - $this->_qill[$grouping][] = ts("%1 %2 %3", array( + $this->_qill[$grouping][] = ts("%1 %2 %3", [ 1 => $field['title'], 2 => $qillop, 3 => $qillVal, - )); + ]); + } + + /** + * Has the pseudoconstant of the field been requested. + * + * For example if the field is payment_instrument_id then it + * has been requested if either payment_instrument_id or payment_instrument + * have been requested. Payment_instrument is the option groun name field value. + * + * @param array $field + * @param string $fieldName + * The unique name of the field - ie. the one it will be aliased to in the query. + * + * @return bool + */ + private function pseudoConstantNameIsInReturnProperties($field, $fieldName = NULL) { + $realField = $this->getMetadataForRealField($fieldName); + if (!isset($realField['pseudoconstant'])) { + return FALSE; + } + $pseudoConstant = $realField['pseudoconstant']; + if (empty($pseudoConstant['optionGroupName']) && + CRM_Utils_Array::value('labelColumn', $pseudoConstant) !== 'name') { + // We are increasing our pseudoconstant handling - but still very cautiously, + // hence the check for labelColumn === name + return FALSE; + } + + if (!empty($pseudoConstant['optionGroupName']) && CRM_Utils_Array::value($pseudoConstant['optionGroupName'], $this->_returnProperties)) { + return TRUE; + } + if (CRM_Utils_Array::value($fieldName, $this->_returnProperties)) { + return TRUE; + } + // Is this still required - the above goes off the unique name. Test with things like + // communication_preferences & prefix_id. + if (CRM_Utils_Array::value($field['name'], $this->_returnProperties)) { + return TRUE; + } + return FALSE; + } + + /** + * Get Select Clause. + * + * @return string + */ + public function getSelect() { + $select = "SELECT "; + if (isset($this->_distinctComponentClause)) { + $select .= "{$this->_distinctComponentClause}, "; + } + $select .= implode(', ', $this->_select); + return $select; + } + + /** + * Add basic statistics to the summary. + * + * @param array $summary + * @param string $where + * @param string $from + * + * @return array + */ + protected function addBasicStatsToSummary(&$summary, $where, $from) { + $summary['total']['count'] = 0; + $summary['total']['amount'] = $summary['total']['avg'] = []; + + $query = " + SELECT COUNT( conts.total_amount ) as total_count, + SUM( conts.total_amount ) as total_amount, + AVG( conts.total_amount ) as total_avg, + conts.currency as currency + FROM ( + SELECT civicrm_contribution.total_amount, COUNT(civicrm_contribution.total_amount) as civicrm_contribution_total_amount_count, + civicrm_contribution.currency + $from + $where AND civicrm_contribution.contribution_status_id = 1 + GROUP BY civicrm_contribution.id + ) as conts + GROUP BY currency"; + + $dao = CRM_Core_DAO::executeQuery($query); + + while ($dao->fetch()) { + $summary['total']['count'] += $dao->total_count; + $summary['total']['amount'][] = CRM_Utils_Money::format($dao->total_amount, $dao->currency); + $summary['total']['avg'][] = CRM_Utils_Money::format($dao->total_avg, $dao->currency); + } + + if (!empty($summary['total']['amount'])) { + $summary['total']['amount'] = implode(', ', $summary['total']['amount']); + $summary['total']['avg'] = implode(', ', $summary['total']['avg']); + } + else { + $summary['total']['amount'] = $summary['total']['avg'] = 0; + } + return $summary; + } + + /** + * Add basic soft credit statistics to summary array. + * + * @param array $summary + * @param string $where + * @param string $from + */ + protected function addBasicSoftCreditStatsToStats(&$summary, $where, $from) { + $query = " + SELECT COUNT( conts.total_amount ) as total_count, + SUM( conts.total_amount ) as total_amount, + AVG( conts.total_amount ) as total_avg, + conts.currency as currency + FROM ( + SELECT civicrm_contribution_soft.amount as total_amount, civicrm_contribution_soft.currency + $from + $where AND civicrm_contribution.contribution_status_id = 1 AND civicrm_contribution_soft.id IS NOT NULL + GROUP BY civicrm_contribution_soft.id + ) as conts + GROUP BY currency"; + + $dao = CRM_Core_DAO::executeQuery($query); + $summary['soft_credit']['count'] = 0; + $summary['soft_credit']['amount'] = $summary['soft_credit']['avg'] = []; + while ($dao->fetch()) { + $summary['soft_credit']['count'] += $dao->total_count; + $summary['soft_credit']['amount'][] = CRM_Utils_Money::format($dao->total_amount, $dao->currency); + $summary['soft_credit']['avg'][] = CRM_Utils_Money::format($dao->total_avg, $dao->currency); + } + if (!empty($summary['soft_credit']['amount'])) { + $summary['soft_credit']['amount'] = implode(', ', $summary['soft_credit']['amount']); + $summary['soft_credit']['avg'] = implode(', ', $summary['soft_credit']['avg']); + } + else { + $summary['soft_credit']['amount'] = $summary['soft_credit']['avg'] = 0; + } + } + + /** + * Add basic stats about cancelled contributions to the summary. + * + * @param array $summary + * @param string $where + * @param string $from + */ + protected function addBasicCancelStatsToSummary(&$summary, $where, $from) { + $query = " + SELECT COUNT( conts.total_amount ) as cancel_count, + SUM( conts.total_amount ) as cancel_amount, + AVG( conts.total_amount ) as cancel_avg, + conts.currency as currency + FROM ( + SELECT civicrm_contribution.total_amount, civicrm_contribution.currency + $from + $where AND civicrm_contribution.cancel_date IS NOT NULL + GROUP BY civicrm_contribution.id + ) as conts + GROUP BY currency"; + + $dao = CRM_Core_DAO::executeQuery($query); + + if ($dao->N <= 1) { + if ($dao->fetch()) { + $summary['cancel']['count'] = $dao->cancel_count; + $summary['cancel']['amount'] = CRM_Utils_Money::format($dao->cancel_amount, $dao->currency); + $summary['cancel']['avg'] = CRM_Utils_Money::format($dao->cancel_avg, $dao->currency); + } + } + else { + $summary['cancel']['count'] = 0; + $summary['cancel']['amount'] = $summary['cancel']['avg'] = []; + while ($dao->fetch()) { + $summary['cancel']['count'] += $dao->cancel_count; + $summary['cancel']['amount'][] = CRM_Utils_Money::format($dao->cancel_amount, $dao->currency); + $summary['cancel']['avg'][] = CRM_Utils_Money::format($dao->cancel_avg, $dao->currency); + } + $summary['cancel']['amount'] = implode(', ', $summary['cancel']['amount']); + $summary['cancel']['avg'] = implode(', ', $summary['cancel']['avg']); + } + } + + /** + * Create the sql query for an contact search. + * + * @param int $offset + * The offset for the query. + * @param int $rowCount + * The number of rows to return. + * @param string|CRM_Utils_Sort $sort + * The order by string. + * @param bool $count + * Is this a count only query ?. + * @param bool $includeContactIds + * Should we include contact ids?. + * @param bool $sortByChar + * If true returns the distinct array of first characters for search results. + * @param bool $groupContacts + * If true, return only the contact ids. + * @param string $additionalWhereClause + * If the caller wants to further restrict the search (used for components). + * @param null $sortOrder + * @param string $additionalFromClause + * Should be clause with proper joins, effective to reduce where clause load. + * + * @param bool $skipOrderAndLimit + * @return string + */ + public function getSearchSQL( + $offset = 0, $rowCount = 0, $sort = NULL, + $count = FALSE, $includeContactIds = FALSE, + $sortByChar = FALSE, $groupContacts = FALSE, + $additionalWhereClause = NULL, $sortOrder = NULL, + $additionalFromClause = NULL, $skipOrderAndLimit = FALSE) { + + $sqlParts = $this->getSearchSQLParts($offset, $rowCount, $sort, $count, $includeContactIds, $sortByChar, $groupContacts, $additionalWhereClause, $sortOrder, $additionalFromClause); + + if ($sortByChar) { + CRM_Core_Error::deprecatedFunctionWarning('sort by char is deprecated - use alphabetQuery method'); + $sqlParts['order_by'] = 'ORDER BY sort_name asc'; + } + + if ($skipOrderAndLimit) { + CRM_Core_Error::deprecatedFunctionWarning('skipOrderAndLimit is deprected - call getSearchSQLParts & construct it in the calling function'); + $query = "{$sqlParts['select']} {$sqlParts['from']} {$sqlParts['where']} {$sqlParts['having']} {$sqlParts['group_by']}"; + } + else { + $query = "{$sqlParts['select']} {$sqlParts['from']} {$sqlParts['where']} {$sqlParts['having']} {$sqlParts['group_by']} {$sqlParts['order_by']} {$sqlParts['limit']}"; + } + return $query; + } + + /** + * Get the component parts of the search query as an array. + * + * @param int $offset + * The offset for the query. + * @param int $rowCount + * The number of rows to return. + * @param string|CRM_Utils_Sort $sort + * The order by string. + * @param bool $count + * Is this a count only query ?. + * @param bool $includeContactIds + * Should we include contact ids?. + * @param bool $sortByChar + * If true returns the distinct array of first characters for search results. + * @param bool $groupContacts + * If true, return only the contact ids. + * @param string $additionalWhereClause + * If the caller wants to further restrict the search (used for components). + * @param null $sortOrder + * @param string $additionalFromClause + * Should be clause with proper joins, effective to reduce where clause load. + * + * @return array + */ + public function getSearchSQLParts($offset = 0, $rowCount = 0, $sort = NULL, + $count = FALSE, $includeContactIds = FALSE, + $sortByChar = FALSE, $groupContacts = FALSE, + $additionalWhereClause = NULL, $sortOrder = NULL, + $additionalFromClause = NULL) { + if ($includeContactIds) { + $this->_includeContactIds = TRUE; + $this->_whereClause = $this->whereClause(); + } + $onlyDeleted = in_array([ + 'deleted_contacts', + '=', + '1', + '0', + '0', + ], $this->_params); + + // if we’re explicitly looking for a certain contact’s contribs, events, etc. + // and that contact happens to be deleted, set $onlyDeleted to true + foreach ($this->_params as $values) { + $name = CRM_Utils_Array::value(0, $values); + $op = CRM_Utils_Array::value(1, $values); + $value = CRM_Utils_Array::value(2, $values); + if ($name == 'contact_id' and $op == '=') { + if (CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $value, 'is_deleted')) { + $onlyDeleted = TRUE; + } + break; + } + } + + // building the query string + $groupBy = $groupByCols = NULL; + if (!$count) { + if (isset($this->_groupByComponentClause)) { + $groupByCols = preg_replace('/^GROUP BY /', '', trim($this->_groupByComponentClause)); + $groupByCols = explode(', ', $groupByCols); + } + elseif ($this->_useGroupBy) { + $groupByCols = ['contact_a.id']; + } + } + if ($this->_mode & CRM_Contact_BAO_Query::MODE_ACTIVITY && (!$count)) { + $groupByCols = ['civicrm_activity.id']; + } + if (!empty($groupByCols)) { + $groupBy = " GROUP BY " . implode(', ', $groupByCols); + } + + $order = $orderBy = ''; + if (!$count) { + if (!$sortByChar) { + $order = $this->prepareOrderBy($sort, $sortOrder); + } + } + // Cases where we are disabling FGB (FULL_GROUP_BY_MODE): + // 1. When GROUP BY columns are present then disable FGB otherwise it demands to add ORDER BY columns in GROUP BY and eventually in SELECT + // clause. This will impact the search query output. + $disableFullGroupByMode = (!empty($groupBy) || $groupContacts); + + if ($disableFullGroupByMode) { + CRM_Core_DAO::disableFullGroupByMode(); + } + + // CRM-15231 + $this->_sort = $sort; + + //CRM-15967 + $this->includePseudoFieldsJoin($sort); + + list($select, $from, $where, $having) = $this->query($count, $sortByChar, $groupContacts, $onlyDeleted); + + if ($additionalWhereClause) { + $where = $where . ' AND ' . $additionalWhereClause; + } + + //additional from clause should be w/ proper joins. + if ($additionalFromClause) { + $from .= "\n" . $additionalFromClause; + } + + // if we are doing a transform, do it here + // use the $from, $where and $having to get the contact ID + if ($this->_displayRelationshipType) { + $this->filterRelatedContacts($from, $where, $having); + } + $limit = (!$count && $rowCount) ? " LIMIT " . CRM_Utils_Type::escape($offset, 'Int') . ", " . CRM_Utils_Type::escape($rowCount, 'Int') : ''; + + return [ + 'select' => $select, + 'from' => $from, + 'where' => $where, + 'order_by' => $order, + 'group_by' => $groupBy, + 'having' => $having, + 'limit' => $limit, + ]; + } + + /** + * Get the metadata for a given field. + * + * @param string $fieldName + * + * @return array + */ + protected function getMetadataForField($fieldName) { + if ($fieldName === 'contact_a.id') { + // This seems to be the only anomaly. + $fieldName = 'id'; + } + $pseudoField = isset($this->_pseudoConstantsSelect[$fieldName]) ? $this->_pseudoConstantsSelect[$fieldName] : []; + $field = isset($this->_fields[$fieldName]) ? $this->_fields[$fieldName] : $pseudoField; + $field = array_merge($field, $pseudoField); + if (!empty($field) && empty($field['name'])) { + // standardising field formatting here - over time we can phase out variants. + // all paths using this currently unit tested + $field['name'] = CRM_Utils_Array::value('field_name', $field, CRM_Utils_Array::value('idCol', $field, $fieldName)); + } + return $field; + } + + /** + * Get the metadata for a given field, returning the 'real field' if it is a pseudofield. + * + * @param string $fieldName + * + * @return array + */ + protected function getMetadataForRealField($fieldName) { + $field = $this->getMetadataForField($fieldName); + if (!empty($field['is_pseudofield_for'])) { + $field = $this->getMetadataForField($field['is_pseudofield_for']); + $field['pseudofield_name'] = $fieldName; + } + elseif (!empty($field['pseudoconstant'])) { + if (!empty($field['pseudoconstant']['optionGroupName'])) { + $field['pseudofield_name'] = $field['pseudoconstant']['optionGroupName']; + if (empty($field['table_name'])) { + if (!empty($field['where'])) { + $field['table_name'] = explode('.', $field['where'])[0]; + } + else { + $field['table_name'] = 'civicrm_contact'; + } + } + } + } + return $field; + } + + /** + * If we have a field that is better rendered via the pseudoconstant handled them here. + * + * Rather than joining in the additional table we render the option value on output. + * + * @todo - so far this applies to a narrow range of pseudocontants. We are adding them + * carefully with test coverage but aim to extend. + * + * @param string $name + */ + protected function addPseudoconstantFieldToSelect($name) { + $field = $this->getMetadataForRealField($name); + $realFieldName = $field['name']; + $pseudoFieldName = CRM_Utils_Array::value('pseudofield_name', $field); + if ($pseudoFieldName) { + // @todo - we don't really need to build this array now we have metadata more available with getMetadataForField fn. + $this->_pseudoConstantsSelect[$pseudoFieldName] = [ + 'pseudoField' => $pseudoFieldName, + 'idCol' => $realFieldName, + 'field_name' => $field['name'], + 'bao' => $field['bao'], + 'pseudoconstant' => $field['pseudoconstant'], + ]; + } + + $this->_tables[$field['table_name']] = 1; + $this->_element[$realFieldName] = 1; + $this->_select[$field['name']] = str_replace('civicrm_contact.', 'contact_a.', "{$field['where']} as `$realFieldName`"); + } + + /** + * Is this pseudofield a foreign key constraint. + * + * We are trying to cautiously expand our pseudoconstant handling. This check allows us + * to extend to a narrowly defined type (and then only if the pseudofield is in the fields + * array which is done for contributions which are mostly handled as pseudoconstants. + * + * @param $fieldSpec + * + * @return bool + */ + protected function isPseudoFieldAnFK($fieldSpec) { + if (empty($fieldSpec['FKClassName']) + || CRM_Utils_Array::value('keyColumn', $fieldSpec['pseudoconstant']) !== 'id' + || CRM_Utils_Array::value('labelColumn', $fieldSpec['pseudoconstant']) !== 'name') { + return FALSE; + } + return TRUE; + } + + /** + * Is the field a relative date field. + * + * @param string $fieldName + * + * @return bool + */ + protected function isARelativeDateField($fieldName) { + if (substr($fieldName, -9, 9) !== '_relative') { + return FALSE; + } + $realField = substr($fieldName, 0, strlen($fieldName) - 9); + return isset($this->_fields[$realField]); + } + + /** + * @param $values + */ + protected function buildRelativeDateQuery(&$values) { + $value = CRM_Utils_Array::value(2, $values); + if (empty($value)) { + return; + } + $fieldName = substr($values[0], 0, strlen($values[0]) - 9); + $fieldSpec = $this->_fields[$fieldName]; + $tableName = $fieldSpec['table_name']; + $filters = CRM_Core_OptionGroup::values('relative_date_filters'); + $grouping = CRM_Utils_Array::value(3, $values); + $this->_tables[$tableName] = $this->_whereTables[$tableName] = 1; + + $dates = CRM_Utils_Date::getFromTo($value, NULL, NULL); + if (empty($dates[0])) { + // ie. no start date we only have end date + $this->_where[$grouping][] = $fieldSpec['where'] . " <= '{$dates[1]}'"; + + $this->_qill[$grouping][] = ts('%1 is ', [$fieldSpec['title']]) . $filters[$value] . ' (' . ts("to %1", [ + CRM_Utils_Date::customFormat($dates[1]), + ]) . ')'; + } + elseif (empty($dates[1])) { + // ie. no end date we only have start date + $this->_where[$grouping][] = $fieldSpec['where'] . " >= '{$dates[1]}'"; + + $this->_qill[$grouping][] = ts('%1 is ', [$fieldSpec['title']]) . $filters[$value] . ' (' . ts("from %1", [ + CRM_Utils_Date::customFormat($dates[0]), + ]) . ')'; + } + else { + // we have start and end dates. + $this->_where[$grouping][] = $fieldSpec['where'] . " BETWEEN '{$dates[0]}' AND '{$dates[1]}'"; + + $this->_qill[$grouping][] = ts('%1 is ', [$fieldSpec['title']]) . $filters[$value] . ' (' . ts("between %1 and %2", [ + CRM_Utils_Date::customFormat($dates[0]), + CRM_Utils_Date::customFormat($dates[1]), + ]) . ')'; + } + } + + /** + * Add the address table into the query. + * + * @param string $tableKey + * @param string $joinCondition + * + * @return array + * - alias name + * - address join. + */ + protected function addAddressTable($tableKey, $joinCondition) { + $tName = "$tableKey-address"; + $aName = "`$tableKey-address`"; + $this->_select["{$tName}_id"] = "`$tName`.id as `{$tName}_id`"; + $this->_element["{$tName}_id"] = 1; + $addressJoin = "\nLEFT JOIN civicrm_address $aName ON ($aName.contact_id = contact_a.id AND $aName.$joinCondition)"; + $this->_tables[$tName] = $addressJoin; + + return [ + $aName, + $addressJoin, + ]; } } diff --git a/CRM/Contact/BAO/Query/Hook.php b/CRM/Contact/BAO/Query/Hook.php index 3cb31b1c391b..4b020772be1e 100644 --- a/CRM/Contact/BAO/Query/Hook.php +++ b/CRM/Contact/BAO/Query/Hook.php @@ -1,9 +1,9 @@ _queryObjects === NULL) { - $this->_queryObjects = array(); + $this->_queryObjects = []; CRM_Utils_Hook::queryObjects($this->_queryObjects, 'Contact'); } return $this->_queryObjects; @@ -72,7 +72,7 @@ public function getSearchQueryObjects() { * @return array */ public function &getFields() { - $extFields = array(); + $extFields = []; foreach (self::getSearchQueryObjects() as $obj) { $flds = $obj->getFields(); $extFields = array_merge($extFields, $flds); diff --git a/CRM/Contact/BAO/Query/Interface.php b/CRM/Contact/BAO/Query/Interface.php index 293645aa8e36..17e3202d7a98 100644 --- a/CRM/Contact/BAO/Query/Interface.php +++ b/CRM/Contact/BAO/Query/Interface.php @@ -1,9 +1,9 @@ copyValues($params); @@ -308,7 +316,7 @@ public static function add(&$params, $ids = array(), $contactId = NULL) { $relationship->contact_id_b = $params['contact_id_b']; $relationship->contact_id_a = $params['contact_id_a']; $relationship->relationship_type_id = $type; - $relationship->id = $relationshipId; + $relationship->id = $params['id']; $dateFields = array('end_date', 'start_date'); @@ -324,7 +332,7 @@ public static function add(&$params, $ids = array(), $contactId = NULL) { $relationship->$defaultField = $params[$defaultField]; } } - elseif (!$relationshipId) { + elseif (empty($params['id'])) { $relationship->$defaultField = $defaultValue; } } @@ -468,8 +476,8 @@ public static function setContactABFromIDs($params, $ids = array(), $contactID = public static function getdefaults() { return array( 'is_active' => 0, - 'is_permission_a_b' => 0, - 'is_permission_b_a' => 0, + 'is_permission_a_b' => self::NONE, + 'is_permission_b_a' => self::NONE, 'description' => '', 'start_date' => 'NULL', 'case_id' => NULL, @@ -477,7 +485,6 @@ public static function getdefaults() { ); } - /** * Check if there is data to create the object. * @@ -509,7 +516,7 @@ public static function dataExists(&$params) { * @param string $column * Name/label that going to retrieve from db. * @param bool $biDirectional - * @param string $contactSubType + * @param array $contactSubType * Includes relationship types between this subtype. * @param bool $onlySubTypeRelationTypes * If set only subtype which is passed by $contactSubType @@ -539,7 +546,7 @@ public static function getContactRelationshipType( $relationship->id = $relationshipId; if ($relationship->find(TRUE)) { $contact = new CRM_Contact_DAO_Contact(); - $contact->id = ($relationship->contact_id_a === $contactId) ? $relationship->contact_id_b : $relationship->contact_id_a; + $contact->id = ($relationship->contact_id_a == $contactId) ? $relationship->contact_id_b : $relationship->contact_id_a; if ($contact->find(TRUE)) { $otherContactType = $contact->contact_type; @@ -551,7 +558,7 @@ public static function getContactRelationshipType( } } - $contactSubType = array(); + $contactSubType = (array) $contactSubType; if ($contactId) { $contactType = CRM_Contact_BAO_Contact::getContactType($contactId); $contactSubType = CRM_Contact_BAO_Contact::getContactSubType($contactId); @@ -654,7 +661,7 @@ public static function clearCurrentEmployer($id, $action) { $relTypes = CRM_Utils_Array::index(array('name_a_b'), CRM_Core_PseudoConstant::relationshipType('name')); if ( (isset($relTypes['Employee of']) && $relationship->relationship_type_id == $relTypes['Employee of']['id']) || - (isset ($relTypes['Household Member of']) && $relationship->relationship_type_id == $relTypes['Household Member of']['id']) + (isset($relTypes['Household Member of']) && $relationship->relationship_type_id == $relTypes['Household Member of']['id']) ) { $sharedContact = new CRM_Contact_DAO_Contact(); $sharedContact->id = $relationship->contact_id_a; @@ -1111,6 +1118,7 @@ public static function makeURLClause($contactId, $status, $numRelationship, $cou civicrm_country.name as country, civicrm_email.email as email, civicrm_contact.contact_type as contact_type, + civicrm_contact.contact_sub_type as contact_sub_type, civicrm_phone.phone as phone, civicrm_contact.id as civicrm_contact_id, civicrm_relationship.contact_id_b as contact_id_b, @@ -1144,13 +1152,17 @@ public static function makeURLClause($contactId, $status, $numRelationship, $cou else { $from .= 'ON ( civicrm_contact.id = civicrm_relationship.contact_id_b ) '; } - $from .= " + + if (!$count) { + $from .= " LEFT JOIN civicrm_address ON (civicrm_address.contact_id = civicrm_contact.id AND civicrm_address.is_primary = 1) LEFT JOIN civicrm_phone ON (civicrm_phone.contact_id = civicrm_contact.id AND civicrm_phone.is_primary = 1) LEFT JOIN civicrm_email ON (civicrm_email.contact_id = civicrm_contact.id AND civicrm_email.is_primary = 1) LEFT JOIN civicrm_state_province ON (civicrm_address.state_province_id = civicrm_state_province.id) LEFT JOIN civicrm_country ON (civicrm_address.country_id = civicrm_country.id) "; + } + $where = 'WHERE ( 1 )'; if ($contactId) { if ($direction == 'a_b') { @@ -1228,6 +1240,8 @@ public static function makeURLClause($contactId, $status, $numRelationship, $cou * @param bool $permissionedContact * to return only permissioned Contact * @param array $params + * @param bool $includeTotalCount + * Should we return a count of total accessable relationships * * @return array|int * relationship records @@ -1238,7 +1252,7 @@ public static function getRelationship( $count = 0, $relationshipId = 0, $links = NULL, $permissionMask = NULL, $permissionedContact = FALSE, - $params = array() + $params = array(), $includeTotalCount = FALSE ) { $values = array(); if (!$contactId && !$relationshipId) { @@ -1272,11 +1286,11 @@ public static function getRelationship( } // building the query string - $queryString = $select1 . $from1 . $where1 . $select2 . $from2 . $where2 . $order . $limit; + $queryString = $select1 . $from1 . $where1 . $select2 . $from2 . $where2; $relationship = new CRM_Contact_DAO_Relationship(); - $relationship->query($queryString); + $relationship->query($queryString . $order . $limit); $row = array(); if ($count) { $relationshipCount = 0; @@ -1287,6 +1301,10 @@ public static function getRelationship( } else { + if ($includeTotalCount) { + $values['total_relationships'] = CRM_Core_DAO::singleValueQuery("SELECT count(*) FROM ({$queryString}) AS r"); + } + $mask = NULL; if ($status != self::INACTIVE) { if ($links) { @@ -1334,6 +1352,7 @@ public static function getRelationship( $values[$rid]['contact_id_a'] = $relationship->contact_id_a; $values[$rid]['contact_id_b'] = $relationship->contact_id_b; $values[$rid]['contact_type'] = $relationship->contact_type; + $values[$rid]['contact_sub_type'] = $relationship->contact_sub_type; $values[$rid]['relationship_type_id'] = $relationship->civicrm_relationship_type_id; $values[$rid]['relation'] = $relationship->relation; $values[$rid]['name'] = $relationship->sort_name; @@ -1414,6 +1433,7 @@ public static function getRelationship( if ($values[$rid]['rtype'] == 'b_a') { $replace['clientid'] = $values[$rid]['cid']; } + $values[$rid]['case'] = ''; } } @@ -1444,12 +1464,12 @@ public static function getRelationship( * @return array * array reference of all relationship types with context to current contact type . */ - static public function getRelationType($targetContactType) { + public static function getRelationType($targetContactType) { $relationshipType = array(); $allRelationshipType = CRM_Core_PseudoConstant::relationshipType(); foreach ($allRelationshipType as $key => $type) { - if ($type['contact_type_b'] == $targetContactType) { + if ($type['contact_type_b'] == $targetContactType || empty($type['contact_type_b'])) { $relationshipType[$key . '_a_b'] = $type['label_a_b']; } } @@ -1701,10 +1721,10 @@ public static function relatedMemberships($contactId, &$params, $ids, $action = $membershipValues['skipStatusCal'] = TRUE; } foreach (array( - 'join_date', - 'start_date', - 'end_date', - ) as $dateField) { + 'join_date', + 'start_date', + 'end_date', + ) as $dateField) { if (!empty($membershipValues[$dateField])) { $membershipValues[$dateField] = CRM_Utils_Date::processDate($membershipValues[$dateField]); } @@ -1722,6 +1742,10 @@ public static function relatedMemberships($contactId, &$params, $ids, $action = //contact before creating new membership record. CRM_Member_BAO_Membership::deleteRelatedMemberships($membershipId, $relatedContactId); } + //skip status calculation for pay later memberships. + if (!empty($membershipValues['status_id']) && $membershipValues['status_id'] == $pendingStatusId) { + $membershipValues['skipStatusCal'] = TRUE; + } // check whether we have some related memberships still available $query = " @@ -2013,7 +2037,6 @@ public static function membershipTypeToRelationshipTypes(&$params, $direction = } } - /** * Wrapper for contact relationship selector. * @@ -2061,20 +2084,15 @@ public static function getContactRelationshipSelector(&$params) { $params['rp'], 0, 0, $links, $mask, $permissionedContacts, - $params + $params, TRUE ); $contactRelationships = array(); - $params['total'] = 0; + $params['total'] = $relationships['total_relationships']; + unset($relationships['total_relationships']); if (!empty($relationships)) { - // FIXME: we cannot directly determine total permissioned relationship, hence re-fire query - $permissionedRelationships = CRM_Contact_BAO_Relationship::getRelationship($params['contact_id'], - $relationshipStatus, - 0, 0, 0, - NULL, NULL, - $permissionedContacts - ); - $params['total'] = count($permissionedRelationships); + + $displayName = CRM_Contact_BAO_Contact::displayName($params['contact_id']); // format params foreach ($relationships as $relationshipId => $values) { @@ -2090,8 +2108,9 @@ public static function getContactRelationshipSelector(&$params) { $relationship['DT_RowAttr']['data-entity'] = 'relationship'; $relationship['DT_RowAttr']['data-id'] = $values['id']; - //Add image icon for related contacts: CRM-14919 - $icon = CRM_Contact_BAO_Contact_Utils::getImage($values['contact_type'], + //Add image icon for related contacts: CRM-14919; CRM-19668 + $contactType = (!empty($values['contact_sub_type'])) ? $values['contact_sub_type'] : $values['contact_type']; + $icon = CRM_Contact_BAO_Contact_Utils::getImage($contactType, FALSE, $values['cid'] ); @@ -2100,29 +2119,49 @@ public static function getContactRelationshipSelector(&$params) { 'civicrm/contact/view', "reset=1&cid={$values['cid']}"); - $relationship['relation'] = CRM_Utils_System::href( + $relationship['relation'] = CRM_Utils_Array::value('case', $values, '') . CRM_Utils_System::href( $values['relation'], 'civicrm/contact/view/rel', "action=view&reset=1&cid={$values['cid']}&id={$values['id']}&rtype={$values['rtype']}"); - if ($params['context'] == 'current') { - if (($params['contact_id'] == $values['contact_id_a'] AND $values['is_permission_a_b'] == 1) OR - ($params['contact_id'] == $values['contact_id_b'] AND $values['is_permission_b_a'] == 1) - ) { - $relationship['sort_name'] .= ' *'; - } - - if (($values['cid'] == $values['contact_id_a'] AND $values['is_permission_a_b'] == 1) OR - ($values['cid'] == $values['contact_id_b'] AND $values['is_permission_b_a'] == 1) - ) { - $relationship['relation'] .= ' *'; - } - } - if (!empty($values['description'])) { $relationship['relation'] .= "

{$values['description']}

"; } + if ($params['context'] == 'current') { + $smarty = CRM_Core_Smarty::singleton(); + + $contactCombos = [ + [ + 'permContact' => $params['contact_id'], + 'permDisplayName' => $displayName, + 'otherContact' => $values['cid'], + 'otherDisplayName' => $values['display_name'], + 'columnKey' => 'sort_name', + ], + [ + 'permContact' => $values['cid'], + 'permDisplayName' => $values['display_name'], + 'otherContact' => $params['contact_id'], + 'otherDisplayName' => $displayName, + 'columnKey' => 'relation', + ], + ]; + + foreach ($contactCombos as $combo) { + foreach ([CRM_Contact_BAO_Relationship::EDIT, CRM_Contact_BAO_Relationship::VIEW] as $permType) { + $smarty->assign('permType', $permType); + if (($combo['permContact'] == $values['contact_id_a'] and $values['is_permission_a_b'] == $permType) + || ($combo['permContact'] == $values['contact_id_b'] and $values['is_permission_b_a'] == $permType) + ) { + $smarty->assign('permDisplayName', $combo['permDisplayName']); + $smarty->assign('otherDisplayName', $combo['otherDisplayName']); + $relationship[$combo['columnKey']] .= $smarty->fetch('CRM/Contact/Page/View/RelationshipPerm.tpl'); + } + } + } + } + $relationship['start_date'] = CRM_Utils_Date::customFormat($values['start_date']); $relationship['end_date'] = CRM_Utils_Date::customFormat($values['end_date']); $relationship['city'] = $values['city']; @@ -2143,4 +2182,107 @@ public static function getContactRelationshipSelector(&$params) { return $relationshipsDT; } + /** + * @inheritdoc + */ + public static function buildOptions($fieldName, $context = NULL, $props = array()) { + if ($fieldName === 'relationship_type_id') { + return self::buildRelationshipTypeOptions($props); + } + + return parent::buildOptions($fieldName, $context, $props); + } + + /** + * Builds a list of options available for relationship types + * + * @param array $params + * - contact_type: Limits by contact type on the "A" side + * - relationship_id: Used to find the value for contact type for "B" side. + * If contact_a matches provided contact_id then type of contact_b will + * be used. Otherwise uses type of contact_a. Must be used with contact_id + * - contact_id: Limits by contact types of this contact on the "A" side + * - is_form: Returns array with keys indexed for use in a quickform + * - relationship_direction: For relationship types with duplicate names + * on both sides, defines which option should be returned, a_b or b_a + * + * @return array + */ + public static function buildRelationshipTypeOptions($params = array()) { + $contactId = CRM_Utils_Array::value('contact_id', $params); + $direction = CRM_Utils_Array::value('relationship_direction', $params, 'a_b'); + $relationshipId = CRM_Utils_Array::value('relationship_id', $params); + $contactType = CRM_Utils_Array::value('contact_type', $params); + $isForm = CRM_Utils_Array::value('is_form', $params); + $showAll = FALSE; + + // getContactRelationshipType will return an empty set if these are not set + if (!$contactId && !$relationshipId && !$contactType) { + $showAll = TRUE; + } + + $labels = self::getContactRelationshipType( + $contactId, + $direction, + $relationshipId, + $contactType, + $showAll, + 'label' + ); + + if ($isForm) { + return $labels; + } + + $names = self::getContactRelationshipType( + $contactId, + $direction, + $relationshipId, + $contactType, + $showAll, + 'name' + ); + + // ensure $names contains only entries in $labels + $names = array_intersect_key($names, $labels); + + $nameToLabels = array_combine($names, $labels); + + return $nameToLabels; + } + + /** + * Process the params from api, form and check if current + * employer should be set or unset. + * + * @param array $params + * @param int $relationshipId + * @param int|NULL $updatedRelTypeID + * + * @return bool + * TRUE if current employer needs to be cleared. + */ + public static function isCurrentEmployerNeedingToBeCleared($params, $relationshipId, $updatedRelTypeID = NULL) { + $existingTypeID = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Relationship', $relationshipId, 'relationship_type_id'); + $existingTypeName = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_RelationshipType', $existingTypeID, 'name_b_a'); + $updatedRelTypeID = $updatedRelTypeID ? $updatedRelTypeID : $existingTypeID; + + if ($existingTypeName !== 'Employer of') { + return FALSE; + } + //Clear employer if relationship is expired. + if (!empty($params['end_date']) && strtotime($params['end_date']) < time()) { + return TRUE; + } + //current employer checkbox is disabled on the form. + //inactive or relationship type(employer of) is updated. + if ((isset($params['is_current_employer']) && empty($params['is_current_employer'])) + || ((isset($params['is_active']) && empty($params['is_active']))) + || $existingTypeID != $updatedRelTypeID) { + return TRUE; + } + + return FALSE; + } + } diff --git a/CRM/Contact/BAO/RelationshipType.php b/CRM/Contact/BAO/RelationshipType.php index 0e3cc3bf89eb..e229a03ea571 100644 --- a/CRM/Contact/BAO/RelationshipType.php +++ b/CRM/Contact/BAO/RelationshipType.php @@ -1,9 +1,9 @@ copyValues($params); + $hook = empty($params['id']) ? 'create' : 'edit'; + CRM_Utils_Hook::pre($hook, 'RelationshipType', CRM_Utils_Array::value('id', $params), $params); - // if label B to A is blank, insert the value label A to B for it - if (!strlen(trim($strName = CRM_Utils_Array::value('name_b_a', $params)))) { - $relationshipType->name_b_a = CRM_Utils_Array::value('name_a_b', $params); - } - if (!strlen(trim($strName = CRM_Utils_Array::value('label_b_a', $params)))) { - $relationshipType->label_b_a = CRM_Utils_Array::value('label_a_b', $params); - } - - $relationshipType->id = CRM_Utils_Array::value('relationshipType', $ids); + $relationshipType->copyValues($params); + $relationshipType->save(); - $result = $relationshipType->save(); + CRM_Utils_Hook::post($hook, 'RelationshipType', $relationshipType->id, $relationshipType); CRM_Core_PseudoConstant::relationshipType('label', TRUE); CRM_Core_PseudoConstant::relationshipType('name', TRUE); CRM_Case_XMLProcessor::flushStaticCaches(); - return $result; + return $relationshipType; } /** @@ -153,10 +142,10 @@ public static function del($relationshipTypeId) { $relationship->delete(); // remove this relationship type from membership types - $mems = civicrm_api3('MembershipType', 'get', array( - 'relationship_type_id' => array('LIKE' => "%{$relationshipTypeId}%"), - 'return' => array('id', 'relationship_type_id', 'relationship_direction'), - )); + $mems = civicrm_api3('MembershipType', 'get', [ + 'relationship_type_id' => ['LIKE' => "%{$relationshipTypeId}%"], + 'return' => ['id', 'relationship_type_id', 'relationship_direction'], + ]); foreach ($mems['values'] as $membershipTypeId => $membershipType) { $pos = array_search($relationshipTypeId, $membershipType['relationship_type_id']); // Api call may have returned false positives but currently the relationship_type_id uses diff --git a/CRM/Contact/BAO/SavedSearch.php b/CRM/Contact/BAO/SavedSearch.php index a635591cee83..049dcaf5170b 100644 --- a/CRM/Contact/BAO/SavedSearch.php +++ b/CRM/Contact/BAO/SavedSearch.php @@ -1,9 +1,9 @@ 'event_date_low', + 'event_end_date_high' => 'event_date_high', + 'participant_register_date_low' => 'participant_date_low', + 'participant_register_date_high' => 'participant_date_high', + 'case_from_start_date_low' => 'case_from_date_low', + 'case_from_start_date_high' => 'case_from_date_high', + 'case_to_end_date_low' => 'case_to_date_low', + 'case_to_end_date_high' => 'case_to_date_high', + ]; + $fv = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_SavedSearch', $id, 'form_values'); $result = NULL; if ($fv) { @@ -99,15 +110,49 @@ public static function getFormValues($id) { $result = unserialize($fv); } - $specialFields = array('contact_type', 'group', 'contact_tags', 'member_membership_type_id', 'member_status_id'); + $specialFields = ['contact_type', 'group', 'contact_tags', 'member_membership_type_id', 'member_status_id']; foreach ($result as $element => $value) { if (CRM_Contact_BAO_Query::isAlreadyProcessedForQueryFormat($value)) { $id = CRM_Utils_Array::value(0, $value); $value = CRM_Utils_Array::value(2, $value); if (is_array($value) && in_array(key($value), CRM_Core_DAO::acceptedSQLOperators(), TRUE)) { - $value = CRM_Utils_Array::value(key($value), $value); + $op = key($value); + $value = CRM_Utils_Array::value($op, $value); + if (in_array($op, ['BETWEEN', '>=', '<='])) { + self::decodeRelativeFields($result, $id, $op, $value); + unset($result[$element]); + continue; + } + } + // Check for a date range field, which might be a standard date + // range or a relative date. + if (strpos($id, '_date_low') !== FALSE || strpos($id, '_date_high') !== FALSE) { + $entityName = strstr($id, '_date', TRUE); + + // This is the default, for non relative dates. We will overwrite + // it if we determine this is a relative date. + $result[$id] = $value; + $result["{$entityName}_date_relative"] = 0; + + if (!empty($result['relative_dates'])) { + if (array_key_exists($entityName, $result['relative_dates'])) { + // We have a match from a regular field. + $result[$id] = NULL; + $result["{$entityName}_date_relative"] = $result['relative_dates'][$entityName]; + } + elseif (!empty($specialDateFields[$id])) { + // We may have a match on a special date field. + $entityName = strstr($specialDateFields[$id], '_date', TRUE); + if (array_key_exists($entityName, $result['relative_dates'])) { + $result[$id] = NULL; + $result["{$entityName}_relative"] = $result['relative_dates'][$entityName]; + } + } + } + } + else { + $result[$id] = $value; } - $result[$id] = $value; unset($result[$element]); continue; } @@ -153,7 +198,7 @@ public static function getFormValues($id) { unset($result['privacy']['do_not_toggle']); } - $result['privacy_options'] = array(); + $result['privacy_options'] = []; foreach ($result['privacy'] as $name => $val) { if ($val) { $result['privacy_options'][] = $name; @@ -164,6 +209,13 @@ public static function getFormValues($id) { } } + if ($customSearchClass = CRM_Utils_Array::value('customSearchClass', $result)) { + // check if there is a special function - formatSavedSearchFields defined in the custom search form + if (method_exists($customSearchClass, 'formatSavedSearchFields')) { + $customSearchClass::formatSavedSearchFields($result); + } + } + return $result; } @@ -227,7 +279,7 @@ public static function contactIDsSQL($id) { return CRM_Contact_BAO_SearchCustom::contactIDSQL(NULL, $id); } else { - $tables = $whereTables = array('civicrm_contact' => 1); + $tables = $whereTables = ['civicrm_contact' => 1]; $where = CRM_Contact_BAO_SavedSearch::whereClause($id, $tables, $whereTables); if (!$where) { $where = '( 1 )'; @@ -255,10 +307,10 @@ public static function fromWhereEmail($id) { return CRM_Contact_BAO_SearchCustom::fromWhereEmail(NULL, $id); } else { - $tables = $whereTables = array('civicrm_contact' => 1, 'civicrm_email' => 1); + $tables = $whereTables = ['civicrm_contact' => 1, 'civicrm_email' => 1]; $where = CRM_Contact_BAO_SavedSearch::whereClause($id, $tables, $whereTables); $from = CRM_Contact_BAO_Query::fromClause($whereTables); - return array($from, $where); + return [$from, $where]; } } else { @@ -270,7 +322,7 @@ public static function fromWhereEmail($id) { $where = " ( 1 ) "; $tables['civicrm_contact'] = $whereTables['civicrm_contact'] = 1; $tables['civicrm_email'] = $whereTables['civicrm_email'] = 1; - return array($from, $where); + return [$from, $where]; } } @@ -288,7 +340,7 @@ public function buildClause() { } if (!empty($params)) { - $tables = $whereTables = array(); + $tables = $whereTables = []; $this->where_clause = CRM_Contact_BAO_Query::getWhereClause($params, NULL, $tables, $whereTables); if (!empty($tables)) { $this->select_tables = serialize($tables); @@ -370,11 +422,111 @@ protected function assignTestValue($fieldName, &$fieldDef, $counter) { if ($fieldName == 'form_values') { // A dummy value for form_values. $this->{$fieldName} = serialize( - array('sort_name' => "SortName{$counter}")); + ['sort_name' => "SortName{$counter}"]); } else { parent::assignTestValues($fieldName, $fieldDef, $counter); } } + /** + * Store relative dates in separate array format + * + * @param array $queryParams + * @param array $formValues + */ + public static function saveRelativeDates(&$queryParams, $formValues) { + $relativeDates = ['relative_dates' => []]; + $specialDateFields = ['event_relative', 'case_from_relative', 'case_to_relative', 'participant_relative']; + foreach ($formValues as $id => $value) { + if ((preg_match('/(_date|custom_[0-9]+)_relative$/', $id) || in_array($id, $specialDateFields)) && !empty($value)) { + $entityName = strstr($id, '_date', TRUE); + if (empty($entityName)) { + $entityName = strstr($id, '_relative', TRUE); + } + $relativeDates['relative_dates'][$entityName] = $value; + } + } + // merge with original queryParams if relative date value(s) found + if (count($relativeDates['relative_dates'])) { + $queryParams = array_merge($queryParams, $relativeDates); + } + } + + /** + * Store search variables in $queryParams which were skipped while processing query params, + * precisely at CRM_Contact_BAO_Query::fixWhereValues(...). But these variable are required in + * building smart group criteria otherwise it will cause issues like CRM-18585,CRM-19571 + * + * @param array $queryParams + * @param array $formValues + */ + public static function saveSkippedElement(&$queryParams, $formValues) { + // these are elements which are skipped in a smart group criteria + $specialElements = [ + 'operator', + 'component_mode', + 'display_relationship_type', + 'uf_group_id', + ]; + foreach ($specialElements as $element) { + if (!empty($formValues[$element])) { + $queryParams[] = [$element, '=', $formValues[$element], 0, 0]; + } + } + } + + /** + * Decode relative custom fields (converted by CRM_Contact_BAO_Query->convertCustomRelativeFields(...)) + * into desired formValues + * + * @param array $formValues + * @param string $fieldName + * @param string $op + * @param array|string|int $value + */ + public static function decodeRelativeFields(&$formValues, $fieldName, $op, $value) { + // check if its a custom date field, if yes then 'searchDate' format the value + $isCustomDateField = CRM_Contact_BAO_Query::isCustomDateField($fieldName); + + // select date range as default + if ($isCustomDateField) { + if (array_key_exists('relative_dates', $formValues) && array_key_exists($fieldName, $formValues['relative_dates'])) { + $formValues[$fieldName . '_relative'] = $formValues['relative_dates'][$fieldName]; + } + else { + $formValues[$fieldName . '_relative'] = 0; + } + } + switch ($op) { + case 'BETWEEN': + if ($isCustomDateField) { + list($formValues[$fieldName . '_from'], $formValues[$fieldName . '_from_time']) = CRM_Utils_Date::setDateDefaults($value[0], 'searchDate'); + list($formValues[$fieldName . '_to'], $formValues[$fieldName . '_to_time']) = CRM_Utils_Date::setDateDefaults($value[1], 'searchDate'); + } + else { + list($formValues[$fieldName . '_from'], $formValues[$fieldName . '_to']) = $value; + } + break; + + case '>=': + if ($isCustomDateField) { + list($formValues[$fieldName . '_from'], $formValues[$fieldName . '_from_time']) = CRM_Utils_Date::setDateDefaults($value, 'searchDate'); + } + else { + $formValues[$fieldName . '_from'] = $value; + } + break; + + case '<=': + if ($isCustomDateField) { + list($formValues[$fieldName . '_to'], $formValues[$fieldName . '_to_time']) = CRM_Utils_Date::setDateDefaults($value, 'searchDate'); + } + else { + $formValues[$fieldName . '_to'] = $value; + } + break; + } + } + } diff --git a/CRM/Contact/BAO/SearchCustom.php b/CRM/Contact/BAO/SearchCustom.php index 263f648a0917..cadd086e4c40 100644 --- a/CRM/Contact/BAO/SearchCustom.php +++ b/CRM/Contact/BAO/SearchCustom.php @@ -1,9 +1,9 @@ 'custom_search', + 'return' => 'name', + 'value' => $customSearchID, + ]); $ext = CRM_Extension_System::singleton()->getMapper(); @@ -96,7 +95,7 @@ public static function details($csID, $ssID = NULL, $gID = NULL) { CRM_Core_Error::fatal('Custom search file: ' . $customSearchFile . ' does not exist. Please verify your custom search settings in CiviCRM administrative panel.'); } - return array($customSearchID, $customSearchClass, $formValues); + return [$customSearchID, $customSearchClass, $formValues]; } /** @@ -139,7 +138,7 @@ public static function &buildFormValues($args) { $args = trim($args); $values = explode("\n", $args); - $formValues = array(); + $formValues = []; foreach ($values as $value) { list($n, $v) = CRM_Utils_System::explode('=', $value, 2); if (!empty($v)) { @@ -161,7 +160,7 @@ public static function fromWhereEmail($csID, $ssID) { $from = $customClass->from(); $where = $customClass->where(); - return array($from, $where); + return [$from, $where]; } } diff --git a/CRM/Contact/BAO/SubscriptionHistory.php b/CRM/Contact/BAO/SubscriptionHistory.php index 3fbb10bf7861..ec1a44d18991 100644 --- a/CRM/Contact/BAO/SubscriptionHistory.php +++ b/CRM/Contact/BAO/SubscriptionHistory.php @@ -1,9 +1,9 @@ __table = 'civicrm_acl_contact_cache'; parent::__construct(); } + /** - * Returns foreign keys and entity references + * Returns foreign keys and entity references. * * @return array * [CRM_Core_Reference_Interface] */ - static function getReferenceColumns() { + public static function getReferenceColumns() { if (!isset(Civi::$statics[__CLASS__]['links'])) { - Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'user_id', 'civicrm_contact', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'contact_id', 'civicrm_contact', 'id'); + Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'contact_id', 'civicrm_contact', 'id'); CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']); } return Civi::$statics[__CLASS__]['links']; } + /** * Returns all the column names of this table * * @return array */ - static function &fields() { + public static function &fields() { if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = array( - 'id' => array( + Civi::$statics[__CLASS__]['fields'] = [ + 'id' => [ 'name' => 'id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('ACL Contact Cache ID') , - 'description' => 'primary key', - 'required' => true, - ) , - 'user_id' => array( + 'title' => ts('ACL Contact Cache ID'), + 'description' => ts('primary key'), + 'required' => TRUE, + 'table_name' => 'civicrm_acl_contact_cache', + 'entity' => 'ACLContactCache', + 'bao' => 'CRM_Contact_DAO_ACLContactCache', + 'localizable' => 0, + ], + 'user_id' => [ 'name' => 'user_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Contact ID') , - 'description' => 'FK to civicrm_contact (could be null for anon user)', - 'FKClassName' => 'CRM_Contact_DAO_Contact', - ) , - 'contact_id' => array( + 'title' => ts('Contact ID'), + 'description' => ts('FK to civicrm_contact (could be null for anon user)'), + 'table_name' => 'civicrm_acl_contact_cache', + 'entity' => 'ACLContactCache', + 'bao' => 'CRM_Contact_DAO_ACLContactCache', + 'localizable' => 0, + ], + 'contact_id' => [ 'name' => 'contact_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Contact ID') , - 'description' => 'FK to civicrm_contact', - 'required' => true, + 'title' => ts('Contact ID'), + 'description' => ts('FK to civicrm_contact'), + 'required' => TRUE, + 'table_name' => 'civicrm_acl_contact_cache', + 'entity' => 'ACLContactCache', + 'bao' => 'CRM_Contact_DAO_ACLContactCache', + 'localizable' => 0, 'FKClassName' => 'CRM_Contact_DAO_Contact', - ) , - 'operation' => array( + ], + 'operation' => [ 'name' => 'operation', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Operation') , - 'description' => 'What operation does this user have permission on?', - 'required' => true, + 'title' => ts('Operation'), + 'description' => ts('What operation does this user have permission on?'), + 'required' => TRUE, 'maxlength' => 8, 'size' => CRM_Utils_Type::EIGHT, - 'html' => array( + 'table_name' => 'civicrm_acl_contact_cache', + 'entity' => 'ACLContactCache', + 'bao' => 'CRM_Contact_DAO_ACLContactCache', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'callback' => 'CRM_ACL_BAO_ACL::operation', - ) - ) , - ); + ] + ], + ]; CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } return Civi::$statics[__CLASS__]['fields']; } + /** * Return a mapping from field-name to the corresponding key (as used in fields()). * * @return array * Array(string $name => string $uniqueName). */ - static function &fieldKeys() { + public static function &fieldKeys() { if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); } return Civi::$statics[__CLASS__]['fieldKeys']; } + /** * Returns the names of this table * * @return string */ - static function getTableName() { + public static function getTableName() { return self::$_tableName; } + /** * Returns if this table needs to be logged * - * @return boolean + * @return bool */ - function getLog() { + public function getLog() { return self::$_log; } + /** * Returns the list of fields that can be imported * @@ -181,10 +183,11 @@ function getLog() { * * @return array */ - static function &import($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'acl_contact_cache', $prefix, array()); + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'acl_contact_cache', $prefix, []); return $r; } + /** * Returns the list of fields that can be exported * @@ -192,8 +195,33 @@ static function &import($prefix = false) { * * @return array */ - static function &export($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'acl_contact_cache', $prefix, array()); + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'acl_contact_cache', $prefix, []); return $r; } + + /** + * Returns the list of indices + * + * @param bool $localize + * + * @return array + */ + public static function indices($localize = TRUE) { + $indices = [ + 'UI_user_contact_operation' => [ + 'name' => 'UI_user_contact_operation', + 'field' => [ + 0 => 'user_id', + 1 => 'contact_id', + 2 => 'operation', + ], + 'localizable' => FALSE, + 'unique' => TRUE, + 'sig' => 'civicrm_acl_contact_cache::1::user_id::contact_id::operation', + ], + ]; + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + } diff --git a/CRM/Contact/DAO/Contact.php b/CRM/Contact/DAO/Contact.php index b82bc3733089..c5833d27b865 100644 --- a/CRM/Contact/DAO/Contact.php +++ b/CRM/Contact/DAO/Contact.php @@ -1,1184 +1,1457 @@ __table = 'civicrm_contact'; parent::__construct(); } + /** - * Returns foreign keys and entity references + * Returns foreign keys and entity references. * * @return array * [CRM_Core_Reference_Interface] */ - static function getReferenceColumns() { + public static function getReferenceColumns() { if (!isset(Civi::$statics[__CLASS__]['links'])) { - Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'primary_contact_id', 'civicrm_contact', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'employer_id', 'civicrm_contact', 'id'); + Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'primary_contact_id', 'civicrm_contact', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'employer_id', 'civicrm_contact', 'id'); CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']); } return Civi::$statics[__CLASS__]['links']; } + /** * Returns all the column names of this table * * @return array */ - static function &fields() { + public static function &fields() { if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = array( - 'id' => array( + Civi::$statics[__CLASS__]['fields'] = [ + 'id' => [ 'name' => 'id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Contact ID') , - 'description' => 'Unique Contact ID', - 'required' => true, - 'import' => true, + 'title' => ts('Contact ID'), + 'description' => ts('Unique Contact ID'), + 'required' => TRUE, + 'import' => TRUE, 'where' => 'civicrm_contact.id', 'headerPattern' => '/internal|contact?|id$/i', 'dataPattern' => '', - 'export' => true, - ) , - 'contact_type' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + ], + 'contact_type' => [ 'name' => 'contact_type', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Contact Type') , - 'description' => 'Type of Contact.', + 'title' => ts('Contact Type'), + 'description' => ts('Type of Contact.'), 'maxlength' => 64, 'size' => CRM_Utils_Type::BIG, - 'export' => true, + 'export' => TRUE, 'where' => 'civicrm_contact.contact_type', 'headerPattern' => '', 'dataPattern' => '', - 'html' => array( + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'table' => 'civicrm_contact_type', 'keyColumn' => 'name', 'labelColumn' => 'label', 'condition' => 'parent_id IS NULL', - ) - ) , - 'contact_sub_type' => array( + ] + ], + 'contact_sub_type' => [ 'name' => 'contact_sub_type', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Contact Subtype') , - 'description' => 'May be used to over-ride contact view and edit templates.', + 'title' => ts('Contact Subtype'), + 'description' => ts('May be used to over-ride contact view and edit templates.'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - 'import' => true, + 'import' => TRUE, 'where' => 'civicrm_contact.contact_sub_type', 'headerPattern' => '/C(ontact )?(subtype|sub-type|sub type)/i', 'dataPattern' => '', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'serialize' => self::SERIALIZE_SEPARATOR_BOOKEND, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'table' => 'civicrm_contact_type', 'keyColumn' => 'name', 'labelColumn' => 'label', 'condition' => 'parent_id IS NOT NULL', - ) - ) , - 'do_not_email' => array( + ] + ], + 'do_not_email' => [ 'name' => 'do_not_email', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Do Not Email') , - 'import' => true, + 'title' => ts('Do Not Email'), + 'import' => TRUE, 'where' => 'civicrm_contact.do_not_email', 'headerPattern' => '/d(o )?(not )?(email)/i', 'dataPattern' => '/^\d{1,}$/', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'default' => '0', + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'CheckBox', - ) , - ) , - 'do_not_phone' => array( + ], + ], + 'do_not_phone' => [ 'name' => 'do_not_phone', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Do Not Phone') , - 'import' => true, + 'title' => ts('Do Not Phone'), + 'import' => TRUE, 'where' => 'civicrm_contact.do_not_phone', 'headerPattern' => '/d(o )?(not )?(call|phone)/i', 'dataPattern' => '/^\d{1,}$/', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'default' => '0', + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'CheckBox', - ) , - ) , - 'do_not_mail' => array( + ], + ], + 'do_not_mail' => [ 'name' => 'do_not_mail', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Do Not Mail') , - 'import' => true, + 'title' => ts('Do Not Mail'), + 'import' => TRUE, 'where' => 'civicrm_contact.do_not_mail', 'headerPattern' => '/^(d(o\s)?n(ot\s)?mail)|(\w*)?bulk\s?(\w*)$/i', 'dataPattern' => '/^\d{1,}$/', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'default' => '0', + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'CheckBox', - ) , - ) , - 'do_not_sms' => array( + ], + ], + 'do_not_sms' => [ 'name' => 'do_not_sms', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Do Not Sms') , - 'import' => true, + 'title' => ts('Do Not Sms'), + 'import' => TRUE, 'where' => 'civicrm_contact.do_not_sms', 'headerPattern' => '/d(o )?(not )?(sms)/i', 'dataPattern' => '/^\d{1,}$/', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'default' => '0', + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'CheckBox', - ) , - ) , - 'do_not_trade' => array( + ], + ], + 'do_not_trade' => [ 'name' => 'do_not_trade', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Do Not Trade') , - 'import' => true, + 'title' => ts('Do Not Trade'), + 'import' => TRUE, 'where' => 'civicrm_contact.do_not_trade', 'headerPattern' => '/d(o )?(not )?(trade)/i', 'dataPattern' => '/^\d{1,}$/', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'default' => '0', + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'CheckBox', - ) , - ) , - 'is_opt_out' => array( + ], + ], + 'is_opt_out' => [ 'name' => 'is_opt_out', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('No Bulk Emails (User Opt Out)') , - 'description' => 'Has the contact opted out from receiving all bulk email from the organization or site domain?', - 'required' => true, - 'import' => true, + 'title' => ts('No Bulk Emails (User Opt Out)'), + 'description' => ts('Has the contact opted out from receiving all bulk email from the organization or site domain?'), + 'required' => TRUE, + 'import' => TRUE, 'where' => 'civicrm_contact.is_opt_out', 'headerPattern' => '', 'dataPattern' => '', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'default' => '0', + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'CheckBox', - ) , - ) , - 'legal_identifier' => array( + ], + ], + 'legal_identifier' => [ 'name' => 'legal_identifier', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Legal Identifier') , - 'description' => 'May be used for SSN, EIN/TIN, Household ID (census) or other applicable unique legal/government ID. - ', + 'title' => ts('Legal Identifier'), + 'description' => ts('May be used for SSN, EIN/TIN, Household ID (census) or other applicable unique legal/government ID. + '), 'maxlength' => 32, 'size' => CRM_Utils_Type::MEDIUM, - 'import' => true, + 'import' => TRUE, 'where' => 'civicrm_contact.legal_identifier', 'headerPattern' => '/legal\s?id/i', 'dataPattern' => '/\w+?\d{5,}/', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'external_identifier' => array( + ], + ], + 'external_identifier' => [ 'name' => 'external_identifier', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('External Identifier') , - 'description' => 'Unique trusted external ID (generally from a legacy app/datasource). Particularly useful for deduping operations.', + 'title' => ts('External Identifier'), + 'description' => ts('Unique trusted external ID (generally from a legacy app/datasource). Particularly useful for deduping operations.'), 'maxlength' => 64, 'size' => 8, - 'import' => true, + 'import' => TRUE, 'where' => 'civicrm_contact.external_identifier', 'headerPattern' => '/external\s?id/i', 'dataPattern' => '/^\d{11,}$/', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'sort_name' => array( + ], + ], + 'sort_name' => [ 'name' => 'sort_name', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Sort Name') , - 'description' => 'Name used for sorting different contact types', + 'title' => ts('Sort Name'), + 'description' => ts('Name used for sorting different contact types'), 'maxlength' => 128, 'size' => 30, - 'export' => true, + 'export' => TRUE, 'where' => 'civicrm_contact.sort_name', 'headerPattern' => '', 'dataPattern' => '', - 'html' => array( + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'display_name' => array( + ], + ], + 'display_name' => [ 'name' => 'display_name', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Display Name') , - 'description' => 'Formatted name representing preferred format for display/print/other output.', + 'title' => ts('Display Name'), + 'description' => ts('Formatted name representing preferred format for display/print/other output.'), 'maxlength' => 128, 'size' => 30, - 'export' => true, + 'export' => TRUE, 'where' => 'civicrm_contact.display_name', 'headerPattern' => '', 'dataPattern' => '', - 'html' => array( + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'nick_name' => array( + ], + ], + 'nick_name' => [ 'name' => 'nick_name', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Nickname') , - 'description' => 'Nickname.', + 'title' => ts('Nickname'), + 'description' => ts('Nickname.'), 'maxlength' => 128, 'size' => 30, - 'import' => true, + 'import' => TRUE, 'where' => 'civicrm_contact.nick_name', 'headerPattern' => '/n(ick\s)name|nick$/i', 'dataPattern' => '/^\w+$/', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'legal_name' => array( + ], + ], + 'legal_name' => [ 'name' => 'legal_name', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Legal Name') , - 'description' => 'Legal Name.', + 'title' => ts('Legal Name'), + 'description' => ts('Legal Name.'), 'maxlength' => 128, 'size' => 30, - 'import' => true, + 'import' => TRUE, 'where' => 'civicrm_contact.legal_name', 'headerPattern' => '/^legal|(l(egal\s)?name)$/i', 'dataPattern' => '', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'image_URL' => array( + ], + ], + 'image_URL' => [ 'name' => 'image_URL', 'type' => CRM_Utils_Type::T_TEXT, - 'title' => ts('Image Url') , - 'description' => 'optional URL for preferred image (photo, logo, etc.) to display for this contact.', - 'import' => true, + 'title' => ts('Image Url'), + 'description' => ts('optional URL for preferred image (photo, logo, etc.) to display for this contact.'), + 'import' => TRUE, 'where' => 'civicrm_contact.image_URL', 'headerPattern' => '', 'dataPattern' => '', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'File', - ) , - ) , - 'preferred_communication_method' => array( + ], + ], + 'preferred_communication_method' => [ 'name' => 'preferred_communication_method', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Preferred Communication Method') , - 'description' => 'What is the preferred mode of communication.', + 'title' => ts('Preferred Communication Method'), + 'description' => ts('What is the preferred mode of communication.'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - 'import' => true, + 'import' => TRUE, 'where' => 'civicrm_contact.preferred_communication_method', 'headerPattern' => '/^p(ref\w*\s)?c(omm\w*)|( meth\w*)$/i', 'dataPattern' => '/^\w+$/', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'serialize' => self::SERIALIZE_SEPARATOR_BOOKEND, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'optionGroupName' => 'preferred_communication_method', 'optionEditPath' => 'civicrm/admin/options/preferred_communication_method', - ) - ) , - 'preferred_language' => array( + ] + ], + 'preferred_language' => [ 'name' => 'preferred_language', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Preferred Language') , - 'description' => 'Which language is preferred for communication. FK to languages in civicrm_option_value.', + 'title' => ts('Preferred Language'), + 'description' => ts('Which language is preferred for communication. FK to languages in civicrm_option_value.'), 'maxlength' => 5, 'size' => CRM_Utils_Type::SIX, - 'import' => true, + 'import' => TRUE, 'where' => 'civicrm_contact.preferred_language', 'headerPattern' => '/^lang/i', 'dataPattern' => '', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'optionGroupName' => 'languages', 'keyColumn' => 'name', 'optionEditPath' => 'civicrm/admin/options/languages', - ) - ) , - 'preferred_mail_format' => array( + ] + ], + 'preferred_mail_format' => [ 'name' => 'preferred_mail_format', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Preferred Mail Format') , - 'description' => 'What is the preferred mode of sending an email.', + 'title' => ts('Preferred Mail Format'), + 'description' => ts('What is the preferred mode of sending an email.'), 'maxlength' => 8, 'size' => CRM_Utils_Type::EIGHT, - 'import' => true, + 'import' => TRUE, 'where' => 'civicrm_contact.preferred_mail_format', 'headerPattern' => '/^p(ref\w*\s)?m(ail\s)?f(orm\w*)$/i', 'dataPattern' => '', - 'export' => true, + 'export' => TRUE, 'default' => 'Both', - 'html' => array( + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'callback' => 'CRM_Core_SelectValues::pmf', - ) - ) , - 'hash' => array( + ] + ], + 'hash' => [ 'name' => 'hash', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Contact Hash') , - 'description' => 'Key for validating requests related to this contact.', + 'title' => ts('Contact Hash'), + 'description' => ts('Key for validating requests related to this contact.'), 'maxlength' => 32, 'size' => CRM_Utils_Type::MEDIUM, - 'export' => true, + 'export' => TRUE, 'where' => 'civicrm_contact.hash', 'headerPattern' => '', 'dataPattern' => '', - ) , - 'api_key' => array( + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + ], + 'api_key' => [ 'name' => 'api_key', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Api Key') , - 'description' => 'API Key for validating requests related to this contact.', + 'title' => ts('Api Key'), + 'description' => ts('API Key for validating requests related to this contact.'), 'maxlength' => 32, 'size' => CRM_Utils_Type::MEDIUM, - ) , - 'contact_source' => array( + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + ], + 'contact_source' => [ 'name' => 'source', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Contact Source') , - 'description' => 'where contact come from, e.g. import, donate module insert...', + 'title' => ts('Contact Source'), + 'description' => ts('where contact come from, e.g. import, donate module insert...'), 'maxlength' => 255, 'size' => 30, - 'import' => true, + 'import' => TRUE, 'where' => 'civicrm_contact.source', 'headerPattern' => '/(C(ontact\s)?Source)$/i', 'dataPattern' => '', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'first_name' => array( + ], + ], + 'first_name' => [ 'name' => 'first_name', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('First Name') , - 'description' => 'First Name.', + 'title' => ts('First Name'), + 'description' => ts('First Name.'), 'maxlength' => 64, 'size' => 30, - 'import' => true, + 'import' => TRUE, 'where' => 'civicrm_contact.first_name', 'headerPattern' => '/^first|(f(irst\s)?name)$/i', 'dataPattern' => '/^\w+$/', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'middle_name' => array( + ], + ], + 'middle_name' => [ 'name' => 'middle_name', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Middle Name') , - 'description' => 'Middle Name.', + 'title' => ts('Middle Name'), + 'description' => ts('Middle Name.'), 'maxlength' => 64, 'size' => 30, - 'import' => true, + 'import' => TRUE, 'where' => 'civicrm_contact.middle_name', 'headerPattern' => '/^middle|(m(iddle\s)?name)$/i', 'dataPattern' => '/^\w+$/', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'last_name' => array( + ], + ], + 'last_name' => [ 'name' => 'last_name', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Last Name') , - 'description' => 'Last Name.', + 'title' => ts('Last Name'), + 'description' => ts('Last Name.'), 'maxlength' => 64, 'size' => 30, - 'import' => true, + 'import' => TRUE, 'where' => 'civicrm_contact.last_name', 'headerPattern' => '/^last|(l(ast\s)?name)$/i', 'dataPattern' => '/^\w+(\s\w+)?+$/', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'prefix_id' => array( + ], + ], + 'prefix_id' => [ 'name' => 'prefix_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Individual Prefix') , - 'description' => 'Prefix or Title for name (Ms, Mr...). FK to prefix ID', - 'import' => true, + 'title' => ts('Individual Prefix'), + 'description' => ts('Prefix or Title for name (Ms, Mr...). FK to prefix ID'), + 'import' => TRUE, 'where' => 'civicrm_contact.prefix_id', 'headerPattern' => '/^(prefix|title)/i', 'dataPattern' => '/^(mr|ms|mrs|sir|dr)\.?$/i', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'optionGroupName' => 'individual_prefix', 'optionEditPath' => 'civicrm/admin/options/individual_prefix', - ) - ) , - 'suffix_id' => array( + ] + ], + 'suffix_id' => [ 'name' => 'suffix_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Individual Suffix') , - 'description' => 'Suffix for name (Jr, Sr...). FK to suffix ID', - 'import' => true, + 'title' => ts('Individual Suffix'), + 'description' => ts('Suffix for name (Jr, Sr...). FK to suffix ID'), + 'import' => TRUE, 'where' => 'civicrm_contact.suffix_id', 'headerPattern' => '/^suffix$/i', 'dataPattern' => '/^(sr|jr)\.?|i{2,}$/', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'optionGroupName' => 'individual_suffix', 'optionEditPath' => 'civicrm/admin/options/individual_suffix', - ) - ) , - 'formal_title' => array( + ] + ], + 'formal_title' => [ 'name' => 'formal_title', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Formal Title') , - 'description' => 'Formal (academic or similar) title in front of name. (Prof., Dr. etc.)', + 'title' => ts('Formal Title'), + 'description' => ts('Formal (academic or similar) title in front of name. (Prof., Dr. etc.)'), 'maxlength' => 64, 'size' => CRM_Utils_Type::BIG, - 'import' => true, + 'import' => TRUE, 'where' => 'civicrm_contact.formal_title', 'headerPattern' => '/^title/i', 'dataPattern' => '', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'communication_style_id' => array( + ], + ], + 'communication_style_id' => [ 'name' => 'communication_style_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Communication Style') , - 'description' => 'Communication style (e.g. formal vs. familiar) to use with this contact. FK to communication styles in civicrm_option_value.', - 'export' => true, + 'title' => ts('Communication Style'), + 'description' => ts('Communication style (e.g. formal vs. familiar) to use with this contact. FK to communication styles in civicrm_option_value.'), + 'export' => TRUE, 'where' => 'civicrm_contact.communication_style_id', 'headerPattern' => '', 'dataPattern' => '', - 'html' => array( + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'optionGroupName' => 'communication_style', 'optionEditPath' => 'civicrm/admin/options/communication_style', - ) - ) , - 'email_greeting_id' => array( + ] + ], + 'email_greeting_id' => [ 'name' => 'email_greeting_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Email Greeting ID') , - 'description' => 'FK to civicrm_option_value.id, that has to be valid registered Email Greeting.', - ) , - 'email_greeting_custom' => array( + 'title' => ts('Email Greeting ID'), + 'description' => ts('FK to civicrm_option_value.id, that has to be valid registered Email Greeting.'), + 'export' => TRUE, + 'where' => 'civicrm_contact.email_greeting_id', + 'headerPattern' => '', + 'dataPattern' => '', + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'pseudoconstant' => [ + 'optionGroupName' => 'email_greeting', + 'optionEditPath' => 'civicrm/admin/options/email_greeting', + ] + ], + 'email_greeting_custom' => [ 'name' => 'email_greeting_custom', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Email Greeting Custom') , - 'description' => 'Custom Email Greeting.', + 'title' => ts('Email Greeting Custom'), + 'description' => ts('Custom Email Greeting.'), 'maxlength' => 128, 'size' => CRM_Utils_Type::HUGE, - 'import' => true, + 'import' => TRUE, 'where' => 'civicrm_contact.email_greeting_custom', 'headerPattern' => '', 'dataPattern' => '', - 'export' => false, - 'html' => array( + 'export' => FALSE, + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'email_greeting_display' => array( + ], + ], + 'email_greeting_display' => [ 'name' => 'email_greeting_display', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Email Greeting') , - 'description' => 'Cache Email Greeting.', + 'title' => ts('Email Greeting'), + 'description' => ts('Cache Email Greeting.'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - 'html' => array( + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'postal_greeting_id' => array( + ], + ], + 'postal_greeting_id' => [ 'name' => 'postal_greeting_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Postal Greeting ID') , - 'description' => 'FK to civicrm_option_value.id, that has to be valid registered Postal Greeting.', - 'html' => array( + 'title' => ts('Postal Greeting ID'), + 'description' => ts('FK to civicrm_option_value.id, that has to be valid registered Postal Greeting.'), + 'export' => TRUE, + 'where' => 'civicrm_contact.postal_greeting_id', + 'headerPattern' => '', + 'dataPattern' => '', + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'postal_greeting_custom' => array( + ], + 'pseudoconstant' => [ + 'optionGroupName' => 'postal_greeting', + 'optionEditPath' => 'civicrm/admin/options/postal_greeting', + ] + ], + 'postal_greeting_custom' => [ 'name' => 'postal_greeting_custom', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Postal Greeting Custom') , - 'description' => 'Custom Postal greeting.', + 'title' => ts('Postal Greeting Custom'), + 'description' => ts('Custom Postal greeting.'), 'maxlength' => 128, 'size' => CRM_Utils_Type::HUGE, - 'import' => true, + 'import' => TRUE, 'where' => 'civicrm_contact.postal_greeting_custom', 'headerPattern' => '', 'dataPattern' => '', - 'export' => false, - 'html' => array( + 'export' => FALSE, + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'postal_greeting_display' => array( + ], + ], + 'postal_greeting_display' => [ 'name' => 'postal_greeting_display', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Postal Greeting') , - 'description' => 'Cache Postal greeting.', + 'title' => ts('Postal Greeting'), + 'description' => ts('Cache Postal greeting.'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - 'html' => array( + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'addressee_id' => array( + ], + ], + 'addressee_id' => [ 'name' => 'addressee_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Addressee ID') , - 'description' => 'FK to civicrm_option_value.id, that has to be valid registered Addressee.', - ) , - 'addressee_custom' => array( + 'title' => ts('Addressee ID'), + 'description' => ts('FK to civicrm_option_value.id, that has to be valid registered Addressee.'), + 'export' => TRUE, + 'where' => 'civicrm_contact.addressee_id', + 'headerPattern' => '', + 'dataPattern' => '', + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'pseudoconstant' => [ + 'optionGroupName' => 'addressee', + 'optionEditPath' => 'civicrm/admin/options/addressee', + ] + ], + 'addressee_custom' => [ 'name' => 'addressee_custom', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Addressee Custom') , - 'description' => 'Custom Addressee.', + 'title' => ts('Addressee Custom'), + 'description' => ts('Custom Addressee.'), 'maxlength' => 128, 'size' => CRM_Utils_Type::HUGE, - 'import' => true, + 'import' => TRUE, 'where' => 'civicrm_contact.addressee_custom', 'headerPattern' => '', 'dataPattern' => '', - 'export' => false, - 'html' => array( + 'export' => FALSE, + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'addressee_display' => array( + ], + ], + 'addressee_display' => [ 'name' => 'addressee_display', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Addressee') , - 'description' => 'Cache Addressee.', + 'title' => ts('Addressee'), + 'description' => ts('Cache Addressee.'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - 'html' => array( + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'job_title' => array( + ], + ], + 'job_title' => [ 'name' => 'job_title', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Job Title') , - 'description' => 'Job Title', + 'title' => ts('Job Title'), + 'description' => ts('Job Title'), 'maxlength' => 255, 'size' => 30, - 'import' => true, + 'import' => TRUE, 'where' => 'civicrm_contact.job_title', 'headerPattern' => '/^job|(j(ob\s)?title)$/i', 'dataPattern' => '//', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'gender_id' => array( + ], + ], + 'gender_id' => [ 'name' => 'gender_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Gender') , - 'description' => 'FK to gender ID', - 'import' => true, + 'title' => ts('Gender'), + 'description' => ts('FK to gender ID'), + 'import' => TRUE, 'where' => 'civicrm_contact.gender_id', 'headerPattern' => '/^gender$/i', 'dataPattern' => '', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'optionGroupName' => 'gender', 'optionEditPath' => 'civicrm/admin/options/gender', - ) - ) , - 'birth_date' => array( + ] + ], + 'birth_date' => [ 'name' => 'birth_date', 'type' => CRM_Utils_Type::T_DATE, - 'title' => ts('Birth Date') , - 'description' => 'Date of birth', - 'import' => true, + 'title' => ts('Birth Date'), + 'description' => ts('Date of birth'), + 'import' => TRUE, 'where' => 'civicrm_contact.birth_date', 'headerPattern' => '/^birth|(b(irth\s)?date)|D(\W*)O(\W*)B(\W*)$/i', 'dataPattern' => '/\d{4}-?\d{2}-?\d{2}/', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'Select Date', - ) , - ) , - 'is_deceased' => array( + 'formatType' => 'birth', + ], + ], + 'is_deceased' => [ 'name' => 'is_deceased', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Deceased') , - 'import' => true, + 'title' => ts('Deceased'), + 'import' => TRUE, 'where' => 'civicrm_contact.is_deceased', 'headerPattern' => '/i(s\s)?d(eceased)$/i', 'dataPattern' => '', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'default' => '0', + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'CheckBox', - ) , - ) , - 'deceased_date' => array( + ], + ], + 'deceased_date' => [ 'name' => 'deceased_date', 'type' => CRM_Utils_Type::T_DATE, - 'title' => ts('Deceased Date') , - 'description' => 'Date of deceased', - 'import' => true, + 'title' => ts('Deceased Date'), + 'description' => ts('Date of deceased'), + 'import' => TRUE, 'where' => 'civicrm_contact.deceased_date', 'headerPattern' => '/^deceased|(d(eceased\s)?date)$/i', 'dataPattern' => '', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'Select Date', - ) , - ) , - 'household_name' => array( + 'formatType' => 'birth', + ], + ], + 'household_name' => [ 'name' => 'household_name', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Household Name') , - 'description' => 'Household Name.', + 'title' => ts('Household Name'), + 'description' => ts('Household Name.'), 'maxlength' => 128, 'size' => 30, - 'import' => true, + 'import' => TRUE, 'where' => 'civicrm_contact.household_name', 'headerPattern' => '/^household|(h(ousehold\s)?name)$/i', 'dataPattern' => '/^\w+$/', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'primary_contact_id' => array( + ], + ], + 'primary_contact_id' => [ 'name' => 'primary_contact_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Household Primary Contact ID') , - 'description' => 'Optional FK to Primary Contact for this household.', + 'title' => ts('Household Primary Contact ID'), + 'description' => ts('Optional FK to Primary Contact for this household.'), + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, 'FKClassName' => 'CRM_Contact_DAO_Contact', - 'html' => array( + 'html' => [ 'type' => 'Select', - ) , - ) , - 'organization_name' => array( + ], + ], + 'organization_name' => [ 'name' => 'organization_name', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Organization Name') , - 'description' => 'Organization Name.', + 'title' => ts('Organization Name'), + 'description' => ts('Organization Name.'), 'maxlength' => 128, 'size' => 30, - 'import' => true, + 'import' => TRUE, 'where' => 'civicrm_contact.organization_name', 'headerPattern' => '/^organization|(o(rganization\s)?name)$/i', 'dataPattern' => '/^\w+$/', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'sic_code' => array( + ], + ], + 'sic_code' => [ 'name' => 'sic_code', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Sic Code') , - 'description' => 'Standard Industry Classification Code.', + 'title' => ts('Sic Code'), + 'description' => ts('Standard Industry Classification Code.'), 'maxlength' => 8, 'size' => CRM_Utils_Type::EIGHT, - 'import' => true, + 'import' => TRUE, 'where' => 'civicrm_contact.sic_code', 'headerPattern' => '/^sic|(s(ic\s)?code)$/i', 'dataPattern' => '', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'user_unique_id' => array( + ], + ], + 'user_unique_id' => [ 'name' => 'user_unique_id', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Unique ID (OpenID)') , - 'description' => 'the OpenID (or OpenID-style http://username.domain/) unique identifier for this contact mainly used for logging in to CiviCRM', + 'title' => ts('Unique ID (OpenID)'), + 'description' => ts('the OpenID (or OpenID-style http://username.domain/) unique identifier for this contact mainly used for logging in to CiviCRM'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - 'import' => true, + 'import' => TRUE, 'where' => 'civicrm_contact.user_unique_id', 'headerPattern' => '/^Open\s?ID|u(niq\w*)?\s?ID/i', 'dataPattern' => '/^[\w\/\:\.]+$/', - 'export' => true, + 'export' => TRUE, 'rule' => 'url', - 'html' => array( + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'current_employer_id' => array( + ], + ], + 'current_employer_id' => [ 'name' => 'employer_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Current Employer') , - 'description' => 'OPTIONAL FK to civicrm_contact record.', - 'export' => true, + 'title' => ts('Current Employer'), + 'description' => ts('OPTIONAL FK to civicrm_contact record.'), + 'export' => TRUE, 'where' => 'civicrm_contact.employer_id', 'headerPattern' => '', 'dataPattern' => '', + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, 'FKClassName' => 'CRM_Contact_DAO_Contact', - 'html' => array( + 'html' => [ 'type' => 'EntityRef', - ) , - ) , - 'contact_is_deleted' => array( + ], + ], + 'contact_is_deleted' => [ 'name' => 'is_deleted', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Contact is in Trash') , - 'required' => true, - 'export' => true, + 'title' => ts('Contact is in Trash'), + 'required' => TRUE, + 'export' => TRUE, 'where' => 'civicrm_contact.is_deleted', 'headerPattern' => '', 'dataPattern' => '', - 'html' => array( + 'default' => '0', + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => [ 'type' => 'CheckBox', - ) , - ) , - 'created_date' => array( + ], + ], + 'created_date' => [ 'name' => 'created_date', 'type' => CRM_Utils_Type::T_TIMESTAMP, - 'title' => ts('Created Date') , - 'description' => 'When was the contact was created.', - 'required' => false, - 'export' => true, + 'title' => ts('Created Date'), + 'description' => ts('When was the contact was created.'), + 'required' => FALSE, + 'export' => TRUE, 'where' => 'civicrm_contact.created_date', 'headerPattern' => '', 'dataPattern' => '', 'default' => 'NULL', - ) , - 'modified_date' => array( + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + ], + 'modified_date' => [ 'name' => 'modified_date', 'type' => CRM_Utils_Type::T_TIMESTAMP, - 'title' => ts('Modified Date') , - 'description' => 'When was the contact (or closely related entity) was created or modified or deleted.', - 'required' => false, - 'export' => true, + 'title' => ts('Modified Date'), + 'description' => ts('When was the contact (or closely related entity) was created or modified or deleted.'), + 'required' => FALSE, + 'export' => TRUE, 'where' => 'civicrm_contact.modified_date', 'headerPattern' => '', 'dataPattern' => '', 'default' => 'CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP', - ) , - ); + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + ], + ]; CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } return Civi::$statics[__CLASS__]['fields']; } + /** * Return a mapping from field-name to the corresponding key (as used in fields()). * * @return array * Array(string $name => string $uniqueName). */ - static function &fieldKeys() { + public static function &fieldKeys() { if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); } return Civi::$statics[__CLASS__]['fieldKeys']; } + /** * Returns the names of this table * * @return string */ - static function getTableName() { + public static function getTableName() { return self::$_tableName; } + /** * Returns if this table needs to be logged * - * @return boolean + * @return bool */ - function getLog() { + public function getLog() { return self::$_log; } + /** * Returns the list of fields that can be imported * @@ -1186,10 +1459,11 @@ function getLog() { * * @return array */ - static function &import($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'contact', $prefix, array()); + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'contact', $prefix, []); return $r; } + /** * Returns the list of fields that can be exported * @@ -1197,8 +1471,161 @@ static function &import($prefix = false) { * * @return array */ - static function &export($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'contact', $prefix, array()); + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'contact', $prefix, []); return $r; } + + /** + * Returns the list of indices + * + * @param bool $localize + * + * @return array + */ + public static function indices($localize = TRUE) { + $indices = [ + 'index_contact_type' => [ + 'name' => 'index_contact_type', + 'field' => [ + 0 => 'contact_type', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_contact::0::contact_type', + ], + 'index_contact_sub_type' => [ + 'name' => 'index_contact_sub_type', + 'field' => [ + 0 => 'contact_sub_type', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_contact::0::contact_sub_type', + ], + 'UI_external_identifier' => [ + 'name' => 'UI_external_identifier', + 'field' => [ + 0 => 'external_identifier', + ], + 'localizable' => FALSE, + 'unique' => TRUE, + 'sig' => 'civicrm_contact::1::external_identifier', + ], + 'index_sort_name' => [ + 'name' => 'index_sort_name', + 'field' => [ + 0 => 'sort_name', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_contact::0::sort_name', + ], + 'index_preferred_communication_method' => [ + 'name' => 'index_preferred_communication_method', + 'field' => [ + 0 => 'preferred_communication_method', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_contact::0::preferred_communication_method', + ], + 'index_hash' => [ + 'name' => 'index_hash', + 'field' => [ + 0 => 'hash', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_contact::0::hash', + ], + 'index_api_key' => [ + 'name' => 'index_api_key', + 'field' => [ + 0 => 'api_key', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_contact::0::api_key', + ], + 'index_first_name' => [ + 'name' => 'index_first_name', + 'field' => [ + 0 => 'first_name', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_contact::0::first_name', + ], + 'index_last_name' => [ + 'name' => 'index_last_name', + 'field' => [ + 0 => 'last_name', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_contact::0::last_name', + ], + 'UI_prefix' => [ + 'name' => 'UI_prefix', + 'field' => [ + 0 => 'prefix_id', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_contact::0::prefix_id', + ], + 'UI_suffix' => [ + 'name' => 'UI_suffix', + 'field' => [ + 0 => 'suffix_id', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_contact::0::suffix_id', + ], + 'index_communication_style_id' => [ + 'name' => 'index_communication_style_id', + 'field' => [ + 0 => 'communication_style_id', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_contact::0::communication_style_id', + ], + 'UI_gender' => [ + 'name' => 'UI_gender', + 'field' => [ + 0 => 'gender_id', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_contact::0::gender_id', + ], + 'index_is_deceased' => [ + 'name' => 'index_is_deceased', + 'field' => [ + 0 => 'is_deceased', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_contact::0::is_deceased', + ], + 'index_household_name' => [ + 'name' => 'index_household_name', + 'field' => [ + 0 => 'household_name', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_contact::0::household_name', + ], + 'index_organization_name' => [ + 'name' => 'index_organization_name', + 'field' => [ + 0 => 'organization_name', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_contact::0::organization_name', + ], + 'index_is_deleted_sort_name' => [ + 'name' => 'index_is_deleted_sort_name', + 'field' => [ + 0 => 'is_deleted', + 1 => 'sort_name', + 2 => 'id', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_contact::0::is_deleted::sort_name::id', + ], + ]; + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + } diff --git a/CRM/Contact/DAO/ContactType.php b/CRM/Contact/DAO/ContactType.php index ac5fb9024aef..db032d503fe6 100644 --- a/CRM/Contact/DAO/ContactType.php +++ b/CRM/Contact/DAO/ContactType.php @@ -1,232 +1,256 @@ __table = 'civicrm_contact_type'; parent::__construct(); } + /** - * Returns foreign keys and entity references + * Returns foreign keys and entity references. * * @return array * [CRM_Core_Reference_Interface] */ - static function getReferenceColumns() { + public static function getReferenceColumns() { if (!isset(Civi::$statics[__CLASS__]['links'])) { - Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'parent_id', 'civicrm_contact_type', 'id'); + Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'parent_id', 'civicrm_contact_type', 'id'); CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']); } return Civi::$statics[__CLASS__]['links']; } + /** * Returns all the column names of this table * * @return array */ - static function &fields() { + public static function &fields() { if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = array( - 'id' => array( + Civi::$statics[__CLASS__]['fields'] = [ + 'id' => [ 'name' => 'id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Contact Type ID') , - 'description' => 'Contact Type ID', - 'required' => true, - ) , - 'name' => array( + 'title' => ts('Contact Type ID'), + 'description' => ts('Contact Type ID'), + 'required' => TRUE, + 'table_name' => 'civicrm_contact_type', + 'entity' => 'ContactType', + 'bao' => 'CRM_Contact_BAO_ContactType', + 'localizable' => 0, + ], + 'name' => [ 'name' => 'name', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Name') , - 'description' => 'Internal name of Contact Type (or Subtype).', + 'title' => ts('Name'), + 'description' => ts('Internal name of Contact Type (or Subtype).'), 'maxlength' => 64, 'size' => CRM_Utils_Type::BIG, - ) , - 'label' => array( + 'table_name' => 'civicrm_contact_type', + 'entity' => 'ContactType', + 'bao' => 'CRM_Contact_BAO_ContactType', + 'localizable' => 0, + ], + 'label' => [ 'name' => 'label', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Contact Type Label') , - 'description' => 'localized Name of Contact Type.', + 'title' => ts('Contact Type Label'), + 'description' => ts('localized Name of Contact Type.'), 'maxlength' => 64, 'size' => CRM_Utils_Type::BIG, - ) , - 'description' => array( + 'table_name' => 'civicrm_contact_type', + 'entity' => 'ContactType', + 'bao' => 'CRM_Contact_BAO_ContactType', + 'localizable' => 1, + ], + 'description' => [ 'name' => 'description', 'type' => CRM_Utils_Type::T_TEXT, - 'title' => ts('Contact Type Description') , - 'description' => 'localized Optional verbose description of the type.', + 'title' => ts('Contact Type Description'), + 'description' => ts('localized Optional verbose description of the type.'), 'rows' => 2, 'cols' => 60, - 'html' => array( + 'table_name' => 'civicrm_contact_type', + 'entity' => 'ContactType', + 'bao' => 'CRM_Contact_BAO_ContactType', + 'localizable' => 1, + 'html' => [ 'type' => 'TextArea', - ) , - ) , - 'image_URL' => array( + ], + ], + 'image_URL' => [ 'name' => 'image_URL', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Contact Type Image URL') , - 'description' => 'URL of image if any.', + 'title' => ts('Contact Type Image URL'), + 'description' => ts('URL of image if any.'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - ) , - 'parent_id' => array( + 'table_name' => 'civicrm_contact_type', + 'entity' => 'ContactType', + 'bao' => 'CRM_Contact_BAO_ContactType', + 'localizable' => 0, + ], + 'parent_id' => [ 'name' => 'parent_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Contact Type Parent') , - 'description' => 'Optional FK to parent contact type.', + 'title' => ts('Contact Type Parent'), + 'description' => ts('Optional FK to parent contact type.'), + 'table_name' => 'civicrm_contact_type', + 'entity' => 'ContactType', + 'bao' => 'CRM_Contact_BAO_ContactType', + 'localizable' => 0, 'FKClassName' => 'CRM_Contact_DAO_ContactType', - 'pseudoconstant' => array( + 'pseudoconstant' => [ 'table' => 'civicrm_contact_type', 'keyColumn' => 'id', 'labelColumn' => 'label', 'condition' => 'parent_id IS NULL', - ) - ) , - 'is_active' => array( + ] + ], + 'is_active' => [ 'name' => 'is_active', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Contact Type Is Active?') , - 'description' => 'Is this entry active?', - ) , - 'is_reserved' => array( + 'title' => ts('Contact Type Is Active?'), + 'description' => ts('Is this entry active?'), + 'table_name' => 'civicrm_contact_type', + 'entity' => 'ContactType', + 'bao' => 'CRM_Contact_BAO_ContactType', + 'localizable' => 0, + ], + 'is_reserved' => [ 'name' => 'is_reserved', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Contact Type is Reserved?') , - 'description' => 'Is this contact type a predefined system type', - ) , - ); + 'title' => ts('Contact Type is Reserved?'), + 'description' => ts('Is this contact type a predefined system type'), + 'table_name' => 'civicrm_contact_type', + 'entity' => 'ContactType', + 'bao' => 'CRM_Contact_BAO_ContactType', + 'localizable' => 0, + ], + ]; CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } return Civi::$statics[__CLASS__]['fields']; } + /** * Return a mapping from field-name to the corresponding key (as used in fields()). * * @return array * Array(string $name => string $uniqueName). */ - static function &fieldKeys() { + public static function &fieldKeys() { if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); } return Civi::$statics[__CLASS__]['fieldKeys']; } + /** * Returns the names of this table * * @return string */ - static function getTableName() { + public static function getTableName() { return CRM_Core_DAO::getLocaleTableName(self::$_tableName); } + /** * Returns if this table needs to be logged * - * @return boolean + * @return bool */ - function getLog() { + public function getLog() { return self::$_log; } + /** * Returns the list of fields that can be imported * @@ -234,10 +258,11 @@ function getLog() { * * @return array */ - static function &import($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'contact_type', $prefix, array()); + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'contact_type', $prefix, []); return $r; } + /** * Returns the list of fields that can be exported * @@ -245,8 +270,31 @@ static function &import($prefix = false) { * * @return array */ - static function &export($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'contact_type', $prefix, array()); + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'contact_type', $prefix, []); return $r; } + + /** + * Returns the list of indices + * + * @param bool $localize + * + * @return array + */ + public static function indices($localize = TRUE) { + $indices = [ + 'contact_type' => [ + 'name' => 'contact_type', + 'field' => [ + 0 => 'name', + ], + 'localizable' => FALSE, + 'unique' => TRUE, + 'sig' => 'civicrm_contact_type::1::name', + ], + ]; + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + } diff --git a/CRM/Contact/DAO/DashboardContact.php b/CRM/Contact/DAO/DashboardContact.php index 8f366e164c2f..8b354a2bb3d8 100644 --- a/CRM/Contact/DAO/DashboardContact.php +++ b/CRM/Contact/DAO/DashboardContact.php @@ -1,193 +1,209 @@ __table = 'civicrm_dashboard_contact'; parent::__construct(); } + /** - * Returns foreign keys and entity references + * Returns foreign keys and entity references. * * @return array * [CRM_Core_Reference_Interface] */ - static function getReferenceColumns() { + public static function getReferenceColumns() { if (!isset(Civi::$statics[__CLASS__]['links'])) { - Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'dashboard_id', 'civicrm_dashboard', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'contact_id', 'civicrm_contact', 'id'); + Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'dashboard_id', 'civicrm_dashboard', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'contact_id', 'civicrm_contact', 'id'); CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']); } return Civi::$statics[__CLASS__]['links']; } + /** * Returns all the column names of this table * * @return array */ - static function &fields() { + public static function &fields() { if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = array( - 'id' => array( + Civi::$statics[__CLASS__]['fields'] = [ + 'id' => [ 'name' => 'id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Dashboard Contact ID') , - 'required' => true, - ) , - 'dashboard_id' => array( + 'title' => ts('Dashboard Contact ID'), + 'required' => TRUE, + 'table_name' => 'civicrm_dashboard_contact', + 'entity' => 'DashboardContact', + 'bao' => 'CRM_Contact_BAO_DashboardContact', + 'localizable' => 0, + ], + 'dashboard_id' => [ 'name' => 'dashboard_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Dashboard') , - 'description' => 'Dashboard ID', - 'required' => true, + 'title' => ts('Dashboard'), + 'description' => ts('Dashboard ID'), + 'required' => TRUE, + 'table_name' => 'civicrm_dashboard_contact', + 'entity' => 'DashboardContact', + 'bao' => 'CRM_Contact_BAO_DashboardContact', + 'localizable' => 0, 'FKClassName' => 'CRM_Core_DAO_Dashboard', - ) , - 'contact_id' => array( + ], + 'contact_id' => [ 'name' => 'contact_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Dashboard Contact') , - 'description' => 'Contact ID', - 'required' => true, + 'title' => ts('Dashboard Contact'), + 'description' => ts('Contact ID'), + 'required' => TRUE, + 'table_name' => 'civicrm_dashboard_contact', + 'entity' => 'DashboardContact', + 'bao' => 'CRM_Contact_BAO_DashboardContact', + 'localizable' => 0, 'FKClassName' => 'CRM_Contact_DAO_Contact', - ) , - 'column_no' => array( + ], + 'column_no' => [ 'name' => 'column_no', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Column No') , - 'description' => 'column no for this widget', - ) , - 'is_active' => array( + 'title' => ts('Column No'), + 'description' => ts('column no for this widget'), + 'default' => '0', + 'table_name' => 'civicrm_dashboard_contact', + 'entity' => 'DashboardContact', + 'bao' => 'CRM_Contact_BAO_DashboardContact', + 'localizable' => 0, + ], + 'is_active' => [ 'name' => 'is_active', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Dashlet is Active?') , - 'description' => 'Is this widget active?', - ) , - 'weight' => array( + 'title' => ts('Dashlet is Active?'), + 'description' => ts('Is this widget active?'), + 'default' => '0', + 'table_name' => 'civicrm_dashboard_contact', + 'entity' => 'DashboardContact', + 'bao' => 'CRM_Contact_BAO_DashboardContact', + 'localizable' => 0, + ], + 'weight' => [ 'name' => 'weight', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Order') , - 'description' => 'Ordering of the widgets.', - ) , - ); + 'title' => ts('Order'), + 'description' => ts('Ordering of the widgets.'), + 'default' => '0', + 'table_name' => 'civicrm_dashboard_contact', + 'entity' => 'DashboardContact', + 'bao' => 'CRM_Contact_BAO_DashboardContact', + 'localizable' => 0, + ], + ]; CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } return Civi::$statics[__CLASS__]['fields']; } + /** * Return a mapping from field-name to the corresponding key (as used in fields()). * * @return array * Array(string $name => string $uniqueName). */ - static function &fieldKeys() { + public static function &fieldKeys() { if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); } return Civi::$statics[__CLASS__]['fieldKeys']; } + /** * Returns the names of this table * * @return string */ - static function getTableName() { + public static function getTableName() { return self::$_tableName; } + /** * Returns if this table needs to be logged * - * @return boolean + * @return bool */ - function getLog() { + public function getLog() { return self::$_log; } + /** * Returns the list of fields that can be imported * @@ -195,10 +211,11 @@ function getLog() { * * @return array */ - static function &import($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'dashboard_contact', $prefix, array()); + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'dashboard_contact', $prefix, []); return $r; } + /** * Returns the list of fields that can be exported * @@ -206,8 +223,32 @@ static function &import($prefix = false) { * * @return array */ - static function &export($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'dashboard_contact', $prefix, array()); + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'dashboard_contact', $prefix, []); return $r; } + + /** + * Returns the list of indices + * + * @param bool $localize + * + * @return array + */ + public static function indices($localize = TRUE) { + $indices = [ + 'index_dashboard_id_contact_id' => [ + 'name' => 'index_dashboard_id_contact_id', + 'field' => [ + 0 => 'dashboard_id', + 1 => 'contact_id', + ], + 'localizable' => FALSE, + 'unique' => TRUE, + 'sig' => 'civicrm_dashboard_contact::1::dashboard_id::contact_id', + ], + ]; + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + } diff --git a/CRM/Contact/DAO/Factory.php b/CRM/Contact/DAO/Factory.php index 5f4432c2b50f..8af2fc1ac416 100644 --- a/CRM/Contact/DAO/Factory.php +++ b/CRM/Contact/DAO/Factory.php @@ -5,7 +5,7 @@ */ class CRM_Contact_DAO_Factory { - static $_classes = array( + public static $_classes = [ 'Address' => 'data', 'Contact' => 'data', 'Email' => 'data', @@ -17,39 +17,35 @@ class CRM_Contact_DAO_Factory { 'Organization' => 'data', 'Phone' => 'data', 'Relationship' => 'data', - ); + ]; - static $_prefix = array( - 'business' => 'CRM/Contact/BAO/', - 'data' => 'CRM/Contact/DAO/', - ); - - static $_suffix = '.php'; + public static $_prefix = [ + 'business' => 'CRM_Contact_BAO_', + 'data' => 'CRM_Contact_DAO_', + ]; /** * @param string $className * * @return mixed */ - static function &create($className) { + public static function create($className) { $type = CRM_Utils_Array::value($className, self::$_classes); if (!$type) { return CRM_Core_DAO_Factory::create($className); } - $file = self::$_prefix[$type] . $className; - $class = str_replace('/', '_', $file); - - require_once($file . self::$_suffix); + $class = self::$_prefix[$type] . $className; if ($type == 'singleton') { $newObj = $class::singleton(); } else { // this is either 'business' or 'data' - $newObj = new $class; + $newObj = new $class(); } return $newObj; } + } diff --git a/CRM/Contact/DAO/Group.php b/CRM/Contact/DAO/Group.php index 2dd24c632e40..1213b90fc6e9 100644 --- a/CRM/Contact/DAO/Group.php +++ b/CRM/Contact/DAO/Group.php @@ -1,389 +1,481 @@ __table = 'civicrm_group'; parent::__construct(); } + /** - * Returns foreign keys and entity references + * Returns foreign keys and entity references. * * @return array * [CRM_Core_Reference_Interface] */ - static function getReferenceColumns() { + public static function getReferenceColumns() { if (!isset(Civi::$statics[__CLASS__]['links'])) { - Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'saved_search_id', 'civicrm_saved_search', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'created_id', 'civicrm_contact', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'modified_id', 'civicrm_contact', 'id'); + Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'saved_search_id', 'civicrm_saved_search', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'created_id', 'civicrm_contact', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'modified_id', 'civicrm_contact', 'id'); CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']); } return Civi::$statics[__CLASS__]['links']; } + /** * Returns all the column names of this table * * @return array */ - static function &fields() { + public static function &fields() { if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = array( - 'id' => array( + Civi::$statics[__CLASS__]['fields'] = [ + 'id' => [ 'name' => 'id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Group ID') , - 'description' => 'Group ID', - 'required' => true, - ) , - 'name' => array( + 'title' => ts('Group ID'), + 'description' => ts('Group ID'), + 'required' => TRUE, + 'table_name' => 'civicrm_group', + 'entity' => 'Group', + 'bao' => 'CRM_Contact_BAO_Group', + 'localizable' => 0, + ], + 'name' => [ 'name' => 'name', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Group Name') , - 'description' => 'Internal name of Group.', + 'title' => ts('Group Name'), + 'description' => ts('Internal name of Group.'), 'maxlength' => 64, 'size' => CRM_Utils_Type::BIG, - ) , - 'title' => array( + 'table_name' => 'civicrm_group', + 'entity' => 'Group', + 'bao' => 'CRM_Contact_BAO_Group', + 'localizable' => 0, + ], + 'title' => [ 'name' => 'title', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Group Title') , - 'description' => 'Name of Group.', + 'title' => ts('Group Title'), + 'description' => ts('Name of Group.'), 'maxlength' => 64, 'size' => CRM_Utils_Type::BIG, - ) , - 'description' => array( + 'table_name' => 'civicrm_group', + 'entity' => 'Group', + 'bao' => 'CRM_Contact_BAO_Group', + 'localizable' => 1, + ], + 'description' => [ 'name' => 'description', 'type' => CRM_Utils_Type::T_TEXT, - 'title' => ts('Group Description') , - 'description' => 'Optional verbose description of the group.', + 'title' => ts('Group Description'), + 'description' => ts('Optional verbose description of the group.'), 'rows' => 2, 'cols' => 60, - 'html' => array( + 'table_name' => 'civicrm_group', + 'entity' => 'Group', + 'bao' => 'CRM_Contact_BAO_Group', + 'localizable' => 0, + 'html' => [ 'type' => 'TextArea', - ) , - ) , - 'source' => array( + ], + ], + 'source' => [ 'name' => 'source', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Group Source') , - 'description' => 'Module or process which created this group.', + 'title' => ts('Group Source'), + 'description' => ts('Module or process which created this group.'), 'maxlength' => 64, 'size' => CRM_Utils_Type::BIG, - ) , - 'saved_search_id' => array( + 'table_name' => 'civicrm_group', + 'entity' => 'Group', + 'bao' => 'CRM_Contact_BAO_Group', + 'localizable' => 0, + ], + 'saved_search_id' => [ 'name' => 'saved_search_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Saved Search ID') , - 'description' => 'FK to saved search table.', + 'title' => ts('Saved Search ID'), + 'description' => ts('FK to saved search table.'), + 'table_name' => 'civicrm_group', + 'entity' => 'Group', + 'bao' => 'CRM_Contact_BAO_Group', + 'localizable' => 0, 'FKClassName' => 'CRM_Contact_DAO_SavedSearch', - ) , - 'is_active' => array( + ], + 'is_active' => [ 'name' => 'is_active', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Group Enabled') , - 'description' => 'Is this entry active?', - ) , - 'visibility' => array( + 'title' => ts('Group Enabled'), + 'description' => ts('Is this entry active?'), + 'table_name' => 'civicrm_group', + 'entity' => 'Group', + 'bao' => 'CRM_Contact_BAO_Group', + 'localizable' => 0, + ], + 'visibility' => [ 'name' => 'visibility', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Group Visibility Setting') , - 'description' => 'In what context(s) is this field visible.', + 'title' => ts('Group Visibility Setting'), + 'description' => ts('In what context(s) is this field visible.'), 'maxlength' => 24, 'size' => CRM_Utils_Type::MEDIUM, 'default' => 'User and User Admin Only', - 'html' => array( + 'table_name' => 'civicrm_group', + 'entity' => 'Group', + 'bao' => 'CRM_Contact_BAO_Group', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'callback' => 'CRM_Core_SelectValues::groupVisibility', - ) - ) , - 'where_clause' => array( + ] + ], + 'where_clause' => [ 'name' => 'where_clause', 'type' => CRM_Utils_Type::T_TEXT, - 'title' => ts('Group Where Clause') , - 'description' => 'the sql where clause if a saved search acl', - ) , - 'select_tables' => array( + 'title' => ts('Group Where Clause'), + 'description' => ts('the sql where clause if a saved search acl'), + 'table_name' => 'civicrm_group', + 'entity' => 'Group', + 'bao' => 'CRM_Contact_BAO_Group', + 'localizable' => 0, + ], + 'select_tables' => [ 'name' => 'select_tables', 'type' => CRM_Utils_Type::T_TEXT, - 'title' => ts('Tables For Select Clause') , - 'description' => 'the tables to be included in a select data', - ) , - 'where_tables' => array( + 'title' => ts('Tables For Select Clause'), + 'description' => ts('the tables to be included in a select data'), + 'table_name' => 'civicrm_group', + 'entity' => 'Group', + 'bao' => 'CRM_Contact_BAO_Group', + 'localizable' => 0, + 'serialize' => self::SERIALIZE_PHP, + ], + 'where_tables' => [ 'name' => 'where_tables', 'type' => CRM_Utils_Type::T_TEXT, - 'title' => ts('Tables For Where Clause') , - 'description' => 'the tables to be included in the count statement', - ) , - 'group_type' => array( + 'title' => ts('Tables For Where Clause'), + 'description' => ts('the tables to be included in the count statement'), + 'table_name' => 'civicrm_group', + 'entity' => 'Group', + 'bao' => 'CRM_Contact_BAO_Group', + 'localizable' => 0, + 'serialize' => self::SERIALIZE_PHP, + ], + 'group_type' => [ 'name' => 'group_type', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Group Type') , - 'description' => 'FK to group type', + 'title' => ts('Group Type'), + 'description' => ts('FK to group type'), 'maxlength' => 128, 'size' => CRM_Utils_Type::HUGE, - 'pseudoconstant' => array( + 'table_name' => 'civicrm_group', + 'entity' => 'Group', + 'bao' => 'CRM_Contact_BAO_Group', + 'localizable' => 0, + 'serialize' => self::SERIALIZE_SEPARATOR_BOOKEND, + 'pseudoconstant' => [ 'optionGroupName' => 'group_type', 'optionEditPath' => 'civicrm/admin/options/group_type', - ) - ) , - 'cache_date' => array( + ] + ], + 'cache_date' => [ 'name' => 'cache_date', 'type' => CRM_Utils_Type::T_TIMESTAMP, - 'title' => ts('Group Cache Date') , - 'description' => 'Date when we created the cache for a smart group', - 'required' => false, - ) , - 'refresh_date' => array( + 'title' => ts('Group Cache Date'), + 'description' => ts('Date when we created the cache for a smart group'), + 'required' => FALSE, + 'table_name' => 'civicrm_group', + 'entity' => 'Group', + 'bao' => 'CRM_Contact_BAO_Group', + 'localizable' => 0, + ], + 'refresh_date' => [ 'name' => 'refresh_date', 'type' => CRM_Utils_Type::T_TIMESTAMP, - 'title' => ts('Next Group Refresh Time') , - 'description' => 'Date and time when we need to refresh the cache next.', - 'required' => false, - ) , - 'parents' => array( + 'title' => ts('Next Group Refresh Time'), + 'description' => ts('Date and time when we need to refresh the cache next.'), + 'required' => FALSE, + 'table_name' => 'civicrm_group', + 'entity' => 'Group', + 'bao' => 'CRM_Contact_BAO_Group', + 'localizable' => 0, + ], + 'parents' => [ 'name' => 'parents', 'type' => CRM_Utils_Type::T_TEXT, - 'title' => ts('Group Parents') , - 'description' => 'IDs of the parent(s)', - ) , - 'children' => array( + 'title' => ts('Group Parents'), + 'description' => ts('IDs of the parent(s)'), + 'table_name' => 'civicrm_group', + 'entity' => 'Group', + 'bao' => 'CRM_Contact_BAO_Group', + 'localizable' => 0, + 'serialize' => self::SERIALIZE_COMMA, + 'pseudoconstant' => [ + 'callback' => 'CRM_Core_PseudoConstant::allGroup', + ] + ], + 'children' => [ 'name' => 'children', 'type' => CRM_Utils_Type::T_TEXT, - 'title' => ts('Group Children') , - 'description' => 'IDs of the child(ren)', - ) , - 'is_hidden' => array( + 'title' => ts('Group Children'), + 'description' => ts('IDs of the child(ren)'), + 'table_name' => 'civicrm_group', + 'entity' => 'Group', + 'bao' => 'CRM_Contact_BAO_Group', + 'localizable' => 0, + ], + 'is_hidden' => [ 'name' => 'is_hidden', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Group is Hidden') , - 'description' => 'Is this group hidden?', - ) , - 'is_reserved' => array( + 'title' => ts('Group is Hidden'), + 'description' => ts('Is this group hidden?'), + 'default' => '0', + 'table_name' => 'civicrm_group', + 'entity' => 'Group', + 'bao' => 'CRM_Contact_BAO_Group', + 'localizable' => 0, + ], + 'is_reserved' => [ 'name' => 'is_reserved', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Group is Reserved') , - ) , - 'created_id' => array( + 'title' => ts('Group is Reserved'), + 'default' => '0', + 'table_name' => 'civicrm_group', + 'entity' => 'Group', + 'bao' => 'CRM_Contact_BAO_Group', + 'localizable' => 0, + ], + 'created_id' => [ 'name' => 'created_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Group Created By') , - 'description' => 'FK to contact table.', + 'title' => ts('Group Created By'), + 'description' => ts('FK to contact table.'), + 'table_name' => 'civicrm_group', + 'entity' => 'Group', + 'bao' => 'CRM_Contact_BAO_Group', + 'localizable' => 0, 'FKClassName' => 'CRM_Contact_DAO_Contact', - ) , - 'modified_id' => array( + ], + 'modified_id' => [ 'name' => 'modified_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Group Modified By') , - 'description' => 'FK to contact table.', + 'title' => ts('Group Modified By'), + 'description' => ts('FK to contact table.'), + 'table_name' => 'civicrm_group', + 'entity' => 'Group', + 'bao' => 'CRM_Contact_BAO_Group', + 'localizable' => 0, 'FKClassName' => 'CRM_Contact_DAO_Contact', - ) , - ); + ], + ]; CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } return Civi::$statics[__CLASS__]['fields']; } + /** * Return a mapping from field-name to the corresponding key (as used in fields()). * * @return array * Array(string $name => string $uniqueName). */ - static function &fieldKeys() { + public static function &fieldKeys() { if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); } return Civi::$statics[__CLASS__]['fieldKeys']; } + /** * Returns the names of this table * * @return string */ - static function getTableName() { + public static function getTableName() { return CRM_Core_DAO::getLocaleTableName(self::$_tableName); } + /** * Returns if this table needs to be logged * - * @return boolean + * @return bool */ - function getLog() { + public function getLog() { return self::$_log; } + /** * Returns the list of fields that can be imported * @@ -391,10 +483,11 @@ function getLog() { * * @return array */ - static function &import($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'group', $prefix, array()); + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'group', $prefix, []); return $r; } + /** * Returns the list of fields that can be exported * @@ -402,8 +495,48 @@ static function &import($prefix = false) { * * @return array */ - static function &export($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'group', $prefix, array()); + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'group', $prefix, []); return $r; } + + /** + * Returns the list of indices + * + * @param bool $localize + * + * @return array + */ + public static function indices($localize = TRUE) { + $indices = [ + 'index_group_type' => [ + 'name' => 'index_group_type', + 'field' => [ + 0 => 'group_type', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_group::0::group_type', + ], + 'UI_title' => [ + 'name' => 'UI_title', + 'field' => [ + 0 => 'title', + ], + 'localizable' => TRUE, + 'unique' => TRUE, + 'sig' => 'civicrm_group::1::title', + ], + 'UI_name' => [ + 'name' => 'UI_name', + 'field' => [ + 0 => 'name', + ], + 'localizable' => FALSE, + 'unique' => TRUE, + 'sig' => 'civicrm_group::1::name', + ], + ]; + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + } diff --git a/CRM/Contact/DAO/GroupContact.php b/CRM/Contact/DAO/GroupContact.php index 964b8e737f44..8a259670c63a 100644 --- a/CRM/Contact/DAO/GroupContact.php +++ b/CRM/Contact/DAO/GroupContact.php @@ -1,215 +1,229 @@ __table = 'civicrm_group_contact'; parent::__construct(); } + /** - * Returns foreign keys and entity references + * Returns foreign keys and entity references. * * @return array * [CRM_Core_Reference_Interface] */ - static function getReferenceColumns() { + public static function getReferenceColumns() { if (!isset(Civi::$statics[__CLASS__]['links'])) { - Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'group_id', 'civicrm_group', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'contact_id', 'civicrm_contact', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'location_id', 'civicrm_loc_block', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'email_id', 'civicrm_email', 'id'); + Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'group_id', 'civicrm_group', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'contact_id', 'civicrm_contact', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'location_id', 'civicrm_loc_block', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'email_id', 'civicrm_email', 'id'); CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']); } return Civi::$statics[__CLASS__]['links']; } + /** * Returns all the column names of this table * * @return array */ - static function &fields() { + public static function &fields() { if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = array( - 'id' => array( + Civi::$statics[__CLASS__]['fields'] = [ + 'id' => [ 'name' => 'id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Group Contact ID') , - 'description' => 'primary key', - 'required' => true, - ) , - 'group_id' => array( + 'title' => ts('Group Contact ID'), + 'description' => ts('primary key'), + 'required' => TRUE, + 'table_name' => 'civicrm_group_contact', + 'entity' => 'GroupContact', + 'bao' => 'CRM_Contact_BAO_GroupContact', + 'localizable' => 0, + ], + 'group_id' => [ 'name' => 'group_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Group ID') , - 'description' => 'FK to civicrm_group', - 'required' => true, + 'title' => ts('Group ID'), + 'description' => ts('FK to civicrm_group'), + 'required' => TRUE, + 'table_name' => 'civicrm_group_contact', + 'entity' => 'GroupContact', + 'bao' => 'CRM_Contact_BAO_GroupContact', + 'localizable' => 0, 'FKClassName' => 'CRM_Contact_DAO_Group', - 'html' => array( + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'table' => 'civicrm_group', 'keyColumn' => 'id', 'labelColumn' => 'title', - ) - ) , - 'contact_id' => array( + ] + ], + 'contact_id' => [ 'name' => 'contact_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Contact ID') , - 'description' => 'FK to civicrm_contact', - 'required' => true, + 'title' => ts('Contact ID'), + 'description' => ts('FK to civicrm_contact'), + 'required' => TRUE, + 'table_name' => 'civicrm_group_contact', + 'entity' => 'GroupContact', + 'bao' => 'CRM_Contact_BAO_GroupContact', + 'localizable' => 0, 'FKClassName' => 'CRM_Contact_DAO_Contact', - ) , - 'status' => array( + ], + 'status' => [ 'name' => 'status', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Group Contact Status') , - 'description' => 'status of contact relative to membership in group', + 'title' => ts('Group Contact Status'), + 'description' => ts('status of contact relative to membership in group'), 'maxlength' => 8, 'size' => CRM_Utils_Type::EIGHT, - 'html' => array( + 'table_name' => 'civicrm_group_contact', + 'entity' => 'GroupContact', + 'bao' => 'CRM_Contact_BAO_GroupContact', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'callback' => 'CRM_Core_SelectValues::groupContactStatus', - ) - ) , - 'location_id' => array( + ] + ], + 'location_id' => [ 'name' => 'location_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Group Contact Location') , - 'description' => 'Optional location to associate with this membership', + 'title' => ts('Group Contact Location'), + 'description' => ts('Optional location to associate with this membership'), + 'table_name' => 'civicrm_group_contact', + 'entity' => 'GroupContact', + 'bao' => 'CRM_Contact_BAO_GroupContact', + 'localizable' => 0, 'FKClassName' => 'CRM_Core_DAO_LocBlock', - ) , - 'email_id' => array( + ], + 'email_id' => [ 'name' => 'email_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Group Contact Email') , - 'description' => 'Optional email to associate with this membership', + 'title' => ts('Group Contact Email'), + 'description' => ts('Optional email to associate with this membership'), + 'table_name' => 'civicrm_group_contact', + 'entity' => 'GroupContact', + 'bao' => 'CRM_Contact_BAO_GroupContact', + 'localizable' => 0, 'FKClassName' => 'CRM_Core_DAO_Email', - ) , - ); + ], + ]; CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } return Civi::$statics[__CLASS__]['fields']; } + /** * Return a mapping from field-name to the corresponding key (as used in fields()). * * @return array * Array(string $name => string $uniqueName). */ - static function &fieldKeys() { + public static function &fieldKeys() { if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); } return Civi::$statics[__CLASS__]['fieldKeys']; } + /** * Returns the names of this table * * @return string */ - static function getTableName() { + public static function getTableName() { return self::$_tableName; } + /** * Returns if this table needs to be logged * - * @return boolean + * @return bool */ - function getLog() { + public function getLog() { return self::$_log; } + /** * Returns the list of fields that can be imported * @@ -217,10 +231,11 @@ function getLog() { * * @return array */ - static function &import($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'group_contact', $prefix, array()); + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'group_contact', $prefix, []); return $r; } + /** * Returns the list of fields that can be exported * @@ -228,8 +243,32 @@ static function &import($prefix = false) { * * @return array */ - static function &export($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'group_contact', $prefix, array()); + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'group_contact', $prefix, []); return $r; } + + /** + * Returns the list of indices + * + * @param bool $localize + * + * @return array + */ + public static function indices($localize = TRUE) { + $indices = [ + 'UI_contact_group' => [ + 'name' => 'UI_contact_group', + 'field' => [ + 0 => 'contact_id', + 1 => 'group_id', + ], + 'localizable' => FALSE, + 'unique' => TRUE, + 'sig' => 'civicrm_group_contact::1::contact_id::group_id', + ], + ]; + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + } diff --git a/CRM/Contact/DAO/GroupContactCache.php b/CRM/Contact/DAO/GroupContactCache.php index 87a88a4fe231..7e97bd7b867d 100644 --- a/CRM/Contact/DAO/GroupContactCache.php +++ b/CRM/Contact/DAO/GroupContactCache.php @@ -1,167 +1,166 @@ __table = 'civicrm_group_contact_cache'; parent::__construct(); } + /** - * Returns foreign keys and entity references + * Returns foreign keys and entity references. * * @return array * [CRM_Core_Reference_Interface] */ - static function getReferenceColumns() { + public static function getReferenceColumns() { if (!isset(Civi::$statics[__CLASS__]['links'])) { - Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'group_id', 'civicrm_group', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'contact_id', 'civicrm_contact', 'id'); + Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'group_id', 'civicrm_group', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'contact_id', 'civicrm_contact', 'id'); CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']); } return Civi::$statics[__CLASS__]['links']; } + /** * Returns all the column names of this table * * @return array */ - static function &fields() { + public static function &fields() { if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = array( - 'id' => array( + Civi::$statics[__CLASS__]['fields'] = [ + 'id' => [ 'name' => 'id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Group Contact Cache ID') , - 'description' => 'primary key', - 'required' => true, - ) , - 'group_id' => array( + 'title' => ts('Group Contact Cache ID'), + 'description' => ts('primary key'), + 'required' => TRUE, + 'table_name' => 'civicrm_group_contact_cache', + 'entity' => 'GroupContactCache', + 'bao' => 'CRM_Contact_BAO_GroupContactCache', + 'localizable' => 0, + ], + 'group_id' => [ 'name' => 'group_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Group') , - 'description' => 'FK to civicrm_group', - 'required' => true, + 'title' => ts('Group'), + 'description' => ts('FK to civicrm_group'), + 'required' => TRUE, + 'table_name' => 'civicrm_group_contact_cache', + 'entity' => 'GroupContactCache', + 'bao' => 'CRM_Contact_BAO_GroupContactCache', + 'localizable' => 0, 'FKClassName' => 'CRM_Contact_DAO_Group', - 'html' => array( + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'table' => 'civicrm_group', 'keyColumn' => 'id', 'labelColumn' => 'title', - ) - ) , - 'contact_id' => array( + ] + ], + 'contact_id' => [ 'name' => 'contact_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Contact ID') , - 'description' => 'FK to civicrm_contact', - 'required' => true, + 'title' => ts('Contact ID'), + 'description' => ts('FK to civicrm_contact'), + 'required' => TRUE, + 'table_name' => 'civicrm_group_contact_cache', + 'entity' => 'GroupContactCache', + 'bao' => 'CRM_Contact_BAO_GroupContactCache', + 'localizable' => 0, 'FKClassName' => 'CRM_Contact_DAO_Contact', - ) , - ); + ], + ]; CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } return Civi::$statics[__CLASS__]['fields']; } + /** * Return a mapping from field-name to the corresponding key (as used in fields()). * * @return array * Array(string $name => string $uniqueName). */ - static function &fieldKeys() { + public static function &fieldKeys() { if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); } return Civi::$statics[__CLASS__]['fieldKeys']; } + /** * Returns the names of this table * * @return string */ - static function getTableName() { + public static function getTableName() { return self::$_tableName; } + /** * Returns if this table needs to be logged * - * @return boolean + * @return bool */ - function getLog() { + public function getLog() { return self::$_log; } + /** * Returns the list of fields that can be imported * @@ -169,10 +168,11 @@ function getLog() { * * @return array */ - static function &import($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'group_contact_cache', $prefix, array()); + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'group_contact_cache', $prefix, []); return $r; } + /** * Returns the list of fields that can be exported * @@ -180,8 +180,32 @@ static function &import($prefix = false) { * * @return array */ - static function &export($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'group_contact_cache', $prefix, array()); + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'group_contact_cache', $prefix, []); return $r; } + + /** + * Returns the list of indices + * + * @param bool $localize + * + * @return array + */ + public static function indices($localize = TRUE) { + $indices = [ + 'UI_contact_group' => [ + 'name' => 'UI_contact_group', + 'field' => [ + 0 => 'contact_id', + 1 => 'group_id', + ], + 'localizable' => FALSE, + 'unique' => TRUE, + 'sig' => 'civicrm_group_contact_cache::1::contact_id::group_id', + ], + ]; + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + } diff --git a/CRM/Contact/DAO/GroupNesting.php b/CRM/Contact/DAO/GroupNesting.php index f1dca5b1a6f3..d7a59df69366 100644 --- a/CRM/Contact/DAO/GroupNesting.php +++ b/CRM/Contact/DAO/GroupNesting.php @@ -1,159 +1,158 @@ __table = 'civicrm_group_nesting'; parent::__construct(); } + /** - * Returns foreign keys and entity references + * Returns foreign keys and entity references. * * @return array * [CRM_Core_Reference_Interface] */ - static function getReferenceColumns() { + public static function getReferenceColumns() { if (!isset(Civi::$statics[__CLASS__]['links'])) { - Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'child_group_id', 'civicrm_group', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'parent_group_id', 'civicrm_group', 'id'); + Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'child_group_id', 'civicrm_group', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'parent_group_id', 'civicrm_group', 'id'); CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']); } return Civi::$statics[__CLASS__]['links']; } + /** * Returns all the column names of this table * * @return array */ - static function &fields() { + public static function &fields() { if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = array( - 'id' => array( + Civi::$statics[__CLASS__]['fields'] = [ + 'id' => [ 'name' => 'id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Group Nesting ID') , - 'description' => 'Relationship ID', - 'required' => true, - ) , - 'child_group_id' => array( + 'title' => ts('Group Nesting ID'), + 'description' => ts('Relationship ID'), + 'required' => TRUE, + 'table_name' => 'civicrm_group_nesting', + 'entity' => 'GroupNesting', + 'bao' => 'CRM_Contact_BAO_GroupNesting', + 'localizable' => 0, + ], + 'child_group_id' => [ 'name' => 'child_group_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Child Group') , - 'description' => 'ID of the child group', - 'required' => true, + 'title' => ts('Child Group'), + 'description' => ts('ID of the child group'), + 'required' => TRUE, + 'table_name' => 'civicrm_group_nesting', + 'entity' => 'GroupNesting', + 'bao' => 'CRM_Contact_BAO_GroupNesting', + 'localizable' => 0, 'FKClassName' => 'CRM_Contact_DAO_Group', - ) , - 'parent_group_id' => array( + ], + 'parent_group_id' => [ 'name' => 'parent_group_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Parent Group') , - 'description' => 'ID of the parent group', - 'required' => true, + 'title' => ts('Parent Group'), + 'description' => ts('ID of the parent group'), + 'required' => TRUE, + 'table_name' => 'civicrm_group_nesting', + 'entity' => 'GroupNesting', + 'bao' => 'CRM_Contact_BAO_GroupNesting', + 'localizable' => 0, 'FKClassName' => 'CRM_Contact_DAO_Group', - ) , - ); + ], + ]; CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } return Civi::$statics[__CLASS__]['fields']; } + /** * Return a mapping from field-name to the corresponding key (as used in fields()). * * @return array * Array(string $name => string $uniqueName). */ - static function &fieldKeys() { + public static function &fieldKeys() { if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); } return Civi::$statics[__CLASS__]['fieldKeys']; } + /** * Returns the names of this table * * @return string */ - static function getTableName() { + public static function getTableName() { return self::$_tableName; } + /** * Returns if this table needs to be logged * - * @return boolean + * @return bool */ - function getLog() { + public function getLog() { return self::$_log; } + /** * Returns the list of fields that can be imported * @@ -161,10 +160,11 @@ function getLog() { * * @return array */ - static function &import($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'group_nesting', $prefix, array()); + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'group_nesting', $prefix, []); return $r; } + /** * Returns the list of fields that can be exported * @@ -172,8 +172,21 @@ static function &import($prefix = false) { * * @return array */ - static function &export($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'group_nesting', $prefix, array()); + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'group_nesting', $prefix, []); return $r; } + + /** + * Returns the list of indices + * + * @param bool $localize + * + * @return array + */ + public static function indices($localize = TRUE) { + $indices = []; + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + } diff --git a/CRM/Contact/DAO/GroupOrganization.php b/CRM/Contact/DAO/GroupOrganization.php index 6de29b76c83c..fd90ae2bfba3 100644 --- a/CRM/Contact/DAO/GroupOrganization.php +++ b/CRM/Contact/DAO/GroupOrganization.php @@ -1,167 +1,166 @@ __table = 'civicrm_group_organization'; parent::__construct(); } + /** - * Returns foreign keys and entity references + * Returns foreign keys and entity references. * * @return array * [CRM_Core_Reference_Interface] */ - static function getReferenceColumns() { + public static function getReferenceColumns() { if (!isset(Civi::$statics[__CLASS__]['links'])) { - Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'group_id', 'civicrm_group', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'organization_id', 'civicrm_contact', 'id'); + Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'group_id', 'civicrm_group', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'organization_id', 'civicrm_contact', 'id'); CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']); } return Civi::$statics[__CLASS__]['links']; } + /** * Returns all the column names of this table * * @return array */ - static function &fields() { + public static function &fields() { if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = array( - 'id' => array( + Civi::$statics[__CLASS__]['fields'] = [ + 'id' => [ 'name' => 'id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Group Organization ID') , - 'description' => 'Relationship ID', - 'required' => true, - ) , - 'group_id' => array( + 'title' => ts('Group Organization ID'), + 'description' => ts('Relationship ID'), + 'required' => TRUE, + 'table_name' => 'civicrm_group_organization', + 'entity' => 'GroupOrganization', + 'bao' => 'CRM_Contact_BAO_GroupOrganization', + 'localizable' => 0, + ], + 'group_id' => [ 'name' => 'group_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Group') , - 'description' => 'ID of the group', - 'required' => true, + 'title' => ts('Group'), + 'description' => ts('ID of the group'), + 'required' => TRUE, + 'table_name' => 'civicrm_group_organization', + 'entity' => 'GroupOrganization', + 'bao' => 'CRM_Contact_BAO_GroupOrganization', + 'localizable' => 0, 'FKClassName' => 'CRM_Contact_DAO_Group', - 'html' => array( + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'table' => 'civicrm_group', 'keyColumn' => 'id', 'labelColumn' => 'title', - ) - ) , - 'organization_id' => array( + ] + ], + 'organization_id' => [ 'name' => 'organization_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Organization') , - 'description' => 'ID of the Organization Contact', - 'required' => true, + 'title' => ts('Organization'), + 'description' => ts('ID of the Organization Contact'), + 'required' => TRUE, + 'table_name' => 'civicrm_group_organization', + 'entity' => 'GroupOrganization', + 'bao' => 'CRM_Contact_BAO_GroupOrganization', + 'localizable' => 0, 'FKClassName' => 'CRM_Contact_DAO_Contact', - ) , - ); + ], + ]; CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } return Civi::$statics[__CLASS__]['fields']; } + /** * Return a mapping from field-name to the corresponding key (as used in fields()). * * @return array * Array(string $name => string $uniqueName). */ - static function &fieldKeys() { + public static function &fieldKeys() { if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); } return Civi::$statics[__CLASS__]['fieldKeys']; } + /** * Returns the names of this table * * @return string */ - static function getTableName() { + public static function getTableName() { return self::$_tableName; } + /** * Returns if this table needs to be logged * - * @return boolean + * @return bool */ - function getLog() { + public function getLog() { return self::$_log; } + /** * Returns the list of fields that can be imported * @@ -169,10 +168,11 @@ function getLog() { * * @return array */ - static function &import($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'group_organization', $prefix, array()); + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'group_organization', $prefix, []); return $r; } + /** * Returns the list of fields that can be exported * @@ -180,8 +180,32 @@ static function &import($prefix = false) { * * @return array */ - static function &export($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'group_organization', $prefix, array()); + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'group_organization', $prefix, []); return $r; } + + /** + * Returns the list of indices + * + * @param bool $localize + * + * @return array + */ + public static function indices($localize = TRUE) { + $indices = [ + 'UI_group_organization' => [ + 'name' => 'UI_group_organization', + 'field' => [ + 0 => 'group_id', + 1 => 'organization_id', + ], + 'localizable' => FALSE, + 'unique' => TRUE, + 'sig' => 'civicrm_group_organization::1::group_id::organization_id', + ], + ]; + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + } diff --git a/CRM/Contact/DAO/Relationship.php b/CRM/Contact/DAO/Relationship.php index c8a926576b4c..c40f42183154 100644 --- a/CRM/Contact/DAO/Relationship.php +++ b/CRM/Contact/DAO/Relationship.php @@ -1,294 +1,339 @@ __table = 'civicrm_relationship'; parent::__construct(); } + /** - * Returns foreign keys and entity references + * Returns foreign keys and entity references. * * @return array * [CRM_Core_Reference_Interface] */ - static function getReferenceColumns() { + public static function getReferenceColumns() { if (!isset(Civi::$statics[__CLASS__]['links'])) { - Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'contact_id_a', 'civicrm_contact', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'contact_id_b', 'civicrm_contact', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'relationship_type_id', 'civicrm_relationship_type', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'case_id', 'civicrm_case', 'id'); + Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'contact_id_a', 'civicrm_contact', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'contact_id_b', 'civicrm_contact', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'relationship_type_id', 'civicrm_relationship_type', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'case_id', 'civicrm_case', 'id'); CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']); } return Civi::$statics[__CLASS__]['links']; } + /** * Returns all the column names of this table * * @return array */ - static function &fields() { + public static function &fields() { if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = array( - 'id' => array( + Civi::$statics[__CLASS__]['fields'] = [ + 'id' => [ 'name' => 'id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Relationship ID') , - 'description' => 'Relationship ID', - 'required' => true, - ) , - 'contact_id_a' => array( + 'title' => ts('Relationship ID'), + 'description' => ts('Relationship ID'), + 'required' => TRUE, + 'table_name' => 'civicrm_relationship', + 'entity' => 'Relationship', + 'bao' => 'CRM_Contact_BAO_Relationship', + 'localizable' => 0, + ], + 'contact_id_a' => [ 'name' => 'contact_id_a', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Contact A') , - 'description' => 'id of the first contact', - 'required' => true, + 'title' => ts('Contact A'), + 'description' => ts('id of the first contact'), + 'required' => TRUE, + 'table_name' => 'civicrm_relationship', + 'entity' => 'Relationship', + 'bao' => 'CRM_Contact_BAO_Relationship', + 'localizable' => 0, 'FKClassName' => 'CRM_Contact_DAO_Contact', - ) , - 'contact_id_b' => array( + ], + 'contact_id_b' => [ 'name' => 'contact_id_b', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Contact B') , - 'description' => 'id of the second contact', - 'required' => true, + 'title' => ts('Contact B'), + 'description' => ts('id of the second contact'), + 'required' => TRUE, + 'table_name' => 'civicrm_relationship', + 'entity' => 'Relationship', + 'bao' => 'CRM_Contact_BAO_Relationship', + 'localizable' => 0, 'FKClassName' => 'CRM_Contact_DAO_Contact', - 'html' => array( + 'html' => [ 'type' => 'EntityRef', - ) , - ) , - 'relationship_type_id' => array( + ], + ], + 'relationship_type_id' => [ 'name' => 'relationship_type_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Relationship Type') , - 'description' => 'id of the relationship', - 'required' => true, + 'title' => ts('Relationship Type'), + 'description' => ts('id of the relationship'), + 'required' => TRUE, + 'table_name' => 'civicrm_relationship', + 'entity' => 'Relationship', + 'bao' => 'CRM_Contact_BAO_Relationship', + 'localizable' => 0, 'FKClassName' => 'CRM_Contact_DAO_RelationshipType', - 'html' => array( + 'html' => [ 'type' => 'Select', - ) , - ) , - 'start_date' => array( + ], + ], + 'start_date' => [ 'name' => 'start_date', 'type' => CRM_Utils_Type::T_DATE, - 'title' => ts('Relationship Start Date') , - 'description' => 'date when the relationship started', - 'html' => array( + 'title' => ts('Relationship Start Date'), + 'description' => ts('date when the relationship started'), + 'table_name' => 'civicrm_relationship', + 'entity' => 'Relationship', + 'bao' => 'CRM_Contact_BAO_Relationship', + 'localizable' => 0, + 'html' => [ 'type' => 'Select Date', - ) , - ) , - 'end_date' => array( + 'formatType' => 'activityDate', + ], + ], + 'end_date' => [ 'name' => 'end_date', 'type' => CRM_Utils_Type::T_DATE, - 'title' => ts('Relationship End Date') , - 'description' => 'date when the relationship ended', - 'html' => array( + 'title' => ts('Relationship End Date'), + 'description' => ts('date when the relationship ended'), + 'table_name' => 'civicrm_relationship', + 'entity' => 'Relationship', + 'bao' => 'CRM_Contact_BAO_Relationship', + 'localizable' => 0, + 'html' => [ 'type' => 'Select Date', - ) , - ) , - 'is_active' => array( + 'formatType' => 'activityDate', + ], + ], + 'is_active' => [ 'name' => 'is_active', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Relationship Is Active') , - 'description' => 'is the relationship active ?', + 'title' => ts('Relationship Is Active'), + 'description' => ts('is the relationship active ?'), 'default' => '1', - 'html' => array( + 'table_name' => 'civicrm_relationship', + 'entity' => 'Relationship', + 'bao' => 'CRM_Contact_BAO_Relationship', + 'localizable' => 0, + 'html' => [ 'type' => 'CheckBox', - ) , - ) , - 'description' => array( + ], + ], + 'description' => [ 'name' => 'description', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Relationship Description') , - 'description' => 'Optional verbose description for the relationship.', + 'title' => ts('Relationship Description'), + 'description' => ts('Optional verbose description for the relationship.'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - 'html' => array( + 'table_name' => 'civicrm_relationship', + 'entity' => 'Relationship', + 'bao' => 'CRM_Contact_BAO_Relationship', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'is_permission_a_b' => array( + ], + ], + 'is_permission_a_b' => [ 'name' => 'is_permission_a_b', - 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Contact A has Permission Over Contact B') , - 'description' => 'is contact a has permission to view / edit contact and - related data for contact b ? - ', - 'html' => array( - 'type' => 'CheckBox', - ) , - ) , - 'is_permission_b_a' => array( + 'type' => CRM_Utils_Type::T_INT, + 'title' => ts('Contact A has Permission Over Contact B'), + 'description' => ts('Permission that Contact A has to view/update Contact B'), + 'required' => TRUE, + 'default' => '0', + 'table_name' => 'civicrm_relationship', + 'entity' => 'Relationship', + 'bao' => 'CRM_Contact_BAO_Relationship', + 'localizable' => 0, + 'html' => [ + 'type' => 'Radio', + ], + 'pseudoconstant' => [ + 'callback' => 'CRM_Core_SelectValues::getPermissionedRelationshipOptions', + ] + ], + 'is_permission_b_a' => [ 'name' => 'is_permission_b_a', - 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Contact B has Permission Over Contact A') , - 'description' => 'is contact b has permission to view / edit contact and - related data for contact a ? - ', - 'html' => array( - 'type' => 'CheckBox', - ) , - ) , - 'case_id' => array( + 'type' => CRM_Utils_Type::T_INT, + 'title' => ts('Contact B has Permission Over Contact A'), + 'description' => ts('Permission that Contact B has to view/update Contact A'), + 'required' => TRUE, + 'default' => '0', + 'table_name' => 'civicrm_relationship', + 'entity' => 'Relationship', + 'bao' => 'CRM_Contact_BAO_Relationship', + 'localizable' => 0, + 'html' => [ + 'type' => 'Radio', + ], + 'pseudoconstant' => [ + 'callback' => 'CRM_Core_SelectValues::getPermissionedRelationshipOptions', + ] + ], + 'case_id' => [ 'name' => 'case_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Relationship Case') , - 'description' => 'FK to civicrm_case', + 'title' => ts('Relationship Case'), + 'description' => ts('FK to civicrm_case'), 'default' => 'NULL', + 'table_name' => 'civicrm_relationship', + 'entity' => 'Relationship', + 'bao' => 'CRM_Contact_BAO_Relationship', + 'localizable' => 0, 'FKClassName' => 'CRM_Case_DAO_Case', - ) , - ); + ], + ]; CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } return Civi::$statics[__CLASS__]['fields']; } + /** * Return a mapping from field-name to the corresponding key (as used in fields()). * * @return array * Array(string $name => string $uniqueName). */ - static function &fieldKeys() { + public static function &fieldKeys() { if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); } return Civi::$statics[__CLASS__]['fieldKeys']; } + /** * Returns the names of this table * * @return string */ - static function getTableName() { + public static function getTableName() { return self::$_tableName; } + /** * Returns if this table needs to be logged * - * @return boolean + * @return bool */ - function getLog() { + public function getLog() { return self::$_log; } + /** * Returns the list of fields that can be imported * @@ -296,10 +341,11 @@ function getLog() { * * @return array */ - static function &import($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'relationship', $prefix, array()); + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'relationship', $prefix, []); return $r; } + /** * Returns the list of fields that can be exported * @@ -307,8 +353,21 @@ static function &import($prefix = false) { * * @return array */ - static function &export($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'relationship', $prefix, array()); + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'relationship', $prefix, []); return $r; } + + /** + * Returns the list of indices + * + * @param bool $localize + * + * @return array + */ + public static function indices($localize = TRUE) { + $indices = []; + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + } diff --git a/CRM/Contact/DAO/RelationshipType.php b/CRM/Contact/DAO/RelationshipType.php index 3e72e48b5f03..11bb3d1ba0eb 100644 --- a/CRM/Contact/DAO/RelationshipType.php +++ b/CRM/Contact/DAO/RelationshipType.php @@ -1,306 +1,364 @@ __table = 'civicrm_relationship_type'; parent::__construct(); } + /** * Returns all the column names of this table * * @return array */ - static function &fields() { + public static function &fields() { if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = array( - 'id' => array( + Civi::$statics[__CLASS__]['fields'] = [ + 'id' => [ 'name' => 'id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Relationship Type ID') , - 'description' => 'Primary key', - 'required' => true, - ) , - 'name_a_b' => array( + 'title' => ts('Relationship Type ID'), + 'description' => ts('Primary key'), + 'required' => TRUE, + 'table_name' => 'civicrm_relationship_type', + 'entity' => 'RelationshipType', + 'bao' => 'CRM_Contact_BAO_RelationshipType', + 'localizable' => 0, + ], + 'name_a_b' => [ 'name' => 'name_a_b', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Relationship Type Name A to B') , - 'description' => 'name for relationship of contact_a to contact_b.', + 'title' => ts('Relationship Type Name A to B'), + 'description' => ts('name for relationship of contact_a to contact_b.'), 'maxlength' => 64, 'size' => CRM_Utils_Type::BIG, - ) , - 'label_a_b' => array( + 'table_name' => 'civicrm_relationship_type', + 'entity' => 'RelationshipType', + 'bao' => 'CRM_Contact_BAO_RelationshipType', + 'localizable' => 0, + ], + 'label_a_b' => [ 'name' => 'label_a_b', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Relationship Type Label A to B') , - 'description' => 'label for relationship of contact_a to contact_b.', + 'title' => ts('Relationship Type Label A to B'), + 'description' => ts('label for relationship of contact_a to contact_b.'), 'maxlength' => 64, 'size' => CRM_Utils_Type::BIG, - ) , - 'name_b_a' => array( + 'table_name' => 'civicrm_relationship_type', + 'entity' => 'RelationshipType', + 'bao' => 'CRM_Contact_BAO_RelationshipType', + 'localizable' => 1, + 'html' => [ + 'type' => 'Text', + ], + ], + 'name_b_a' => [ 'name' => 'name_b_a', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Relationship Type Name B to A') , - 'description' => 'Optional name for relationship of contact_b to contact_a.', + 'title' => ts('Relationship Type Name B to A'), + 'description' => ts('Optional name for relationship of contact_b to contact_a.'), 'maxlength' => 64, 'size' => CRM_Utils_Type::BIG, - ) , - 'label_b_a' => array( + 'table_name' => 'civicrm_relationship_type', + 'entity' => 'RelationshipType', + 'bao' => 'CRM_Contact_BAO_RelationshipType', + 'localizable' => 0, + ], + 'label_b_a' => [ 'name' => 'label_b_a', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Relationship Type Label B to A') , - 'description' => 'Optional label for relationship of contact_b to contact_a.', + 'title' => ts('Relationship Type Label B to A'), + 'description' => ts('Optional label for relationship of contact_b to contact_a.'), 'maxlength' => 64, 'size' => CRM_Utils_Type::BIG, - ) , - 'description' => array( + 'table_name' => 'civicrm_relationship_type', + 'entity' => 'RelationshipType', + 'bao' => 'CRM_Contact_BAO_RelationshipType', + 'localizable' => 1, + 'html' => [ + 'type' => 'Text', + ], + ], + 'description' => [ 'name' => 'description', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Relationship Description') , - 'description' => 'Optional verbose description of the relationship type.', + 'title' => ts('Relationship Description'), + 'description' => ts('Optional verbose description of the relationship type.'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - ) , - 'contact_type_a' => array( + 'table_name' => 'civicrm_relationship_type', + 'entity' => 'RelationshipType', + 'bao' => 'CRM_Contact_BAO_RelationshipType', + 'localizable' => 1, + 'html' => [ + 'type' => 'Text', + ], + ], + 'contact_type_a' => [ 'name' => 'contact_type_a', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Contact Type for Contact A') , - 'description' => 'If defined, contact_a in a relationship of this type must be a specific contact_type.', + 'title' => ts('Contact Type for Contact A'), + 'description' => ts('If defined, contact_a in a relationship of this type must be a specific contact_type.'), 'maxlength' => 12, 'size' => CRM_Utils_Type::TWELVE, - 'html' => array( + 'table_name' => 'civicrm_relationship_type', + 'entity' => 'RelationshipType', + 'bao' => 'CRM_Contact_BAO_RelationshipType', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'table' => 'civicrm_contact_type', 'keyColumn' => 'name', 'labelColumn' => 'label', 'condition' => 'parent_id IS NULL', - ) - ) , - 'contact_type_b' => array( + ] + ], + 'contact_type_b' => [ 'name' => 'contact_type_b', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Contact Type for Contact B') , - 'description' => 'If defined, contact_b in a relationship of this type must be a specific contact_type.', + 'title' => ts('Contact Type for Contact B'), + 'description' => ts('If defined, contact_b in a relationship of this type must be a specific contact_type.'), 'maxlength' => 12, 'size' => CRM_Utils_Type::TWELVE, - 'html' => array( + 'table_name' => 'civicrm_relationship_type', + 'entity' => 'RelationshipType', + 'bao' => 'CRM_Contact_BAO_RelationshipType', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'table' => 'civicrm_contact_type', 'keyColumn' => 'name', 'labelColumn' => 'label', 'condition' => 'parent_id IS NULL', - ) - ) , - 'contact_sub_type_a' => array( + ] + ], + 'contact_sub_type_a' => [ 'name' => 'contact_sub_type_a', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Contact Subtype A') , - 'description' => 'If defined, contact_sub_type_a in a relationship of this type must be a specific contact_sub_type. - ', + 'title' => ts('Contact Subtype A'), + 'description' => ts('If defined, contact_sub_type_a in a relationship of this type must be a specific contact_sub_type. + '), 'maxlength' => 64, 'size' => CRM_Utils_Type::BIG, - 'html' => array( + 'table_name' => 'civicrm_relationship_type', + 'entity' => 'RelationshipType', + 'bao' => 'CRM_Contact_BAO_RelationshipType', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'table' => 'civicrm_contact_type', 'keyColumn' => 'name', 'labelColumn' => 'label', 'condition' => 'parent_id IS NOT NULL', - ) - ) , - 'contact_sub_type_b' => array( + ] + ], + 'contact_sub_type_b' => [ 'name' => 'contact_sub_type_b', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Contact Subtype B') , - 'description' => 'If defined, contact_sub_type_b in a relationship of this type must be a specific contact_sub_type. - ', + 'title' => ts('Contact Subtype B'), + 'description' => ts('If defined, contact_sub_type_b in a relationship of this type must be a specific contact_sub_type. + '), 'maxlength' => 64, 'size' => CRM_Utils_Type::BIG, - 'html' => array( + 'table_name' => 'civicrm_relationship_type', + 'entity' => 'RelationshipType', + 'bao' => 'CRM_Contact_BAO_RelationshipType', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'table' => 'civicrm_contact_type', 'keyColumn' => 'name', 'labelColumn' => 'label', 'condition' => 'parent_id IS NOT NULL', - ) - ) , - 'is_reserved' => array( + ] + ], + 'is_reserved' => [ 'name' => 'is_reserved', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Relationship Type is Reserved') , - 'description' => 'Is this relationship type a predefined system type (can not be changed or de-activated)?', - ) , - 'is_active' => array( + 'title' => ts('Relationship Type is Reserved'), + 'description' => ts('Is this relationship type a predefined system type (can not be changed or de-activated)?'), + 'table_name' => 'civicrm_relationship_type', + 'entity' => 'RelationshipType', + 'bao' => 'CRM_Contact_BAO_RelationshipType', + 'localizable' => 0, + 'html' => [ + 'type' => 'CheckBox', + ], + ], + 'is_active' => [ 'name' => 'is_active', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Relationship Type is Active') , - 'description' => 'Is this relationship type currently active (i.e. can be used when creating or editing relationships)? - ', + 'title' => ts('Relationship Type is Active'), + 'description' => ts('Is this relationship type currently active (i.e. can be used when creating or editing relationships)? + '), 'default' => '1', - ) , - ); + 'table_name' => 'civicrm_relationship_type', + 'entity' => 'RelationshipType', + 'bao' => 'CRM_Contact_BAO_RelationshipType', + 'localizable' => 0, + 'html' => [ + 'type' => 'CheckBox', + ], + ], + ]; CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } return Civi::$statics[__CLASS__]['fields']; } + /** * Return a mapping from field-name to the corresponding key (as used in fields()). * * @return array * Array(string $name => string $uniqueName). */ - static function &fieldKeys() { + public static function &fieldKeys() { if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); } return Civi::$statics[__CLASS__]['fieldKeys']; } + /** * Returns the names of this table * * @return string */ - static function getTableName() { + public static function getTableName() { return CRM_Core_DAO::getLocaleTableName(self::$_tableName); } + /** * Returns if this table needs to be logged * - * @return boolean + * @return bool */ - function getLog() { + public function getLog() { return self::$_log; } + /** * Returns the list of fields that can be imported * @@ -308,10 +366,11 @@ function getLog() { * * @return array */ - static function &import($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'relationship_type', $prefix, array()); + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'relationship_type', $prefix, []); return $r; } + /** * Returns the list of fields that can be exported * @@ -319,8 +378,40 @@ static function &import($prefix = false) { * * @return array */ - static function &export($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'relationship_type', $prefix, array()); + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'relationship_type', $prefix, []); return $r; } + + /** + * Returns the list of indices + * + * @param bool $localize + * + * @return array + */ + public static function indices($localize = TRUE) { + $indices = [ + 'UI_name_a_b' => [ + 'name' => 'UI_name_a_b', + 'field' => [ + 0 => 'name_a_b', + ], + 'localizable' => FALSE, + 'unique' => TRUE, + 'sig' => 'civicrm_relationship_type::1::name_a_b', + ], + 'UI_name_b_a' => [ + 'name' => 'UI_name_b_a', + 'field' => [ + 0 => 'name_b_a', + ], + 'localizable' => FALSE, + 'unique' => TRUE, + 'sig' => 'civicrm_relationship_type::1::name_b_a', + ], + ]; + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + } diff --git a/CRM/Contact/DAO/SavedSearch.php b/CRM/Contact/DAO/SavedSearch.php index 58abf71a950f..e1c75e9bac0e 100644 --- a/CRM/Contact/DAO/SavedSearch.php +++ b/CRM/Contact/DAO/SavedSearch.php @@ -1,208 +1,230 @@ __table = 'civicrm_saved_search'; parent::__construct(); } + /** - * Returns foreign keys and entity references + * Returns foreign keys and entity references. * * @return array * [CRM_Core_Reference_Interface] */ - static function getReferenceColumns() { + public static function getReferenceColumns() { if (!isset(Civi::$statics[__CLASS__]['links'])) { - Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'mapping_id', 'civicrm_mapping', 'id'); + Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'mapping_id', 'civicrm_mapping', 'id'); CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']); } return Civi::$statics[__CLASS__]['links']; } + /** * Returns all the column names of this table * * @return array */ - static function &fields() { + public static function &fields() { if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = array( - 'id' => array( + Civi::$statics[__CLASS__]['fields'] = [ + 'id' => [ 'name' => 'id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Saved Search ID') , - 'description' => 'Saved Search ID', - 'required' => true, - ) , - 'form_values' => array( + 'title' => ts('Saved Search ID'), + 'description' => ts('Saved Search ID'), + 'required' => TRUE, + 'table_name' => 'civicrm_saved_search', + 'entity' => 'SavedSearch', + 'bao' => 'CRM_Contact_BAO_SavedSearch', + 'localizable' => 0, + ], + 'form_values' => [ 'name' => 'form_values', 'type' => CRM_Utils_Type::T_TEXT, - 'title' => ts('Submitted Form Values') , - 'description' => 'Submitted form values for this search', - 'import' => true, + 'title' => ts('Submitted Form Values'), + 'description' => ts('Submitted form values for this search'), + 'import' => TRUE, 'where' => 'civicrm_saved_search.form_values', 'headerPattern' => '', 'dataPattern' => '', - 'export' => true, - ) , - 'mapping_id' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_saved_search', + 'entity' => 'SavedSearch', + 'bao' => 'CRM_Contact_BAO_SavedSearch', + 'localizable' => 0, + 'serialize' => self::SERIALIZE_PHP, + ], + 'mapping_id' => [ 'name' => 'mapping_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Mapping ID') , - 'description' => 'Foreign key to civicrm_mapping used for saved search-builder searches.', + 'title' => ts('Mapping ID'), + 'description' => ts('Foreign key to civicrm_mapping used for saved search-builder searches.'), + 'table_name' => 'civicrm_saved_search', + 'entity' => 'SavedSearch', + 'bao' => 'CRM_Contact_BAO_SavedSearch', + 'localizable' => 0, 'FKClassName' => 'CRM_Core_DAO_Mapping', - ) , - 'search_custom_id' => array( + ], + 'search_custom_id' => [ 'name' => 'search_custom_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Option Value ID') , - 'description' => 'Foreign key to civicrm_option value table used for saved custom searches.', - ) , - 'where_clause' => array( + 'title' => ts('Option Value ID'), + 'description' => ts('Foreign key to civicrm_option value table used for saved custom searches.'), + 'table_name' => 'civicrm_saved_search', + 'entity' => 'SavedSearch', + 'bao' => 'CRM_Contact_BAO_SavedSearch', + 'localizable' => 0, + ], + 'where_clause' => [ 'name' => 'where_clause', 'type' => CRM_Utils_Type::T_TEXT, - 'title' => ts('Where Clause') , - 'description' => 'the sql where clause if a saved search acl', - ) , - 'select_tables' => array( + 'title' => ts('Where Clause'), + 'description' => ts('the sql where clause if a saved search acl'), + 'table_name' => 'civicrm_saved_search', + 'entity' => 'SavedSearch', + 'bao' => 'CRM_Contact_BAO_SavedSearch', + 'localizable' => 0, + ], + 'select_tables' => [ 'name' => 'select_tables', 'type' => CRM_Utils_Type::T_TEXT, - 'title' => ts('Select Tables') , - 'description' => 'the tables to be included in a select data', - ) , - 'where_tables' => array( + 'title' => ts('Select Tables'), + 'description' => ts('the tables to be included in a select data'), + 'table_name' => 'civicrm_saved_search', + 'entity' => 'SavedSearch', + 'bao' => 'CRM_Contact_BAO_SavedSearch', + 'localizable' => 0, + 'serialize' => self::SERIALIZE_PHP, + ], + 'where_tables' => [ 'name' => 'where_tables', 'type' => CRM_Utils_Type::T_TEXT, - 'title' => ts('Where Tables') , - 'description' => 'the tables to be included in the count statement', - ) , - ); + 'title' => ts('Where Tables'), + 'description' => ts('the tables to be included in the count statement'), + 'table_name' => 'civicrm_saved_search', + 'entity' => 'SavedSearch', + 'bao' => 'CRM_Contact_BAO_SavedSearch', + 'localizable' => 0, + 'serialize' => self::SERIALIZE_PHP, + ], + ]; CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } return Civi::$statics[__CLASS__]['fields']; } + /** * Return a mapping from field-name to the corresponding key (as used in fields()). * * @return array * Array(string $name => string $uniqueName). */ - static function &fieldKeys() { + public static function &fieldKeys() { if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); } return Civi::$statics[__CLASS__]['fieldKeys']; } + /** * Returns the names of this table * * @return string */ - static function getTableName() { + public static function getTableName() { return self::$_tableName; } + /** * Returns if this table needs to be logged * - * @return boolean + * @return bool */ - function getLog() { + public function getLog() { return self::$_log; } + /** * Returns the list of fields that can be imported * @@ -210,10 +232,11 @@ function getLog() { * * @return array */ - static function &import($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'saved_search', $prefix, array()); + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'saved_search', $prefix, []); return $r; } + /** * Returns the list of fields that can be exported * @@ -221,8 +244,21 @@ static function &import($prefix = false) { * * @return array */ - static function &export($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'saved_search', $prefix, array()); + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'saved_search', $prefix, []); return $r; } + + /** + * Returns the list of indices + * + * @param bool $localize + * + * @return array + */ + public static function indices($localize = TRUE) { + $indices = []; + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + } diff --git a/CRM/Contact/DAO/SubscriptionHistory.php b/CRM/Contact/DAO/SubscriptionHistory.php index 5559618bce33..90f7ea3d8f81 100644 --- a/CRM/Contact/DAO/SubscriptionHistory.php +++ b/CRM/Contact/DAO/SubscriptionHistory.php @@ -1,230 +1,250 @@ __table = 'civicrm_subscription_history'; parent::__construct(); } + /** - * Returns foreign keys and entity references + * Returns foreign keys and entity references. * * @return array * [CRM_Core_Reference_Interface] */ - static function getReferenceColumns() { + public static function getReferenceColumns() { if (!isset(Civi::$statics[__CLASS__]['links'])) { - Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'contact_id', 'civicrm_contact', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'group_id', 'civicrm_group', 'id'); + Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'contact_id', 'civicrm_contact', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'group_id', 'civicrm_group', 'id'); CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']); } return Civi::$statics[__CLASS__]['links']; } + /** * Returns all the column names of this table * * @return array */ - static function &fields() { + public static function &fields() { if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = array( - 'id' => array( + Civi::$statics[__CLASS__]['fields'] = [ + 'id' => [ 'name' => 'id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Group Membership History ID') , - 'description' => 'Internal Id', - 'required' => true, - ) , - 'contact_id' => array( + 'title' => ts('Group Membership History ID'), + 'description' => ts('Internal Id'), + 'required' => TRUE, + 'table_name' => 'civicrm_subscription_history', + 'entity' => 'SubscriptionHistory', + 'bao' => 'CRM_Contact_BAO_SubscriptionHistory', + 'localizable' => 0, + ], + 'contact_id' => [ 'name' => 'contact_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Contact ID') , - 'description' => 'Contact Id', - 'required' => true, + 'title' => ts('Contact ID'), + 'description' => ts('Contact Id'), + 'required' => TRUE, + 'table_name' => 'civicrm_subscription_history', + 'entity' => 'SubscriptionHistory', + 'bao' => 'CRM_Contact_BAO_SubscriptionHistory', + 'localizable' => 0, 'FKClassName' => 'CRM_Contact_DAO_Contact', - ) , - 'group_id' => array( + ], + 'group_id' => [ 'name' => 'group_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Group') , - 'description' => 'Group Id', + 'title' => ts('Group'), + 'description' => ts('Group Id'), + 'table_name' => 'civicrm_subscription_history', + 'entity' => 'SubscriptionHistory', + 'bao' => 'CRM_Contact_BAO_SubscriptionHistory', + 'localizable' => 0, 'FKClassName' => 'CRM_Contact_DAO_Group', - 'html' => array( + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'table' => 'civicrm_group', 'keyColumn' => 'id', 'labelColumn' => 'title', - ) - ) , - 'date' => array( + ] + ], + 'date' => [ 'name' => 'date', - 'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME, - 'title' => ts('Group Membership Action Date') , - 'description' => 'Date of the (un)subscription', - 'required' => true, - ) , - 'method' => array( + 'type' => CRM_Utils_Type::T_TIMESTAMP, + 'title' => ts('Group Membership Action Date'), + 'description' => ts('Date of the (un)subscription'), + 'required' => TRUE, + 'default' => 'CURRENT_TIMESTAMP', + 'table_name' => 'civicrm_subscription_history', + 'entity' => 'SubscriptionHistory', + 'bao' => 'CRM_Contact_BAO_SubscriptionHistory', + 'localizable' => 0, + ], + 'method' => [ 'name' => 'method', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Group Membership Action') , - 'description' => 'How the (un)subscription was triggered', + 'title' => ts('Group Membership Action'), + 'description' => ts('How the (un)subscription was triggered'), 'maxlength' => 8, 'size' => CRM_Utils_Type::EIGHT, - 'html' => array( + 'table_name' => 'civicrm_subscription_history', + 'entity' => 'SubscriptionHistory', + 'bao' => 'CRM_Contact_BAO_SubscriptionHistory', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'callback' => 'CRM_Core_SelectValues::getSubscriptionHistoryMethods', - ) - ) , - 'status' => array( + ] + ], + 'status' => [ 'name' => 'status', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Group Membership Status') , - 'description' => 'The state of the contact within the group', + 'title' => ts('Group Membership Status'), + 'description' => ts('The state of the contact within the group'), 'maxlength' => 8, 'size' => CRM_Utils_Type::EIGHT, - 'pseudoconstant' => array( + 'table_name' => 'civicrm_subscription_history', + 'entity' => 'SubscriptionHistory', + 'bao' => 'CRM_Contact_BAO_SubscriptionHistory', + 'localizable' => 0, + 'pseudoconstant' => [ 'callback' => 'CRM_Core_SelectValues::groupContactStatus', - ) - ) , - 'tracking' => array( + ] + ], + 'tracking' => [ 'name' => 'tracking', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Group Membership Tracking') , - 'description' => 'IP address or other tracking info', + 'title' => ts('Group Membership Tracking'), + 'description' => ts('IP address or other tracking info'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - ) , - ); + 'table_name' => 'civicrm_subscription_history', + 'entity' => 'SubscriptionHistory', + 'bao' => 'CRM_Contact_BAO_SubscriptionHistory', + 'localizable' => 0, + ], + ]; CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } return Civi::$statics[__CLASS__]['fields']; } + /** * Return a mapping from field-name to the corresponding key (as used in fields()). * * @return array * Array(string $name => string $uniqueName). */ - static function &fieldKeys() { + public static function &fieldKeys() { if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); } return Civi::$statics[__CLASS__]['fieldKeys']; } + /** * Returns the names of this table * * @return string */ - static function getTableName() { + public static function getTableName() { return self::$_tableName; } + /** * Returns if this table needs to be logged * - * @return boolean + * @return bool */ - function getLog() { + public function getLog() { return self::$_log; } + /** * Returns the list of fields that can be imported * @@ -232,10 +252,11 @@ function getLog() { * * @return array */ - static function &import($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'subscription_history', $prefix, array()); + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'subscription_history', $prefix, []); return $r; } + /** * Returns the list of fields that can be exported * @@ -243,8 +264,21 @@ static function &import($prefix = false) { * * @return array */ - static function &export($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'subscription_history', $prefix, array()); + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'subscription_history', $prefix, []); return $r; } + + /** + * Returns the list of indices + * + * @param bool $localize + * + * @return array + */ + public static function indices($localize = TRUE) { + $indices = []; + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + } diff --git a/CRM/Contact/Form/Contact.php b/CRM/Contact/Form/Contact.php index 1c04b66e516b..f275cfa377f4 100644 --- a/CRM/Contact/Form/Contact.php +++ b/CRM/Contact/Form/Contact.php @@ -1,9 +1,9 @@ _contactType, - array('Individual', 'Household', 'Organization') + ['Individual', 'Household', 'Organization'] ) ) { CRM_Core_Error::statusBounce(ts('Could not get a contact id and/or contact type')); @@ -177,7 +180,7 @@ public function preProcess() { $this->_contactSubType && !(CRM_Contact_BAO_ContactType::isExtendsContactType($this->_contactSubType, $this->_contactType, TRUE)) ) { - CRM_Core_Error::statusBounce(ts("Could not get a valid contact subtype for contact type '%1'", array(1 => $this->_contactType))); + CRM_Core_Error::statusBounce(ts("Could not get a valid contact subtype for contact type '%1'", [1 => $this->_contactType])); } $this->_gid = CRM_Utils_Request::retrieve('gid', 'Integer', @@ -193,7 +196,7 @@ public function preProcess() { ); $typeLabel = implode(' / ', $typeLabel); - CRM_Utils_System::setTitle(ts('New %1', array(1 => $typeLabel))); + CRM_Utils_System::setTitle(ts('New %1', [1 => $typeLabel])); $session->pushUserContext(CRM_Utils_System::url('civicrm/dashboard', 'reset=1')); $this->_contactId = NULL; } @@ -204,13 +207,13 @@ public function preProcess() { } if ($this->_contactId) { - $defaults = array(); - $params = array('id' => $this->_contactId); - $returnProperities = array('id', 'contact_type', 'contact_sub_type', 'modified_date', 'is_deceased'); + $defaults = []; + $params = ['id' => $this->_contactId]; + $returnProperities = ['id', 'contact_type', 'contact_sub_type', 'modified_date', 'is_deceased']; CRM_Core_DAO::commonRetrieve('CRM_Contact_DAO_Contact', $params, $defaults, $returnProperities); if (empty($defaults['id'])) { - CRM_Core_Error::statusBounce(ts('A Contact with that ID does not exist: %1', array(1 => $this->_contactId))); + CRM_Core_Error::statusBounce(ts('A Contact with that ID does not exist: %1', [1 => $this->_contactId])); } $this->_contactType = CRM_Utils_Array::value('contact_type', $defaults); @@ -226,7 +229,7 @@ public function preProcess() { if ($defaults['is_deceased']) { $displayName .= ' (deceased)'; } - $displayName = ts('Edit %1', array(1 => $displayName)); + $displayName = ts('Edit %1', [1 => $displayName]); // Check if this is default domain contact CRM-10482 if (CRM_Contact_BAO_Contact::checkDomainContact($this->_contactId)) { @@ -235,7 +238,7 @@ public function preProcess() { // omitting contactImage from title for now since the summary overlay css doesn't work outside of our crm-container CRM_Utils_System::setTitle($displayName); - $context = CRM_Utils_Request::retrieve('context', 'String', $this); + $context = CRM_Utils_Request::retrieve('context', 'Alphanumeric', $this); $qfKey = CRM_Utils_Request::retrieve('key', 'String', $this); $urlParams = 'reset=1&cid=' . $this->_contactId; @@ -256,13 +259,13 @@ public function preProcess() { $this->_values = $values; } else { - $params = array( + $params = [ 'id' => $this->_contactId, 'contact_id' => $this->_contactId, 'noRelationships' => TRUE, 'noNotes' => TRUE, 'noGroups' => TRUE, - ); + ]; $contact = CRM_Contact_BAO_Contact::retrieve($params, $this->_values, TRUE); $this->set('values', $this->_values); @@ -374,7 +377,7 @@ public function preProcess() { $this->assign('paramSubType', $paramSubType); } - if (CRM_Utils_Request::retrieve('type', 'String', CRM_Core_DAO::$_nullObject)) { + if (CRM_Utils_Request::retrieve('type', 'String')) { CRM_Contact_Form_Edit_CustomData::preProcess($this); } else { @@ -430,7 +433,7 @@ public function setDefaultValues() { } // set defaults for blocks ( custom data, address, communication preference, notes, tags and groups ) foreach ($this->_editOptions as $name => $label) { - if (!in_array($name, array('Address', 'Notes'))) { + if (!in_array($name, ['Address', 'Notes'])) { $className = 'CRM_Contact_Form_Edit_' . $name; $className::setDefaultValues($this, $defaults); } @@ -440,13 +443,7 @@ public function setDefaultValues() { CRM_Contact_Form_Edit_Address::setDefaultValues($defaults, $this); if (!empty($defaults['image_URL'])) { - list($imageWidth, $imageHeight) = getimagesize(CRM_Utils_String::unstupifyUrl($defaults['image_URL'])); - list($imageThumbWidth, $imageThumbHeight) = CRM_Contact_BAO_Contact::getThumbSize($imageWidth, $imageHeight); - $this->assign('imageWidth', $imageWidth); - $this->assign('imageHeight', $imageHeight); - $this->assign('imageThumbWidth', $imageThumbWidth); - $this->assign('imageThumbHeight', $imageThumbHeight); - $this->assign('imageURL', $defaults['image_URL']); + $this->assign("imageURL", CRM_Utils_File::getImageURL($defaults['image_URL'])); } //set location type and country to default for each block @@ -567,19 +564,19 @@ public function addRules() { return; } - $this->addFormRule(array('CRM_Contact_Form_Edit_' . $this->_contactType, 'formRule'), $this->_contactId); + $this->addFormRule(['CRM_Contact_Form_Edit_' . $this->_contactType, 'formRule'], $this->_contactId); // Call Locking check if editing existing contact if ($this->_contactId) { - $this->addFormRule(array('CRM_Contact_Form_Edit_Lock', 'formRule'), $this->_contactId); + $this->addFormRule(['CRM_Contact_Form_Edit_Lock', 'formRule'], $this->_contactId); } if (array_key_exists('Address', $this->_editOptions)) { - $this->addFormRule(array('CRM_Contact_Form_Edit_Address', 'formRule'), $this); + $this->addFormRule(['CRM_Contact_Form_Edit_Address', 'formRule'], $this); } if (array_key_exists('CommunicationPreferences', $this->_editOptions)) { - $this->addFormRule(array('CRM_Contact_Form_Edit_CommunicationPreferences', 'formRule'), $this); + $this->addFormRule(['CRM_Contact_Form_Edit_CommunicationPreferences', 'formRule'], $this); } } @@ -592,11 +589,12 @@ public function addRules() { * List of errors to be posted back to the form. * @param int $contactId * Contact id if doing update. + * @param string $contactType * * @return bool * email/openId */ - public static function formRule($fields, &$errors, $contactId = NULL) { + public static function formRule($fields, &$errors, $contactId, $contactType) { $config = CRM_Core_Config::singleton(); // validations. @@ -621,10 +619,11 @@ public static function formRule($fields, &$errors, $contactId = NULL) { $blocks['Address'] = $otherEditOptions['Address']; } - $openIds = array(); + $website_types = []; + $openIds = []; $primaryID = FALSE; foreach ($blocks as $name => $label) { - $hasData = $hasPrimary = array(); + $hasData = $hasPrimary = []; $name = strtolower($name); if (!empty($fields[$name]) && is_array($fields[$name])) { foreach ($fields[$name] as $instance => $blockValues) { @@ -635,8 +634,17 @@ public static function formRule($fields, &$errors, $contactId = NULL) { } if ($dataExists) { - // skip remaining checks for website if ($name == 'website') { + if (!empty($blockValues['website_type_id'])) { + if (empty($website_types[$blockValues['website_type_id']])) { + $website_types[$blockValues['website_type_id']] = $blockValues['website_type_id']; + } + else { + $errors["{$name}[1][website_type_id]"] = ts('Contacts may only have one website of each type at most.'); + } + } + + // skip remaining checks for website continue; } @@ -644,17 +652,17 @@ public static function formRule($fields, &$errors, $contactId = NULL) { if (!empty($blockValues['is_primary'])) { $hasPrimary[] = $instance; if (!$primaryID && - in_array($name, array( + in_array($name, [ 'email', 'openid', - )) && !empty($blockValues[$name]) + ]) && !empty($blockValues[$name]) ) { $primaryID = $blockValues[$name]; } } if (empty($blockValues['location_type_id'])) { - $errors["{$name}[$instance][location_type_id]"] = ts('The Location Type should be set if there is %1 information.', array(1 => $label)); + $errors["{$name}[$instance][location_type_id]"] = ts('The Location Type should be set if there is %1 information.', [1 => $label]); } } @@ -663,18 +671,18 @@ public static function formRule($fields, &$errors, $contactId = NULL) { $oid->openid = $openIds[$instance] = CRM_Utils_Array::value($name, $blockValues); $cid = isset($contactId) ? $contactId : 0; if ($oid->find(TRUE) && ($oid->contact_id != $cid)) { - $errors["{$name}[$instance][openid]"] = ts('%1 already exist.', array(1 => $blocks['OpenID'])); + $errors["{$name}[$instance][openid]"] = ts('%1 already exist.', [1 => $blocks['OpenID']]); } } } if (empty($hasPrimary) && !empty($hasData)) { - $errors["{$name}[1][is_primary]"] = ts('One %1 should be marked as primary.', array(1 => $label)); + $errors["{$name}[1][is_primary]"] = ts('One %1 should be marked as primary.', [1 => $label]); } if (count($hasPrimary) > 1) { $errors["{$name}[" . array_pop($hasPrimary) . "][is_primary]"] = ts('Only one %1 can be marked as primary.', - array(1 => $label) + [1 => $label] ); } } @@ -684,7 +692,7 @@ public static function formRule($fields, &$errors, $contactId = NULL) { if (!empty($openIds) && (count(array_unique($openIds)) != count($openIds))) { foreach ($openIds as $instance => $value) { if (!array_key_exists($instance, array_unique($openIds))) { - $errors["openid[$instance][openid]"] = ts('%1 already used.', array(1 => $blocks['OpenID'])); + $errors["openid[$instance][openid]"] = ts('%1 already used.', [1 => $blocks['OpenID']]); } } } @@ -699,7 +707,7 @@ public static function formRule($fields, &$errors, $contactId = NULL) { if (isset($fields['address']) && is_array($fields['address']) ) { - $invalidStreetNumbers = array(); + $invalidStreetNumbers = []; foreach ($fields['address'] as $cnt => $address) { if ($streetNumber = CRM_Utils_Array::value('street_number', $address)) { $parsedAddress = CRM_Core_BAO_Address::parseStreetAddress($address['street_number']); @@ -714,11 +722,16 @@ public static function formRule($fields, &$errors, $contactId = NULL) { foreach ($invalidStreetNumbers as & $num) { $num = CRM_Contact_Form_Contact::ordinalNumber($num); } - $errors["address[$first][street_number]"] = ts('The street number you entered for the %1 address block(s) is not in an expected format. Street numbers may include numeric digit(s) followed by other characters. You can still enter the complete street address (unparsed) by clicking "Edit Complete Street Address".', array(1 => implode(', ', $invalidStreetNumbers))); + $errors["address[$first][street_number]"] = ts('The street number you entered for the %1 address block(s) is not in an expected format. Street numbers may include numeric digit(s) followed by other characters. You can still enter the complete street address (unparsed) by clicking "Edit Complete Street Address".', [1 => implode(', ', $invalidStreetNumbers)]); } } } + // Check for duplicate contact if it wasn't already handled by ajax or disabled + if (!Civi::settings()->get('contact_ajax_check_similar') || !empty($fields['_qf_Contact_refresh_dedupe'])) { + self::checkDuplicateContacts($fields, $errors, $contactId, $contactType); + } + return $primaryID; } @@ -734,19 +747,19 @@ public function buildQuickForm() { if ($this->_action == CRM_Core_Action::UPDATE) { $deleteExtra = json_encode(ts('Are you sure you want to delete contact image.')); - $deleteURL = array( - CRM_Core_Action::DELETE => array( + $deleteURL = [ + CRM_Core_Action::DELETE => [ 'name' => ts('Delete Contact Image'), 'url' => 'civicrm/contact/image', 'qs' => 'reset=1&cid=%%id%%&action=delete', 'extra' => 'onclick = "' . htmlspecialchars("if (confirm($deleteExtra)) this.href+='&confirmed=1'; else return false;") . '"', - ), - ); + ], + ]; $deleteURL = CRM_Core_Action::formLink($deleteURL, CRM_Core_Action::DELETE, - array( + [ 'id' => $this->_contactId, - ), + ], ts('more'), FALSE, 'contact.image.delete', @@ -760,6 +773,14 @@ public function buildQuickForm() { $className = 'CRM_Contact_Form_Edit_' . $this->_contactType; $className::buildQuickForm($this); + // Ajax duplicate checking + $checkSimilar = Civi::settings()->get('contact_ajax_check_similar'); + $this->assign('checkSimilar', $checkSimilar); + if ($checkSimilar == 1) { + $ruleParams = ['used' => 'Supervised', 'contact_type' => $this->_contactType]; + $this->assign('ruleFields', CRM_Dedupe_BAO_Rule::dedupeRuleFields($ruleParams)); + } + // build Custom data if Custom data present in edit option $buildCustomData = 'noCustomDataPresent'; if (array_key_exists('CustomData', $this->_editOptions)) { @@ -767,15 +788,15 @@ public function buildQuickForm() { } // subtype is a common field. lets keep it here - $subtypes = CRM_Contact_BAO_Contact::buildOptions('contact_sub_type', 'create', array('contact_type' => $this->_contactType)); + $subtypes = CRM_Contact_BAO_Contact::buildOptions('contact_sub_type', 'create', ['contact_type' => $this->_contactType]); if (!empty($subtypes)) { - $this->addField('contact_sub_type', array( + $this->addField('contact_sub_type', [ 'label' => ts('Contact Type'), 'options' => $subtypes, 'class' => $buildCustomData, 'multiple' => 'multiple', 'option_url' => NULL, - )); + ]); } // build edit blocks ( custom data, demographics, communication preference, notes, tags and groups ) @@ -800,7 +821,7 @@ public function buildQuickForm() { CRM_Contact_Form_Location::buildQuickForm($this); // add attachment - $this->addField('image_URL', array('maxlength' => '255', 'label' => ts('Browse/Upload Image'))); + $this->addField('image_URL', ['maxlength' => '255', 'label' => ts('Browse/Upload Image')]); // add the dedupe button $this->addElement('submit', @@ -816,26 +837,26 @@ public function buildQuickForm() { ts('Save With Duplicate Household') ); - $buttons = array( - array( + $buttons = [ + [ 'type' => 'upload', 'name' => ts('Save'), 'subName' => 'view', 'isDefault' => TRUE, - ), - ); + ], + ]; if (CRM_Core_Permission::check('add contacts')) { - $buttons[] = array( + $buttons[] = [ 'type' => 'upload', 'name' => ts('Save and New'), 'spacing' => '                 ', 'subName' => 'new', - ); + ]; } - $buttons[] = array( + $buttons[] = [ 'type' => 'cancel', 'name' => ts('Cancel'), - ); + ]; if (!empty($this->_values['contact_sub_type'])) { $this->_oldSubtypes = explode(CRM_Core_DAO::VALUE_SEPARATOR, @@ -859,6 +880,10 @@ public function postProcess() { //get the submitted values in an array $params = $this->controller->exportValues($this->_name); + if (!isset($params['preferred_communication_method'])) { + // If this field is empty QF will trim it so we have to add it in. + $params['preferred_communication_method'] = 'null'; + } $group = CRM_Utils_Array::value('group', $params); if (!empty($group) && is_array($group)) { @@ -868,8 +893,6 @@ public function postProcess() { } } - CRM_Contact_BAO_Contact_Optimizer::edit($params, $this->_preEditValues); - if (!empty($params['image_URL'])) { CRM_Contact_BAO_Contact::processImageParams($params); } @@ -887,7 +910,7 @@ public function postProcess() { $params['contact_type'] = $this->_contactType; if (empty($params['contact_sub_type']) && $this->_isContactSubType) { - $params['contact_sub_type'] = array($this->_contactSubType); + $params['contact_sub_type'] = [$this->_contactSubType]; } if ($this->_contactId) { @@ -902,11 +925,11 @@ public function postProcess() { if (isset($params['contact_id'])) { // process membership status for deceased contact - $deceasedParams = array( + $deceasedParams = [ 'contact_id' => CRM_Utils_Array::value('contact_id', $params), 'is_deceased' => CRM_Utils_Array::value('is_deceased', $params, FALSE), 'deceased_date' => CRM_Utils_Array::value('deceased_date', $params, NULL), - ); + ]; $updateMembershipMsg = $this->updateMembershipStatus($deceasedParams); } @@ -918,8 +941,6 @@ public function postProcess() { CRM_Utils_Hook::pre('create', $params['contact_type'], NULL, $params); } - $customFields = CRM_Core_BAO_CustomField::getFields($params['contact_type'], FALSE, TRUE); - //CRM-5143 //if subtype is set, send subtype as extend to validate subtype customfield $customFieldExtends = (CRM_Utils_Array::value('contact_sub_type', $params)) ? $params['contact_sub_type'] : $params['contact_type']; @@ -945,11 +966,10 @@ public function postProcess() { // process shared contact address. CRM_Contact_BAO_Contact_Utils::processSharedAddress($params['address']); - if (!array_key_exists('TagsAndGroups', $this->_editOptions) && !empty($params['group'])) { + if (!array_key_exists('TagsAndGroups', $this->_editOptions)) { unset($params['group']); } - - if (!empty($params['contact_id']) && ($this->_action & CRM_Core_Action::UPDATE) && !empty($params['group'])) { + elseif (!empty($params['contact_id']) && ($this->_action & CRM_Core_Action::UPDATE)) { // figure out which all groups are intended to be removed $contactGroupList = CRM_Contact_BAO_GroupContact::getContactGroup($params['contact_id'], 'Added'); if (is_array($contactGroupList)) { @@ -968,7 +988,7 @@ public function postProcess() { $parseStatusMsg = self::parseAddressStatusMsg($parseResult); } - $blocks = array('email', 'phone', 'im', 'openid', 'address', 'website'); + $blocks = ['email', 'phone', 'im', 'openid', 'address', 'website']; foreach ($blocks as $block) { if (!empty($this->_preEditValues[$block]) && is_array($this->_preEditValues[$block])) { foreach ($this->_preEditValues[$block] as $count => $value) { @@ -987,10 +1007,10 @@ public function postProcess() { // status message if ($this->_contactId) { - $message = ts('%1 has been updated.', array(1 => $contact->display_name)); + $message = ts('%1 has been updated.', [1 => $contact->display_name]); } else { - $message = ts('%1 has been created.', array(1 => $contact->display_name)); + $message = ts('%1 has been created.', [1 => $contact->display_name]); } // set the contact ID @@ -998,8 +1018,10 @@ public function postProcess() { if (array_key_exists('TagsAndGroups', $this->_editOptions)) { //add contact to tags - CRM_Core_BAO_EntityTag::create($params['tag'], 'civicrm_contact', $params['contact_id']); - + if (isset($params['tag'])) { + $params['tag'] = array_flip(explode(',', $params['tag'])); + CRM_Core_BAO_EntityTag::create($params['tag'], 'civicrm_contact', $params['contact_id']); + } //save free tags if (isset($params['contact_taglist']) && !empty($params['contact_taglist'])) { CRM_Core_Form_Tag::postProcess($params['contact_taglist'], $params['contact_id'], 'civicrm_contact', $this); @@ -1017,7 +1039,7 @@ public function postProcess() { $session->setStatus($message, ts('Contact Saved'), 'success'); // add the recently viewed contact - $recentOther = array(); + $recentOther = []; if (($session->get('userID') == $contact->id) || CRM_Contact_BAO_Contact_Permission::allow($contact->id, CRM_Core_Permission::EDIT) ) { @@ -1046,7 +1068,7 @@ public function postProcess() { $session->replaceUserContext(CRM_Utils_System::url('civicrm/contact/add', $resetStr)); } else { - $context = CRM_Utils_Request::retrieve('context', 'String', $this); + $context = CRM_Utils_Request::retrieve('context', 'Alphanumeric', $this); $qfKey = CRM_Utils_Request::retrieve('key', 'String', $this); //validate the qfKey $urlParams = 'reset=1&cid=' . $contact->id; @@ -1083,7 +1105,7 @@ public static function blockDataExists(&$fields) { return FALSE; } - static $skipFields = array( + static $skipFields = [ 'location_type_id', 'is_primary', 'phone_type_id', @@ -1091,7 +1113,7 @@ public static function blockDataExists(&$fields) { 'country_id', 'website_type_id', 'master_id', - ); + ]; foreach ($fields as $name => $value) { $skipField = FALSE; foreach ($skipFields as $skip) { @@ -1136,28 +1158,27 @@ public static function checkDuplicateContacts(&$fields, &$errors, $contactID, $c // if this is a forced save, ignore find duplicate rule if (empty($fields['_qf_Contact_upload_duplicate'])) { - $dedupeParams = CRM_Dedupe_Finder::formatParams($fields, $contactType); - $ids = CRM_Dedupe_Finder::dupesByParams($dedupeParams, $contactType, 'Supervised', array($contactID)); + $ids = CRM_Contact_BAO_Contact::getDuplicateContacts($fields, $contactType, 'Supervised', [$contactID]); if ($ids) { $contactLinks = CRM_Contact_BAO_Contact_Utils::formatContactIDSToLinks($ids, TRUE, TRUE, $contactID); $duplicateContactsLinks = '
'; - $duplicateContactsLinks .= ts('One matching contact was found. ', array( - 'count' => count($contactLinks['rows']), - 'plural' => '%count matching contacts were found.
', - )); + $duplicateContactsLinks .= ts('One matching contact was found. ', [ + 'count' => count($contactLinks['rows']), + 'plural' => '%count matching contacts were found.
', + ]); if ($contactLinks['msg'] == 'view') { - $duplicateContactsLinks .= ts('You can View the existing contact', array( - 'count' => count($contactLinks['rows']), - 'plural' => 'You can View the existing contacts', - )); + $duplicateContactsLinks .= ts('You can View the existing contact', [ + 'count' => count($contactLinks['rows']), + 'plural' => 'You can View the existing contacts', + ]); } else { - $duplicateContactsLinks .= ts('You can View or Edit the existing contact', array( - 'count' => count($contactLinks['rows']), - 'plural' => 'You can View or Edit the existing contacts', - )); + $duplicateContactsLinks .= ts('You can View or Edit the existing contact', [ + 'count' => count($contactLinks['rows']), + 'plural' => 'You can View or Edit the existing contacts', + ]); } if ($contactLinks['msg'] == 'merge') { // We should also get a merge link if this is for an existing contact @@ -1170,14 +1191,14 @@ public static function checkDuplicateContacts(&$fields, &$errors, $contactID, $c for ($i = 0; $i < count($contactLinks['rows']); $i++) { $row .= ' '; $row .= ' '; - $row .= $contactLinks['rows'][$i]['display_name']; + $row .= CRM_Utils_Array::value('display_name', $contactLinks['rows'][$i]); $row .= ' '; $row .= ' '; - $row .= $contactLinks['rows'][$i]['primary_email']; + $row .= CRM_Utils_Array::value('primary_email', $contactLinks['rows'][$i]); $row .= ' '; $row .= ' '; - $row .= $contactLinks['rows'][$i]['view']; - $row .= $contactLinks['rows'][$i]['edit']; + $row .= CRM_Utils_Array::value('view', $contactLinks['rows'][$i]); + $row .= CRM_Utils_Array::value('edit', $contactLinks['rows'][$i]); $row .= CRM_Utils_Array::value('merge', $contactLinks['rows'][$i]); $row .= ' '; $row .= ' '; @@ -1228,7 +1249,7 @@ public function getTemplateFileName() { * as array of success/fails for each address block */ public function parseAddress(&$params) { - $parseSuccess = $parsedFields = array(); + $parseSuccess = $parsedFields = []; if (!is_array($params['address']) || CRM_Utils_System::isNull($params['address']) ) { @@ -1238,11 +1259,11 @@ public function parseAddress(&$params) { foreach ($params['address'] as $instance => & $address) { $buildStreetAddress = FALSE; $parseFieldName = 'street_address'; - foreach (array( - 'street_number', - 'street_name', - 'street_unit', - ) as $fld) { + foreach ([ + 'street_number', + 'street_name', + 'street_unit', + ] as $fld) { if (!empty($address[$fld])) { $parseFieldName = 'street_number'; $buildStreetAddress = TRUE; @@ -1270,16 +1291,16 @@ public function parseAddress(&$params) { $address['street_number'] = $parsedFields['street_number']; $streetAddress = NULL; - foreach (array( - 'street_number', - 'street_number_suffix', - 'street_name', - 'street_unit', - ) as $fld) { - if (in_array($fld, array( + foreach ([ + 'street_number', + 'street_number_suffix', + 'street_name', + 'street_unit', + ] as $fld) { + if (in_array($fld, [ 'street_name', 'street_unit', - ))) { + ])) { $streetAddress .= ' '; } // CRM-17619 - if the street number suffix begins with a number, add a space @@ -1336,7 +1357,7 @@ public static function parseAddressStatusMsg($parseResult) { return $statusMsg; } - $parseFails = array(); + $parseFails = []; foreach ($parseResult as $instance => $success) { if (!$success) { $parseFails[] = self::ordinalNumber($instance); @@ -1345,7 +1366,7 @@ public static function parseAddressStatusMsg($parseResult) { if (!empty($parseFails)) { $statusMsg = ts("Complete street address(es) have been saved. However we were unable to split the address in the %1 address block(s) into address elements (street number, street name, street unit) due to an unrecognized address format. You can set the address elements manually by clicking 'Edit Address Elements' next to the Street Address field while in edit mode.", - array(1 => implode(', ', $parseFails)) + [1 => implode(', ', $parseFails)] ); } @@ -1442,7 +1463,7 @@ public function updateMembershipStatus($deceasedParams) { ); // add membership log - $membershipLog = array( + $membershipLog = [ 'membership_id' => $dao->id, 'status_id' => $deceasedStatusId, 'start_date' => CRM_Utils_Date::isoToMysql($dao->start_date), @@ -1451,12 +1472,12 @@ public function updateMembershipStatus($deceasedParams) { 'modified_date' => date('Ymd'), 'membership_type_id' => $dao->membership_type_id, 'max_related' => $dao->max_related, - ); + ]; - CRM_Member_BAO_MembershipLog::add($membershipLog, CRM_Core_DAO::$_nullArray); + CRM_Member_BAO_MembershipLog::add($membershipLog); //create activity when membership status is changed - $activityParam = array( + $activityParam = [ 'subject' => "Status changed from {$allStatus[$dao->status_id]} to {$allStatus[$deceasedStatusId]}", 'source_contact_id' => $userId, 'target_contact_id' => $dao->contact_id, @@ -1469,7 +1490,7 @@ public function updateMembershipStatus($deceasedParams) { 'is_auto' => 0, 'is_current_revision' => 1, 'is_deleted' => 0, - ); + ]; $activityResult = civicrm_api('activity', 'create', $activityParam); $memCount++; @@ -1478,7 +1499,7 @@ public function updateMembershipStatus($deceasedParams) { // set status msg if ($memCount) { $updateMembershipMsg = ts("%1 Current membership(s) for this contact have been set to 'Deceased' status.", - array(1 => $memCount) + [1 => $memCount] ); } } diff --git a/CRM/Contact/Form/CustomData.php b/CRM/Contact/Form/CustomData.php index bbb3c4e09014..ddd06219bb0f 100644 --- a/CRM/Contact/Form/CustomData.php +++ b/CRM/Contact/Form/CustomData.php @@ -1,9 +1,9 @@ _groupID); $mode = CRM_Utils_Request::retrieve('mode', 'String', CRM_Core_DAO::$_nullObject, FALSE, NULL, 'GET'); $mode = ucfirst($mode); - CRM_Utils_System::setTitle(ts('%1 %2 Record', array(1 => $mode, 2 => $groupTitle))); + CRM_Utils_System::setTitle(ts('%1 %2 Record', [1 => $mode, 2 => $groupTitle])); if (!empty($_POST['hidden_custom'])) { $this->assign('postedInfo', TRUE); @@ -177,23 +177,22 @@ public function buildQuickForm() { if ($isMultiple) { $this->assign('multiRecordDisplay', $this->_multiRecordDisplay); $saveButtonName = $this->_copyValueId ? ts('Save a Copy') : ts('Save'); - $this->addButtons(array( - array( - 'type' => 'upload', - 'name' => $saveButtonName, - 'isDefault' => TRUE, - ), - array( - 'type' => 'upload', - 'name' => ts('Save and New'), - 'subName' => 'new', - ), - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'upload', + 'name' => $saveButtonName, + 'isDefault' => TRUE, + ], + [ + 'type' => 'upload', + 'name' => ts('Save and New'), + 'subName' => 'new', + ], + [ + 'type' => 'cancel', + 'name' => ts('Cancel'), + ], + ]); } } return CRM_Custom_Form_CustomData::buildQuickForm($this); @@ -205,18 +204,17 @@ public function buildQuickForm() { // make this form an upload since we dont know if the custom data injected dynamically // is of type file etc - $this->addButtons(array( - array( - 'type' => 'upload', - 'name' => ts('Save'), - 'isDefault' => TRUE, - ), - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'upload', + 'name' => ts('Save'), + 'isDefault' => TRUE, + ], + [ + 'type' => 'cancel', + 'name' => ts('Cancel'), + ], + ]); } /** @@ -231,11 +229,18 @@ public function setDefaultValues() { if ($this->_copyValueId) { // cached tree is fetched $groupTree = CRM_Core_BAO_CustomGroup::getTree($this->_type, - $this, + NULL, $this->_entityId, - $this->_groupID + $this->_groupID, + [], + NULL, + TRUE, + NULL, + FALSE, + TRUE, + $this->_copyValueId ); - $valueIdDefaults = array(); + $valueIdDefaults = []; $groupTreeValueId = CRM_Core_BAO_CustomGroup::formatGroupTree($groupTree, $this->_copyValueId, $this); CRM_Core_BAO_CustomGroup::setDefaults($groupTreeValueId, $valueIdDefaults, FALSE, FALSE, $this->get('action')); $tableId = $groupTreeValueId[$this->_groupID]['table_id']; @@ -254,7 +259,7 @@ public function setDefaultValues() { } $groupTree = CRM_Core_BAO_CustomGroup::getTree($this->_contactType, - $this, + NULL, $this->_tableID, $this->_groupID, $this->_contactSubType @@ -262,7 +267,7 @@ public function setDefaultValues() { if (empty($_POST['hidden_custom_group_count'])) { // custom data building in edit mode (required to handle multi-value) - $groupTree = CRM_Core_BAO_CustomGroup::getTree($this->_contactType, $this, $this->_tableID, + $groupTree = CRM_Core_BAO_CustomGroup::getTree($this->_contactType, NULL, $this->_tableID, $this->_groupID, $this->_contactSubType ); $customValueCount = CRM_Core_BAO_CustomGroup::buildCustomDataView($this, $groupTree, TRUE, $this->_groupID, NULL, NULL, $this->_tableID); @@ -273,7 +278,7 @@ public function setDefaultValues() { $this->assign('customValueCount', $customValueCount); - $defaults = array(); + $defaults = []; return $defaults; } diff --git a/CRM/Contact/Form/DedupeFind.php b/CRM/Contact/Form/DedupeFind.php index 966ce55714b4..7f9ebaec2466 100644 --- a/CRM/Contact/Form/DedupeFind.php +++ b/CRM/Contact/Form/DedupeFind.php @@ -1,9 +1,9 @@ ts('- All Contacts -')) + CRM_Core_PseudoConstant::nestedGroup(); + $groupList = ['' => ts('- All Contacts -')] + CRM_Core_PseudoConstant::nestedGroup(); - $this->add('select', 'group_id', ts('Select Group'), $groupList, FALSE, array('class' => 'crm-select2 huge')); + $this->add('select', 'group_id', ts('Select Group'), $groupList, FALSE, ['class' => 'crm-select2 huge']); if (Civi::settings()->get('dedupe_default_limit')) { $this->add('text', 'limit', ts('No of contacts to find matches for ')); } - $this->addButtons(array( - array( - 'type' => 'next', - 'name' => ts('Continue'), - 'isDefault' => TRUE, - ), - //hack to support cancel button functionality - array( - 'type' => 'submit', - 'class' => 'cancel', - 'icon' => 'fa-times', - 'name' => ts('Cancel'), - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'next', + 'name' => ts('Continue'), + 'isDefault' => TRUE, + ], + //hack to support cancel button functionality + [ + 'type' => 'submit', + 'class' => 'cancel', + 'icon' => 'fa-times', + 'name' => ts('Cancel'), + ], + ]); } + /** + * Set the default values for the form. + * + * @return array + */ public function setDefaultValues() { $this->_defaults['limit'] = Civi::settings()->get('dedupe_default_limit'); return $this->_defaults; diff --git a/CRM/Contact/Form/DedupeRules.php b/CRM/Contact/Form/DedupeRules.php index d3d36ccc8f6e..82bd6ef93f6a 100644 --- a/CRM/Contact/Form/DedupeRules.php +++ b/CRM/Contact/Form/DedupeRules.php @@ -1,9 +1,9 @@ _options = CRM_Core_SelectValues::getDedupeRuleTypes(); $this->_rgid = CRM_Utils_Request::retrieve('id', 'Positive', $this, FALSE, 0); - $this->_contactType = CRM_Utils_Request::retrieve('contact_type', 'String', $this, FALSE, 0); + + // check if $contactType is valid + $contactTypes = civicrm_api3('Contact', 'getOptions', ['field' => "contact_type", 'context' => "validate"]); + $contactType = CRM_Utils_Request::retrieve('contact_type', 'String', $this, FALSE, 0); + if (CRM_Utils_Array::value($contactType, $contactTypes['values'])) { + $this->_contactType = $contactType; + } + elseif (!empty($contactType)) { + throw new CRM_Core_Exception('Contact Type is Not valid'); + } if ($this->_rgid) { $rgDao = new CRM_Dedupe_DAO_RuleGroup(); $rgDao->id = $this->_rgid; @@ -98,38 +107,38 @@ public function preProcess() { * Build the form object. */ public function buildQuickForm() { - $this->addField('title', array('label' => ts('Rule Name')), TRUE); + $this->addField('title', ['label' => ts('Rule Name')], TRUE); $this->addRule('title', ts('A duplicate matching rule with this name already exists. Please select another name.'), - 'objectExists', array('CRM_Dedupe_DAO_RuleGroup', $this->_rgid, 'title') + 'objectExists', ['CRM_Dedupe_DAO_RuleGroup', $this->_rgid, 'title'] ); - $this->addField('used', array('label' => ts('Usage')), TRUE); - $disabled = array(); - $reserved = $this->addField('is_reserved', array('label' => ts('Reserved?'))); + $this->addField('used', ['label' => ts('Usage')], TRUE); + $disabled = []; + $reserved = $this->addField('is_reserved', ['label' => ts('Reserved?')]); if (!empty($this->_defaults['is_reserved'])) { $reserved->freeze(); } - $attributes = array('class' => 'two'); + $attributes = ['class' => 'two']; if (!empty($disabled)) { $attributes = array_merge($attributes, $disabled); } for ($count = 0; $count < self::RULES_COUNT; $count++) { $this->add('select', "where_$count", ts('Field'), - array( + [ NULL => ts('- none -'), - ) + $this->_fields, FALSE, $disabled + ] + $this->_fields, FALSE, $disabled ); - $this->addField("length_$count", array('entity' => 'Rule', 'name' => 'rule_length') + $attributes); - $this->addField("weight_$count", array('entity' => 'Rule', 'name' => 'rule_weight') + $attributes); + $this->addField("length_$count", ['entity' => 'Rule', 'name' => 'rule_length'] + $attributes); + $this->addField("weight_$count", ['entity' => 'Rule', 'name' => 'rule_weight'] + $attributes); } - $this->addField('threshold', array('label' => ts("Weight Threshold to Consider Contacts 'Matching':")) + $attributes); + $this->addField('threshold', ['label' => ts("Weight Threshold to Consider Contacts 'Matching':")] + $attributes); $this->assign('contact_type', $this->_contactType); - $this->addFormRule(array('CRM_Contact_Form_DedupeRules', 'formRule'), $this); + $this->addFormRule(['CRM_Contact_Form_DedupeRules', 'formRule'], $this); parent::buildQuickForm(); } @@ -147,7 +156,7 @@ public function buildQuickForm() { * list of errors to be posted back to the form */ public static function formRule($fields, $files, $self) { - $errors = array(); + $errors = []; $fieldSelected = FALSE; for ($count = 0; $count < self::RULES_COUNT; $count++) { if (!empty($fields["where_$count"]) || (isset($self->_defaults['is_reserved']) && !empty($self->_defaults["where_$count"]))) { @@ -155,6 +164,13 @@ public static function formRule($fields, $files, $self) { break; } } + if (empty($fields['threshold'])) { + // CRM-20607 - Don't validate the threshold of hard-coded rules + if (!(CRM_Utils_Array::value('is_reserved', $fields) && + CRM_Utils_File::isIncludable("CRM/Dedupe/BAO/QueryBuilder/{$self->_defaultValues['name']}.php"))) { + $errors['threshold'] = ts('Threshold weight cannot be empty or zero.'); + } + } if (!$fieldSelected) { $errors['_qf_default'] = ts('Please select at least one field.'); @@ -170,6 +186,7 @@ public static function formRule($fields, $files, $self) { * * @return array */ + /** * @return array */ @@ -192,10 +209,10 @@ public function postProcess() { SET used = 'General' WHERE contact_type = %1 AND used = %2"; - $queryParams = array( - 1 => array($this->_contactType, 'String'), - 2 => array($values['used'], 'String'), - ); + $queryParams = [ + 1 => [$this->_contactType, 'String'], + 2 => [$values['used'], 'String'], + ]; CRM_Core_DAO::executeQuery($query, $queryParams); } @@ -221,7 +238,7 @@ public function postProcess() { // lets skip updating of fields for reserved dedupe group if (CRM_Utils_Array::value('is_reserved', $this->_defaults)) { - CRM_Core_Session::setStatus(ts("The rule '%1' has been saved.", array(1 => $rgDao->title)), ts('Saved'), 'success'); + CRM_Core_Session::setStatus(ts("The rule '%1' has been saved.", [1 => $rgDao->title]), ts('Saved'), 'success'); return; } @@ -230,9 +247,9 @@ public function postProcess() { $ruleDao->delete(); $ruleDao->free(); - $substrLenghts = array(); + $substrLenghts = []; - $tables = array(); + $tables = []; $daoObj = new CRM_Core_DAO(); $database = $daoObj->database(); for ($count = 0; $count < self::RULES_COUNT; $count++) { @@ -253,7 +270,7 @@ public function postProcess() { $ruleDao->free(); if (!array_key_exists($table, $tables)) { - $tables[$table] = array(); + $tables[$table] = []; } $tables[$table][] = $field; } @@ -261,7 +278,7 @@ public function postProcess() { // CRM-6245: we must pass table/field/length triples to the createIndexes() call below if ($length) { if (!isset($substrLenghts[$table])) { - $substrLenghts[$table] = array(); + $substrLenghts[$table] = []; } //CRM-13417 to avoid fatal error "Incorrect prefix key; the used key part isn't a string, the used length is longer than the key part, or the storage engine doesn't support unique prefix keys, 1089" @@ -272,15 +289,14 @@ public function postProcess() { if ($dao->fetch()) { // set the length to null for all the fields where prefix length is not supported. eg. int,tinyint,date,enum etc dataTypes. - if ($dao->COLUMN_NAME == $field && !in_array($dao->DATA_TYPE, array( - 'char', - 'varchar', - 'binary', - 'varbinary', - 'text', - 'blob', - )) - ) { + if ($dao->COLUMN_NAME == $field && !in_array($dao->DATA_TYPE, [ + 'char', + 'varchar', + 'binary', + 'varbinary', + 'text', + 'blob', + ])) { $length = NULL; } elseif ($dao->COLUMN_NAME == $field && !empty($dao->CHARACTER_MAXIMUM_LENGTH) && ($length > $dao->CHARACTER_MAXIMUM_LENGTH)) { @@ -303,7 +319,7 @@ public function postProcess() { CRM_Core_BAO_PrevNextCache::deleteItem(NULL, $cacheKey); - CRM_Core_Session::setStatus(ts("The rule '%1' has been saved.", array(1 => $rgDao->title)), ts('Saved'), 'success'); + CRM_Core_Session::setStatus(ts("The rule '%1' has been saved.", [1 => $rgDao->title]), ts('Saved'), 'success'); } } diff --git a/CRM/Contact/Form/Domain.php b/CRM/Contact/Form/Domain.php index a4064d2bb173..dcd9eb914677 100644 --- a/CRM/Contact/Form/Domain.php +++ b/CRM/Contact/Form/Domain.php @@ -1,9 +1,9 @@ _id)) { $params['id'] = $this->_id; CRM_Core_BAO_Domain::retrieve($params, $domainDefaults); $this->_contactId = $domainDefaults['contact_id']; - //get the default domain from email address. fix CRM-3552 - $optionValues = array(); - $grpParams['name'] = 'from_email_address'; - CRM_Core_OptionValue::getValues($grpParams, $optionValues); - foreach ($optionValues as $Id => $value) { - if ($value['is_default'] && $value['is_active']) { - $this->_fromEmailId = $Id; - $list = explode('"', $value['label']); - $domainDefaults['email_name'] = CRM_Utils_Array::value(1, $list); - $domainDefaults['email_address'] = CRM_Utils_Mail::pluckEmailFromHeader($value['label']); - break; - } - } unset($params['id']); - $locParams = array('contact_id' => $domainDefaults['contact_id']); + $locParams = ['contact_id' => $domainDefaults['contact_id']]; $this->_locationDefaults = $defaults = CRM_Core_BAO_Location::getValues($locParams); $config = CRM_Core_Config::singleton(); @@ -154,27 +141,24 @@ public function setDefaultValues() { * Build the form object. */ public function buildQuickForm() { - $this->addField('name', array('label' => ts('Organization Name')), TRUE); - $this->addField('description', array('label' => ts('Description'), 'size' => 30)); - $this->add('text', 'email_name', ts('FROM Name'), CRM_Core_DAO::getAttribute('CRM_Core_DAO_Email', 'email'), TRUE); - $this->add('text', 'email_address', ts('FROM Email Address'), CRM_Core_DAO::getAttribute('CRM_Core_DAO_Email', 'email'), TRUE); - $this->addRule('email_address', ts('Domain Email Address must use a valid email address format (e.g. \'info@example.org\').'), 'email'); + $this->addField('name', ['label' => ts('Organization Name')], TRUE); + $this->addField('description', ['label' => ts('Description'), 'size' => 30]); //build location blocks. CRM_Contact_Form_Location::buildQuickForm($this); - $this->addButtons(array( - array( + $this->addButtons([ + [ 'type' => 'next', 'name' => ts('Save'), 'subName' => 'view', 'isDefault' => TRUE, - ), - array( + ], + [ 'type' => 'cancel', 'name' => ts('Cancel'), - ), - )); + ], + ]); if ($this->_action & CRM_Core_Action::VIEW) { $this->freeze(); @@ -186,7 +170,7 @@ public function buildQuickForm() { * Add local and global form rules. */ public function addRules() { - $this->addFormRule(array('CRM_Contact_Form_Domain', 'formRule')); + $this->addFormRule(['CRM_Contact_Form_Domain', 'formRule']); } /** @@ -204,19 +188,9 @@ public static function formRule($fields) { // $errors === TRUE means no errors from above formRule excution, // so declaring $errors to array for further processing if ($errors === TRUE) { - $errors = array(); - } - - //fix for CRM-3552, - //as we use "fromName" format for domain email. - if (strpos($fields['email_name'], '"') !== FALSE) { - $errors['email_name'] = ts('Double quotes are not allow in from name.'); + $errors = []; } - // Check for default from email address and organization (domain) name. Force them to change it. - if ($fields['email_address'] == 'info@EXAMPLE.ORG') { - $errors['email_address'] = ts('Please enter a valid default FROM email address for system-generated emails.'); - } if ($fields['name'] == 'Default Domain Name') { $errors['name'] = ts('Please enter the name of the organization or entity which owns this CiviCRM site.'); } @@ -256,15 +230,15 @@ public function postProcess() { $params['email'][1]['location_type_id'] = $defaultLocationType->id; } - $params += array('contact_id' => $this->_contactId); - $contactParams = array( + $params += ['contact_id' => $this->_contactId]; + $contactParams = [ 'sort_name' => $domain->name, 'display_name' => $domain->name, 'legal_name' => $domain->name, 'organization_name' => $domain->name, 'contact_id' => $this->_contactId, 'contact_type' => 'Organization', - ); + ]; if ($this->_contactId) { $contactParams['contact_sub_type'] = CRM_Contact_BAO_Contact::getContactSubType($this->_contactId); @@ -275,37 +249,7 @@ public function postProcess() { CRM_Core_BAO_Domain::edit($params, $this->_id); - //set domain from email address, CRM-3552 - $emailName = '"' . $params['email_name'] . '" <' . $params['email_address'] . '>'; - - $emailParams = array( - 'label' => $emailName, - 'description' => $params['description'], - 'is_active' => 1, - 'is_default' => 1, - ); - - $groupParams = array('name' => 'from_email_address'); - - //get the option value wt. - if ($this->_fromEmailId) { - $action = $this->_action; - $emailParams['weight'] = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionValue', $this->_fromEmailId, 'weight'); - } - else { - //add from email address. - $action = CRM_Core_Action::ADD; - $grpId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionGroup', 'from_email_address', 'id', 'name'); - $fieldValues = array('option_group_id' => $grpId); - $emailParams['weight'] = CRM_Utils_Weight::getDefaultWeight('CRM_Core_DAO_OptionValue', $fieldValues); - } - - //reset default within domain. - $emailParams['reset_default_for'] = array('domain_id' => CRM_Core_Config::domainID()); - - CRM_Core_OptionValue::addOptionValue($emailParams, $groupParams, $action, $this->_fromEmailId); - - CRM_Core_Session::setStatus(ts("Domain information for '%1' has been saved.", array(1 => $domain->name)), ts('Saved'), 'success'); + CRM_Core_Session::setStatus(ts("Domain information for '%1' has been saved.", [1 => $domain->name]), ts('Saved'), 'success'); $session = CRM_Core_Session::singleton(); $session->replaceUserContext(CRM_Utils_System::url('civicrm/admin', 'reset=1')); } diff --git a/CRM/Contact/Form/Edit/Address.php b/CRM/Contact/Form/Edit/Address.php index 8184822aab8b..8cc795e56a58 100644 --- a/CRM/Contact/Form/Edit/Address.php +++ b/CRM/Contact/Form/Edit/Address.php @@ -1,9 +1,9 @@ defaultContactCountry; - $form->applyFilter('__ALL__', 'trim'); - $js = array(); + $js = []; if (!$inlineEdit) { - $js = array('onChange' => 'checkLocation( this.id );', 'placeholder' => NULL); + $js = ['onChange' => 'checkLocation( this.id );', 'placeholder' => NULL]; } //make location type required for inline edit - $form->addField("address[$blockId][location_type_id]", array('entity' => 'address', 'class' => 'eight', 'option_url' => NULL) + $js, $inlineEdit); + $form->addField("address[$blockId][location_type_id]", ['entity' => 'address', 'class' => 'eight', 'option_url' => NULL] + $js, $inlineEdit); if (!$inlineEdit) { - $js = array('id' => 'Address_' . $blockId . '_IsPrimary', 'onClick' => 'singleSelect( this.id );'); + $js = ['id' => 'Address_' . $blockId . '_IsPrimary', 'onClick' => 'singleSelect( this.id );']; } $form->addField( - "address[$blockId][is_primary]", array( + "address[$blockId][is_primary]", [ 'entity' => 'address', 'label' => ts('Primary location for this contact'), - 'text' => ts('Primary location for this contact')) + $js); + 'text' => ts('Primary location for this contact'), + ] + $js); if (!$inlineEdit) { - $js = array('id' => 'Address_' . $blockId . '_IsBilling', 'onClick' => 'singleSelect( this.id );'); + $js = ['id' => 'Address_' . $blockId . '_IsBilling', 'onClick' => 'singleSelect( this.id );']; } $form->addField( - "address[$blockId][is_billing]", array( + "address[$blockId][is_billing]", [ 'entity' => 'address', 'label' => ts('Billing location for this contact'), - 'text' => ts('Billing location for this contact')) + $js); + 'text' => ts('Billing location for this contact'), + ] + $js); // hidden element to store master address id - $form->addField("address[$blockId][master_id]", array('entity' => 'address', 'type' => 'hidden')); + $form->addField("address[$blockId][master_id]", ['entity' => 'address', 'type' => 'hidden']); $addressOptions = CRM_Core_BAO_Setting::valueOptions(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'address_options', TRUE, NULL, TRUE ); - $attributes = CRM_Core_DAO::getAttribute('CRM_Core_DAO_Address'); - $elements = array( + $elements = [ 'address_name', 'street_address', 'supplemental_address_1', 'supplemental_address_2', + 'supplemental_address_3', 'city', 'postal_code', 'postal_code_suffix', @@ -111,21 +110,20 @@ public static function buildQuickForm(&$form, $addressBlockCount = NULL, $sharin 'street_number', 'street_name', 'street_unit', - ); + ]; foreach ($elements as $name) { - //Remove id from name, to allow comparison against enabled addressOtions. + //Remove id from name, to allow comparison against enabled addressOptions. $nameWithoutID = strpos($name, '_id') !== FALSE ? substr($name, 0, -3) : $name; // Skip fields which are not enabled in the address options. if (empty($addressOptions[$nameWithoutID])) { $continue = TRUE; //Don't skip street parsed fields when parsing is enabled. - if (in_array($nameWithoutID, array( - 'street_number', - 'street_name', - 'street_unit', - )) && !empty($addressOptions['street_address_parsing']) - ) { + if (in_array($nameWithoutID, [ + 'street_number', + 'street_name', + 'street_unit', + ]) && !empty($addressOptions['street_address_parsing'])) { $continue = FALSE; } if ($continue) { @@ -136,7 +134,7 @@ public static function buildQuickForm(&$form, $addressBlockCount = NULL, $sharin $name = 'name'; } - $params = array('entity' => 'address'); + $params = ['entity' => 'address']; if ($name == 'postal_code_suffix') { $params['label'] = ts('Suffix'); @@ -152,7 +150,7 @@ public static function buildQuickForm(&$form, $addressBlockCount = NULL, $sharin // CRM-11665 geocode override option $geoCode = FALSE; - if (!empty($config->geocodeMethod)) { + if (CRM_Utils_GeocodeProvider::getUsableClassName()) { $geoCode = TRUE; $form->addElement('checkbox', "address[$blockId][manual_geo_code]", @@ -161,74 +159,18 @@ public static function buildQuickForm(&$form, $addressBlockCount = NULL, $sharin } $form->assign('geoCode', $geoCode); - // Process any address custom data - - $groupTree = CRM_Core_BAO_CustomGroup::getTree('Address', - $form, - $entityId - ); - - if (isset($groupTree) && is_array($groupTree)) { - // use simplified formatted groupTree - $groupTree = CRM_Core_BAO_CustomGroup::formatGroupTree($groupTree, 1, $form); - - // make sure custom fields are added /w element-name in the format - 'address[$blockId][custom-X]' - foreach ($groupTree as $id => $group) { - foreach ($group['fields'] as $fldId => $field) { - $groupTree[$id]['fields'][$fldId]['element_custom_name'] = $field['element_name']; - $groupTree[$id]['fields'][$fldId]['element_name'] = "address[$blockId][{$field['element_name']}]"; - } - } - - $defaults = array(); - CRM_Core_BAO_CustomGroup::setDefaults($groupTree, $defaults); - - // since we change element name for address custom data, we need to format the setdefault values - $addressDefaults = array(); - foreach ($defaults as $key => $val) { - if (empty($val)) { - continue; - } - - // inorder to set correct defaults for checkbox custom data, we need to converted flat key to array - // this works for all types custom data - $keyValues = explode('[', str_replace(']', '', $key)); - $addressDefaults[$keyValues[0]][$keyValues[1]][$keyValues[2]] = $val; - } - - $form->setDefaults($addressDefaults); - - // we setting the prefix to 'dnc_' below, so that we don't overwrite smarty's grouptree var. - // And we can't set it to 'address_' because we want to set it in a slightly different format. - CRM_Core_BAO_CustomGroup::buildQuickForm($form, $groupTree, FALSE, 'dnc_'); - - // during contact editing : if no address is filled - // required custom data must not produce 'required' form rule error - // more handling done in formRule func - if (!$inlineEdit) { - CRM_Contact_Form_Edit_Address::storeRequiredCustomDataInfo($form, $groupTree); - } - - $template = CRM_Core_Smarty::singleton(); - $tplGroupTree = $template->get_template_vars('address_groupTree'); - $tplGroupTree = empty($tplGroupTree) ? array() : $tplGroupTree; - - $form->assign('address_groupTree', $tplGroupTree + array($blockId => $groupTree)); - // unset the temp smarty var that got created - $form->assign('dnc_groupTree', NULL); - } - // address custom data processing ends .. + self::addCustomDataToForm($form, $entityId, $blockId); if ($sharing) { // shared address $form->addElement('checkbox', "address[$blockId][use_shared_address]", NULL, ts('Use another contact\'s address')); // Override the default profile links to add address form - $profileLinks = CRM_Core_BAO_UFGroup::getCreateLinks(array( - 'new_individual', - 'new_organization', - 'new_household', - ), 'shared_address'); - $form->addEntityRef("address[$blockId][master_contact_id]", ts('Share With'), array('create' => $profileLinks)); + $profileLinks = CRM_Contact_BAO_Contact::getEntityRefCreateLinks('shared_address'); + $form->addEntityRef("address[$blockId][master_contact_id]", ts('Share With'), ['create' => $profileLinks, 'api' => ['extra' => ['contact_type']]]); + + // do we want to update employer for shared address + $form->addElement('checkbox', "address[$blockId][update_current_employer]", NULL, ts('Set this organization as current employer')); } } @@ -242,10 +184,10 @@ public static function buildQuickForm(&$form, $addressBlockCount = NULL, $sharin * @return array|bool * if no errors */ - public static function formRule($fields, $files, $self) { - $errors = array(); + public static function formRule($fields, $files = [], $self = NULL) { + $errors = []; - $customDataRequiredFields = array(); + $customDataRequiredFields = []; if ($self && property_exists($self, '_addressRequireOmission')) { $customDataRequiredFields = explode(',', $self->_addressRequireOmission); } @@ -269,8 +211,9 @@ public static function formRule($fields, $files, $self) { } // DETACH 'required' form rule error to - // custom data only if address data not exists upon submission - if (!empty($customDataRequiredFields) && !CRM_Core_BAO_Address::dataExists($addressValues)) { + // custom data if address data not exists upon submission + // or if master address is selected + if (!empty($customDataRequiredFields) && (!CRM_Core_BAO_Address::dataExists($addressValues) || !empty($addressValues['master_id']))) { foreach ($customDataRequiredFields as $customElementName) { $elementName = "address[$instance][$customElementName]"; if ($self->getElementError($elementName)) { @@ -298,14 +241,14 @@ public static function formRule($fields, $files, $self) { * Form object. */ public static function setDefaultValues(&$defaults, &$form) { - $addressValues = array(); + $addressValues = []; if (isset($defaults['address']) && is_array($defaults['address']) && !CRM_Utils_System::isNull($defaults['address']) ) { // start of contact shared adddress defaults - $sharedAddresses = array(); - $masterAddress = array(); + $sharedAddresses = []; + $masterAddress = []; // get contact name of shared contact names $shareAddressContactNames = CRM_Contact_BAO_Contact_Utils::getAddressShareContactNames($defaults['address']); @@ -313,15 +256,15 @@ public static function setDefaultValues(&$defaults, &$form) { foreach ($defaults['address'] as $key => $addressValue) { if (!empty($addressValue['master_id']) && !$shareAddressContactNames[$addressValue['master_id']]['is_deleted']) { $master_cid = $shareAddressContactNames[$addressValue['master_id']]['contact_id']; - $sharedAddresses[$key]['shared_address_display'] = array( + $sharedAddresses[$key]['shared_address_display'] = [ 'address' => $addressValue['display'], 'name' => $shareAddressContactNames[$addressValue['master_id']]['name'], - 'options' => CRM_Core_BAO_Address::getValues(array( - 'entity_id' => $master_cid, - 'contact_id' => $master_cid, - )), + 'options' => CRM_Core_BAO_Address::getValues([ + 'entity_id' => $master_cid, + 'contact_id' => $master_cid, + ]), 'master_id' => $addressValue['master_id'], - ); + ]; $defaults['address'][$key]['master_contact_id'] = $master_cid; } else { @@ -339,19 +282,19 @@ public static function setDefaultValues(&$defaults, &$form) { // start of parse address functionality // build street address, CRM-5450. if ($form->_parseStreetAddress) { - $parseFields = array('street_address', 'street_number', 'street_name', 'street_unit'); + $parseFields = ['street_address', 'street_number', 'street_name', 'street_unit']; foreach ($defaults['address'] as $cnt => & $address) { $streetAddress = NULL; - foreach (array( - 'street_number', - 'street_number_suffix', - 'street_name', - 'street_unit', - ) as $fld) { - if (in_array($fld, array( + foreach ([ + 'street_number', + 'street_number_suffix', + 'street_name', + 'street_unit', + ] as $fld) { + if (in_array($fld, [ 'street_name', 'street_unit', - ))) { + ])) { $streetAddress .= ' '; } // CRM-17619 - if the street number suffix begins with a number, add a space @@ -382,7 +325,7 @@ public static function setDefaultValues(&$defaults, &$form) { $addressValues["{$field}_{$cnt}"] = CRM_Utils_Array::value($field, $address); } // don't load fields, use js to populate. - foreach (array('street_number', 'street_name', 'street_unit') as $f) { + foreach (['street_number', 'street_name', 'street_unit'] as $f) { if (isset($address[$f])) { unset($address[$f]); } @@ -391,12 +334,12 @@ public static function setDefaultValues(&$defaults, &$form) { $form->assign('allAddressFieldValues', json_encode($addressValues)); //hack to handle show/hide address fields. - $parsedAddress = array(); + $parsedAddress = []; if ($form->_contactId && !empty($_POST['address']) && is_array($_POST['address']) ) { foreach ($_POST['address'] as $cnt => $values) { $showField = 'streetAddress'; - foreach (array('street_number', 'street_name', 'street_unit') as $fld) { + foreach (['street_number', 'street_name', 'street_unit'] as $fld) { if (!empty($values[$fld])) { $showField = 'addressElements'; break; @@ -419,7 +362,7 @@ public static function setDefaultValues(&$defaults, &$form) { * @param array $groupTree */ public static function storeRequiredCustomDataInfo(&$form, $groupTree) { - if (CRM_Utils_System::getClassName($form) == 'CRM_Contact_Form_Contact') { + if (in_array(CRM_Utils_System::getClassName($form), ['CRM_Contact_Form_Contact', 'CRM_Contact_Form_Inline_Address'])) { $requireOmission = NULL; foreach ($groupTree as $csId => $csVal) { // only process Address entity fields @@ -442,4 +385,64 @@ public static function storeRequiredCustomDataInfo(&$form, $groupTree) { } } + /** + * Add custom data to the form. + * + * @param CRM_Core_Form $form + * @param int $entityId + * @param int $blockId + */ + protected static function addCustomDataToForm(&$form, $entityId, $blockId) { + $groupTree = CRM_Core_BAO_CustomGroup::getTree('Address', NULL, $entityId); + + if (isset($groupTree) && is_array($groupTree)) { + // use simplified formatted groupTree + $groupTree = CRM_Core_BAO_CustomGroup::formatGroupTree($groupTree, 1, $form); + + // make sure custom fields are added /w element-name in the format - 'address[$blockId][custom-X]' + foreach ($groupTree as $id => $group) { + foreach ($group['fields'] as $fldId => $field) { + $groupTree[$id]['fields'][$fldId]['element_custom_name'] = $field['element_name']; + $groupTree[$id]['fields'][$fldId]['element_name'] = "address[$blockId][{$field['element_name']}]"; + } + } + + $defaults = []; + CRM_Core_BAO_CustomGroup::setDefaults($groupTree, $defaults); + + // since we change element name for address custom data, we need to format the setdefault values + $addressDefaults = []; + foreach ($defaults as $key => $val) { + if (!isset($val)) { + continue; + } + + // inorder to set correct defaults for checkbox custom data, we need to converted flat key to array + // this works for all types custom data + $keyValues = explode('[', str_replace(']', '', $key)); + $addressDefaults[$keyValues[0]][$keyValues[1]][$keyValues[2]] = $val; + } + + $form->setDefaults($addressDefaults); + + // we setting the prefix to 'dnc_' below, so that we don't overwrite smarty's grouptree var. + // And we can't set it to 'address_' because we want to set it in a slightly different format. + CRM_Core_BAO_CustomGroup::buildQuickForm($form, $groupTree, FALSE, 'dnc_'); + + // during contact editing : if no address is filled + // required custom data must not produce 'required' form rule error + // more handling done in formRule func + CRM_Contact_Form_Edit_Address::storeRequiredCustomDataInfo($form, $groupTree); + + $tplGroupTree = CRM_Core_Smarty::singleton() + ->get_template_vars('address_groupTree'); + $tplGroupTree = empty($tplGroupTree) ? [] : $tplGroupTree; + + $form->assign('address_groupTree', $tplGroupTree + [$blockId => $groupTree]); + // unset the temp smarty var that got created + $form->assign('dnc_groupTree', NULL); + } + // address custom data processing ends .. + } + } diff --git a/CRM/Contact/Form/Edit/CommunicationPreferences.php b/CRM/Contact/Form/Edit/CommunicationPreferences.php index 7ab867c8bba3..91d125fa1ea2 100644 --- a/CRM/Contact/Form/Edit/CommunicationPreferences.php +++ b/CRM/Contact/Form/Edit/CommunicationPreferences.php @@ -1,9 +1,9 @@ addGroup($privacy, 'privacy', ts('Privacy'), ' 
'); // preferred communication method - $comm = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'preferred_communication_method', array('loclize' => TRUE)); + $comm = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'preferred_communication_method', ['loclize' => TRUE]); foreach ($comm as $value => $title) { $commPreff[] = $form->createElement('advcheckbox', $value, NULL, $title); } - $form->addField('preferred_communication_method', array('entity' => 'contact', 'type' => 'CheckBoxGroup')); - $form->addField('preferred_language', array('entity' => 'contact')); + $form->addField('preferred_communication_method', ['entity' => 'contact', 'type' => 'CheckBoxGroup']); + $form->addField('preferred_language', ['entity' => 'contact']); if (!empty($privacyOptions)) { $commPreference['privacy'] = $privacyOptions; @@ -84,27 +84,27 @@ public static function buildQuickForm(&$form) { //using for display purpose. $form->assign('commPreference', $commPreference); - $form->addField('preferred_mail_format', array('entity' => 'contact', 'label' => ts('Email Format'))); + $form->addField('preferred_mail_format', ['entity' => 'contact', 'label' => ts('Email Format')]); - $form->addField('is_opt_out', array('entity' => 'contact', 'label' => ts('NO BULK EMAILS (User Opt Out)'))); + $form->addField('is_opt_out', ['entity' => 'contact', 'label' => ts('NO BULK EMAILS (User Opt Out)')]); - $form->addField('communication_style_id', array('entity' => 'contact', 'type' => 'RadioGroup')); + $form->addField('communication_style_id', ['entity' => 'contact', 'type' => 'RadioGroup']); //check contact type and build filter clause accordingly for greeting types, CRM-4575 $greetings = self::getGreetingFields($form->_contactType); foreach ($greetings as $greeting => $fields) { - $filter = array( + $filter = [ 'contact_type' => $form->_contactType, 'greeting_type' => $greeting, - ); + ]; //add addressee in Contact form $greetingTokens = CRM_Core_PseudoConstant::greeting($filter); if (!empty($greetingTokens)) { $form->addElement('select', $fields['field'], $fields['label'], - array( + [ '' => ts('- select -'), - ) + $greetingTokens + ] + $greetingTokens ); //custom addressee $form->addElement('text', $fields['customField'], $fields['customLabel'], @@ -131,13 +131,17 @@ public static function formRule($fields, $files, $self) { $greetings = self::getGreetingFields($self->_contactType); foreach ($greetings as $greeting => $details) { - $customizedValue = CRM_Core_OptionGroup::getValue($greeting, 'Customized', 'name'); + $customizedValue = CRM_Core_PseudoConstant::getKey('CRM_Contact_BAO_Contact', $details['field'], 'Customized'); if (CRM_Utils_Array::value($details['field'], $fields) == $customizedValue && empty($fields[$details['customField']])) { $errors[$details['customField']] = ts('Custom %1 is a required field if %1 is of type Customized.', - array(1 => $details['label']) + [1 => $details['label']] ); } } + + if (array_key_exists('preferred_mail_format', $fields) && empty($fields['preferred_mail_format'])) { + $errors['preferred_mail_format'] = ts('Please select an email format preferred by this contact.'); + } return empty($errors) ? TRUE : $errors; } @@ -198,36 +202,36 @@ public static function setDefaultValues(&$form, &$defaults) { */ public static function getGreetingFields($contactType) { if (empty(self::$greetings[$contactType])) { - self::$greetings[$contactType] = array(); + self::$greetings[$contactType] = []; - $js = array( + $js = [ 'onfocus' => "if (!this.value) { this.value='Dear ';} else return false", 'onblur' => "if ( this.value == 'Dear') { this.value='';} else return false", - ); + ]; - self::$greetings[$contactType] = array( - 'addressee' => array( + self::$greetings[$contactType] = [ + 'addressee' => [ 'field' => 'addressee_id', 'customField' => 'addressee_custom', 'label' => ts('Addressee'), 'customLabel' => ts('Custom Addressee'), 'js' => NULL, - ), - 'email_greeting' => array( + ], + 'email_greeting' => [ 'field' => 'email_greeting_id', 'customField' => 'email_greeting_custom', 'label' => ts('Email Greeting'), 'customLabel' => ts('Custom Email Greeting'), 'js' => $js, - ), - 'postal_greeting' => array( + ], + 'postal_greeting' => [ 'field' => 'postal_greeting_id', 'customField' => 'postal_greeting_custom', 'label' => ts('Postal Greeting'), 'customLabel' => ts('Custom Postal Greeting'), 'js' => $js, - ), - ); + ], + ]; } return self::$greetings[$contactType]; diff --git a/CRM/Contact/Form/Edit/CustomData.php b/CRM/Contact/Form/Edit/CustomData.php index 2a227e397ba3..3833b288e6c7 100644 --- a/CRM/Contact/Form/Edit/CustomData.php +++ b/CRM/Contact/Form/Edit/CustomData.php @@ -1,9 +1,9 @@ _type = CRM_Utils_Request::retrieve('type', 'String', CRM_Core_DAO::$_nullObject); - $form->_subType = CRM_Utils_Request::retrieve('subType', 'String', CRM_Core_DAO::$_nullObject); + $form->_type = CRM_Utils_Request::retrieve('type', 'String'); + $form->_subType = CRM_Utils_Request::retrieve('subType', 'String'); //build the custom data as other blocks. //$form->assign( "addBlock", false ); diff --git a/CRM/Contact/Form/Edit/Demographics.php b/CRM/Contact/Form/Edit/Demographics.php index c8a277eb75e2..0a2105a94adc 100644 --- a/CRM/Contact/Form/Edit/Demographics.php +++ b/CRM/Contact/Form/Edit/Demographics.php @@ -1,9 +1,9 @@ addField('gender_id', array('entity' => 'contact', 'type' => 'Radio', 'allowClear' => TRUE)); + $form->addField('gender_id', ['entity' => 'contact', 'type' => 'Radio', 'allowClear' => TRUE]); - $form->addField('birth_date', array('entity' => 'contact', 'formatType' => 'birth')); + $form->addField('birth_date', ['entity' => 'contact'], FALSE, FALSE); - $form->addField('is_deceased', array('entity' => 'contact', 'label' => ts('Contact is Deceased'), 'onclick' => "showDeceasedDate()")); - $form->addField('deceased_date', array('entity' => 'contact', 'formatType' => 'birth')); + $form->addField('is_deceased', ['entity' => 'contact', 'label' => ts('Contact is Deceased'), 'onclick' => "showDeceasedDate()"]); + $form->addField('deceased_date', ['entity' => 'contact'], FALSE, FALSE); } /** diff --git a/CRM/Contact/Form/Edit/Email.php b/CRM/Contact/Form/Edit/Email.php index 813a9197bfb9..91ddeffea012 100644 --- a/CRM/Contact/Form/Edit/Email.php +++ b/CRM/Contact/Form/Edit/Email.php @@ -1,9 +1,9 @@ applyFilter('__ALL__', 'trim'); //Email box - $form->addField("email[$blockId][email]", array('entity' => 'email')); + $form->addField("email[$blockId][email]", ['entity' => 'email', 'aria-label' => ts('Email %1', [1 => $blockId])]); $form->addRule("email[$blockId][email]", ts('Email is not valid.'), 'email'); if (isset($form->_contactType) || $blockEdit) { //Block type - $form->addField("email[$blockId][location_type_id]", array('entity' => 'email', 'placeholder' => NULL, 'class' => 'eight', 'option_url' => NULL)); + $form->addField("email[$blockId][location_type_id]", ['entity' => 'email', 'placeholder' => NULL, 'class' => 'eight', 'option_url' => NULL]); //TODO: Refactor on_hold field to select. $multipleBulk = CRM_Core_BAO_Email::isMultipleBulkMail(); //On-hold select if ($multipleBulk) { - $holdOptions = array( + $holdOptions = [ 0 => ts('- select -'), 1 => ts('On Hold Bounce'), 2 => ts('On Hold Opt Out'), - ); + ]; $form->addElement('select', "email[$blockId][on_hold]", '', $holdOptions); } else { - $form->addField("email[$blockId][on_hold]", array('entity' => 'email', 'type' => 'advcheckbox')); + $form->addField("email[$blockId][on_hold]", ['entity' => 'email', 'type' => 'advcheckbox', 'aria-label' => ts('On Hold for Email %1?', [1 => $blockId])]); } //Bulkmail checkbox $form->assign('multipleBulk', $multipleBulk); - if ($multipleBulk) { - $js = array('id' => "Email_" . $blockId . "_IsBulkmail"); - $form->addElement('advcheckbox', "email[$blockId][is_bulkmail]", NULL, '', $js); - } - else { - $js = array('id' => "Email_" . $blockId . "_IsBulkmail"); - if (!$blockEdit) { - $js['onClick'] = 'singleSelect( this.id );'; - } - $form->addElement('radio', "email[$blockId][is_bulkmail]", '', '', '1', $js); + $js = ['id' => "Email_" . $blockId . "_IsBulkmail" , 'aria-label' => ts('Bulk Mailing for Email %1?', [1 => $blockId])]; + if (!$blockEdit) { + $js['onClick'] = 'singleSelect( this.id );'; } + $form->addElement('advcheckbox', "email[$blockId][is_bulkmail]", NULL, '', $js); //is_Primary radio - $js = array('id' => "Email_" . $blockId . "_IsPrimary"); + $js = ['id' => "Email_" . $blockId . "_IsPrimary", 'aria-label' => ts('Email %1 is primary?', [1 => $blockId])]; if (!$blockEdit) { $js['onClick'] = 'singleSelect( this.id );'; } @@ -105,11 +99,11 @@ public static function buildQuickForm(&$form, $blockCount = NULL, $blockEdit = F if (CRM_Utils_System::getClassName($form) == 'CRM_Contact_Form_Contact') { $form->add('textarea', "email[$blockId][signature_text]", ts('Signature (Text)'), - array('rows' => 2, 'cols' => 40) + ['rows' => 2, 'cols' => 40] ); $form->add('wysiwyg', "email[$blockId][signature_html]", ts('Signature (HTML)'), - array('rows' => 2, 'cols' => 40) + ['rows' => 2, 'cols' => 40] ); } } diff --git a/CRM/Contact/Form/Edit/Household.php b/CRM/Contact/Form/Edit/Household.php index e58c3cf728ca..1d9fc60fdf3e 100644 --- a/CRM/Contact/Form/Edit/Household.php +++ b/CRM/Contact/Form/Edit/Household.php @@ -1,9 +1,9 @@ addField('nick_name'); - $form->addField('contact_source', array('label' => ts('Source'))); + $form->addField('contact_source', ['label' => ts('Source')]); } if (!$inlineEditMode) { - $form->addField('external_identifier', array('label' => ts('External ID'))); + $form->addField('external_identifier', ['label' => ts('External ID')]); $form->addRule('external_identifier', ts('External ID already exists in Database.'), 'objectExists', - array('CRM_Contact_DAO_Contact', $form->_contactId, 'external_identifier') + ['CRM_Contact_DAO_Contact', $form->_contactId, 'external_identifier'] ); } } @@ -84,17 +84,14 @@ public static function buildQuickForm(&$form, $inlineEditMode = NULL) { * $error */ public static function formRule($fields, $files, $contactID = NULL) { - $errors = array(); - $primaryID = CRM_Contact_Form_Contact::formRule($fields, $errors, $contactID); + $errors = []; + $primaryID = CRM_Contact_Form_Contact::formRule($fields, $errors, $contactID, 'Household'); // make sure that household name is set if (empty($fields['household_name'])) { $errors['household_name'] = 'Household Name should be set.'; } - //check for duplicate - dedupe rules - CRM_Contact_Form_Contact::checkDuplicateContacts($fields, $errors, $contactID, 'Household'); - return empty($errors) ? TRUE : $errors; } diff --git a/CRM/Contact/Form/Edit/IM.php b/CRM/Contact/Form/Edit/IM.php index d2cc369d0548..78781d8c2242 100644 --- a/CRM/Contact/Form/Edit/IM.php +++ b/CRM/Contact/Form/Edit/IM.php @@ -1,9 +1,9 @@ applyFilter('__ALL__', 'trim'); //IM provider select - $form->addField("im[$blockId][provider_id]", array('entity' => 'im', 'class' => 'eight', 'placeholder' => NULL)); + $form->addField("im[$blockId][provider_id]", ['entity' => 'im', 'class' => 'eight', 'placeholder' => NULL]); //Block type select - $form->addField("im[$blockId][location_type_id]", array('entity' => 'im', 'class' => 'eight', 'placeholder' => NULL, 'option_url' => NULL)); + $form->addField("im[$blockId][location_type_id]", ['entity' => 'im', 'class' => 'eight', 'placeholder' => NULL, 'option_url' => NULL]); //IM box - $form->addField("im[$blockId][name]", array('entity' => 'im')); + $form->addField("im[$blockId][name]", ['entity' => 'im', 'aria-label' => ts('Instant Messenger %1', [1 => $blockId])]); //is_Primary radio - $js = array('id' => 'IM_' . $blockId . '_IsPrimary'); + $js = ['id' => 'IM_' . $blockId . '_IsPrimary', 'aria-label' => ts('Instant Messenger %1 is primary?', [1 => $blockId])]; if (!$blockEdit) { $js['onClick'] = 'singleSelect( this.id );'; } diff --git a/CRM/Contact/Form/Edit/Individual.php b/CRM/Contact/Form/Edit/Individual.php index 90ece4b9f6df..d39c7d95e8e1 100644 --- a/CRM/Contact/Form/Edit/Individual.php +++ b/CRM/Contact/Form/Edit/Individual.php @@ -1,9 +1,9 @@ 'eight', 'placeholder' => ' ', 'label' => $name == 'prefix_id' ? ts('Prefix') : ts('Suffix')); + $props = ['class' => 'eight', 'placeholder' => ' ', 'label' => $name == 'prefix_id' ? ts('Prefix') : ts('Suffix')]; } $form->addField($name, $props); } @@ -84,32 +84,25 @@ public static function buildQuickForm(&$form, $inlineEditMode = NULL) { // job title // override the size for UI to look better - $form->addField('job_title', array('size' => '30')); + $form->addField('job_title', ['size' => '30']); //Current Employer Element - $props = array( - 'api' => array('params' => array('contact_type' => 'Organization')), + $props = [ + 'api' => ['params' => ['contact_type' => 'Organization']], 'create' => TRUE, - ); + ]; $form->addField('employer_id', $props); - $form->addField('contact_source', array('class' => 'big')); + $form->addField('contact_source', ['class' => 'big']); } if (!$inlineEditMode) { - $checkSimilar = Civi::settings()->get('contact_ajax_check_similar'); - - if ($checkSimilar == NULL) { - $checkSimilar = 0; - } - $form->assign('checkSimilar', $checkSimilar); - //External Identifier Element - $form->addField('external_identifier', array('label' => 'External ID')); + $form->addField('external_identifier', ['label' => 'External ID']); $form->addRule('external_identifier', ts('External ID already exists in Database.'), 'objectExists', - array('CRM_Contact_DAO_Contact', $form->_contactId, 'external_identifier') + ['CRM_Contact_DAO_Contact', $form->_contactId, 'external_identifier'] ); CRM_Core_ShowHideBlocks::links($form, 'demographics', '', ''); } @@ -128,17 +121,14 @@ public static function buildQuickForm(&$form, $inlineEditMode = NULL) { * TRUE if no errors, else array of errors. */ public static function formRule($fields, $files, $contactID = NULL) { - $errors = array(); - $primaryID = CRM_Contact_Form_Contact::formRule($fields, $errors, $contactID); + $errors = []; + $primaryID = CRM_Contact_Form_Contact::formRule($fields, $errors, $contactID, 'Individual'); // make sure that firstName and lastName or a primary OpenID is set if (!$primaryID && (empty($fields['first_name']) || empty($fields['last_name']))) { $errors['_qf_default'] = ts('First Name and Last Name OR an email OR an OpenID in the Primary Location should be set.'); } - //check for duplicate - dedupe rules - CRM_Contact_Form_Contact::checkDuplicateContacts($fields, $errors, $contactID, 'Individual'); - return empty($errors) ? TRUE : $errors; } diff --git a/CRM/Contact/Form/Edit/Lock.php b/CRM/Contact/Form/Edit/Lock.php index 503e5a2a7b33..c844b8cea5e4 100644 --- a/CRM/Contact/Form/Edit/Lock.php +++ b/CRM/Contact/Form/Edit/Lock.php @@ -1,9 +1,9 @@ addField('modified_date', array('type' => 'hidden', 'id' => 'modified_date', 'label' => '')); + $form->addField('modified_date', ['type' => 'hidden', 'id' => 'modified_date', 'label' => '']); } /** @@ -59,7 +59,7 @@ public static function buildQuickForm(&$form) { * true if no errors, else array of errors */ public static function formRule($fields, $files, $contactID = NULL) { - $errors = array(); + $errors = []; $timestamps = CRM_Contact_BAO_Contact::getTimestamps($contactID); if ($fields['modified_date'] != $timestamps['modified_date']) { diff --git a/CRM/Contact/Form/Edit/Notes.php b/CRM/Contact/Form/Edit/Notes.php index 14252bf58e87..d926847685d7 100644 --- a/CRM/Contact/Form/Edit/Notes.php +++ b/CRM/Contact/Form/Edit/Notes.php @@ -1,9 +1,9 @@ applyFilter('__ALL__', 'trim'); - $form->addField('subject', array('entity' => 'note', 'size' => '60')); - $form->addField('note', array('entity' => 'note', 'rows' => 3)); + $form->addField('subject', ['entity' => 'note', 'size' => '60']); + $form->addField('note', ['entity' => 'note', 'rows' => 3]); } } diff --git a/CRM/Contact/Form/Edit/OpenID.php b/CRM/Contact/Form/Edit/OpenID.php index 460e0019a0f4..2d4ab2ca0c6d 100644 --- a/CRM/Contact/Form/Edit/OpenID.php +++ b/CRM/Contact/Form/Edit/OpenID.php @@ -1,9 +1,9 @@ addElement('select', "openid[$blockId][location_type_id]", '', CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id')); //is_Primary radio - $js = array('id' => "OpenID_" . $blockId . "_IsPrimary"); + $js = ['id' => "OpenID_" . $blockId . "_IsPrimary"]; if (!$blockEdit) { $js['onClick'] = 'singleSelect( this.id );'; } diff --git a/CRM/Contact/Form/Edit/Organization.php b/CRM/Contact/Form/Edit/Organization.php index 4a877b585406..05ae544a4f9d 100644 --- a/CRM/Contact/Form/Edit/Organization.php +++ b/CRM/Contact/Form/Edit/Organization.php @@ -1,9 +1,9 @@ addField('external_identifier', array('label' => ts('External ID'))); + $form->addField('external_identifier', ['label' => ts('External ID')]); $form->addRule('external_identifier', ts('External ID already exists in Database.'), 'objectExists', - array('CRM_Contact_DAO_Contact', $form->_contactId, 'external_identifier') + ['CRM_Contact_DAO_Contact', $form->_contactId, 'external_identifier'] ); } } @@ -85,17 +85,14 @@ public static function buildQuickForm(&$form, $inlineEditMode = NULL) { * @return array|bool */ public static function formRule($fields, $files, $contactID = NULL) { - $errors = array(); - $primaryID = CRM_Contact_Form_Contact::formRule($fields, $errors, $contactID); + $errors = []; + $primaryID = CRM_Contact_Form_Contact::formRule($fields, $errors, $contactID, 'Organization'); // make sure that organization name is set if (empty($fields['organization_name'])) { $errors['organization_name'] = 'Organization Name should be set.'; } - //check for duplicate - dedupe rules - CRM_Contact_Form_Contact::checkDuplicateContacts($fields, $errors, $contactID, 'Organization'); - // add code to make sure that the uniqueness criteria is satisfied return empty($errors) ? TRUE : $errors; } diff --git a/CRM/Contact/Form/Edit/Phone.php b/CRM/Contact/Form/Edit/Phone.php index fdaa2d1fcbaa..79538fbaf977 100644 --- a/CRM/Contact/Form/Edit/Phone.php +++ b/CRM/Contact/Form/Edit/Phone.php @@ -1,9 +1,9 @@ applyFilter('__ALL__', 'trim'); //phone type select - $form->addField("phone[$blockId][phone_type_id]", array( + $form->addField("phone[$blockId][phone_type_id]", [ 'entity' => 'phone', 'class' => 'eight', 'placeholder' => NULL, - )); + ]); //main phone number with crm_phone class - $form->addField("phone[$blockId][phone]", array('entity' => 'phone', 'class' => 'crm_phone twelve')); - $form->addField("phone[$blockId][phone_ext]", array('entity' => 'phone')); + $form->addField("phone[$blockId][phone]", ['entity' => 'phone', 'class' => 'crm_phone twelve', 'aria-label' => ts('Phone %1', [1 => $blockId])]); + $form->addField("phone[$blockId][phone_ext]", ['entity' => 'phone', 'aria-label' => ts('Phone Extension %1', [1 => $blockId])]); if (isset($form->_contactType) || $blockEdit) { //Block type select - $form->addField("phone[$blockId][location_type_id]", array( + $form->addField("phone[$blockId][location_type_id]", [ 'entity' => 'phone', - 'class' => 'eight', - 'placeholder' => NULL, - 'option_url' => NULL, - )); + 'class' => 'eight', + 'placeholder' => NULL, + 'option_url' => NULL, + ]); //is_Primary radio - $js = array('id' => 'Phone_' . $blockId . '_IsPrimary', 'onClick' => 'singleSelect( this.id );'); + $js = ['id' => 'Phone_' . $blockId . '_IsPrimary', 'onClick' => 'singleSelect( this.id );', 'aria-label' => ts('Phone %1 is primary?', [1 => $blockId])]; $form->addElement('radio', "phone[$blockId][is_primary]", '', '', '1', $js); } // TODO: set this up as a group, we need a valid phone_type_id if we have a phone number diff --git a/CRM/Contact/Form/Edit/TagsAndGroups.php b/CRM/Contact/Form/Edit/TagsAndGroups.php index 229fa469b2c8..b89c3a023e9e 100644 --- a/CRM/Contact/Form/Edit/TagsAndGroups.php +++ b/CRM/Contact/Form/Edit/TagsAndGroups.php @@ -1,9 +1,9 @@ _tagGroup)) { - $form->_tagGroup = array(); + $form->_tagGroup = []; } // NYSS 5670 @@ -91,7 +91,7 @@ public static function buildQuickForm( $groupID = isset($form->_grid) ? $form->_grid : NULL; if ($groupID && $visibility) { - $ids = array($groupID => $groupID); + $ids = [$groupID => $groupID]; } else { if ($visibility) { @@ -107,8 +107,8 @@ public static function buildQuickForm( $groups = CRM_Contact_BAO_Group::getGroupsHierarchy($ids); $attributes['skiplabel'] = TRUE; - $elements = array(); - $groupsOptions = array(); + $elements = []; + $groupsOptions = []; foreach ($groups as $id => $group) { // make sure that this group has public visibility if ($visibility && @@ -128,7 +128,7 @@ public static function buildQuickForm( if ($groupElementType == 'select' && !empty($groupsOptions)) { $form->add('select', $fName, $groupName, $groupsOptions, FALSE, - array('id' => $fName, 'multiple' => 'multiple', 'class' => 'crm-select2') + ['id' => $fName, 'multiple' => 'multiple', 'class' => 'crm-select2 twenty'] ); $form->assign('groupCount', count($groupsOptions)); } @@ -137,7 +137,7 @@ public static function buildQuickForm( $form->addGroup($elements, $fName, $groupName, ' 
'); $form->assign('groupCount', count($elements)); if ($isRequired) { - $form->addRule($fName, ts('%1 is a required field.', array(1 => $groupName)), 'required'); + $form->addRule($fName, ts('%1 is a required field.', [1 => $groupName]), 'required'); } } $form->assign('groupElementType', $groupElementType); @@ -145,39 +145,10 @@ public static function buildQuickForm( } if ($type & self::TAG) { - $fName = 'tag'; - if ($fieldName) { - $fName = $fieldName; - } - $form->_tagGroup[$fName] = 1; - - // get the list of all the categories - $tags = new CRM_Core_BAO_Tag(); - $tree = $tags->getTree('civicrm_contact', TRUE); - // let's not load jstree if there are not children. This also fixes blank - // display at the beginning of checkboxes - $loadJsTree = CRM_Utils_Array::retrieveValueRecursive($tree, 'children'); - $form->assign('loadjsTree', FALSE); - if (!empty($loadJsTree)) { - // CODE FROM CRM/Tag/Form/Tag.php // - CRM_Core_Resources::singleton() - ->addScriptFile('civicrm', 'packages/jquery/plugins/jstree/jquery.jstree.js', 0, 'html-header', FALSE) - ->addStyleFile('civicrm', 'packages/jquery/plugins/jstree/themes/default/style.css', 0, 'html-header'); - $form->assign('loadjsTree', TRUE); - } - - $elements = array(); - self::climbtree($form, $tree, $elements); + $tags = CRM_Core_BAO_Tag::getColorTags('civicrm_contact'); - $form->addGroup($elements, $fName, $tagName, '
'); - $form->assign('tagCount', count($elements)); - $form->assign('tree', $tree); - $form->assign('tag', $tree); - $form->assign('entityID', $contactId); - $form->assign('entityTable', 'civicrm_contact'); - - if ($isRequired) { - $form->addRule($fName, ts('%1 is a required field.', array(1 => $tagName)), 'required'); + if (!empty($tags)) { + $form->add('select2', 'tag', ts('Tag(s)'), $tags, FALSE, ['class' => 'huge', 'placeholder' => ts('- select -'), 'multiple' => TRUE]); } // build tag widget @@ -187,31 +158,6 @@ public static function buildQuickForm( $form->assign('tagGroup', $form->_tagGroup); } - /** - * Climb tree. - * - * @param $form - * @param $tree - * @param $elements - * - * @return mixed - */ - public static function climbtree($form, $tree, &$elements) { - foreach ($tree as $tagID => $varValue) { - $tagAttribute = array( - 'onclick' => "return changeRowColor(\"rowidtag_$tagID\")", - 'id' => "tag_{$tagID}", - ); - - $elements[$tagID] = $form->createElement('checkbox', $tagID, '', $varValue['name'], $tagAttribute); - - if (array_key_exists('children', $varValue)) { - self::climbtree($form, $varValue['children'], $elements); - } - } - return $elements; - } - /** * Set defaults for relevant form elements. * @@ -248,17 +194,7 @@ public static function setDefaults($id, &$defaults, $type = self::ALL, $fieldNam } if ($type & self::TAG) { - $fName = 'tag'; - if ($fieldName) { - $fName = $fieldName; - } - - $contactTag = CRM_Core_BAO_EntityTag::getTag($id); - if ($contactTag) { - foreach ($contactTag as $tag) { - $defaults[$fName . '[' . $tag . ']'] = 1; - } - } + $defaults['tag'] = implode(',', CRM_Core_BAO_EntityTag::getTag($id, 'civicrm_contact')); } } diff --git a/CRM/Contact/Form/Edit/Website.php b/CRM/Contact/Form/Edit/Website.php index 8c05f8b5b899..8930e9956c2d 100644 --- a/CRM/Contact/Form/Edit/Website.php +++ b/CRM/Contact/Form/Edit/Website.php @@ -1,9 +1,9 @@ applyFilter('__ALL__', 'trim'); //Website type select - $form->addField("website[$blockId][website_type_id]", array('entity' => 'website', 'class' => 'eight')); + $form->addField("website[$blockId][website_type_id]", ['entity' => 'website', 'class' => 'eight', 'placeholder' => NULL]); //Website box - $form->addField("website[$blockId][url]", array('entity' => 'website')); + $form->addField("website[$blockId][url]", ['entity' => 'website', 'aria-label' => ts('Website URL %1', [1 => $blockId])]); $form->addRule("website[$blockId][url]", ts('Enter a valid web address beginning with \'http://\' or \'https://\'.'), 'url'); } diff --git a/CRM/Contact/Form/GroupContact.php b/CRM/Contact/Form/GroupContact.php index d57ee65e24ad..da68e58b7755 100644 --- a/CRM/Contact/Form/GroupContact.php +++ b/CRM/Contact/Form/GroupContact.php @@ -1,9 +1,9 @@ _contactId = $this->get('contactId'); $this->_groupContactId = $this->get('groupContactId'); - $this->_context = CRM_Utils_Request::retrieve('context', 'String', $this); + $this->_context = CRM_Utils_Request::retrieve('context', 'Alphanumeric', $this); } /** @@ -80,7 +80,17 @@ public function buildQuickForm() { // get the list of all the groups if ($this->_context == 'user') { $onlyPublicGroups = CRM_Utils_Request::retrieve('onlyPublicGroups', 'Boolean', $this, FALSE); - $allGroups = CRM_Core_PseudoConstant::staticGroup($onlyPublicGroups); + $ids = CRM_Core_PseudoConstant::allGroup(); + $heirGroups = CRM_Contact_BAO_Group::getGroupsHierarchy($ids); + + $allGroups = []; + foreach ($heirGroups as $id => $group) { + // make sure that this group has public visibility + if ($onlyPublicGroups && $group['visibility'] == 'User and User Admin Only') { + continue; + } + $allGroups[$id] = $group; + } } else { $allGroups = CRM_Core_PseudoConstant::group(); @@ -101,7 +111,7 @@ public function buildQuickForm() { $groupSelect = $groupHierarchy; } - $groupSelect = array('' => ts('- select group -')) + $groupSelect; + $groupSelect = ['' => ts('- select group -')] + $groupSelect; if (count($groupSelect) > 1) { $session = CRM_Core_Session::singleton(); @@ -113,16 +123,15 @@ public function buildQuickForm() { $msg = ts('Add to a group'); } - $this->addField('group_id', array('class' => 'crm-action-menu fa-plus', 'placeholder' => $msg, 'options' => $groupSelect)); + $this->addField('group_id', ['class' => 'crm-action-menu fa-plus', 'placeholder' => $msg, 'options' => $groupSelect]); - $this->addButtons(array( - array( - 'type' => 'next', - 'name' => ts('Add'), - 'isDefault' => TRUE, - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'next', + 'name' => ts('Add'), + 'isDefault' => TRUE, + ], + ]); } } @@ -130,7 +139,7 @@ public function buildQuickForm() { * Post process form. */ public function postProcess() { - $contactID = array($this->_contactId); + $contactID = [$this->_contactId]; $groupId = $this->controller->exportValue('GroupContact', 'group_id'); $method = ($this->_context == 'user') ? 'Web' : 'Admin'; @@ -144,7 +153,7 @@ public function postProcess() { if ($groupContact && $this->_context != 'user') { $groups = CRM_Core_PseudoConstant::group(); - CRM_Core_Session::setStatus(ts("Contact has been added to '%1'.", array(1 => $groups[$groupId])), ts('Added to Group'), 'success'); + CRM_Core_Session::setStatus(ts("Contact has been added to '%1'.", [1 => $groups[$groupId]]), ts('Added to Group'), 'success'); } } diff --git a/CRM/Contact/Form/Inline.php b/CRM/Contact/Form/Inline.php index c7996c9f7f3c..0a03b0475e34 100644 --- a/CRM/Contact/Form/Inline.php +++ b/CRM/Contact/Form/Inline.php @@ -1,9 +1,9 @@ _contactId); - $buttons = array( - array( + $buttons = [ + [ 'type' => 'upload', 'name' => ts('Save'), 'isDefault' => TRUE, - ), - array( + ], + [ 'type' => 'cancel', 'name' => ts('Cancel'), - ), - ); + ], + ]; $this->addButtons($buttons); } @@ -114,7 +117,7 @@ public function buildQuickForm() { * Override default cancel action. */ public function cancelAction() { - $response = array('status' => 'cancel'); + $response = ['status' => 'cancel']; CRM_Utils_JSON::output($response); } @@ -124,7 +127,7 @@ public function cancelAction() { * @return array */ public function setDefaultValues() { - $defaults = $params = array(); + $defaults = $params = []; $params['id'] = $this->_contactId; CRM_Contact_BAO_Contact::getValues($params, $defaults); @@ -175,11 +178,11 @@ public static function renderFooter($cid, $includeCount = TRUE) { 'contact_view_options', TRUE ); $smarty->assign('changeLog', $viewOptions['log']); - $ret = array('markup' => $smarty->fetch('CRM/common/contactFooter.tpl')); + $ret = ['markup' => $smarty->fetch('CRM/common/contactFooter.tpl')]; if ($includeCount) { $ret['count'] = CRM_Contact_BAO_Contact::getCountComponent('log', $cid); } - return array('changeLog' => $ret); + return ['changeLog' => $ret]; } } diff --git a/CRM/Contact/Form/Inline/Address.php b/CRM/Contact/Form/Inline/Address.php index d7e3161d57b4..eda850c7d3ec 100644 --- a/CRM/Contact/Form/Inline/Address.php +++ b/CRM/Contact/Form/Inline/Address.php @@ -1,9 +1,9 @@ assign('addressSequence', $addressSequence); - $this->_values = array(); + $this->_values = []; $this->_addressId = CRM_Utils_Request::retrieve('aid', 'Positive', $this, FALSE, NULL, $_REQUEST); $this->_action = CRM_Core_Action::ADD; if ($this->_addressId) { - $params = array('id' => $this->_addressId); + $params = ['id' => $this->_addressId]; $address = CRM_Core_BAO_Address::getValues($params, FALSE, 'id'); $this->_values['address'][$this->_locBlockNo] = array_pop($address); $this->_action = CRM_Core_Action::UPDATE; @@ -125,6 +130,7 @@ public function preProcess() { public function buildQuickForm() { parent::buildQuickForm(); CRM_Contact_Form_Edit_Address::buildQuickForm($this, $this->_locBlockNo, TRUE, TRUE); + $this->addFormRule(['CRM_Contact_Form_Edit_Address', 'formRule'], $this); } /** diff --git a/CRM/Contact/Form/Inline/CommunicationPreferences.php b/CRM/Contact/Form/Inline/CommunicationPreferences.php index e792eb0d00c0..adbc82f07924 100644 --- a/CRM/Contact/Form/Inline/CommunicationPreferences.php +++ b/CRM/Contact/Form/Inline/CommunicationPreferences.php @@ -1,9 +1,9 @@ addFormRule(array('CRM_Contact_Form_Edit_CommunicationPreferences', 'formRule'), $this); + $this->addFormRule(['CRM_Contact_Form_Edit_CommunicationPreferences', 'formRule'], $this); } /** @@ -98,6 +98,9 @@ public function postProcess() { $params['contact_sub_type'] = $this->_contactSubType; } + if (!isset($params['preferred_communication_method'])) { + $params['preferred_communication_method'] = 'null'; + } CRM_Contact_BAO_Contact::create($params); $this->response(); diff --git a/CRM/Contact/Form/Inline/ContactInfo.php b/CRM/Contact/Form/Inline/ContactInfo.php index 6b6b0739c349..55f1a1ddc822 100644 --- a/CRM/Contact/Form/Inline/ContactInfo.php +++ b/CRM/Contact/Form/Inline/ContactInfo.php @@ -1,9 +1,9 @@ ajaxResponse['updateTabs'] = array( + $this->ajaxResponse['updateTabs'] = [ '#tab_rel' => CRM_Contact_BAO_Contact::getCountComponent('rel', $this->_contactId), - ); + ]; if (CRM_Core_Permission::access('CiviContribute')) { $this->ajaxResponse['updateTabs']['#tab_contribute'] = CRM_Contact_BAO_Contact::getCountComponent('contribution', $this->_contactId); } diff --git a/CRM/Contact/Form/Inline/ContactName.php b/CRM/Contact/Form/Inline/ContactName.php index c6af3fcfbcc4..2458683ff26f 100644 --- a/CRM/Contact/Form/Inline/ContactName.php +++ b/CRM/Contact/Form/Inline/ContactName.php @@ -1,9 +1,9 @@ _contactType; $class::buildQuickForm($this, 1); - $this->addFormRule(array('CRM_Contact_Form_Inline_ContactName', 'formRule'), $this); + $this->addFormRule(['CRM_Contact_Form_Inline_ContactName', 'formRule'], $this); } /** @@ -63,7 +63,7 @@ public static function formRule($fields, $errors, $form) { if (empty($fields['first_name']) && empty($fields['last_name']) && empty($fields['organization_name']) && empty($fields['household_name'])) { - $emails = civicrm_api3('Email', 'getcount', array('contact_id' => $form->_contactId)); + $emails = civicrm_api3('Email', 'getcount', ['contact_id' => $form->_contactId]); if (!$emails) { $errorField = $form->_contactType == 'Individual' ? 'last' : strtolower($form->_contactType); $errors[$errorField . '_name'] = ts('Contact with no email must have a name.'); diff --git a/CRM/Contact/Form/Inline/CustomData.php b/CRM/Contact/Form/Inline/CustomData.php index 0c7d4630270c..046ff6e4cb6b 100644 --- a/CRM/Contact/Form/Inline/CustomData.php +++ b/CRM/Contact/Form/Inline/CustomData.php @@ -1,9 +1,9 @@ addFormRule(array('CRM_Contact_Form_Inline_Email', 'formRule'), $this); + $this->addFormRule(['CRM_Contact_Form_Inline_Email', 'formRule'], $this); } /** @@ -119,7 +121,7 @@ public function buildQuickForm() { * @return array */ public static function formRule($fields, $errors, $form) { - $hasData = $hasPrimary = $errors = array(); + $hasData = $hasPrimary = $errors = []; if (!empty($fields['email']) && is_array($fields['email'])) { foreach ($fields['email'] as $instance => $blockValues) { $dataExists = CRM_Contact_Form_Contact::blockDataExists($blockValues); @@ -152,7 +154,7 @@ public static function formRule($fields, $errors, $form) { * @return array */ public function setDefaultValues() { - $defaults = array(); + $defaults = []; if (!empty($this->_emails)) { foreach ($this->_emails as $id => $value) { $defaults['email'][$id] = $value; @@ -191,7 +193,7 @@ public function postProcess() { if ($email['is_primary']) { CRM_Core_DAO::setFieldValue('CRM_Contact_DAO_Contact', $this->_contactId, 'display_name', $email['email']); CRM_Core_DAO::setFieldValue('CRM_Contact_DAO_Contact', $this->_contactId, 'sort_name', $email['email']); - $this->ajaxResponse['reloadBlocks'] = array('#crm-contactname-content'); + $this->ajaxResponse['reloadBlocks'] = ['#crm-contactname-content']; break; } } diff --git a/CRM/Contact/Form/Inline/IM.php b/CRM/Contact/Form/Inline/IM.php index 3e546111ec77..89658d42bced 100644 --- a/CRM/Contact/Form/Inline/IM.php +++ b/CRM/Contact/Form/Inline/IM.php @@ -1,9 +1,9 @@ addFormRule(array('CRM_Contact_Form_Inline_IM', 'formRule')); + $this->addFormRule(['CRM_Contact_Form_Inline_IM', 'formRule']); } /** @@ -102,7 +104,7 @@ public function buildQuickForm() { * @return array */ public static function formRule($fields, $errors) { - $hasData = $hasPrimary = $errors = array(); + $hasData = $hasPrimary = $errors = []; if (!empty($fields['im']) && is_array($fields['im'])) { foreach ($fields['im'] as $instance => $blockValues) { $dataExists = CRM_Contact_Form_Contact::blockDataExists($blockValues); @@ -135,7 +137,7 @@ public static function formRule($fields, $errors) { * @return array */ public function setDefaultValues() { - $defaults = array(); + $defaults = []; if (!empty($this->_ims)) { foreach ($this->_ims as $id => $value) { $defaults['im'][$id] = $value; diff --git a/CRM/Contact/Form/Inline/Lock.php b/CRM/Contact/Form/Inline/Lock.php index 9be79b328446..62c94420f5b0 100644 --- a/CRM/Contact/Form/Inline/Lock.php +++ b/CRM/Contact/Form/Inline/Lock.php @@ -1,9 +1,9 @@ addElement('hidden', 'oplock_ts', $timestamps['modified_date'], array('id' => 'oplock_ts')); - $form->addFormRule(array('CRM_Contact_Form_Inline_Lock', 'formRule'), $contactID); + $form->addElement('hidden', 'oplock_ts', $timestamps['modified_date'], ['id' => 'oplock_ts']); + $form->addFormRule(['CRM_Contact_Form_Inline_Lock', 'formRule'], $contactID); } /** @@ -70,7 +70,7 @@ public static function buildQuickForm(&$form, $contactID) { * true if no errors, else array of errors */ public static function formRule($fields, $files, $contactID = NULL) { - $errors = array(); + $errors = []; $timestamps = CRM_Contact_BAO_Contact::getTimestamps($contactID); if ($fields['oplock_ts'] != $timestamps['modified_date']) { @@ -93,7 +93,7 @@ public static function formRule($fields, $files, $contactID = NULL) { */ public static function getResponse($contactID) { $timestamps = CRM_Contact_BAO_Contact::getTimestamps($contactID); - return array('oplock_ts' => $timestamps['modified_date']); + return ['oplock_ts' => $timestamps['modified_date']]; } } diff --git a/CRM/Contact/Form/Inline/OpenID.php b/CRM/Contact/Form/Inline/OpenID.php index 616f7639d262..399279fd98e1 100644 --- a/CRM/Contact/Form/Inline/OpenID.php +++ b/CRM/Contact/Form/Inline/OpenID.php @@ -1,9 +1,9 @@ addFormRule(array('CRM_Contact_Form_Inline_OpenID', 'formRule')); + $this->addFormRule(['CRM_Contact_Form_Inline_OpenID', 'formRule']); } /** @@ -102,7 +104,7 @@ public function buildQuickForm() { * @return array */ public static function formRule($fields, $errors) { - $hasData = $hasPrimary = $errors = array(); + $hasData = $hasPrimary = $errors = []; if (!empty($fields['openid']) && is_array($fields['openid'])) { foreach ($fields['openid'] as $instance => $blockValues) { $dataExists = CRM_Contact_Form_Contact::blockDataExists($blockValues); @@ -135,7 +137,7 @@ public static function formRule($fields, $errors) { * @return array */ public function setDefaultValues() { - $defaults = array(); + $defaults = []; if (!empty($this->_openids)) { foreach ($this->_openids as $id => $value) { $defaults['openid'][$id] = $value; diff --git a/CRM/Contact/Form/Inline/Phone.php b/CRM/Contact/Form/Inline/Phone.php index f45b2afdd28a..8cd7b6f729b1 100644 --- a/CRM/Contact/Form/Inline/Phone.php +++ b/CRM/Contact/Form/Inline/Phone.php @@ -1,9 +1,9 @@ addFormRule(array('CRM_Contact_Form_Inline_Phone', 'formRule')); + $this->addFormRule(['CRM_Contact_Form_Inline_Phone', 'formRule']); } /** @@ -102,7 +104,7 @@ public function buildQuickForm() { * @return array */ public static function formRule($fields, $errors) { - $hasData = $hasPrimary = $errors = array(); + $hasData = $hasPrimary = $errors = []; if (!empty($fields['phone']) && is_array($fields['phone'])) { $primaryID = NULL; foreach ($fields['phone'] as $instance => $blockValues) { @@ -136,7 +138,7 @@ public static function formRule($fields, $errors) { * @return array */ public function setDefaultValues() { - $defaults = array(); + $defaults = []; if (!empty($this->_phones)) { foreach ($this->_phones as $id => $value) { $defaults['phone'][$id] = $value; diff --git a/CRM/Contact/Form/Inline/Website.php b/CRM/Contact/Form/Inline/Website.php index db6f022a42ff..a90ae37734a7 100644 --- a/CRM/Contact/Form/Inline/Website.php +++ b/CRM/Contact/Form/Inline/Website.php @@ -1,9 +1,9 @@ $this->_contactId); - $values = array(); + $params = ['contact_id' => $this->_contactId]; + $values = []; $this->_websites = CRM_Core_BAO_Website::getValues($params, $values); } @@ -87,6 +89,8 @@ public function buildQuickForm() { CRM_Contact_Form_Edit_Website::buildQuickForm($this, $blockId, TRUE); } + $this->addFormRule(['CRM_Contact_Form_Inline_Website', 'formRule'], $this); + } /** @@ -95,7 +99,7 @@ public function buildQuickForm() { * @return array */ public function setDefaultValues() { - $defaults = array(); + $defaults = []; if (!empty($this->_websites)) { foreach ($this->_websites as $id => $value) { $defaults['website'][$id] = $value; @@ -122,10 +126,44 @@ public function postProcess() { } } // Process / save websites - CRM_Core_BAO_Website::create($params['website'], $this->_contactId, TRUE); + CRM_Core_BAO_Website::process($params['website'], $this->_contactId, TRUE); $this->log(); $this->response(); } + /** + * Global validation rules for the form. + * + * @param array $fields + * Posted values of the form. + * @param array $errors + * List of errors to be posted back to the form. + * @param CRM_Contact_Form_Inline_Website $form + * + * @return array + */ + public static function formRule($fields, $errors, $form) { + $hasData = $errors = []; + if (!empty($fields['website']) && is_array($fields['website'])) { + $types = []; + foreach ($fields['website'] as $instance => $blockValues) { + $dataExists = CRM_Contact_Form_Contact::blockDataExists($blockValues); + + if ($dataExists) { + $hasData[] = $instance; + if (!empty($blockValues['website_type_id'])) { + if (empty($types[$blockValues['website_type_id']])) { + $types[$blockValues['website_type_id']] = $blockValues['website_type_id']; + } + else { + $errors["website[" . $instance . "][website_type_id]"] = ts('Contacts may only have one website of each type at most.'); + } + } + } + } + } + return $errors; + } + } diff --git a/CRM/Contact/Form/Location.php b/CRM/Contact/Form/Location.php index e28876b3a28d..0f3bd5c63b77 100644 --- a/CRM/Contact/Form/Location.php +++ b/CRM/Contact/Form/Location.php @@ -1,9 +1,9 @@ _addBlockName = CRM_Utils_Request::retrieve('block', 'String', CRM_Core_DAO::$_nullObject); - $additionalblockCount = CRM_Utils_Request::retrieve('count', 'Positive', CRM_Core_DAO::$_nullObject); + $form->_addBlockName = CRM_Utils_Request::retrieve('block', 'String'); + $additionalblockCount = CRM_Utils_Request::retrieve('count', 'Positive'); $form->assign('addBlock', FALSE); if ($form->_addBlockName && $additionalblockCount) { @@ -51,11 +51,11 @@ public static function preProcess(&$form) { if (is_a($form, 'CRM_Event_Form_ManageEvent_Location') || is_a($form, 'CRM_Contact_Form_Domain')) { - $form->_blocks = array( + $form->_blocks = [ 'Address' => ts('Address'), 'Email' => ts('Email'), 'Phone' => ts('Phone'), - ); + ]; } $form->assign('blocks', $form->_blocks); @@ -76,15 +76,14 @@ public static function preProcess(&$form) { */ public static function buildQuickForm(&$form) { // required for subsequent AJAX requests. - $ajaxRequestBlocks = array(); + $ajaxRequestBlocks = []; $generateAjaxRequest = 0; //build 1 instance of all blocks, without using ajax ... foreach ($form->_blocks as $blockName => $label) { - require_once str_replace('_', DIRECTORY_SEPARATOR, 'CRM_Contact_Form_Edit_' . $blockName) . '.php'; $name = strtolower($blockName); - $instances = array(1); + $instances = [1]; if (!empty($_POST[$name]) && is_array($_POST[$name])) { $instances = array_keys($_POST[$name]); } diff --git a/CRM/Contact/Form/Merge.php b/CRM/Contact/Form/Merge.php index b574c3115e7e..1434c173f647 100644 --- a/CRM/Contact/Form/Merge.php +++ b/CRM/Contact/Form/Merge.php @@ -1,9 +1,9 @@ _cid = CRM_Utils_Request::retrieve('cid', 'Positive', $this, TRUE); + $this->_oid = CRM_Utils_Request::retrieve('oid', 'Positive', $this, TRUE); + $flip = CRM_Utils_Request::retrieve('flip', 'Positive', $this, FALSE); - $this->_rgid = CRM_Utils_Request::retrieve('rgid', 'Positive', $this, FALSE); - $this->_gid = $gid = CRM_Utils_Request::retrieve('gid', 'Positive', $this, FALSE); - $this->_mergeId = CRM_Utils_Request::retrieve('mergeId', 'Positive', $this, FALSE); - $this->limit = CRM_Utils_Request::retrieve('limit', 'Positive', $this, FALSE); - $urlParams = "reset=1&rgid={$this->_rgid}&gid={$this->_gid}&limit=" . $this->limit; + $this->_rgid = CRM_Utils_Request::retrieve('rgid', 'Positive', $this, FALSE); + $this->_gid = $gid = CRM_Utils_Request::retrieve('gid', 'Positive', $this, FALSE); + $this->_mergeId = CRM_Utils_Request::retrieve('mergeId', 'Positive', $this, FALSE); + $this->limit = CRM_Utils_Request::retrieve('limit', 'Positive', $this, FALSE); + $this->criteria = CRM_Utils_Request::retrieve('criteria', 'Json', $this, FALSE, '{}'); - // Sanity check - if ($cid == $oid) { - CRM_Core_Error::statusBounce(ts('Cannot merge a contact with itself.')); - } - - if (!CRM_Dedupe_BAO_Rule::validateContacts($cid, $oid)) { - CRM_Core_Error::statusBounce(ts('The selected pair of contacts are marked as non duplicates. If these records should be merged, you can remove this exception on the Dedupe Exceptions page.', array(1 => CRM_Utils_System::url('civicrm/dedupe/exception', 'reset=1')))); - } - $this->_contactType = civicrm_api3('Contact', 'getvalue', array('id' => $cid, 'return' => 'contact_type')); - $isFromDedupeScreen = TRUE; - if (!$this->_rgid) { - $isFromDedupeScreen = FALSE; - $this->_rgid = civicrm_api3('RuleGroup', 'getvalue', array( - 'contact_type' => $this->_contactType, - 'used' => 'Supervised', - 'return' => 'id', - )); - } + $urlParams = ['reset' => 1, 'rgid' => $this->_rgid, 'gid' => $this->_gid, 'limit' => $this->limit, 'criteria' => $this->criteria]; - $cacheKey = CRM_Dedupe_Merger::getMergeCacheKeyString($this->_rgid, $gid); + $this->bounceIfInvalid($this->_cid, $this->_oid); - $join = CRM_Dedupe_Merger::getJoinOnDedupeTable(); - $where = "de.id IS NULL"; + $contacts = civicrm_api3('Contact', 'get', [ + 'id' => ['IN' => [$this->_cid, $this->_oid]], + 'return' => ['contact_type', 'modified_date', 'created_date', 'contact_sub_type'], + ])['values']; - $pos = CRM_Core_BAO_PrevNextCache::getPositions($cacheKey, $cid, $oid, $this->_mergeId, $join, $where, $flip); + $this->_contactType = $contacts[$this->_cid]['contact_type']; - // Block access if user does not have EDIT permissions for both contacts. - if (!(CRM_Contact_BAO_Contact_Permission::allow($cid, CRM_Core_Permission::EDIT) && - CRM_Contact_BAO_Contact_Permission::allow($oid, CRM_Core_Permission::EDIT) - ) - ) { - CRM_Utils_System::permissionDenied(); - } + $browseUrl = CRM_Utils_System::url('civicrm/contact/dedupefind', array_merge($urlParams, ['action' => 'browse'])); - // get user info of main contact. - $config = CRM_Core_Config::singleton(); - $config->doNotResetCache = 1; - - $viewUser = CRM_Core_Permission::check('access user profiles'); - $mainUfId = CRM_Core_BAO_UFMatch::getUFId($cid); - $mainUser = NULL; - if ($mainUfId) { - // d6 compatible - if ($config->userSystem->is_drupal == '1') { - $mainUser = user_load($mainUfId); + if (!$this->_rgid) { + // Unset browse URL as we have come from the search screen. + $browseUrl = ''; + $this->_rgid = civicrm_api3('RuleGroup', 'getvalue', [ + 'contact_type' => $this->_contactType, + 'used' => 'Supervised', + 'return' => 'id', + ]); } - elseif ($config->userFramework == 'Joomla') { - $mainUser = JFactory::getUser($mainUfId); + $this->assign('browseUrl', $browseUrl); + if ($browseUrl) { + CRM_Core_Session::singleton()->pushUserContext($browseUrl); } - $this->assign('mainUfId', $mainUfId); - $this->assign('mainUfName', $mainUser ? $mainUser->name : NULL); - } + $cacheKey = CRM_Dedupe_Merger::getMergeCacheKeyString($this->_rgid, $gid, json_decode($this->criteria, TRUE)); - $flipUrl = CRM_Utils_System::url('civicrm/contact/merge', - "reset=1&action=update&cid={$oid}&oid={$cid}&rgid={$this->_rgid}&gid={$gid}" - ); - if (!$flip) { - $flipUrl .= '&flip=1'; - } - $this->assign('flip', $flipUrl); - - $this->prev = $this->next = NULL; - foreach (array( - 'prev', - 'next', - ) as $position) { - if (!empty($pos[$position])) { - if ($pos[$position]['id1'] && $pos[$position]['id2']) { - $urlParams .= "&cid={$pos[$position]['id1']}&oid={$pos[$position]['id2']}&mergeId={$pos[$position]['mergeId']}&action=update"; - $this->$position = CRM_Utils_System::url('civicrm/contact/merge', $urlParams); - $this->assign($position, $this->$position); + $join = CRM_Dedupe_Merger::getJoinOnDedupeTable(); + $where = "de.id IS NULL"; + + $pos = CRM_Core_BAO_PrevNextCache::getPositions($cacheKey, $this->_cid, $this->_oid, $this->_mergeId, $join, $where, $flip); + + // get user info of main contact. + $config = CRM_Core_Config::singleton(); + CRM_Core_Config::setPermitCacheFlushMode(FALSE); + + $mainUfId = CRM_Core_BAO_UFMatch::getUFId($this->_cid); + $mainUser = NULL; + if ($mainUfId) { + $mainUser = $config->userSystem->getUser($this->_cid); + $this->assign('mainUfId', $mainUfId); + $this->assign('mainUfName', $mainUser ? $mainUser['name'] : NULL); + } + $flipParams = array_merge($urlParams, ['action' => 'update', 'cid' => $this->_oid, 'oid' => $this->_cid]); + if (!$flip) { + $flipParams['flip'] = '1'; + } + $flipUrl = CRM_Utils_System::url('civicrm/contact/merge', + $flipParams + ); + $this->assign('flip', $flipUrl); + + $this->prev = $this->next = NULL; + foreach ([ + 'prev', + 'next', + ] as $position) { + if (!empty($pos[$position])) { + if ($pos[$position]['id1'] && $pos[$position]['id2']) { + $rowParams = array_merge($urlParams, [ + 'action' => 'update', + 'cid' => $pos[$position]['id1'], + 'oid' => $pos[$position]['id2'], + 'mergeId' => $pos[$position]['mergeId'], + ]); + $this->$position = CRM_Utils_System::url('civicrm/contact/merge', $rowParams); + $this->assign($position, $this->$position); + } } } - } - // get user info of other contact. - $otherUfId = CRM_Core_BAO_UFMatch::getUFId($oid); - $otherUser = NULL; + // get user info of other contact. + $otherUfId = CRM_Core_BAO_UFMatch::getUFId($this->_oid); + $otherUser = NULL; - if ($otherUfId) { - // d6 compatible - if ($config->userSystem->is_drupal == '1') { - $otherUser = user_load($otherUfId); - } - elseif ($config->userFramework == 'Joomla') { - $otherUser = JFactory::getUser($otherUfId); + if ($otherUfId) { + $otherUser = $config->userSystem->getUser($this->_oid); + $this->assign('otherUfId', $otherUfId); + $this->assign('otherUfName', $otherUser ? $otherUser['name'] : NULL); } - $this->assign('otherUfId', $otherUfId); - $this->assign('otherUfName', $otherUser ? $otherUser->name : NULL); - } + $cmsUser = ($mainUfId && $otherUfId) ? TRUE : FALSE; + $this->assign('user', $cmsUser); - $cmsUser = ($mainUfId && $otherUfId) ? TRUE : FALSE; - $this->assign('user', $cmsUser); + $rowsElementsAndInfo = CRM_Dedupe_Merger::getRowsElementsAndInfo($this->_cid, $this->_oid); + $main = $this->_mainDetails = $rowsElementsAndInfo['main_details']; + $other = $this->_otherDetails = $rowsElementsAndInfo['other_details']; - $session = CRM_Core_Session::singleton(); + $this->assign('contact_type', $main['contact_type']); + $this->assign('main_name', $main['display_name']); + $this->assign('other_name', $other['display_name']); + $this->assign('main_cid', $main['contact_id']); + $this->assign('other_cid', $other['contact_id']); + $this->assign('rgid', $this->_rgid); + $this->assignSummaryRowsToTemplate($contacts); - // context fixed. - if ($isFromDedupeScreen) { - $browseUrl = CRM_Utils_System::url('civicrm/contact/dedupefind', $urlParams . '&action=browse'); - $session->pushUserContext($browseUrl); - } - $this->assign('browseUrl', empty($browseUrl) ? '' : $browseUrl); + $this->addElement('checkbox', 'toggleSelect', NULL, NULL, ['class' => 'select-rows']); - // ensure that oid is not the current user, if so refuse to do the merge - if ($session->get('userID') == $oid) { - $display_name = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $oid, 'display_name'); - $message = ts('The contact record which is linked to the currently logged in user account - \'%1\' - cannot be deleted.', - array(1 => $display_name) - ); - CRM_Core_Error::statusBounce($message); - } + $this->assign('mainLocBlock', json_encode($rowsElementsAndInfo['main_details']['location_blocks'])); + $this->assign('locationBlockInfo', json_encode(CRM_Dedupe_Merger::getLocationBlockInfo())); + $this->assign('mainContactTypeIcon', CRM_Contact_BAO_Contact_Utils::getImage($contacts[$this->_cid]['contact_sub_type'] ? $contacts[$this->_cid]['contact_sub_type'] : $contacts[$this->_cid]['contact_type'], + FALSE, + $this->_cid + )); + $this->assign('otherContactTypeIcon', CRM_Contact_BAO_Contact_Utils::getImage($contacts[$this->_oid]['contact_sub_type'] ? $contacts[$this->_oid]['contact_sub_type'] : $contacts[$this->_oid]['contact_type'], + FALSE, + $this->_oid + )); - $rowsElementsAndInfo = CRM_Dedupe_Merger::getRowsElementsAndInfo($cid, $oid); - $main = $this->_mainDetails = &$rowsElementsAndInfo['main_details']; - $other = $this->_otherDetails = &$rowsElementsAndInfo['other_details']; + if (isset($rowsElementsAndInfo['rows']['move_contact_type'])) { + // We don't permit merging contacts of different types so this is just clutter - putting + // the icon next to the contact name is consistent with elsewhere and permits hover-info + // https://lab.civicrm.org/dev/core/issues/824 + unset($rowsElementsAndInfo['rows']['move_contact_type']); + } - if ($main['contact_id'] != $cid) { - CRM_Core_Error::fatal(ts('The main contact record does not exist')); - } + $this->assign('rows', $rowsElementsAndInfo['rows']); + + // add elements + foreach ($rowsElementsAndInfo['elements'] as $element) { + // We could push this down to the getRowsElementsAndInfo function but it's + // already so overloaded - let's start moving towards doing form-things + // on the form. + if (substr($element[1], 0, 13) === 'move_location') { + $element[4] = array_merge( + (array) CRM_Utils_Array::value(4, $element, []), + [ + 'data-location' => substr($element[1], 14), + 'data-is_location' => TRUE, + ]); + } + if (substr($element[1], 0, 15) === 'location_blocks') { + // @todo We could add some data elements here to make jquery manipulation more straight-forward + // @todo consider enabling if it is an add & defaulting to true. + $element[4] = array_merge((array) CRM_Utils_Array::value(4, $element, []), ['disabled' => TRUE]); + } + $this->addElement($element[0], + $element[1], + array_key_exists('2', $element) ? $element[2] : NULL, + array_key_exists('3', $element) ? $element[3] : NULL, + array_key_exists('4', $element) ? $element[4] : NULL, + array_key_exists('5', $element) ? $element[5] : NULL + ); + } - if ($other['contact_id'] != $oid) { - CRM_Core_Error::fatal(ts('The other contact record does not exist')); - } + // add related table elements + foreach ($rowsElementsAndInfo['rel_table_elements'] as $relTableElement) { + $element = $this->addElement($relTableElement[0], $relTableElement[1]); + $element->setChecked(TRUE); + } - $this->assign('contact_type', $main['contact_type']); - $this->assign('main_name', $main['display_name']); - $this->assign('other_name', $other['display_name']); - $this->assign('main_cid', $main['contact_id']); - $this->assign('other_cid', $other['contact_id']); - $this->assign('rgid', $this->_rgid); - - $this->_cid = $cid; - $this->_oid = $oid; - - $this->addElement('checkbox', 'toggleSelect', NULL, NULL, array('class' => 'select-rows')); - - $this->assign('mainLocBlock', json_encode($rowsElementsAndInfo['main_details']['location_blocks'])); - $this->assign('locationBlockInfo', json_encode(CRM_Dedupe_Merger::getLocationBlockInfo())); - $this->assign('rows', $rowsElementsAndInfo['rows']); - - // add elements - foreach ($rowsElementsAndInfo['elements'] as $element) { - $this->addElement($element[0], - $element[1], - array_key_exists('2', $element) ? $element[2] : NULL, - array_key_exists('3', $element) ? $element[3] : NULL, - array_key_exists('4', $element) ? $element[4] : NULL, - array_key_exists('5', $element) ? $element[5] : NULL - ); + $this->assign('rel_tables', $rowsElementsAndInfo['rel_tables']); + $this->assign('userContextURL', CRM_Core_Session::singleton() + ->readUserContext()); } - - // add related table elements - foreach ($rowsElementsAndInfo['rel_table_elements'] as $relTableElement) { - $element = $this->addElement($relTableElement[0], $relTableElement[1]); - $element->setChecked(TRUE); + catch (CRM_Core_Exception $e) { + CRM_Core_Error::statusBounce(ts($e->getMessage())); } - - $this->assign('rel_tables', $rowsElementsAndInfo['rel_tables']); - $this->assign('userContextURL', $session->readUserContext()); - } - - /** - * This virtual function is used to set the default values of - * various form elements - * - * access public - * - * @return array - * reference to the array of default values - */ - /** - * @return array - */ - public function setDefaultValues() { - return array('deleteOther' => 1); } public function addRules() { } public function buildQuickForm() { - CRM_Utils_System::setTitle(ts('Merge %1 contacts', array(1 => $this->_contactType))); - $buttons = array(); + $this->unsavedChangesWarn = FALSE; + CRM_Utils_System::setTitle(ts('Merge %1 contacts', [1 => $this->_contactType])); + $buttons = []; - $buttons[] = array( + $buttons[] = [ 'type' => 'next', 'name' => $this->next ? ts('Merge and go to Next Pair') : ts('Merge'), 'isDefault' => TRUE, - 'icon' => $this->next ? 'circle-triangle-e' : 'check', - ); + 'icon' => $this->next ? 'fa-play-circle' : 'check', + ]; if ($this->next || $this->prev) { - $buttons[] = array( + $buttons[] = [ 'type' => 'submit', 'name' => ts('Merge and go to Listing'), - ); - $buttons[] = array( + ]; + $buttons[] = [ 'type' => 'done', 'name' => ts('Merge and View Result'), 'icon' => 'fa-check-circle', - ); + ]; } - $buttons[] = array( + $buttons[] = [ 'type' => 'cancel', 'name' => ts('Cancel'), - ); + ]; $this->addButtons($buttons); - $this->addFormRule(array('CRM_Contact_Form_Merge', 'formRule'), $this); + $this->addFormRule(['CRM_Contact_Form_Merge', 'formRule'], $this); } /** @@ -294,13 +293,13 @@ public function buildQuickForm() { * @return array */ public static function formRule($fields, $files, $self) { - $errors = array(); + $errors = []; $link = CRM_Utils_System::href(ts('Flip between the original and duplicate contacts.'), 'civicrm/contact/merge', 'reset=1&action=update&cid=' . $self->_oid . '&oid=' . $self->_cid . '&rgid=' . $self->_rgid . '&flip=1' ); if (CRM_Contact_BAO_Contact::checkDomainContact($self->_oid)) { - $errors['_qf_default'] = ts("The Default Organization contact cannot be merged into another contact record. It is associated with the CiviCRM installation for this domain and contains information used for system functions. If you want to merge these records, you can: %1", array(1 => $link)); + $errors['_qf_default'] = ts("The Default Organization contact cannot be merged into another contact record. It is associated with the CiviCRM installation for this domain and contains information used for system functions. If you want to merge these records, you can: %1", [1 => $link]); } return $errors; } @@ -308,39 +307,31 @@ public static function formRule($fields, $files, $self) { public function postProcess() { $formValues = $this->exportValues(); - // reset all selected contact ids from session - // when we came from search context, CRM-3526 - $session = CRM_Core_Session::singleton(); - if ($session->get('selectedSearchContactIds')) { - $session->resetScope('selectedSearchContactIds'); - } - $formValues['main_details'] = $this->_mainDetails; $formValues['other_details'] = $this->_otherDetails; - $migrationData = array('migration_info' => $formValues); + $migrationData = ['migration_info' => $formValues]; CRM_Utils_Hook::merge('form', $migrationData, $this->_cid, $this->_oid); CRM_Dedupe_Merger::moveAllBelongings($this->_cid, $this->_oid, $migrationData['migration_info']); $name = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $this->_cid, 'display_name'); - $message = '
  • ' . ts('%1 has been updated.', array(1 => $name)) . '
  • ' . ts('Contact ID %1 has been deleted.', array(1 => $this->_oid)) . '
'; + $message = '
  • ' . ts('%1 has been updated.', [1 => $name]) . '
  • ' . ts('Contact ID %1 has been deleted.', [1 => $this->_oid]) . '
'; CRM_Core_Session::setStatus($message, ts('Contacts Merged'), 'success'); - $url = CRM_Utils_System::url('civicrm/contact/view', "reset=1&cid={$this->_cid}"); - $urlParams = "reset=1&gid={$this->_gid}&rgid={$this->_rgid}&limit={$this->limit}"; + $urlParams = ['reset' => 1, 'cid' => $this->_cid, 'rgid' => $this->_rgid, 'gid' => $this->_gid, 'limit' => $this->limit, 'criteria' => $this->criteria]; + $contactViewUrl = CRM_Utils_System::url('civicrm/contact/view', ['reset' => 1, 'cid' => $this->_cid]); if (!empty($formValues['_qf_Merge_submit'])) { - $urlParams .= "&action=update"; - $lisitingURL = CRM_Utils_System::url('civicrm/contact/dedupefind', + $urlParams['action'] = "update"; + CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contact/dedupefind', $urlParams - ); - CRM_Utils_System::redirect($lisitingURL); + )); } if (!empty($formValues['_qf_Merge_done'])) { - CRM_Utils_System::redirect($url); + CRM_Utils_System::redirect($contactViewUrl); } if ($this->next && $this->_mergeId) { - $cacheKey = CRM_Dedupe_Merger::getMergeCacheKeyString($this->_rgid, $this->_gid); + $cacheKey = CRM_Dedupe_Merger::getMergeCacheKeyString($this->_rgid, $this->_gid, json_decode($this->criteria, TRUE)); $join = CRM_Dedupe_Merger::getJoinOnDedupeTable(); $where = "de.id IS NULL"; @@ -352,12 +343,81 @@ public function postProcess() { $pos['next']['id2'] ) { - $urlParams .= "&cid={$pos['next']['id1']}&oid={$pos['next']['id2']}&mergeId={$pos['next']['mergeId']}&action=update"; - $url = CRM_Utils_System::url('civicrm/contact/merge', $urlParams); + $urlParams['cid'] = $pos['next']['id1']; + $urlParams['oid'] = $pos['next']['id2']; + $urlParams['mergeId'] = $pos['next']['mergeId']; + $urlParams['action'] = 'update'; + CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contact/merge', $urlParams)); } } - CRM_Utils_System::redirect($url); + // Perhaps never reached. + CRM_Utils_System::redirect($contactViewUrl); + } + + /** + * Bounce if the merge action is invalid. + * + * We don't allow the merge if it is nonsensical, marked as a duplicate + * or outside the user's permission. + * + * @param int $cid + * Contact ID to retain + * @param int $oid + * Contact ID to delete. + */ + public function bounceIfInvalid($cid, $oid) { + if ($cid == $oid) { + CRM_Core_Error::statusBounce(ts('Cannot merge a contact with itself.')); + } + + if (!CRM_Dedupe_BAO_Rule::validateContacts($cid, $oid)) { + CRM_Core_Error::statusBounce(ts('The selected pair of contacts are marked as non duplicates. If these records should be merged, you can remove this exception on the Dedupe Exceptions page.', [1 => CRM_Utils_System::url('civicrm/dedupe/exception', 'reset=1')])); + } + + if (!(CRM_Contact_BAO_Contact_Permission::allow($cid, CRM_Core_Permission::EDIT) && + CRM_Contact_BAO_Contact_Permission::allow($oid, CRM_Core_Permission::EDIT) + ) + ) { + CRM_Utils_System::permissionDenied(); + } + // ensure that oid is not the current user, if so refuse to do the merge + if (CRM_Core_Session::singleton()->getLoggedInContactID() == $oid) { + $message = ts('The contact record which is linked to the currently logged in user account - \'%1\' - cannot be deleted.', + [1 => CRM_Core_Session::singleton()->getLoggedInContactDisplayName()] + ); + CRM_Core_Error::statusBounce($message); + } + } + + /** + * Assign the summary_rows variable to the tpl. + * + * This adds rows to the beginning of the block that will help in making merge choices. + * + * It can be modified by a hook by altering what is assigned. Although not technically supported this + * is an easy tweak with no earth-shattering impacts if later changes stop if from working. + * + * https://lab.civicrm.org/dev/core/issues/824 + * + * @param array $contacts + */ + protected function assignSummaryRowsToTemplate($contacts) { + $mostRecent = ($contacts[$this->_cid]['modified_date'] < $contacts[$this->_oid]['modified_date']) ? $this->_oid : $this->_cid; + $this->assign('summary_rows', [ + [ + 'name' => 'created_date', + 'label' => ts('Created'), + 'main_contact_value' => CRM_Utils_Date::customFormat($contacts[$this->_cid]['created_date']), + 'other_contact_value' => CRM_Utils_Date::customFormat($contacts[$this->_oid]['created_date']), + ], + [ + 'name' => 'modified_date', + 'label' => ts('Last Modified'), + 'main_contact_value' => CRM_Utils_Date::customFormat($contacts[$this->_cid]['modified_date']) . ($mostRecent == $this->_cid ? ' (' . ts('Most Recent') . ')' : ''), + 'other_contact_value' => CRM_Utils_Date::customFormat($contacts[$this->_oid]['modified_date']) . ($mostRecent == $this->_oid ? ' (' . ts('Most Recent') . ')' : ''), + ], + ]); } } diff --git a/CRM/Contact/Form/RelatedContact.php b/CRM/Contact/Form/RelatedContact.php index 74cd406445e5..2795870041ad 100644 --- a/CRM/Contact/Form/RelatedContact.php +++ b/CRM/Contact/Form/RelatedContact.php @@ -1,9 +1,9 @@ id = $this->_contactId; if (!$contact->find(TRUE)) { - CRM_Core_Error::statusBounce(ts('contact does not exist: %1', array(1 => $this->_contactId))); + CRM_Core_Error::statusBounce(ts('contact does not exist: %1', [1 => $this->_contactId])); } $this->_contactType = $contact->contact_type; @@ -104,7 +111,7 @@ public function setDefaultValues() { * Build the form object. */ public function buildQuickForm() { - $params = array(); + $params = []; $params['id'] = $params['contact_id'] = $this->_contactId; $contact = CRM_Contact_BAO_Contact::retrieve($params, $this->_defaults); @@ -125,17 +132,17 @@ public function buildQuickForm() { ts('Contact Information') ); - $this->addButtons(array( - array( + $this->addButtons([ + [ 'type' => 'next', 'name' => ts('Save'), 'isDefault' => TRUE, - ), - array( + ], + [ 'type' => 'cancel', 'name' => ts('Cancel'), - ), - )); + ], + ]); } /** @@ -146,11 +153,11 @@ public function postProcess() { $params = $this->controller->exportValues($this->_name); $locType = CRM_Core_BAO_LocationType::getDefault(); - foreach (array( - 'phone', - 'email', - 'address', - ) as $locFld) { + foreach ([ + 'phone', + 'email', + 'address', + ] as $locFld) { if (!empty($this->_defaults[$locFld]) && $this->_defaults[$locFld][1]['location_type_id']) { $params[$locFld][1]['is_primary'] = $this->_defaults[$locFld][1]['is_primary']; $params[$locFld][1]['location_type_id'] = $this->_defaults[$locFld][1]['location_type_id']; @@ -172,10 +179,10 @@ public function postProcess() { // set status message. if ($this->_contactId) { - $message = ts('%1 has been updated.', array(1 => $contact->display_name)); + $message = ts('%1 has been updated.', [1 => $contact->display_name]); } else { - $message = ts('%1 has been created.', array(1 => $contact->display_name)); + $message = ts('%1 has been created.', [1 => $contact->display_name]); } CRM_Core_Session::setStatus($message, ts('Contact Saved'), 'success'); } diff --git a/CRM/Contact/Form/Relationship.php b/CRM/Contact/Form/Relationship.php index f764c3eac956..1516238bbf77 100644 --- a/CRM/Contact/Form/Relationship.php +++ b/CRM/Contact/Form/Relationship.php @@ -1,9 +1,9 @@ assign('display_name_a', $this->_display_name_a); //get the relationship values. - $this->_values = array(); + $this->_values = []; if ($this->_relationshipId) { - $params = array('id' => $this->_relationshipId); + $params = ['id' => $this->_relationshipId]; CRM_Core_DAO::commonRetrieve('CRM_Contact_DAO_Relationship', $params, $this->_values); } // Check for permissions - if (in_array($this->_action, array(CRM_Core_Action::ADD, CRM_Core_Action::UPDATE, CRM_Core_Action::DELETE))) { + if (in_array($this->_action, [CRM_Core_Action::ADD, CRM_Core_Action::UPDATE, CRM_Core_Action::DELETE])) { if (!CRM_Contact_BAO_Contact_Permission::allow($this->_contactId, CRM_Core_Permission::EDIT) && !CRM_Contact_BAO_Contact_Permission::allow($this->_values['contact_id_b'], CRM_Core_Permission::EDIT)) { CRM_Core_Error::statusBounce(ts('You do not have the necessary permission to edit this contact.')); @@ -149,19 +155,19 @@ public function preProcess() { // Set page title based on action switch ($this->_action) { case CRM_Core_Action::VIEW: - CRM_Utils_System::setTitle(ts('View Relationship for %1', array(1 => $this->_display_name_a))); + CRM_Utils_System::setTitle(ts('View Relationship for %1', [1 => $this->_display_name_a])); break; case CRM_Core_Action::ADD: - CRM_Utils_System::setTitle(ts('Add Relationship for %1', array(1 => $this->_display_name_a))); + CRM_Utils_System::setTitle(ts('Add Relationship for %1', [1 => $this->_display_name_a])); break; case CRM_Core_Action::UPDATE: - CRM_Utils_System::setTitle(ts('Edit Relationship for %1', array(1 => $this->_display_name_a))); + CRM_Utils_System::setTitle(ts('Edit Relationship for %1', [1 => $this->_display_name_a])); break; case CRM_Core_Action::DELETE: - CRM_Utils_System::setTitle(ts('Delete Relationship for %1', array(1 => $this->_display_name_a))); + CRM_Utils_System::setTitle(ts('Delete Relationship for %1', [1 => $this->_display_name_a])); break; } @@ -178,7 +184,7 @@ public function preProcess() { } //get the relationship type id - $this->_relationshipTypeId = str_replace(array('_a_b', '_b_a'), array('', ''), $this->_rtypeId); + $this->_relationshipTypeId = str_replace(['_a_b', '_b_a'], ['', ''], $this->_rtypeId); //get the relationship type if (!$this->_rtype) { @@ -212,22 +218,17 @@ public function preProcess() { * Set default values for the form. */ public function setDefaultValues() { - - $defaults = array(); + $defaults = []; if ($this->_action & CRM_Core_Action::UPDATE) { if (!empty($this->_values)) { $defaults['relationship_type_id'] = $this->_rtypeId; - if (!empty($this->_values['start_date'])) { - list($defaults['start_date']) = CRM_Utils_Date::setDateDefaults($this->_values['start_date']); - } - if (!empty($this->_values['end_date'])) { - list($defaults['end_date']) = CRM_Utils_Date::setDateDefaults($this->_values['end_date']); - } + $defaults['start_date'] = CRM_Utils_Array::value('start_date', $this->_values); + $defaults['end_date'] = CRM_Utils_Array::value('end_date', $this->_values); $defaults['description'] = CRM_Utils_Array::value('description', $this->_values); $defaults['is_active'] = CRM_Utils_Array::value('is_active', $this->_values); - // The javascript on the form will swap these fields if it is a b_a relationship, so we compensate here + // The postprocess function will swap these fields if it is a b_a relationship, so we compensate here $defaults['is_permission_a_b'] = CRM_Utils_Array::value('is_permission_' . $this->_rtype, $this->_values); $defaults['is_permission_b_a'] = CRM_Utils_Array::value('is_permission_' . strrev($this->_rtype), $this->_values); @@ -247,12 +248,12 @@ public function setDefaultValues() { $this->assign('display_name_b', $this->_display_name_b); } - $noteParams = array( + $noteParams = [ 'entity_id' => $this->_relationshipId, 'entity_table' => 'civicrm_relationship', 'limit' => 1, 'version' => 3, - ); + ]; $note = civicrm_api('Note', 'getsingle', $noteParams); $defaults['note'] = CRM_Utils_Array::value('note', $note); } @@ -260,6 +261,7 @@ public function setDefaultValues() { else { $defaults['is_active'] = $defaults['is_current_employer'] = 1; $defaults['relationship_type_id'] = $this->_rtypeId; + $defaults['is_permission_a_b'] = $defaults['is_permission_b_a'] = CRM_Contact_BAO_Relationship::NONE; } $this->_enabled = $defaults['is_active']; @@ -270,9 +272,8 @@ public function setDefaultValues() { * Add the rules for form. */ public function addRules() { - if (!($this->_action & CRM_Core_Action::DELETE)) { - $this->addFormRule(array('CRM_Contact_Form_Relationship', 'dateRule')); + $this->addFormRule(['CRM_Contact_Form_Relationship', 'dateRule']); } } @@ -281,26 +282,24 @@ public function addRules() { */ public function buildQuickForm() { if ($this->_action & CRM_Core_Action::DELETE) { - $this->addButtons(array( - array( - 'type' => 'next', - 'name' => ts('Delete'), - 'isDefault' => TRUE, - ), - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'next', + 'name' => ts('Delete'), + 'isDefault' => TRUE, + ], + [ + 'type' => 'cancel', + 'name' => ts('Cancel'), + ], + ]); return; } // Select list $relationshipList = CRM_Contact_BAO_Relationship::getContactRelationshipType($this->_contactId, $this->_rtype, $this->_relationshipId); - // Metadata needed on clientside - $this->assign('relationshipData', self::getRelationshipTypeMetadata($relationshipList)); + $this->assign('contactTypes', CRM_Contact_BAO_ContactType::contactTypeInfo(TRUE)); foreach ($this->_allRelationshipNames as $id => $vals) { if ($vals['name_a_b'] === 'Employee of') { @@ -309,10 +308,25 @@ public function buildQuickForm() { } } - $this->addField('relationship_type_id', array('options' => array('' => ts('- select -')) + $relationshipList, 'class' => 'huge', 'placeholder' => '- select -'), TRUE); + $this->addField( + 'relationship_type_id', + [ + 'options' => ['' => ts('- select -')] + $relationshipList, + 'class' => 'huge', + 'placeholder' => '- select -', + 'option_url' => 'civicrm/admin/reltype', + 'option_context' => [ + 'contact_id' => $this->_contactId, + 'relationship_direction' => $this->_rtype, + 'relationship_id' => $this->_relationshipId, + 'is_form' => TRUE, + ], + ], + TRUE + ); $label = $this->_action & CRM_Core_Action::ADD ? ts('Contact(s)') : ts('Contact'); - $contactField = $this->addField('related_contact_id', array('label' => $label, 'name' => 'contact_id_b', 'multiple' => TRUE, 'create' => TRUE), TRUE); + $contactField = $this->addField('related_contact_id', ['label' => $label, 'name' => 'contact_id_b', 'multiple' => TRUE, 'create' => TRUE], TRUE); // This field cannot be updated if ($this->_action & CRM_Core_Action::UPDATE) { $contactField->freeze(); @@ -320,39 +334,61 @@ public function buildQuickForm() { $this->add('advcheckbox', 'is_current_employer', $this->_contactType == 'Organization' ? ts('Current Employee') : ts('Current Employer')); - $this->addField('start_date', array('label' => ts('Start Date'), 'formatType' => 'searchDate')); - $this->addField('end_date', array('label' => ts('End Date'), 'formatType' => 'searchDate')); + $this->addField('start_date', ['label' => ts('Start Date')], FALSE, FALSE); + $this->addField('end_date', ['label' => ts('End Date')], FALSE, FALSE); - $this->addField('is_active', array('label' => ts('Enabled?'), 'type' => 'advcheckbox')); + $this->addField('is_active', ['label' => ts('Enabled?'), 'type' => 'advcheckbox']); - $this->addField('is_permission_a_b'); - $this->addField('is_permission_b_a'); + $this->addField('is_permission_a_b', [], TRUE); + $this->addField('is_permission_b_a', [], TRUE); - $this->addField('description', array('label' => ts('Description'))); + $this->addField('description', ['label' => ts('Description')]); CRM_Contact_Form_Edit_Notes::buildQuickForm($this); if ($this->_action & CRM_Core_Action::VIEW) { - $this->addButtons(array( - array( + $this->addButtons([ + [ 'type' => 'cancel', 'name' => ts('Done'), - ), - )); + ], + ]); } else { // make this form an upload since we don't know if the custom data injected dynamically is of type file etc. - $this->addButtons(array( - array( + $this->addButtons([ + [ 'type' => 'upload', 'name' => ts('Save Relationship'), 'isDefault' => TRUE, - ), - array( + ], + [ 'type' => 'cancel', 'name' => ts('Cancel'), - ), - )); + ], + ]); + } + } + + /** + * This function is called when the form is submitted and also from unit test. + * + * @param array $params + * + * @return array + * @throws \CRM_Core_Exception + */ + public function submit($params) { + switch ($this->getAction()) { + case CRM_Core_Action::DELETE: + $this->deleteAction($this->_relationshipId); + return []; + + case CRM_Core_Action::UPDATE: + return $this->updateAction($params); + + default: + return $this->createAction($params); } } @@ -363,88 +399,11 @@ public function postProcess() { // Store the submitted values in an array. $params = $this->controller->exportValues($this->_name); - // CRM-14612 - Don't use adv-checkbox as it interferes with the form js - $params['is_permission_a_b'] = CRM_Utils_Array::value('is_permission_a_b', $params, 0); - $params['is_permission_b_a'] = CRM_Utils_Array::value('is_permission_b_a', $params, 0); - - // action is taken depending upon the mode - if ($this->_action & CRM_Core_Action::DELETE) { - CRM_Contact_BAO_Relationship::del($this->_relationshipId); - - // CRM-15881 UPDATES - // Since the line above nullifies the organization_name and employer_id fiels in the contact record, we need to reload all blocks to reflect this chage on the user interface. - $this->ajaxResponse['reloadBlocks'] = array('#crm-contactinfo-content'); - + $values = $this->submit($params); + if (empty($values)) { return; } - - $relationshipTypeParts = explode('_', $params['relationship_type_id']); - $params['relationship_type_id'] = $relationshipTypeParts[0]; - if (!$this->_rtype) { - // Do we need to wrap this in an if - when is rtype used & is relationship_type_id always set then? - $this->_rtype = $params['relationship_type_id']; - } - $params['contact_id_' . $relationshipTypeParts[1]] = $this->_contactId; - - // Update mode (always single) - if ($this->_action & CRM_Core_Action::UPDATE) { - $update = TRUE; - $params['id'] = $this->_relationshipId; - $ids['relationship'] = $this->_relationshipId; - $relation = CRM_Contact_BAO_Relationship::getRelationshipByID($this->_relationshipId); - if ($relation->contact_id_a == $this->_contactId) { - // I couldn't replicate this path in testing. See below. - $params['contact_id_a'] = $this->_contactId; - $params['contact_id_b'] = array($params['related_contact_id']); - $outcome = CRM_Contact_BAO_Relationship::createMultiple($params, $relationshipTypeParts[1]); - $relationshipIds = $outcome['relationship_ids']; - } - else { - // The only reason we have changed this to use the api & not the above is that this was broken. - // Recommend extracting all of update into a function that uses the api - // and ensuring api / bao take care of 'other stuff' in this form - // the contact_id_a & b can't be changed on this form so don't really need setting. - $params['contact_id_b'] = $this->_contactId; - $params['contact_id_a'] = $params['related_contact_id']; - $result = civicrm_api3('relationship', 'create', $params); - $relationshipIds = array($result['id']); - } - $ids['contactTarget'] = ($relation->contact_id_a == $this->_contactId) ? $relation->contact_id_b : $relation->contact_id_a; - - // @todo this belongs in the BAO. - if ($this->_isCurrentEmployer) { - // if relationship type changes, relationship is disabled, or "current employer" is unchecked, - // clear the current employer. CRM-3235. - $relChanged = $params['relationship_type_id'] != $this->_values['relationship_type_id']; - if (!$params['is_active'] || !$params['is_current_employer'] || $relChanged) { - - // CRM-15881 UPDATES - // If not is_active then is_current_employer needs to be set false as well! Logically a contact cannot be a current employee of a disabled employer relationship. - // If this is not done, then the below process will go ahead and disable the organization_name and employer_id fields in the contact record (which is what is wanted) but then further down will be re-enabled becuase is_current_employer is not false, therefore undoing what was done correctly. - if (!$params['is_active']) { - $params['is_current_employer'] = FALSE; - } - - CRM_Contact_BAO_Contact_Utils::clearCurrentEmployer($this->_values['contact_id_a']); - // Refresh contact summary if in ajax mode - $this->ajaxResponse['reloadBlocks'] = array('#crm-contactinfo-content'); - } - } - if (empty($outcome['saved']) && !empty($update)) { - $outcome['saved'] = $update; - } - $this->setMessage($outcome); - } - // Create mode (could be 1 or more relationships) - else { - $params['contact_id_' . $relationshipTypeParts[2]] = explode(',', $params['related_contact_id']); - $outcome = CRM_Contact_BAO_Relationship::createMultiple($params, $relationshipTypeParts[1]); - $relationshipIds = $outcome['relationship_ids']; - if (empty($outcome['saved']) && !empty($update)) { - $outcome['saved'] = $update; - } - $this->setMessage($outcome); - } + list ($params, $relationshipIds) = $values; // if this is called from case view, //create an activity for case role removal.CRM-4480 @@ -453,49 +412,20 @@ public function postProcess() { CRM_Case_BAO_Case::createCaseRoleActivity($this->_caseId, $relationshipIds, $params['contact_check'], $this->_contactId); } - // Save notes // @todo this belongs in the BAO. - if ($this->_action & CRM_Core_Action::UPDATE || $params['note']) { - foreach ($relationshipIds as $id) { - $noteParams = array( - 'entity_id' => $id, - 'entity_table' => 'civicrm_relationship', - ); - $existing = civicrm_api3('note', 'get', $noteParams); - if (!empty($existing['id'])) { - $noteParams['id'] = $existing['id']; - } - $noteParams['note'] = $params['note']; - $noteParams['contact_id'] = $this->_contactId; - if (!empty($existing['id']) || $params['note']) { - $action = $params['note'] ? 'create' : 'delete'; - civicrm_api3('note', $action, $noteParams); - } - } - } + $note = !empty($params['note']) ? $params['note'] : ''; + $this->saveRelationshipNotes($relationshipIds, $note); - $params['relationship_ids'] = $relationshipIds; + $this->setEmploymentRelationship($params, $relationshipIds); // Refresh contact tabs which might have been affected - $this->ajaxResponse['updateTabs'] = array( - '#tab_member' => CRM_Contact_BAO_Contact::getCountComponent('membership', $this->_contactId), - '#tab_contribute' => CRM_Contact_BAO_Contact::getCountComponent('contribution', $this->_contactId), - ); - - // Set current employee/employer relationship, CRM-3532 - if ($params['is_current_employer'] && $this->_allRelationshipNames[$params['relationship_type_id']]["name_a_b"] == - 'Employee of') { - $employerParams = array(); - foreach ($relationshipIds as $id) { - // Fixme this is dumb why do we have to look this up again? - $rel = CRM_Contact_BAO_Relationship::getRelationshipByID($id); - $employerParams[$rel->contact_id_a] = $rel->contact_id_b; - } - // @todo this belongs in the BAO. - CRM_Contact_BAO_Contact_Utils::setCurrentEmployer($employerParams); - // Refresh contact summary if in ajax mode - $this->ajaxResponse['reloadBlocks'] = array('#crm-contactinfo-content'); - } + $this->ajaxResponse = [ + 'reloadBlocks' => ['#crm-contactinfo-content'], + 'updateTabs' => [ + '#tab_member' => CRM_Contact_BAO_Contact::getCountComponent('membership', $this->_contactId), + '#tab_contribute' => CRM_Contact_BAO_Contact::getCountComponent('contribution', $this->_contactId), + ], + ]; } /** @@ -508,13 +438,11 @@ public function postProcess() { * mixed true or array of errors */ public static function dateRule($params) { - $errors = array(); + $errors = []; // check start and end date if (!empty($params['start_date']) && !empty($params['end_date'])) { - $start_date = CRM_Utils_Date::format(CRM_Utils_Array::value('start_date', $params)); - $end_date = CRM_Utils_Date::format(CRM_Utils_Array::value('end_date', $params)); - if ($start_date && $end_date && (int ) $end_date < (int ) $start_date) { + if ($params['end_date'] < $params['start_date']) { $errors['end_date'] = ts('The relationship end date cannot be prior to the start date.'); } } @@ -534,28 +462,28 @@ public static function dateRule($params) { */ protected function setMessage($outcome) { if (!empty($outcome['valid']) && empty($outcome['saved'])) { - CRM_Core_Session::setStatus(ts('Relationship created.', array( + CRM_Core_Session::setStatus(ts('Relationship created.', [ 'count' => $outcome['valid'], 'plural' => '%count relationships created.', - )), ts('Saved'), 'success'); + ]), ts('Saved'), 'success'); } if (!empty($outcome['invalid'])) { - CRM_Core_Session::setStatus(ts('%count relationship record was not created due to an invalid contact type.', array( + CRM_Core_Session::setStatus(ts('%count relationship record was not created due to an invalid contact type.', [ 'count' => $outcome['invalid'], 'plural' => '%count relationship records were not created due to invalid contact types.', - )), ts('%count invalid relationship record', array( + ]), ts('%count invalid relationship record', [ 'count' => $outcome['invalid'], 'plural' => '%count invalid relationship records', - ))); + ])); } if (!empty($outcome['duplicate'])) { - CRM_Core_Session::setStatus(ts('One relationship was not created because it already exists.', array( + CRM_Core_Session::setStatus(ts('One relationship was not created because it already exists.', [ 'count' => $outcome['duplicate'], 'plural' => '%count relationships were not created because they already exist.', - )), ts('%count duplicate relationship', array( + ]), ts('%count duplicate relationship', [ 'count' => $outcome['duplicate'], 'plural' => '%count duplicate relationships', - ))); + ])); } if (!empty($outcome['saved'])) { CRM_Core_Session::setStatus(ts('Relationship record has been updated.'), ts('Saved'), 'success'); @@ -564,30 +492,173 @@ protected function setMessage($outcome) { /** * @param $relationshipList + * * @return array */ public static function getRelationshipTypeMetadata($relationshipList) { $contactTypes = CRM_Contact_BAO_ContactType::contactTypeInfo(TRUE); $allRelationshipNames = CRM_Core_PseudoConstant::relationshipType('name'); - $jsData = array(); + $jsData = []; // Get just what we need to keep the dom small - $whatWeWant = array_flip(array( + $whatWeWant = array_flip([ 'contact_type_a', 'contact_type_b', 'contact_sub_type_a', 'contact_sub_type_b', - )); + ]); foreach ($allRelationshipNames as $id => $vals) { if (isset($relationshipList["{$id}_a_b"]) || isset($relationshipList["{$id}_b_a"])) { $jsData[$id] = array_filter(array_intersect_key($allRelationshipNames[$id], $whatWeWant)); // Add user-friendly placeholder - foreach (array('a', 'b') as $x) { + foreach (['a', 'b'] as $x) { $type = !empty($jsData[$id]["contact_sub_type_$x"]) ? $jsData[$id]["contact_sub_type_$x"] : CRM_Utils_Array::value("contact_type_$x", $jsData[$id]); - $jsData[$id]["placeholder_$x"] = $type ? ts('- select %1 -', array(strtolower($contactTypes[$type]['label']))) : ts('- select contact -'); + $jsData[$id]["placeholder_$x"] = $type ? ts('- select %1 -', [strtolower($contactTypes[$type]['label'])]) : ts('- select contact -'); } } } return $jsData; } + /** + * Handling 'delete relationship' action + * + * @param int $id + * Relationship ID + */ + private function deleteAction($id) { + CRM_Contact_BAO_Relationship::del($id); + + // reload all blocks to reflect this change on the user interface. + $this->ajaxResponse['reloadBlocks'] = ['#crm-contactinfo-content']; + } + + /** + * Handling updating relationship action + * + * @param array $params + * + * @return array + * @throws \CRM_Core_Exception + */ + private function updateAction($params) { + list($params, $_) = $this->preparePostProcessParameters($params); + try { + civicrm_api3('relationship', 'create', $params); + } + catch (CiviCRM_API3_Exception $e) { + throw new CRM_Core_Exception('Relationship create error ' . $e->getMessage()); + } + + $this->setMessage(['saved' => TRUE]); + return [$params, [$this->_relationshipId]]; + } + + /** + * Handling creating relationship action + * + * @param array $params + * + * @return array + * @throws \CRM_Core_Exception + */ + private function createAction($params) { + list($params, $primaryContactLetter) = $this->preparePostProcessParameters($params); + + $outcome = CRM_Contact_BAO_Relationship::createMultiple($params, $primaryContactLetter); + + $relationshipIds = $outcome['relationship_ids']; + + $this->setMessage($outcome); + + return [$params, $relationshipIds]; + } + + /** + * Prepares parameters to be used for create/update actions + * + * @param array $values + * + * @return array + */ + private function preparePostProcessParameters($values) { + $params = $values; + list($relationshipTypeId, $a, $b) = explode('_', $params['relationship_type_id']); + + $params['relationship_type_id'] = $relationshipTypeId; + $params['contact_id_' . $a] = $this->_contactId; + + if (empty($this->_relationshipId)) { + $params['contact_id_' . $b] = explode(',', $params['related_contact_id']); + } + else { + $params['id'] = $this->_relationshipId; + $params['contact_id_' . $b] = $params['related_contact_id']; + } + + // If this is a b_a relationship these form elements are flipped + $params['is_permission_a_b'] = CRM_Utils_Array::value("is_permission_{$a}_{$b}", $values, 0); + $params['is_permission_b_a'] = CRM_Utils_Array::value("is_permission_{$b}_{$a}", $values, 0); + + return [$params, $a]; + } + + /** + * Updates/Creates relationship notes + * + * @param array $relationshipIds + * @param string $note + * + * @throws \CiviCRM_API3_Exception + */ + private function saveRelationshipNotes($relationshipIds, $note) { + foreach ($relationshipIds as $id) { + $noteParams = [ + 'entity_id' => $id, + 'entity_table' => 'civicrm_relationship', + ]; + + $existing = civicrm_api3('note', 'get', $noteParams); + if (!empty($existing['id'])) { + $noteParams['id'] = $existing['id']; + } + + $action = NULL; + if (!empty($note)) { + $action = 'create'; + $noteParams['note'] = $note; + $noteParams['contact_id'] = $this->_contactId; + } + elseif (!empty($noteParams['id'])) { + $action = 'delete'; + } + + if (!empty($action)) { + civicrm_api3('note', $action, $noteParams); + } + } + } + + /** + * Sets current employee/employer relationship + * + * @param $params + * @param array $relationshipIds + */ + private function setEmploymentRelationship($params, $relationshipIds) { + $employerParams = []; + foreach ($relationshipIds as $id) { + if (!CRM_Contact_BAO_Relationship::isCurrentEmployerNeedingToBeCleared($params, $id) + //don't think this is required to check again. + && $this->_allRelationshipNames[$params['relationship_type_id']]["name_a_b"] == 'Employee of') { + // Fixme this is dumb why do we have to look this up again? + $rel = CRM_Contact_BAO_Relationship::getRelationshipByID($id); + $employerParams[$rel->contact_id_a] = $rel->contact_id_b; + } + } + if (!empty($employerParams)) { + // @todo this belongs in the BAO. + CRM_Contact_BAO_Contact_Utils::setCurrentEmployer($employerParams); + } + } + } diff --git a/CRM/Contact/Form/Search.php b/CRM/Contact/Form/Search.php index db90c66280eb..297b0f3face6 100644 --- a/CRM/Contact/Form/Search.php +++ b/CRM/Contact/Form/Search.php @@ -1,9 +1,9 @@ 'Show members of group', 'amtg' => 'Add members to group', 'basic' => 'Basic Search', @@ -183,7 +184,7 @@ public static function &validContext() { 'builder' => 'Search Builder', 'advanced' => 'Advanced Search', 'custom' => 'Custom Search', - ); + ]; } return self::$_validContext; } @@ -199,124 +200,170 @@ public static function isSearchContext($context) { } public static function setModeValues() { - if (!self::$_modeValues) { - self::$_modeValues = array( - 1 => array( - 'selectorName' => self::$_selectorName, - 'selectorLabel' => ts('Contacts'), - 'taskFile' => 'CRM/Contact/Form/Search/ResultTasks.tpl', - 'taskContext' => NULL, - 'resultFile' => 'CRM/Contact/Form/Selector.tpl', - 'resultContext' => NULL, - 'taskClassName' => 'CRM_Contact_Task', - ), - 2 => array( - 'selectorName' => 'CRM_Contribute_Selector_Search', - 'selectorLabel' => ts('Contributions'), - 'taskFile' => 'CRM/common/searchResultTasks.tpl', - 'taskContext' => 'Contribution', - 'resultFile' => 'CRM/Contribute/Form/Selector.tpl', - 'resultContext' => 'Search', - 'taskClassName' => 'CRM_Contribute_Task', - ), - 3 => array( - 'selectorName' => 'CRM_Event_Selector_Search', - 'selectorLabel' => ts('Event Participants'), - 'taskFile' => 'CRM/common/searchResultTasks.tpl', - 'taskContext' => NULL, - 'resultFile' => 'CRM/Event/Form/Selector.tpl', - 'resultContext' => 'Search', - 'taskClassName' => 'CRM_Event_Task', - ), - 4 => array( - 'selectorName' => 'CRM_Activity_Selector_Search', - 'selectorLabel' => ts('Activities'), - 'taskFile' => 'CRM/common/searchResultTasks.tpl', - 'taskContext' => NULL, - 'resultFile' => 'CRM/Activity/Form/Selector.tpl', - 'resultContext' => 'Search', - 'taskClassName' => 'CRM_Activity_Task', - ), - 5 => array( - 'selectorName' => 'CRM_Member_Selector_Search', - 'selectorLabel' => ts('Memberships'), - 'taskFile' => "CRM/common/searchResultTasks.tpl", - 'taskContext' => NULL, - 'resultFile' => 'CRM/Member/Form/Selector.tpl', - 'resultContext' => 'Search', - 'taskClassName' => 'CRM_Member_Task', - ), - 6 => array( - 'selectorName' => 'CRM_Case_Selector_Search', - 'selectorLabel' => ts('Cases'), - 'taskFile' => "CRM/common/searchResultTasks.tpl", - 'taskContext' => NULL, - 'resultFile' => 'CRM/Case/Form/Selector.tpl', - 'resultContext' => 'Search', - 'taskClassName' => 'CRM_Case_Task', - ), - 7 => array( - 'selectorName' => self::$_selectorName, - 'selectorLabel' => ts('Related Contacts'), - 'taskFile' => 'CRM/Contact/Form/Search/ResultTasks.tpl', - 'taskContext' => NULL, - 'resultFile' => 'CRM/Contact/Form/Selector.tpl', - 'resultContext' => NULL, - 'taskClassName' => 'CRM_Contact_Task', - ), - 8 => array( - 'selectorName' => 'CRM_Mailing_Selector_Search', - 'selectorLabel' => ts('Mailings'), - 'taskFile' => "CRM/common/searchResultTasks.tpl", - 'taskContext' => NULL, - 'resultFile' => 'CRM/Mailing/Form/Selector.tpl', - 'resultContext' => 'Search', - 'taskClassName' => 'CRM_Mailing_Task', - ), - ); - } + self::$_modeValues = [ + CRM_Contact_BAO_Query::MODE_CONTACTS => [ + 'selectorName' => self::$_selectorName, + 'selectorLabel' => ts('Contacts'), + 'taskFile' => 'CRM/Contact/Form/Search/ResultTasks.tpl', + 'taskContext' => NULL, + 'resultFile' => 'CRM/Contact/Form/Selector.tpl', + 'resultContext' => NULL, + 'taskClassName' => 'CRM_Contact_Task', + 'component' => '', + ], + CRM_Contact_BAO_Query::MODE_CONTRIBUTE => [ + 'selectorName' => 'CRM_Contribute_Selector_Search', + 'selectorLabel' => ts('Contributions'), + 'taskFile' => 'CRM/common/searchResultTasks.tpl', + 'taskContext' => 'Contribution', + 'resultFile' => 'CRM/Contribute/Form/Selector.tpl', + 'resultContext' => 'Search', + 'taskClassName' => 'CRM_Contribute_Task', + 'component' => 'CiviContribute', + ], + CRM_Contact_BAO_Query::MODE_EVENT => [ + 'selectorName' => 'CRM_Event_Selector_Search', + 'selectorLabel' => ts('Event Participants'), + 'taskFile' => 'CRM/common/searchResultTasks.tpl', + 'taskContext' => NULL, + 'resultFile' => 'CRM/Event/Form/Selector.tpl', + 'resultContext' => 'Search', + 'taskClassName' => 'CRM_Event_Task', + 'component' => 'CiviEvent', + ], + CRM_Contact_BAO_Query::MODE_ACTIVITY => [ + 'selectorName' => 'CRM_Activity_Selector_Search', + 'selectorLabel' => ts('Activities'), + 'taskFile' => 'CRM/common/searchResultTasks.tpl', + 'taskContext' => NULL, + 'resultFile' => 'CRM/Activity/Form/Selector.tpl', + 'resultContext' => 'Search', + 'taskClassName' => 'CRM_Activity_Task', + 'component' => 'activity', + ], + CRM_Contact_BAO_Query::MODE_MEMBER => [ + 'selectorName' => 'CRM_Member_Selector_Search', + 'selectorLabel' => ts('Memberships'), + 'taskFile' => "CRM/common/searchResultTasks.tpl", + 'taskContext' => NULL, + 'resultFile' => 'CRM/Member/Form/Selector.tpl', + 'resultContext' => 'Search', + 'taskClassName' => 'CRM_Member_Task', + 'component' => 'CiviMember', + ], + CRM_Contact_BAO_Query::MODE_CASE => [ + 'selectorName' => 'CRM_Case_Selector_Search', + 'selectorLabel' => ts('Cases'), + 'taskFile' => "CRM/common/searchResultTasks.tpl", + 'taskContext' => NULL, + 'resultFile' => 'CRM/Case/Form/Selector.tpl', + 'resultContext' => 'Search', + 'taskClassName' => 'CRM_Case_Task', + 'component' => 'CiviCase', + ], + CRM_Contact_BAO_Query::MODE_CONTACTSRELATED => [ + 'selectorName' => self::$_selectorName, + 'selectorLabel' => ts('Related Contacts'), + 'taskFile' => 'CRM/Contact/Form/Search/ResultTasks.tpl', + 'taskContext' => NULL, + 'resultFile' => 'CRM/Contact/Form/Selector.tpl', + 'resultContext' => NULL, + 'taskClassName' => 'CRM_Contact_Task', + 'component' => 'related_contact', + ], + CRM_Contact_BAO_Query::MODE_MAILING => [ + 'selectorName' => 'CRM_Mailing_Selector_Search', + 'selectorLabel' => ts('Mailings'), + 'taskFile' => "CRM/common/searchResultTasks.tpl", + 'taskContext' => NULL, + 'resultFile' => 'CRM/Mailing/Form/Selector.tpl', + 'resultContext' => 'Search', + 'taskClassName' => 'CRM_Mailing_Task', + 'component' => 'CiviMail', + ], + ]; } /** + * Get the metadata for the query mode (this includes task class names) + * * @param int $mode * - * @return mixed + * @return array + * @throws \CRM_Core_Exception */ - public static function getModeValue($mode = 1) { - self::setModeValues(); + public static function getModeValue($mode = CRM_Contact_BAO_Query::MODE_CONTACTS) { + $searchPane = CRM_Utils_Request::retrieve('searchPane', 'String'); + if (!empty($searchPane)) { + $mode = array_search($searchPane, self::getModeToComponentMapping()); + } + self::setModeValues(); if (!array_key_exists($mode, self::$_modeValues)) { - $mode = 1; + $mode = CRM_Contact_BAO_Query::MODE_CONTACTS; } return self::$_modeValues[$mode]; } + /** + * Get a mapping of modes to components. + * + * This will map the integers to the components. Contact has an empty component + * an pseudo-components exist for activity & related_contact. + * + * @return array + */ + public static function getModeToComponentMapping() { + $mapping = []; + self::setModeValues(); + + foreach (self::$_modeValues as $id => $metadata) { + $mapping[$id] = $metadata['component']; + } + return $mapping; + } + /** * @return array */ public static function getModeSelect() { self::setModeValues(); - $select = array(); + $enabledComponents = CRM_Core_Component::getEnabledComponents(); + $componentModes = []; foreach (self::$_modeValues as $id => & $value) { - $select[$id] = $value['selectorLabel']; + if (strpos($value['component'], 'Civi') !== FALSE + && !array_key_exists($value['component'], $enabledComponents) + ) { + continue; + } + $componentModes[$id] = $value['selectorLabel']; + } + + // unset disabled components + if (!array_key_exists('CiviMail', $enabledComponents)) { + unset($componentModes[CRM_Contact_BAO_Query::MODE_MAILING]); } - // unset contributions or participants if user does not have - // permission on them + // unset contributions or participants if user does not have permission on them if (!CRM_Core_Permission::access('CiviContribute')) { - unset($select['2']); + unset($componentModes[CRM_Contact_BAO_Query::MODE_CONTRIBUTE]); } if (!CRM_Core_Permission::access('CiviEvent')) { - unset($select['3']); + unset($componentModes[CRM_Contact_BAO_Query::MODE_EVENT]); + } + + if (!CRM_Core_Permission::access('CiviMember')) { + unset($componentModes[CRM_Contact_BAO_Query::MODE_MEMBER]); } if (!CRM_Core_Permission::check('view all activities')) { - unset($select['4']); + unset($componentModes[CRM_Contact_BAO_Query::MODE_ACTIVITY]); } - return $select; + + return $componentModes; } /** @@ -325,26 +372,17 @@ public static function getModeSelect() { * @return array */ public function buildTaskList() { + // amtg = 'Add members to group' if ($this->_context !== 'amtg') { - $permission = CRM_Core_Permission::getPermission(); - - if ($this->_componentMode == 1 || $this->_componentMode == 7) { - $this->_taskList += CRM_Contact_Task::permissionedTaskTitles($permission, - CRM_Utils_Array::value('deleted_contacts', $this->_formValues) - ); - } - else { - $className = $this->_modeValue['taskClassName']; - $this->_taskList += $className::permissionedTaskTitles($permission, FALSE); - } - - // Only offer the "Update Smart Group" task if a smart group/saved search is already in play - if (isset($this->_ssID) && $permission == CRM_Core_Permission::EDIT) { - $this->_taskList += CRM_Contact_Task::optionalTaskTitle(); + $taskParams['deletedContacts'] = FALSE; + if ($this->_componentMode == CRM_Contact_BAO_Query::MODE_CONTACTS || $this->_componentMode == CRM_Contact_BAO_Query::MODE_CONTACTSRELATED) { + $taskParams['deletedContacts'] = CRM_Utils_Array::value('deleted_contacts', $this->_formValues); } + $className = $this->_modeValue['taskClassName']; + $taskParams['ssID'] = isset($this->_ssID) ? $this->_ssID : NULL; + $this->_taskList += $className::permissionedTaskTitles(CRM_Core_Permission::getPermission(), $taskParams); } - asort($this->_taskList); return $this->_taskList; } @@ -353,36 +391,19 @@ public function buildTaskList() { */ public function buildQuickForm() { parent::buildQuickForm(); - CRM_Core_Resources::singleton() - // jsTree is needed for tags popup - ->addScriptFile('civicrm', 'packages/jquery/plugins/jstree/jquery.jstree.js', 0, 'html-header', FALSE) - ->addStyleFile('civicrm', 'packages/jquery/plugins/jstree/themes/default/style.css', 0, 'html-header'); - $permission = CRM_Core_Permission::getPermission(); + // some tasks.. what do we want to do with the selected contacts ? - $tasks = array(); - if ($this->_componentMode == 1 || $this->_componentMode == 7) { - $tasks += CRM_Contact_Task::permissionedTaskTitles($permission, - CRM_Utils_Array::value('deleted_contacts', $this->_formValues) - ); - } - else { - $className = $this->_modeValue['taskClassName']; - $tasks += $className::permissionedTaskTitles($permission, FALSE); - } + $this->_taskList = $this->buildTaskList(); if (isset($this->_ssID)) { - if ($permission == CRM_Core_Permission::EDIT) { - $tasks = $tasks + CRM_Contact_Task::optionalTaskTitle(); - } - $search_custom_id = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_SavedSearch', $this->_ssID, 'search_custom_id'); - $savedSearchValues = array( + $savedSearchValues = [ 'id' => $this->_ssID, 'name' => CRM_Contact_BAO_SavedSearch::getName($this->_ssID, 'title'), 'search_custom_id' => $search_custom_id, - ); + ]; $this->assign_by_ref('savedSearch', $savedSearchValues); $this->assign('ssID', $this->_ssID); } @@ -413,7 +434,7 @@ public function buildQuickForm() { } // set the group title - $groupValues = array('id' => $this->_groupID, 'title' => $this->_group[$this->_groupID]); + $groupValues = ['id' => $this->_groupID, 'title' => $this->_group[$this->_groupID]]; $this->assign_by_ref('group', $groupValues); // also set ssID if this is a saved search @@ -428,10 +449,10 @@ public function buildQuickForm() { } // Set dynamic page title for 'Show Members of Group' - CRM_Utils_System::setTitle(ts('Contacts in Group: %1', array(1 => $this->_group[$this->_groupID]))); + CRM_Utils_System::setTitle(ts('Contacts in Group: %1', [1 => $this->_group[$this->_groupID]])); } - $group_contact_status = array(); + $group_contact_status = []; foreach (CRM_Core_SelectValues::groupContactStatus() as $k => $v) { if (!empty($k)) { $group_contact_status[] = $this->createElement('checkbox', $k, NULL, $v); @@ -455,29 +476,29 @@ public function buildQuickForm() { } // Set dynamic page title for 'Add Members Group' - CRM_Utils_System::setTitle(ts('Add to Group: %1', array(1 => $this->_group[$this->_amtgID]))); + CRM_Utils_System::setTitle(ts('Add to Group: %1', [1 => $this->_group[$this->_amtgID]])); // also set the group title and freeze the action task with Add Members to Group - $groupValues = array('id' => $this->_amtgID, 'title' => $this->_group[$this->_amtgID]); + $groupValues = ['id' => $this->_amtgID, 'title' => $this->_group[$this->_amtgID]]; $this->assign_by_ref('group', $groupValues); - $this->add('submit', $this->_actionButtonName, ts('Add Contacts to %1', array(1 => $this->_group[$this->_amtgID])), - array( + $this->add('submit', $this->_actionButtonName, ts('Add Contacts to %1', [1 => $this->_group[$this->_amtgID]]), + [ 'class' => 'crm-form-submit', - ) + ] ); - $this->add('hidden', 'task', CRM_Contact_Task::GROUP_CONTACTS); - $selectedRowsRadio = $this->addElement('radio', 'radio_ts', NULL, '', 'ts_sel', array('checked' => 'checked')); + $this->add('hidden', 'task', CRM_Contact_Task::GROUP_ADD); + $selectedRowsRadio = $this->addElement('radio', 'radio_ts', NULL, '', 'ts_sel', ['checked' => 'checked']); $allRowsRadio = $this->addElement('radio', 'radio_ts', NULL, '', 'ts_all'); $this->assign('ts_sel_id', $selectedRowsRadio->_attributes['id']); $this->assign('ts_all_id', $allRowsRadio->_attributes['id']); } - $selectedContactIds = array(); + $selectedContactIds = []; $qfKeyParam = CRM_Utils_Array::value('qfKey', $this->_formValues); // We use ajax to handle selections only if the search results component_mode is set to "contacts" - if ($qfKeyParam && ($this->get('component_mode') <= 1 || $this->get('component_mode') == 7)) { + if ($qfKeyParam && ($this->get('component_mode') <= CRM_Contact_BAO_Query::MODE_CONTACTS || $this->get('component_mode') == CRM_Contact_BAO_Query::MODE_CONTACTSRELATED)) { $this->addClass('crm-ajax-selection-form'); $qfKeyParam = "civicrm search {$qfKeyParam}"; - $selectedContactIdsArr = CRM_Core_BAO_PrevNextCache::getSelection($qfKeyParam); + $selectedContactIdsArr = Civi::service('prevnext')->getSelection($qfKeyParam); $selectedContactIds = array_keys($selectedContactIdsArr[$qfKeyParam]); } @@ -507,18 +528,16 @@ public function preProcess() { * driven by the wizard framework */ - $this->_reset = CRM_Utils_Request::retrieve('reset', 'Boolean', - CRM_Core_DAO::$_nullObject - ); + $this->_reset = CRM_Utils_Request::retrieve('reset', 'Boolean'); - $this->_force = CRM_Utils_Request::retrieve('force', 'Boolean', CRM_Core_DAO::$_nullObject); + $this->_force = CRM_Utils_Request::retrieve('force', 'Boolean'); $this->_groupID = CRM_Utils_Request::retrieve('gid', 'Positive', $this); $this->_amtgID = CRM_Utils_Request::retrieve('amtgID', 'Positive', $this); $this->_ssID = CRM_Utils_Request::retrieve('ssID', 'Positive', $this); $this->_sortByCharacter = CRM_Utils_Request::retrieve('sortByCharacter', 'String', $this); $this->_ufGroupID = CRM_Utils_Request::retrieve('id', 'Positive', $this); - $this->_componentMode = CRM_Utils_Request::retrieve('component_mode', 'Positive', $this, FALSE, 1, $_REQUEST); - $this->_operator = CRM_Utils_Request::retrieve('operator', 'String', $this, FALSE, 1, $_REQUEST, 'AND'); + $this->_componentMode = CRM_Utils_Request::retrieve('component_mode', 'Positive', $this, FALSE, CRM_Contact_BAO_Query::MODE_CONTACTS, $_REQUEST); + $this->_operator = CRM_Utils_Request::retrieve('operator', 'String', $this, FALSE, CRM_Contact_BAO_Query::SEARCH_OPERATOR_AND, 'REQUEST'); /** * set the button names @@ -528,12 +547,6 @@ public function preProcess() { $this->assign('actionButtonName', $this->_actionButtonName); - // reset from session, CRM-3526 - $session = CRM_Core_Session::singleton(); - if ($this->_force && $session->get('selectedSearchContactIds')) { - $session->resetScope('selectedSearchContactIds'); - } - // if we dont get this from the url, use default if one exsts $config = CRM_Core_Config::singleton(); if ($this->_ufGroupID == NULL && @@ -543,7 +556,7 @@ public function preProcess() { } // assign context to drive the template display, make sure context is valid - $this->_context = CRM_Utils_Request::retrieve('context', 'String', $this, FALSE, 'search'); + $this->_context = CRM_Utils_Request::retrieve('context', 'Alphanumeric', $this, FALSE, 'search'); if (!CRM_Utils_Array::value($this->_context, self::validContext())) { $this->_context = 'search'; } @@ -625,23 +638,23 @@ public function preProcess() { // FIXME: we should generalise in a way that components could inject url-filters // just like they build their own form elements - foreach (array( - 'mailing_id', - 'mailing_delivery_status', - 'mailing_open_status', - 'mailing_click_status', - 'mailing_reply_status', - 'mailing_optout', - 'mailing_forward', - 'mailing_unsubscribe', - 'mailing_date_low', - 'mailing_date_high', - ) as $mailingFilter) { + foreach ([ + 'mailing_id', + 'mailing_delivery_status', + 'mailing_open_status', + 'mailing_click_status', + 'mailing_reply_status', + 'mailing_optout', + 'mailing_forward', + 'mailing_unsubscribe', + 'mailing_date_low', + 'mailing_date_high', + ] as $mailingFilter) { $type = 'String'; if ($mailingFilter == 'mailing_id' && $filterVal = CRM_Utils_Request::retrieve('mailing_id', 'Positive', $this) ) { - $this->_formValues[$mailingFilter] = array($filterVal); + $this->_formValues[$mailingFilter] = [$filterVal]; } elseif ($filterVal = CRM_Utils_Request::retrieve($mailingFilter, $type, $this)) { $this->_formValues[$mailingFilter] = $filterVal; @@ -656,9 +669,9 @@ public function preProcess() { $this->assign('id', CRM_Utils_Array::value('uf_group_id', $this->_formValues) ); - $operator = CRM_Utils_Array::value('operator', $this->_formValues, 'AND'); + $operator = CRM_Utils_Array::value('operator', $this->_formValues, CRM_Contact_BAO_Query::SEARCH_OPERATOR_AND); $this->set('queryOperator', $operator); - if ($operator == 'OR') { + if ($operator == CRM_Contact_BAO_Query::SEARCH_OPERATOR_OR) { $this->assign('operator', ts('OR')); } else { @@ -668,17 +681,16 @@ public function preProcess() { // show the context menu only when we’re not searching for deleted contacts; CRM-5673 if (empty($this->_formValues['deleted_contacts'])) { $menuItems = CRM_Contact_BAO_Contact::contextMenu(); - $primaryActions = CRM_Utils_Array::value('primaryActions', $menuItems, array()); - $this->_contextMenu = CRM_Utils_Array::value('moreActions', $menuItems, array()); + $primaryActions = CRM_Utils_Array::value('primaryActions', $menuItems, []); + $this->_contextMenu = CRM_Utils_Array::value('moreActions', $menuItems, []); $this->assign('contextMenu', $primaryActions + $this->_contextMenu); } if (!isset($this->_componentMode)) { $this->_componentMode = CRM_Contact_BAO_Query::MODE_CONTACTS; } - self::setModeValues(); - self::$_selectorName = $this->_modeValue['selectorName']; + self::setModeValues(); $setDynamic = FALSE; if (strpos(self::$_selectorName, 'CRM_Contact_Selector') !== FALSE) { @@ -764,14 +776,14 @@ public function postProcess() { $this->_done = TRUE; //for prev/next pagination - $crmPID = CRM_Utils_Request::retrieve('crmPID', 'Integer', CRM_Core_DAO::$_nullObject); + $crmPID = CRM_Utils_Request::retrieve('crmPID', 'Integer'); if (array_key_exists($this->_searchButtonName, $_POST) || ($this->_force && !$crmPID) ) { //reset the cache table for new search $cacheKey = "civicrm search {$this->controller->_key}"; - CRM_Core_BAO_PrevNextCache::deleteItem(NULL, $cacheKey); + Civi::service('prevnext')->deleteItem(NULL, $cacheKey); } //get the button name diff --git a/CRM/Contact/Form/Search/Advanced.php b/CRM/Contact/Form/Search/Advanced.php index 8d9f4b439212..0656d737d63d 100644 --- a/CRM/Contact/Form/Search/Advanced.php +++ b/CRM/Contact/Form/Search/Advanced.php @@ -1,9 +1,9 @@ _formValues; + // Set ssID for unit tests. + if (empty($this->_ssID)) { + $this->_ssID = $this->get('ssID'); + } + + $defaults = array_merge($this->_formValues, array( + 'privacy_toggle' => 1, + 'operator' => 'AND', + )); $this->normalizeDefaultValues($defaults); if ($this->_context === 'amtg') { - $defaults['task'] = CRM_Contact_Task::GROUP_CONTACTS; + $defaults['task'] = CRM_Contact_Task::GROUP_ADD; } - $defaults['privacy_toggle'] = 1; - $defaults['operator'] = 'AND'; - return $defaults; } @@ -231,9 +237,9 @@ public function postProcess() { // FIXME: so leaving this as a dependency for now if (array_key_exists('contribution_amount_low', $this->_formValues)) { foreach (array( - 'contribution_amount_low', - 'contribution_amount_high', - ) as $f) { + 'contribution_amount_low', + 'contribution_amount_high', + ) as $f) { $this->_formValues[$f] = CRM_Utils_Rule::cleanMoney($this->_formValues[$f]); } } @@ -264,11 +270,11 @@ public function postProcess() { !$this->_force ) { foreach (array( - 'case_type_id', - 'case_status_id', - 'case_deleted', - 'case_tags', - ) as $caseCriteria) { + 'case_type_id', + 'case_status_id', + 'case_deleted', + 'case_tags', + ) as $caseCriteria) { if (!empty($this->_formValues[$caseCriteria])) { $allCases = TRUE; $this->_formValues['case_owner'] = 1; @@ -342,7 +348,9 @@ public function normalizeFormValues() { 'contribution_trxn_id', 'activity_type_id', 'status_id', + 'priority_id', 'activity_subject', + 'activity_details', 'contribution_page_id', 'contribution_product_id', 'payment_instrument_id', @@ -350,7 +358,10 @@ public function normalizeFormValues() { 'contact_tags', 'preferred_communication_method', ); - $changeNames = array('status_id' => 'activity_status_id'); + $changeNames = array( + 'status_id' => 'activity_status_id', + 'priority_id' => 'activity_priority_id', + ); CRM_Contact_BAO_Query::processSpecialFormValue($this->_formValues, $specialParams, $changeNames); $taglist = CRM_Utils_Array::value('contact_taglist', $this->_formValues); diff --git a/CRM/Contact/Form/Search/Basic.php b/CRM/Contact/Form/Search/Basic.php index 56ee6723e281..9a3386836dbb 100644 --- a/CRM/Contact/Form/Search/Basic.php +++ b/CRM/Contact/Form/Search/Basic.php @@ -1,9 +1,9 @@ ts('- any contact type -')) + CRM_Contact_BAO_ContactType::getSelectElements(); + $contactTypes = ['' => ts('- any contact type -')] + CRM_Contact_BAO_ContactType::getSelectElements(); $this->add('select', 'contact_type', ts('is...'), $contactTypes, FALSE, - array('class' => 'crm-select2') + ['class' => 'crm-select2'] ); } // add select for groups + // Get hierarchical listing of groups, respecting ACLs for CRM-16836. + $groupHierarchy = CRM_Contact_BAO_Group::getGroupsHierarchy($this->_group, NULL, '  ', TRUE); if (!empty($searchOptions['groups'])) { - $this->addField('group', array( - 'entity' => 'group_contact', - 'label' => ts('in'), - 'placeholder' => ts('- any group -'), - )); + $this->addField('group', [ + 'entity' => 'group_contact', + 'label' => ts('in'), + 'placeholder' => ts('- any group -'), + 'options' => $groupHierarchy, + ]); } if (!empty($searchOptions['tags'])) { // tag criteria if (!empty($this->_tag)) { - $this->addField('tag', array( - 'entity' => 'entity_tag', - 'label' => ts('with'), - 'placeholder' => ts('- any tag -'), - )); + $this->addField('tag', [ + 'entity' => 'entity_tag', + 'label' => ts('with'), + 'placeholder' => ts('- any tag -'), + ]); } } @@ -94,7 +97,7 @@ public function buildQuickForm() { * the default array reference */ public function setDefaultValues() { - $defaults = array(); + $defaults = []; $defaults['sort_name'] = CRM_Utils_Array::value('sort_name', $this->_formValues); foreach (self::$csv as $v) { @@ -108,7 +111,7 @@ public function setDefaultValues() { } if ($this->_context === 'amtg') { - $defaults['task'] = CRM_Contact_Task::GROUP_CONTACTS; + $defaults['task'] = CRM_Contact_Task::GROUP_ADD; } if ($this->_context === 'smog') { @@ -122,7 +125,7 @@ public function setDefaultValues() { * Add local and global form rules. */ public function addRules() { - $this->addFormRule(array('CRM_Contact_Form_Search_Basic', 'formRule')); + $this->addFormRule(['CRM_Contact_Form_Search_Basic', 'formRule']); } /** @@ -194,14 +197,16 @@ public function postProcess() { * If Go is pressed then we must select some checkboxes and an action. * * @param array $fields + * @param array $files + * @param object $form * * @return array|bool */ - public static function formRule($fields) { + public static function formRule($fields, $files, $form) { // check actionName and if next, then do not repeat a search, since we are going to the next page if (array_key_exists('_qf_Search_next', $fields)) { if (empty($fields['task'])) { - return array('task' => 'Please select a valid action.'); + return ['task' => 'Please select a valid action.']; } if (CRM_Utils_Array::value('task', $fields) == CRM_Contact_Task::SAVE_SEARCH) { @@ -219,7 +224,7 @@ public static function formRule($fields) { return TRUE; } } - return array('task' => 'Please select one or more checkboxes to perform the action on.'); + return ['task' => 'Please select one or more checkboxes to perform the action on.']; } return TRUE; } @@ -229,6 +234,7 @@ public static function formRule($fields) { * * @return string */ + /** * @return string */ diff --git a/CRM/Contact/Form/Search/Builder.php b/CRM/Contact/Form/Search/Builder.php index 75d6eeb9f53c..2a73f11a250f 100644 --- a/CRM/Contact/Form/Search/Builder.php +++ b/CRM/Contact/Form/Search/Builder.php @@ -1,9 +1,9 @@ $field) { - if (strpos($name, '_date') || CRM_Utils_Array::value('data_type', $field) == 'Date') { - $dateFields[] = $name; - } - // it's necessary to know which of the fields are from string data type - if (isset($field['type']) && $field['type'] === CRM_Utils_Type::T_STRING) { - $stringFields[] = $name; - } + // Assign date type to respective field name, which will be later used to modify operator list + $fieldNameTypes[$name] = CRM_Utils_Type::typeToString(CRM_Utils_Array::value('type', $field)); // it's necessary to know which of the fields are searchable by label if (isset($field['searchByLabel']) && $field['searchByLabel']) { $searchByLabelFields[] = $name; @@ -112,18 +106,16 @@ public function buildQuickForm() { // Add javascript CRM_Core_Resources::singleton() ->addScriptFile('civicrm', 'templates/CRM/Contact/Form/Search/Builder.js', 1, 'html-header') - ->addSetting(array( - 'searchBuilder' => array( + ->addSetting([ + 'searchBuilder' => [ // Index of newly added/expanded block (1-based index) 'newBlock' => $this->get('newBlock'), - 'dateFields' => $dateFields, 'fieldOptions' => self::fieldOptions(), - 'stringFields' => $stringFields, 'searchByLabelFields' => $searchByLabelFields, - 'generalOperators' => array('' => ts('-operator-')) + CRM_Core_SelectValues::getSearchBuilderOperators(), - 'stringOperators' => array('' => ts('-operator-')) + CRM_Core_SelectValues::getSearchBuilderOperators(CRM_Utils_Type::T_STRING), - ), - )); + 'fieldTypes' => $fieldNameTypes, + 'generalOperators' => ['' => ts('-operator-')] + CRM_Core_SelectValues::getSearchBuilderOperators(), + ], + ]); //get the saved search mapping id $mappingId = NULL; if ($this->_ssID) { @@ -139,7 +131,7 @@ public function buildQuickForm() { * Add local and global form rules. */ public function addRules() { - $this->addFormRule(array('CRM_Contact_Form_Search_Builder', 'formRule'), $this); + $this->addFormRule(['CRM_Contact_Form_Search_Builder', 'formRule'], $this); } /** @@ -159,7 +151,7 @@ public static function formRule($values, $files, $self) { $fields = self::fields(); $fld = CRM_Core_BAO_Mapping::formattedFields($values, TRUE); - $errorMsg = array(); + $errorMsg = []; foreach ($fld as $k => $v) { if (!$v[1]) { $errorMsg["operator[$v[3]][$v[4]]"] = ts("Please enter the operator."); @@ -168,19 +160,17 @@ public static function formRule($values, $files, $self) { // CRM-10338 $v[2] = self::checkArrayKeyEmpty($v[2]); - if (in_array($v[1], array( - 'IS NULL', - 'IS NOT NULL', - 'IS EMPTY', - 'IS NOT EMPTY', - )) && - !empty($v[2]) - ) { - $errorMsg["value[$v[3]][$v[4]]"] = ts('Please clear your value if you want to use %1 operator.', array(1 => $v[1])); + if (in_array($v[1], [ + 'IS NULL', + 'IS NOT NULL', + 'IS EMPTY', + 'IS NOT EMPTY', + ]) && !empty($v[2])) { + $errorMsg["value[$v[3]][$v[4]]"] = ts('Please clear your value if you want to use %1 operator.', [1 => $v[1]]); } elseif (substr($v[0], 0, 7) === 'do_not_' or substr($v[0], 0, 3) === 'is_') { if (isset($v[2])) { - $v2 = array($v[2]); + $v2 = [$v[2]]; if (!isset($v[2])) { $errorMsg["value[$v[3]][$v[4]]"] = ts("Please enter a value."); } @@ -201,10 +191,10 @@ public static function formRule($values, $files, $self) { $type = $fields[$fieldKey]['data_type']; // hack to handle custom data of type state and country - if (in_array($type, array( + if (in_array($type, [ 'Country', 'StateProvince', - ))) { + ])) { $type = "Integer"; } } @@ -227,7 +217,7 @@ public static function formRule($values, $files, $self) { } // Check Empty values for Integer Or Boolean Or Date type For operators other than IS NULL and IS NOT NULL. elseif (!in_array($v[1], - array('IS NULL', 'IS NOT NULL', 'IS EMPTY', 'IS NOT EMPTY')) + ['IS NULL', 'IS NOT NULL', 'IS EMPTY', 'IS NOT EMPTY']) ) { if ((($type == 'Int' || $type == 'Boolean') && !is_array($v[2]) && !trim($v[2])) && $v[2] != '0') { $errorMsg["value[$v[3]][$v[4]]"] = ts("Please enter a value."); @@ -258,7 +248,7 @@ public static function formRule($values, $files, $self) { // Validate each value in parenthesis to avoid db errors if (empty($errorMsg)) { - $parenValues = array(); + $parenValues = []; $parenValues = is_array($v[2]) ? (array_key_exists($v[1], $v[2])) ? $v[2][$v[1]] : $v[2] : explode(',', trim($inVal, "(..)")); foreach ($parenValues as $val) { if ($type == 'Date' || $type == 'Timestamp') { @@ -284,6 +274,12 @@ public static function formRule($values, $files, $self) { } elseif (trim($v[2])) { //else check value for rest of the Operators + if ($type == 'Date' || $type == 'Timestamp') { + $v[2] = CRM_Utils_Date::processDate($v[2]); + if ($type == 'Date') { + $v[2] = substr($v[2], 0, 8); + } + } $error = CRM_Utils_Type::validate($v[2], $type, FALSE); if ($error != $v[2]) { $errorMsg["value[$v[3]][$v[4]]"] = ts("Please enter a valid value."); @@ -440,7 +436,7 @@ public static function fields() { public static function fieldOptions() { // Hack to add options not retrieved by getfields // This list could go on and on, but it would be better to fix getfields - $options = array( + $options = [ 'group' => 'group_contact', 'tag' => 'entity_tag', 'on_hold' => 'yesno', @@ -452,8 +448,8 @@ public static function fieldOptions() { 'member_is_test' => 'yesno', 'member_is_pay_later' => 'yesno', 'is_override' => 'yesno', - ); - $entities = array( + ]; + $entities = [ 'contact', 'address', 'activity', @@ -463,7 +459,7 @@ public static function fieldOptions() { 'contribution', 'case', 'grant', - ); + ]; CRM_Contact_BAO_Query_Hook::singleton()->alterSearchBuilderOptions($entities, $options); foreach ($entities as $entity) { $fields = civicrm_api3($entity, 'getfields'); @@ -475,13 +471,15 @@ public static function fieldOptions() { $options[substr($field, 0, -3)] = $entity; } } - elseif (!empty($info['data_type']) && in_array($info['data_type'], array('StateProvince', 'Country'))) { - $options[$field] = $entity; + elseif (!empty($info['data_type'])) { + if (in_array($info['data_type'], ['StateProvince', 'Country'])) { + $options[$field] = $entity; + } } - elseif (in_array(substr($field, 0, 3), array( - 'is_', - 'do_', - )) || CRM_Utils_Array::value('data_type', $info) == 'Boolean' + elseif (in_array(substr($field, 0, 3), [ + 'is_', + 'do_', + ]) || CRM_Utils_Array::value('data_type', $info) == 'Boolean' ) { $options[$field] = 'yesno'; if ($entity != 'contact') { diff --git a/CRM/Contact/Form/Search/Criteria.php b/CRM/Contact/Form/Search/Criteria.php index ae312775481d..a22d9a95ecc0 100644 --- a/CRM/Contact/Form/Search/Criteria.php +++ b/CRM/Contact/Form/Search/Criteria.php @@ -1,9 +1,9 @@ addElement('hidden', 'hidden_basic', 1); if ($form->_searchOptions['contactType']) { @@ -42,7 +44,7 @@ public static function basic(&$form) { if ($contactTypes) { $form->add('select', 'contact_type', ts('Contact Type(s)'), $contactTypes, FALSE, - array('id' => 'contact_type', 'multiple' => 'multiple', 'class' => 'crm-select2', 'style' => 'width: 100%;') + ['id' => 'contact_type', 'multiple' => 'multiple', 'class' => 'crm-select2', 'style' => 'width: 100%;'] ); } } @@ -54,11 +56,11 @@ public static function basic(&$form) { $groupHierarchy = CRM_Contact_BAO_Group::getGroupsHierarchy($form->_group, NULL, '  ', TRUE); $form->add('select', 'group', ts('Groups'), $groupHierarchy, FALSE, - array('id' => 'group', 'multiple' => 'multiple', 'class' => 'crm-select2') + ['id' => 'group', 'multiple' => 'multiple', 'class' => 'crm-select2'] ); $groupOptions = CRM_Core_BAO_OptionValue::getOptionValuesAssocArrayFromName('group_type'); $form->add('select', 'group_type', ts('Group Types'), $groupOptions, FALSE, - array('id' => 'group_type', 'multiple' => 'multiple', 'class' => 'crm-select2') + ['id' => 'group_type', 'multiple' => 'multiple', 'class' => 'crm-select2'] ); $form->add('hidden', 'group_search_selected', 'group'); } @@ -69,8 +71,8 @@ public static function basic(&$form) { $contactTags = CRM_Core_BAO_Tag::getTags(); if ($contactTags) { - $form->add('select', 'contact_tags', ts('Tags'), $contactTags, FALSE, - array('id' => 'contact_tags', 'multiple' => 'multiple', 'class' => 'crm-select2', 'style' => 'width: 100%;') + $form->add('select', 'contact_tags', ts('Select Tag(s)'), $contactTags, FALSE, + ['id' => 'contact_tags', 'multiple' => 'multiple', 'class' => 'crm-select2', 'style' => 'width: 100%;'] ); } @@ -78,7 +80,7 @@ public static function basic(&$form) { CRM_Core_Form_Tag::buildQuickForm($form, $parentNames, 'civicrm_contact', NULL, TRUE, FALSE); $used_for = CRM_Core_OptionGroup::values('tag_used_for'); - $tagsTypes = array(); + $tagsTypes = []; $showAllTagTypes = FALSE; foreach ($used_for as $key => $value) { //check tags for every type and find if there are any defined @@ -93,16 +95,16 @@ public static function basic(&$form) { } $tagTypesText = implode(" or ", $tagsTypes); if ($showAllTagTypes) { - $form->add('checkbox', 'all_tag_types', ts('Include tags used for %1', array(1 => $tagTypesText))); + $form->add('checkbox', 'all_tag_types', ts('Include tags used for %1', [1 => $tagTypesText])); $form->add('hidden', 'tag_types_text', $tagTypesText); } } // add text box for last name, first name, street name, city - $form->addElement('text', 'sort_name', ts('Find...'), CRM_Core_DAO::getAttribute('CRM_Contact_DAO_Contact', 'sort_name')); + $form->addElement('text', 'sort_name', ts('Complete OR Partial Name'), CRM_Core_DAO::getAttribute('CRM_Contact_DAO_Contact', 'sort_name')); // add text box for last name, first name, street name, city - $form->add('text', 'email', ts('Contact Email'), CRM_Core_DAO::getAttribute('CRM_Contact_DAO_Contact', 'sort_name')); + $form->add('text', 'email', ts('Complete OR Partial Email'), CRM_Core_DAO::getAttribute('CRM_Contact_DAO_Contact', 'sort_name')); //added contact source $form->add('text', 'contact_source', ts('Contact Source'), CRM_Core_DAO::getAttribute('CRM_Contact_DAO_Contact', 'contact_source')); @@ -111,7 +113,7 @@ public static function basic(&$form) { $form->addElement('text', 'job_title', ts('Job Title'), CRM_Core_DAO::getAttribute('CRM_Contact_DAO_Contact', 'job_title')); //added internal ID - $form->add('number', 'contact_id', ts('Contact ID'), CRM_Core_DAO::getAttribute('CRM_Contact_DAO_Contact', 'id') + array('min' => 1)); + $form->add('number', 'contact_id', ts('Contact ID'), CRM_Core_DAO::getAttribute('CRM_Contact_DAO_Contact', 'id') + ['min' => 1]); $form->addRule('contact_id', ts('Please enter valid Contact ID'), 'positiveInteger'); //added external ID @@ -131,16 +133,16 @@ public static function basic(&$form) { // FIXME: This is probably a part of profiles - need to be // FIXME: eradicated from here when profiles are reworked. - $types = array('Participant', 'Contribution', 'Membership'); + $types = ['Participant', 'Contribution', 'Membership']; // get component profiles - $componentProfiles = array(); + $componentProfiles = []; $componentProfiles = CRM_Core_BAO_UFGroup::getProfiles($types); $ufGroups = CRM_Core_BAO_UFGroup::getModuleUFGroup('Search Profile', 1); $accessibleUfGroups = CRM_Core_Permission::ufGroup(CRM_Core_Permission::VIEW); - $searchProfiles = array(); + $searchProfiles = []; foreach ($ufGroups as $key => $var) { if (!array_key_exists($key, $componentProfiles) && in_array($key, $accessibleUfGroups)) { $searchProfiles[$key] = $var['title']; @@ -150,56 +152,38 @@ public static function basic(&$form) { $form->add('select', 'uf_group_id', ts('Views For Display Contacts'), - array( + [ '0' => ts('- default view -'), - ) + $searchProfiles, + ] + $searchProfiles, FALSE, - array('class' => 'crm-select2') + ['class' => 'crm-select2'] ); $componentModes = CRM_Contact_Form_Search::getModeSelect(); - - // unset contributions or participants if user does not have - // permission on them - if (!CRM_Core_Permission::access('CiviContribute')) { - unset($componentModes['2']); - } - - if (!CRM_Core_Permission::access('CiviEvent')) { - unset($componentModes['3']); - } - - if (!CRM_Core_Permission::access('CiviMember')) { - unset($componentModes['5']); - } - - if (!CRM_Core_Permission::check('view all activities')) { - unset($componentModes['4']); - } - + $form->assign('component_mappings', json_encode(CRM_Contact_Form_Search::getModeToComponentMapping())); if (count($componentModes) > 1) { $form->add('select', 'component_mode', ts('Display Results As'), $componentModes, FALSE, - array('class' => 'crm-select2') + ['class' => 'crm-select2'] ); } $form->addRadio( 'operator', ts('Search Operator'), - array( - 'AND' => ts('AND'), - 'OR' => ts('OR'), - ), - array('allowClear' => FALSE) + [ + CRM_Contact_BAO_Query::SEARCH_OPERATOR_AND => ts('AND'), + CRM_Contact_BAO_Query::SEARCH_OPERATOR_OR => ts('OR'), + ], + ['allowClear' => FALSE] ); // add the option to display relationships $rTypes = CRM_Core_PseudoConstant::relationshipType(); - $rSelect = array('' => ts('- Select Relationship Type-')); + $rSelect = ['' => ts('- Select Relationship Type-')]; foreach ($rTypes as $rid => $rValue) { if ($rValue['label_a_b'] == $rValue['label_b_a']) { $rSelect[$rid] = $rValue['label_a_b']; @@ -214,7 +198,7 @@ public static function basic(&$form) { 'display_relationship_type', ts('Display Results as Relationship'), $rSelect, - array('class' => 'crm-select2') + ['class' => 'crm-select2'] ); // checkboxes for DO NOT phone, email, mail @@ -225,47 +209,118 @@ public static function basic(&$form) { ts('Privacy'), $t, FALSE, - array( + [ 'id' => 'privacy_options', 'multiple' => 'multiple', 'class' => 'crm-select2', - ) + ] ); $form->addElement('select', 'privacy_operator', ts('Operator'), - array( + [ 'OR' => ts('OR'), 'AND' => ts('AND'), - ) + ] ); - $options = array( + $options = [ 1 => ts('Exclude'), 2 => ts('Include by Privacy Option(s)'), - ); - $form->addRadio('privacy_toggle', ts('Privacy Options'), $options, array('allowClear' => FALSE)); + ]; + $form->addRadio('privacy_toggle', ts('Privacy Options'), $options, ['allowClear' => FALSE]); // preferred communication method - - $onHold[] = $form->createElement('advcheckbox', 'on_hold', NULL, ''); - $form->addGroup($onHold, 'email_on_hold', ts('Email On Hold')); + if (Civi::settings()->get('civimail_multiple_bulk_emails')) { + $form->addSelect('email_on_hold', + ['entity' => 'email', 'multiple' => 'multiple', 'label' => ts('Email On Hold'), 'options' => CRM_Core_PseudoConstant::emailOnHoldOptions()]); + } + else { + $form->add('advcheckbox', 'email_on_hold', ts('Email On Hold')); + } $form->addSelect('preferred_communication_method', - array('entity' => 'contact', 'multiple' => 'multiple', 'label' => ts('Preferred Communication Method'), 'option_url' => NULL, 'placeholder' => ts('- any -'))); + ['entity' => 'contact', 'multiple' => 'multiple', 'label' => ts('Preferred Communication Method'), 'option_url' => NULL, 'placeholder' => ts('- any -')]); //CRM-6138 Preferred Language - $form->addSelect('preferred_language', array('class' => 'twenty', 'context' => 'search')); + $form->addSelect('preferred_language', ['class' => 'twenty', 'context' => 'search']); // Phone search $form->addElement('text', 'phone_numeric', ts('Phone'), CRM_Core_DAO::getAttribute('CRM_Core_DAO_Phone', 'phone')); $locationType = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id'); $phoneType = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Phone', 'phone_type_id'); - $form->add('select', 'phone_location_type_id', ts('Phone Location'), array('' => ts('- any -')) + $locationType, FALSE, array('class' => 'crm-select2')); - $form->add('select', 'phone_phone_type_id', ts('Phone Type'), array('' => ts('- any -')) + $phoneType, FALSE, array('class' => 'crm-select2')); + $form->add('select', 'phone_location_type_id', ts('Phone Location'), ['' => ts('- any -')] + $locationType, FALSE, ['class' => 'crm-select2']); + $form->add('select', 'phone_phone_type_id', ts('Phone Type'), ['' => ts('- any -')] + $phoneType, FALSE, ['class' => 'crm-select2']); } + /** + * Defines the fields that can be displayed for the basic search section. + * + * @param CRM_Core_Form $form + */ + protected static function setBasicSearchFields($form) { + $userFramework = CRM_Core_Config::singleton()->userFramework; + + $form->assign('basicSearchFields', [ + 'sort_name' => ['name' => 'sort_name'], + 'email' => ['name' => 'email'], + 'contact_type' => ['name' => 'contact_type'], + 'group' => [ + 'name' => 'group', + 'template' => 'CRM/Contact/Form/Search/Criteria/Fields/group.tpl', + ], + 'contact_tags' => ['name' => 'contact_tags'], + 'tag_types_text' => ['name' => 'tag_types_text'], + 'tag_search' => [ + 'name' => 'tag_search', + 'help' => ['id' => 'id-all-tags'], + ], + 'tag_set' => [ + 'name' => 'tag_set', + 'is_custom' => TRUE, + 'template' => 'CRM/Contact/Form/Search/Criteria/Fields/tag_set.tpl', + ], + 'all_tag_types' => [ + 'name' => 'all_tag_types', + 'class' => 'search-field__span-3 search-field__checkbox', + 'help' => ['id' => 'id-all-tag-types'], + ], + 'phone_numeric' => [ + 'name' => 'phone_numeric', + 'description' => ts('Punctuation and spaces are ignored.'), + ], + 'phone_location_type_id' => ['name' => 'phone_location_type_id'], + 'phone_phone_type_id' => ['name' => 'phone_phone_type_id'], + 'privacy_toggle' => [ + 'name' => 'privacy_toggle', + 'class' => 'search-field__span-2', + 'template' => 'CRM/Contact/Form/Search/Criteria/Fields/privacy_toggle.tpl', + ], + 'preferred_communication_method' => [ + 'name' => 'preferred_communication_method', + 'template' => 'CRM/Contact/Form/Search/Criteria/Fields/preferred_communication_method.tpl', + ], + 'contact_source' => [ + 'name' => 'contact_source', + 'help' => ['id' => 'id-source', 'file' => 'CRM/Contact/Form/Contact'], + ], + 'job_title' => ['name' => 'job_title'], + 'preferred_language' => ['name' => 'preferred_language'], + 'contact_id' => [ + 'name' => 'contact_id', + 'help' => ['id' => 'id-contact-id', 'file' => 'CRM/Contact/Form/Contact'], + ], + 'external_identifier' => [ + 'name' => 'external_identifier', + 'help' => ['id' => 'id-external-id', 'file' => 'CRM/Contact/Form/Contact'], + ], + 'uf_user' => [ + 'name' => 'uf_user', + 'description' => ts('Does the contact have a %1 Account?', [$userFramework]), + ], + ]); + } /** * @param CRM_Core_Form $form @@ -288,18 +343,21 @@ public static function location(&$form) { $attributes = CRM_Core_DAO::getAttribute('CRM_Core_DAO_Address'); - $elements = array( - 'street_address' => array(ts('Street Address'), $attributes['street_address'], NULL, NULL), - 'city' => array(ts('City'), $attributes['city'], NULL, NULL), - 'postal_code' => array(ts('Postal Code'), $attributes['postal_code'], NULL, NULL), - 'country' => array(ts('Country'), $attributes['country_id'], 'country', FALSE), - 'state_province' => array(ts('State/Province'), $attributes['state_province_id'], 'stateProvince', TRUE), - 'county' => array(ts('County'), $attributes['county_id'], 'county', TRUE), - 'address_name' => array(ts('Address Name'), $attributes['address_name'], NULL, NULL), - 'street_number' => array(ts('Street Number'), $attributes['street_number'], NULL, NULL), - 'street_name' => array(ts('Street Name'), $attributes['street_name'], NULL, NULL), - 'street_unit' => array(ts('Apt/Unit/Suite'), $attributes['street_unit'], NULL, NULL), - ); + $elements = [ + 'street_address' => [ts('Street Address'), $attributes['street_address'], NULL, NULL], + 'supplemental_address_1' => [ts('Supplemental Address 1'), $attributes['supplemental_address_1'], NULL, NULL], + 'supplemental_address_2' => [ts('Supplemental Address 2'), $attributes['supplemental_address_2'], NULL, NULL], + 'supplemental_address_3' => [ts('Supplemental Address 3'), $attributes['supplemental_address_3'], NULL, NULL], + 'city' => [ts('City'), $attributes['city'], NULL, NULL], + 'postal_code' => [ts('Postal Code'), $attributes['postal_code'], NULL, NULL], + 'country' => [ts('Country'), $attributes['country_id'], 'country', FALSE], + 'state_province' => [ts('State/Province'), $attributes['state_province_id'], 'stateProvince', TRUE], + 'county' => [ts('County'), $attributes['county_id'], 'county', TRUE], + 'address_name' => [ts('Address Name'), $attributes['address_name'], NULL, NULL], + 'street_number' => [ts('Street Number'), $attributes['street_number'], NULL, NULL], + 'street_name' => [ts('Street Name'), $attributes['street_name'], NULL, NULL], + 'street_unit' => [ts('Apt/Unit/Suite'), $attributes['street_unit'], NULL, NULL], + ]; $parseStreetAddress = CRM_Utils_Array::value('street_address_parsing', $addressOptions, 0); $form->assign('parseStreetAddress', $parseStreetAddress); @@ -307,7 +365,7 @@ public static function location(&$form) { list($title, $attributes, $select, $multiSelect) = $v; if (in_array($name, - array('street_number', 'street_name', 'street_unit') + ['street_number', 'street_name', 'street_unit'] )) { if (!$parseStreetAddress) { continue; @@ -326,8 +384,8 @@ public static function location(&$form) { $element = $form->addChainSelect($name); } else { - $selectElements = array('' => ts('- any -')) + CRM_Core_PseudoConstant::$select(); - $element = $form->add('select', $name, $title, $selectElements, FALSE, array('class' => 'crm-select2')); + $selectElements = ['' => ts('- any -')] + CRM_Core_PseudoConstant::$select(); + $element = $form->add('select', $name, $title, $selectElements, FALSE, ['class' => 'crm-select2']); } if ($multiSelect) { $element->setMultiple(TRUE); @@ -338,44 +396,34 @@ public static function location(&$form) { } if ($addressOptions['postal_code']) { - $attr = array('class' => 'six') + (array) CRM_Utils_Array::value('postal_code', $attributes); - $form->addElement('text', 'postal_code_low', NULL, $attr + array('placeholder' => ts('From'))); - $form->addElement('text', 'postal_code_high', NULL, $attr + array('placeholder' => ts('To'))); + $attr = ['class' => 'six'] + (array) CRM_Utils_Array::value('postal_code', $attributes); + $form->addElement('text', 'postal_code_low', NULL, $attr + ['placeholder' => ts('From')]); + $form->addElement('text', 'postal_code_high', NULL, $attr + ['placeholder' => ts('To')]); } } // extend addresses with proximity search - if (!empty($config->geocodeMethod)) { - $form->addElement('text', 'prox_distance', ts('Find contacts within'), array('class' => 'six')); - $form->addElement('select', 'prox_distance_unit', NULL, array( + if (CRM_Utils_GeocodeProvider::getUsableClassName()) { + $form->addElement('text', 'prox_distance', ts('Find contacts within'), ['class' => 'six']); + $form->addElement('select', 'prox_distance_unit', NULL, [ 'miles' => ts('Miles'), 'kilos' => ts('Kilometers'), - )); + ]); $form->addRule('prox_distance', ts('Please enter positive number as a distance'), 'numeric'); } - $form->addSelect('world_region', array('entity' => 'address', 'context' => 'search')); + $form->addSelect('world_region', ['entity' => 'address', 'context' => 'search']); // select for location type $locationType = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id'); - $form->add('select', 'location_type', ts('Address Location'), $locationType, FALSE, array( + $form->add('select', 'location_type', ts('Address Location'), $locationType, FALSE, [ 'multiple' => TRUE, 'class' => 'crm-select2', 'placeholder' => ts('Primary'), - )); - - // custom data extending addresses - - $extends = array('Address'); - $groupDetails = CRM_Core_BAO_CustomGroup::getGroupDetail(NULL, TRUE, $extends); - if ($groupDetails) { - $form->assign('addressGroupTree', $groupDetails); - foreach ($groupDetails as $group) { - foreach ($group['fields'] as $field) { - $elementName = 'custom_' . $field['id']; - CRM_Core_BAO_CustomField::addQuickFormElement($form, $elementName, $field['id'], FALSE, TRUE); - } - } - } + ]); + + // custom data extending addresses + CRM_Core_BAO_Query::addCustomFormFields($form, ['Address']); } /** @@ -395,10 +443,10 @@ public static function changeLog(&$form) { // block for change log $form->addElement('text', 'changed_by', ts('Modified By'), NULL); - $dates = array(1 => ts('Added'), 2 => ts('Modified')); - $form->addRadio('log_date', NULL, $dates, array('allowClear' => TRUE), '
'); + $dates = [1 => ts('Added'), 2 => ts('Modified')]; + $form->addRadio('log_date', NULL, $dates, ['allowClear' => TRUE]); - CRM_Core_Form_Date::buildDateRange($form, 'log_date', 1, '_low', '_high', ts('From'), FALSE, FALSE); + CRM_Core_Form_Date::buildDateRange($form, 'log_date', 1, '_low', '_high', ts('From:'), FALSE, FALSE); } /** @@ -414,67 +462,58 @@ public static function task(&$form) { public static function relationship(&$form) { $form->add('hidden', 'hidden_relationship', 1); - $allRelationshipType = array(); + $allRelationshipType = []; $allRelationshipType = CRM_Contact_BAO_Relationship::getContactRelationshipType(NULL, NULL, NULL, NULL, TRUE); - $form->add('select', 'relation_type_id', ts('Relationship Type'), array('' => ts('- select -')) + $allRelationshipType, FALSE, array('class' => 'crm-select2')); + $form->add('select', 'relation_type_id', ts('Relationship Type'), ['' => ts('- select -')] + $allRelationshipType, FALSE, ['multiple' => TRUE, 'class' => 'crm-select2']); $form->addElement('text', 'relation_target_name', ts('Target Contact'), CRM_Core_DAO::getAttribute('CRM_Contact_DAO_Contact', 'sort_name')); // relation status - $relStatusOption = array(ts('Active'), ts('Inactive'), ts('All')); + $relStatusOption = [ts('Active'), ts('Inactive'), ts('All')]; $form->addRadio('relation_status', ts('Relationship Status'), $relStatusOption); - $form->setDefaults(array('relation_status' => 0)); + $form->setDefaults(['relation_status' => 0]); // relation permission - $relPermissionOption = array(ts('Any'), ts('Yes'), ts('No')); - $form->addRadio('relation_permission', ts('Permissioned Relationship?'), $relPermissionOption); - $form->setDefaults(array('relation_permission' => 0)); + $allRelationshipPermissions = CRM_Contact_BAO_Relationship::buildOptions('is_permission_a_b'); + $form->add('select', 'relation_permission', ts('Permissioned Relationship'), + ['' => ts('- select -')] + $allRelationshipPermissions, FALSE, ['multiple' => TRUE, 'class' => 'crm-select2']); //add the target group if ($form->_group) { $form->add('select', 'relation_target_group', ts('Target Contact(s) in Group'), $form->_group, FALSE, - array('id' => 'relation_target_group', 'multiple' => 'multiple', 'class' => 'crm-select2') + ['id' => 'relation_target_group', 'multiple' => 'multiple', 'class' => 'crm-select2'] ); } CRM_Core_Form_Date::buildDateRange($form, 'relation_start_date', 1, '_low', '_high', ts('From:'), FALSE, FALSE); CRM_Core_Form_Date::buildDateRange($form, 'relation_end_date', 1, '_low', '_high', ts('From:'), FALSE, FALSE); + CRM_Core_Form_Date::buildDateRange($form, 'relation_active_period_date', 1, '_low', '_high', ts('From:'), FALSE, FALSE); + // Add reltionship dates CRM_Core_Form_Date::buildDateRange($form, 'relation_date', 1, '_low', '_high', ts('From:'), FALSE, FALSE); // add all the custom searchable fields - $relationship = array('Relationship'); - $groupDetails = CRM_Core_BAO_CustomGroup::getGroupDetail(NULL, TRUE, $relationship); - if ($groupDetails) { - $form->assign('relationshipGroupTree', $groupDetails); - foreach ($groupDetails as $group) { - foreach ($group['fields'] as $field) { - $fieldId = $field['id']; - $elementName = 'custom_' . $fieldId; - CRM_Core_BAO_CustomField::addQuickFormElement($form, $elementName, $fieldId, FALSE, TRUE); - } - } - } + CRM_Core_BAO_Query::addCustomFormFields($form, ['Relationship']); } /** - * @param $form + * @param CRM_Core_Form_Search $form */ public static function demographics(&$form) { $form->add('hidden', 'hidden_demographics', 1); // radio button for gender - $genderOptions = array(); + $genderOptions = []; $gender = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'gender_id'); foreach ($gender as $key => $var) { $genderOptions[$key] = $form->createElement('radio', NULL, ts('Gender'), $var, $key, - array('id' => "civicrm_gender_{$var}_{$key}") + ['id' => "civicrm_gender_{$var}_{$key}"] ); } $form->addGroup($genderOptions, 'gender_id', ts('Gender'))->setAttribute('allowClear', TRUE); - $form->add('text', 'age_low', ts('Min Age'), array('size' => 6)); + $form->add('number', 'age_low', ts('Min Age'), ['class' => 'four', 'min' => 0]); $form->addRule('age_low', ts('Please enter a positive integer'), 'positiveInteger'); - $form->add('text', 'age_high', ts('Max Age'), array('size' => 6)); + $form->add('number', 'age_high', ts('Max Age'), ['class' => 'four', 'min' => 0]); $form->addRule('age_high', ts('Please enter a positive integer'), 'positiveInteger'); - $form->addDate('age_asof_date', ts('Age as of Date'), FALSE, array('formatType' => 'searchDate')); + $form->add('datepicker', 'age_asof_date', ts('As of'), NULL, FALSE, ['time' => FALSE]); CRM_Core_Form_Date::buildDateRange($form, 'birth_date', 1, '_low', '_high', ts('From'), FALSE, FALSE, 'birth'); @@ -490,16 +529,16 @@ public static function demographics(&$form) { public static function notes(&$form) { $form->add('hidden', 'hidden_notes', 1); - $options = array( + $options = [ 2 => ts('Body Only'), 3 => ts('Subject Only'), 6 => ts('Both'), - ); + ]; $form->addRadio('note_option', '', $options); $form->addElement('text', 'note', ts('Note Text'), CRM_Core_DAO::getAttribute('CRM_Contact_DAO_Contact', 'sort_name')); - $form->setDefaults(array('note_option' => 6)); + $form->setDefaults(['note_option' => 6]); } /** @@ -509,7 +548,7 @@ public static function notes(&$form) { */ public static function custom(&$form) { $form->add('hidden', 'hidden_custom', 1); - $extends = array_merge(array('Contact', 'Individual', 'Household', 'Organization'), + $extends = array_merge(['Contact', 'Individual', 'Household', 'Organization'], CRM_Contact_BAO_ContactType::subTypes() ); $groupDetails = CRM_Core_BAO_CustomGroup::getGroupDetail(NULL, TRUE, @@ -525,7 +564,12 @@ public static function custom(&$form) { foreach ($group['fields'] as $field) { $fieldId = $field['id']; $elementName = 'custom_' . $fieldId; - CRM_Core_BAO_CustomField::addQuickFormElement($form, $elementName, $fieldId, FALSE, TRUE); + if ($field['data_type'] == 'Date' && $field['is_search_range']) { + CRM_Core_Form_Date::buildDateRange($form, $elementName, 1, '_from', '_to', ts('From:'), FALSE); + } + else { + CRM_Core_BAO_CustomField::addQuickFormElement($form, $elementName, $fieldId, FALSE, TRUE); + } } } } diff --git a/CRM/Contact/Form/Search/Custom.php b/CRM/Contact/Form/Search/Custom.php index 7d8dd678e40d..fad711ba9017 100644 --- a/CRM/Contact/Form/Search/Custom.php +++ b/CRM/Contact/Form/Search/Custom.php @@ -1,9 +1,9 @@ _customClass = new $this->_customSearchClass($this->_formValues); + $this->addFormRule(array($this->_customClass, 'formRule'), $this); + // CRM-12747 if (isset($this->_customClass->_permissionedComponent) && !self::isPermissioned($this->_customClass->_permissionedComponent) @@ -91,6 +93,13 @@ public function preProcess() { } } + /** + * Add local and global form rules. + */ + public function addRules() { + $this->addFormRule(array($this->_customClass, 'formRule')); + } + /** * Set the default values of various form elements. * @@ -127,6 +136,7 @@ public function buildQuickForm() { * * @return string */ + /** * @return string */ diff --git a/CRM/Contact/Form/Search/Custom/ActivitySearch.php b/CRM/Contact/Form/Search/Custom/ActivitySearch.php index 649679f24fae..afda78618c2c 100644 --- a/CRM/Contact/Form/Search/Custom/ActivitySearch.php +++ b/CRM/Contact/Form/Search/Custom/ActivitySearch.php @@ -1,9 +1,9 @@ _formValues = $formValues; + $this->_formValues = self::formatSavedSearchFields($formValues); /** * Define the columns for search result rows */ - $this->_columns = array( + $this->_columns = [ ts('Name') => 'sort_name', ts('Status') => 'activity_status', ts('Activity Type') => 'activity_type', @@ -61,7 +61,7 @@ public function __construct(&$formValues) { ts('Duration') => 'duration', ts('Details') => 'details', ts('Assignee') => 'assignee', - ); + ]; $this->_groupId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionGroup', 'activity_status', @@ -107,7 +107,7 @@ public function buildForm(&$form) { ); // Select box for Activity Type - $activityType = array('' => ' - select activity - ') + CRM_Core_PseudoConstant::activityType(); + $activityType = ['' => ' - select activity - '] + CRM_Core_PseudoConstant::activityType(); $form->add('select', 'activity_type_id', ts('Activity Type'), $activityType, @@ -115,7 +115,7 @@ public function buildForm(&$form) { ); // textbox for Activity Status - $activityStatus = array('' => ' - select status - ') + CRM_Core_PseudoConstant::activityStatus(); + $activityStatus = ['' => ' - select status - '] + CRM_Core_PseudoConstant::activityStatus(); $form->add('select', 'activity_status_id', ts('Activity Status'), $activityStatus, @@ -123,8 +123,8 @@ public function buildForm(&$form) { ); // Activity Date range - $form->addDate('start_date', ts('Activity Date From'), FALSE, array('formatType' => 'custom')); - $form->addDate('end_date', ts('...through'), FALSE, array('formatType' => 'custom')); + $form->add('datepicker', 'start_date', ts('Activity Date From'), [], FALSE, ['time' => FALSE]); + $form->add('datepicker', 'end_date', ts('...through'), [], FALSE, ['time' => FALSE]); // Contact Name field $form->add('text', 'sort_name', ts('Contact Name')); @@ -133,7 +133,7 @@ public function buildForm(&$form) { * If you are using the sample template, this array tells the template fields to render * for the search form. */ - $form->assign('elements', array( + $form->assign('elements', [ 'contact_type', 'activity_subject', 'activity_type_id', @@ -141,7 +141,7 @@ public function buildForm(&$form) { 'start_date', 'end_date', 'sort_name', - )); + ]); } /** @@ -259,7 +259,7 @@ public function alterRow(&$row) { */ public function from() { $this->buildACLClause('contact_a'); - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts); $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts); $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts); @@ -296,7 +296,7 @@ public function from() { * @return string */ public function where($includeContactIDs = FALSE) { - $clauses = array(); + $clauses = []; // add contact name search; search on primary name, source contact, assignee $contactname = $this->_formValues['sort_name']; @@ -328,26 +328,16 @@ public function where($includeContactIDs = FALSE) { $clauses[] = "activity.activity_type_id = {$this->_formValues['activity_type_id']}"; } - $startDate = $this->_formValues['start_date']; - if (!empty($startDate)) { - $startDate .= '00:00:00'; - $startDateFormatted = CRM_Utils_Date::processDate($startDate); - if ($startDateFormatted) { - $clauses[] = "activity.activity_date_time >= $startDateFormatted"; - } + if (!empty($this->_formValues['start_date'])) { + $clauses[] = "activity.activity_date_time >= '{$this->_formValues['start_date']} 00:00:00'"; } - $endDate = $this->_formValues['end_date']; - if (!empty($endDate)) { - $endDate .= '23:59:59'; - $endDateFormatted = CRM_Utils_Date::processDate($endDate); - if ($endDateFormatted) { - $clauses[] = "activity.activity_date_time <= $endDateFormatted"; - } + if (!empty($this->_formValues['end_date'])) { + $clauses[] = "activity.activity_date_time <= '{$this->_formValues['end_date']} 23:59:59'"; } if ($includeContactIDs) { - $contactIDs = array(); + $contactIDs = []; foreach ($this->_formValues as $id => $value) { if ($value && substr($id, 0, CRM_Core_Form::CB_PREFIX_LEN) == CRM_Core_Form::CB_PREFIX @@ -429,4 +419,28 @@ public function buildACLClause($tableAlias = 'contact') { list($this->_aclFrom, $this->_aclWhere) = CRM_Contact_BAO_Contact_Permission::cacheClause($tableAlias); } + /** + * Format saved search fields for this custom group. + * + * Note this is a function to facilitate the transition to jcalendar for + * saved search groups. In time it can be stripped out again. + * + * @param array $formValues + * + * @return array + */ + public static function formatSavedSearchFields($formValues) { + $dateFields = [ + 'start_date', + 'end_date', + ]; + foreach ($formValues as $element => $value) { + if (in_array($element, $dateFields) && !empty($value)) { + $formValues[$element] = date('Y-m-d', strtotime($value)); + } + } + + return $formValues; + } + } diff --git a/CRM/Contact/Form/Search/Custom/Base.php b/CRM/Contact/Form/Search/Custom/Base.php index f96b34bd38d8..b4edd5986d43 100644 --- a/CRM/Contact/Form/Search/Custom/Base.php +++ b/CRM/Contact/Form/Search/Custom/Base.php @@ -1,9 +1,9 @@ $value) { if ($value && substr($id, 0, CRM_Core_Form::CB_PREFIX_LEN) == CRM_Core_Form::CB_PREFIX @@ -200,17 +200,17 @@ public function addSortOffset(&$sql, $offset, $rowcount, $sort) { * @throws Exception */ public function validateUserSQL(&$sql, $onlyWhere = FALSE) { - $includeStrings = array('contact_a'); - $excludeStrings = array('insert', 'delete', 'update'); + $includeStrings = ['contact_a']; + $excludeStrings = ['insert', 'delete', 'update']; if (!$onlyWhere) { - $includeStrings += array('select', 'from', 'where', 'civicrm_contact'); + $includeStrings += ['select', 'from', 'where', 'civicrm_contact']; } foreach ($includeStrings as $string) { if (stripos($sql, $string) === FALSE) { CRM_Core_Error::fatal(ts('Could not find \'%1\' string in SQL clause.', - array(1 => $string) + [1 => $string] )); } } @@ -218,7 +218,7 @@ public function validateUserSQL(&$sql, $onlyWhere = FALSE) { foreach ($excludeStrings as $string) { if (preg_match('/(\s' . $string . ')|(' . $string . '\s)/i', $sql)) { CRM_Core_Error::fatal(ts('Found illegal \'%1\' string in SQL clause.', - array(1 => $string) + [1 => $string] )); } } @@ -257,4 +257,18 @@ public function setTitle($title) { } } + /** + * Validate form input. + * + * @param array $fields + * @param array $files + * @param CRM_Core_Form $self + * + * @return array + * Input errors from the form. + */ + public function formRule($fields, $files, $self) { + return []; + } + } diff --git a/CRM/Contact/Form/Search/Custom/Basic.php b/CRM/Contact/Form/Search/Custom/Basic.php index 6dc90fd7e10e..63a55860c0d7 100644 --- a/CRM/Contact/Form/Search/Custom/Basic.php +++ b/CRM/Contact/Form/Search/Custom/Basic.php @@ -1,9 +1,9 @@ _columns = array( + $this->_columns = [ '' => 'contact_type', ts('Name') => 'sort_name', ts('Address') => 'street_address', @@ -54,10 +54,10 @@ public function __construct(&$formValues) { ts('Country') => 'country', ts('Email') => 'email', ts('Phone') => 'phone', - ); + ]; $params = CRM_Contact_BAO_Query::convertFormValues($this->_formValues); - $returnProperties = array(); + $returnProperties = []; $returnProperties['contact_sub_type'] = 1; $addressOptions = CRM_Core_BAO_Setting::valueOptions(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, @@ -65,14 +65,13 @@ public function __construct(&$formValues) { ); foreach ($this->_columns as $name => $field) { - if (in_array($field, array( - 'street_address', - 'city', - 'state_province', - 'postal_code', - 'country', - )) && empty($addressOptions[$field]) - ) { + if (in_array($field, [ + 'street_address', + 'city', + 'state_province', + 'postal_code', + 'country', + ]) && empty($addressOptions[$field])) { unset($this->_columns[$name]); continue; } @@ -88,21 +87,21 @@ public function __construct(&$formValues) { * @param CRM_Core_Form $form */ public function buildForm(&$form) { - $contactTypes = array('' => ts('- any contact type -')) + CRM_Contact_BAO_ContactType::getSelectElements(); - $form->add('select', 'contact_type', ts('Find...'), $contactTypes, FALSE, array('class' => 'crm-select2 huge')); + $contactTypes = ['' => ts('- any contact type -')] + CRM_Contact_BAO_ContactType::getSelectElements(); + $form->add('select', 'contact_type', ts('Find...'), $contactTypes, FALSE, ['class' => 'crm-select2 huge']); // add select for groups - $group = array('' => ts('- any group -')) + CRM_Core_PseudoConstant::nestedGroup(); - $form->addElement('select', 'group', ts('in'), $group, array('class' => 'crm-select2 huge')); + $group = ['' => ts('- any group -')] + CRM_Core_PseudoConstant::nestedGroup(); + $form->addElement('select', 'group', ts('in'), $group, ['class' => 'crm-select2 huge']); // add select for categories - $tag = array('' => ts('- any tag -')) + CRM_Core_PseudoConstant::get('CRM_Core_DAO_EntityTag', 'tag_id', array('onlyActive' => FALSE)); - $form->addElement('select', 'tag', ts('Tagged'), $tag, array('class' => 'crm-select2 huge')); + $tag = ['' => ts('- any tag -')] + CRM_Core_PseudoConstant::get('CRM_Core_DAO_EntityTag', 'tag_id', ['onlyActive' => FALSE]); + $form->addElement('select', 'tag', ts('Tagged'), $tag, ['class' => 'crm-select2 huge']); // text for sort_name $form->add('text', 'sort_name', ts('Name')); - $form->assign('elements', array('sort_name', 'contact_type', 'group', 'tag')); + $form->assign('elements', ['sort_name', 'contact_type', 'group', 'tag']); } /** diff --git a/CRM/Contact/Form/Search/Custom/ContribSYBNT.php b/CRM/Contact/Form/Search/Custom/ContribSYBNT.php index c0e33bf8b070..326388909005 100644 --- a/CRM/Contact/Form/Search/Custom/ContribSYBNT.php +++ b/CRM/Contact/Form/Search/Custom/ContribSYBNT.php @@ -1,9 +1,9 @@ _formValues = $formValues; + $this->_formValues = self::formatSavedSearchFields($formValues); $this->_permissionedComponent = 'CiviContribute'; - $this->_columns = array( + $this->_columns = [ ts('Contact ID') => 'contact_id', ts('Name') => 'display_name', ts('Contribution Count') => 'donation_count', ts('Contribution Amount') => 'donation_amount', - ); + ]; - $this->_amounts = array( + $this->_amounts = [ 'min_amount_1' => ts('Min Amount One'), 'max_amount_1' => ts('Max Amount One'), 'min_amount_2' => ts('Min Amount Two'), 'max_amount_2' => ts('Max Amount Two'), 'exclude_min_amount' => ts('Exclusion Min Amount'), 'exclude_max_amount' => ts('Exclusion Max Amount'), - ); + ]; - $this->_dates = array( + $this->_dates = [ 'start_date_1' => ts('Start Date One'), 'end_date_1' => ts('End Date One'), 'start_date_2' => ts('Start Date Two'), 'end_date_2' => ts('End Date Two'), 'exclude_start_date' => ts('Exclusion Start Date'), 'exclude_end_date' => ts('Exclusion End Date'), - ); + ]; - $this->_checkboxes = array('is_first_amount' => ts('First Donation?')); + $this->_checkboxes = ['is_first_amount' => ts('First Donation?')]; foreach ($this->_amounts as $name => $title) { $this->{$name} = CRM_Utils_Array::value($name, $this->_formValues); @@ -83,7 +83,7 @@ public function __construct(&$formValues) { foreach ($this->_dates as $name => $title) { if (!empty($this->_formValues[$name])) { - $this->{$name} = CRM_Utils_Date::processDate($this->_formValues[$name]); + $this->{$name} = $this->_formValues[$name]; } } } @@ -101,7 +101,7 @@ public function buildForm(&$form) { } foreach ($this->_dates as $name => $title) { - $form->addDate($name, $title, FALSE, array('formatType' => 'custom')); + $form->add('datepicker', $name, $title, [], FALSE, ['time' => FALSE]); } foreach ($this->_checkboxes as $name => $title) { @@ -192,10 +192,8 @@ public function all( "; if ($justIDs) { - CRM_Core_DAO::executeQuery("DROP TEMPORARY TABLE IF EXISTS CustomSearch_SYBNT_temp"); - $query = "CREATE TEMPORARY TABLE CustomSearch_SYBNT_temp AS ({$sql})"; - $dao = CRM_Core_DAO::executeQuery($query); - $sql = "SELECT contact_a.id as contact_id FROM CustomSearch_SYBNT_temp as contact_a"; + $tempTable = CRM_Utils_SQL_TempTable::build()->createWithQuery($sql); + $sql = "SELECT contact_a.id as contact_id FROM {$tempTable->getName()} as contact_a"; } return $sql; } @@ -243,25 +241,25 @@ public function from() { * @return string */ public function where($includeContactIDs = FALSE) { - $clauses = array(); + $clauses = []; if (!empty($this->start_date_1)) { - $clauses[] = "contrib_1.receive_date >= {$this->start_date_1}"; + $clauses[] = CRM_Core_DAO::composeQuery('contrib_1.receive_date >= %1', [1 => [$this->start_date_1, 'String']]); } if (!empty($this->end_date_1)) { - $clauses[] = "contrib_1.receive_date <= {$this->end_date_1}"; + $clauses[] = CRM_Core_DAO::composeQuery('contrib_1.receive_date <= %1', [1 => [$this->end_date_1, 'String']]); } if (!empty($this->start_date_2) || !empty($this->end_date_2)) { $clauses[] = "contrib_2.is_test = 0"; if (!empty($this->start_date_2)) { - $clauses[] = "contrib_2.receive_date >= {$this->start_date_2}"; + $clauses[] = CRM_Core_DAO::composeQuery('contrib_2.receive_date >= %1', [1 => [$this->start_date_2, 'String']]); } if (!empty($this->end_date_2)) { - $clauses[] = "contrib_2.receive_date <= {$this->end_date_2}"; + $clauses[] = CRM_Core_DAO::composeQuery('contrib_2.receive_date <= %1', [1 => [$this->end_date_2, 'String']]); } } @@ -277,13 +275,13 @@ public function where($includeContactIDs = FALSE) { $sql = "CREATE TEMPORARY TABLE XG_CustomSearch_SYBNT ( contact_id int primary key) ENGINE=HEAP"; CRM_Core_DAO::executeQuery($sql); - $excludeClauses = array(); + $excludeClauses = []; if ($this->exclude_start_date) { - $excludeClauses[] = "c.receive_date >= {$this->exclude_start_date}"; + $excludeClauses[] = CRM_Core_DAO::composeQuery('c.receive_date >= %1', [1 => [$this->exclude_start_date, 'String']]); } if ($this->exclude_end_date) { - $excludeClauses[] = "c.receive_date <= {$this->exclude_end_date}"; + $excludeClauses[] = CRM_Core_DAO::composeQuery('c.receive_date <= %1', [1 => [$this->exclude_end_date, 'String']]); } $excludeClause = NULL; @@ -291,7 +289,7 @@ public function where($includeContactIDs = FALSE) { $excludeClause = ' AND ' . implode(' AND ', $excludeClauses); } - $having = array(); + $having = []; if ($this->exclude_min_amount) { $having[] = "sum(c.total_amount) >= {$this->exclude_min_amount}"; } @@ -317,7 +315,7 @@ public function where($includeContactIDs = FALSE) { $havingClause "; - $dao = CRM_Core_DAO::executeQuery($query); + CRM_Core_DAO::executeQuery($query); } // now ensure we dont consider donors that are not first time @@ -329,7 +327,7 @@ public function where($includeContactIDs = FALSE) { WHERE c.is_test = 0 AND c.receive_date < {$this->start_date_1} "; - $dao = CRM_Core_DAO::executeQuery($query); + CRM_Core_DAO::executeQuery($query); } $clauses[] = " xg.contact_id IS NULL "; @@ -346,7 +344,7 @@ public function where($includeContactIDs = FALSE) { * @return string */ public function having($includeContactIDs = FALSE) { - $clauses = array(); + $clauses = []; $min = CRM_Utils_Array::value('min_amount', $this->_formValues); if ($min) { $clauses[] = "sum(contrib_1.total_amount) >= $min"; @@ -400,4 +398,32 @@ public function buildACLClause($tableAlias = 'contact') { list($this->_aclFrom, $this->_aclWhere) = CRM_Contact_BAO_Contact_Permission::cacheClause($tableAlias); } + /** + * Format saved search fields for this custom group. + * + * Note this is a function to facilitate the transition to jcalendar for + * saved search groups. In time it can be stripped out again. + * + * @param array $formValues + * + * @return array + */ + public static function formatSavedSearchFields($formValues) { + $dateFields = [ + 'start_date_1', + 'end_date_1', + 'start_date_2', + 'end_date_2', + 'exclude_start_date', + 'exclude_end_date', + ]; + foreach ($formValues as $element => $value) { + if (in_array($element, $dateFields) && !empty($value)) { + $formValues[$element] = date('Y-m-d', strtotime($value)); + } + } + + return $formValues; + } + } diff --git a/CRM/Contact/Form/Search/Custom/ContributionAggregate.php b/CRM/Contact/Form/Search/Custom/ContributionAggregate.php index c5015b0a3a1d..f776f8448e2d 100644 --- a/CRM/Contact/Form/Search/Custom/ContributionAggregate.php +++ b/CRM/Contact/Form/Search/Custom/ContributionAggregate.php @@ -1,9 +1,9 @@ _formValues = $formValues; // Define the columns for search result rows - $this->_columns = array( + $this->_columns = [ ts('Contact ID') => 'contact_id', ts('Name') => 'sort_name', ts('Contribution Count') => 'donation_count', ts('Contribution Amount') => 'donation_amount', - ); + ]; // define component access permission needed $this->_permissionedComponent = 'CiviContribute'; @@ -83,19 +83,17 @@ public function buildForm(&$form) { ts('...and $') ); $form->addRule('max_amount', ts('Please enter a valid amount (numbers and decimal point only).'), 'money'); - - $form->addDate('start_date', ts('Contribution Date From'), FALSE, array('formatType' => 'custom')); - $form->addDate('end_date', ts('...through'), FALSE, array('formatType' => 'custom')); + CRM_Core_Form_Date::buildDateRange($form, 'contribution_date', 1, '_low', '_high', ts('From:'), FALSE, FALSE); $form->addSelect('financial_type_id', - array('entity' => 'contribution', 'multiple' => 'multiple', 'context' => 'search') + ['entity' => 'contribution', 'multiple' => 'multiple', 'context' => 'search'] ); /** * If you are using the sample template, this array tells the template fields to render * for the search form. */ - $form->assign('elements', array('min_amount', 'max_amount', 'start_date', 'end_date')); + $form->assign('elements', ['min_amount', 'max_amount']); } /** @@ -197,26 +195,34 @@ public function from() { * @return string */ public function where($includeContactIDs = FALSE) { - $clauses = array(); - - $clauses[] = "contrib.contact_id = contact_a.id"; - $clauses[] = "contrib.is_test = 0"; - - $startTime = !empty($this->_formValues['start_date_time']) ? $this->_formValues['start_date_time'] : '00:00:00'; - $endTime = !empty($this->_formValues['end_date_time']) ? $this->_formValues['end_date_time'] : '23:59:59'; - - $startDate = CRM_Utils_Date::processDate($this->_formValues['start_date'], $startTime); - if ($startDate) { - $clauses[] = "contrib.receive_date >= $startDate"; + $clauses = [ + "contrib.contact_id = contact_a.id", + "contrib.is_test = 0", + ]; + + foreach ([ + 'contribution_date_relative', + 'contribution_date_low', + 'contribution_date_high', + ] as $dateFieldName) { + $dateParams[$dateFieldName] = CRM_Utils_Array::value( + $dateFieldName, + $this->_formValues + ); } - $endDate = CRM_Utils_Date::processDate($this->_formValues['end_date'], $endTime); - if ($endDate) { - $clauses[] = "contrib.receive_date <= $endDate"; + foreach (CRM_Contact_BAO_Query::convertFormValues($dateParams) as $values) { + list($name, $op, $value) = $values; + if (strstr($name, '_low')) { + $clauses[] = "contrib.receive_date >= " . CRM_Utils_Date::processDate($value); + } + else { + $clauses[] = "contrib.receive_date <= " . CRM_Utils_Date::processDate($value); + } } if ($includeContactIDs) { - $contactIDs = array(); + $contactIDs = []; foreach ($this->_formValues as $id => $value) { if ($value && substr($id, 0, CRM_Core_Form::CB_PREFIX_LEN) == CRM_Core_Form::CB_PREFIX @@ -248,7 +254,7 @@ public function where($includeContactIDs = FALSE) { * @return string */ public function having($includeContactIDs = FALSE) { - $clauses = array(); + $clauses = []; $min = CRM_Utils_Array::value('min_amount', $this->_formValues); if ($min) { $min = CRM_Utils_Rule::cleanMoney($min); diff --git a/CRM/Contact/Form/Search/Custom/DateAdded.php b/CRM/Contact/Form/Search/Custom/DateAdded.php index 2186d3386080..27c954641238 100644 --- a/CRM/Contact/Form/Search/Custom/DateAdded.php +++ b/CRM/Contact/Form/Search/Custom/DateAdded.php @@ -1,9 +1,9 @@ _formValues = self::formatSavedSearchFields($formValues); - $this->_includeGroups = CRM_Utils_Array::value('includeGroups', $formValues, array()); - $this->_excludeGroups = CRM_Utils_Array::value('excludeGroups', $formValues, array()); + $this->_includeGroups = CRM_Utils_Array::value('includeGroups', $formValues, []); + $this->_excludeGroups = CRM_Utils_Array::value('excludeGroups', $formValues, []); - $this->_columns = array( + $this->_columns = [ ts('Contact ID') => 'contact_id', ts('Contact Type') => 'contact_type', ts('Name') => 'sort_name', ts('Date Added') => 'date_added', - ); + ]; } /** * @param CRM_Core_Form $form */ public function buildForm(&$form) { - $form->addDate('start_date', ts('Start Date'), FALSE, array('formatType' => 'custom')); - $form->addDate('end_date', ts('End Date'), FALSE, array('formatType' => 'custom')); + $form->add('datepicker', 'start_date', ts('Start Date'), [], FALSE, ['time' => FALSE]); + $form->add('datepicker', 'end_date', ts('End Date'), [], FALSE, ['time' => FALSE]); $groups = CRM_Core_PseudoConstant::nestedGroup(); - $select2style = array( + $select2style = [ 'multiple' => TRUE, 'style' => 'width: 100%; max-width: 60em;', 'class' => 'crm-select2', 'placeholder' => ts('- select -'), - ); + ]; $form->add('select', 'includeGroups', ts('Include Group(s)'), @@ -100,7 +103,7 @@ public function buildForm(&$form) { * if you are using the standard template, this array tells the template what elements * are part of the search criteria */ - $form->assign('elements', array('start_date', 'end_date', 'includeGroups', 'excludeGroups')); + $form->assign('elements', ['start_date', 'end_date', 'includeGroups', 'excludeGroups']); } /** @@ -136,9 +139,9 @@ public function all( $includeContactIDs = FALSE, $justIDs = FALSE ) { - $this->_includeGroups = CRM_Utils_Array::value('includeGroups', $this->_formValues, array()); + $this->_includeGroups = CRM_Utils_Array::value('includeGroups', $this->_formValues, []); - $this->_excludeGroups = CRM_Utils_Array::value('excludeGroups', $this->_formValues, array()); + $this->_excludeGroups = CRM_Utils_Array::value('excludeGroups', $this->_formValues, []); $this->_allSearch = FALSE; $this->_groups = FALSE; @@ -177,27 +180,24 @@ public function all( */ public function from() { //define table name - $randomNum = md5(uniqid()); - $this->_tableName = "civicrm_temp_custom_{$randomNum}"; + $datesTable = CRM_Utils_SQL_TempTable::build()->setCategory('dates')->setMemory(); + $this->_datesTable = $datesTable->getName(); + $xgTable = CRM_Utils_SQL_TempTable::build()->setCategory('xg')->setMemory(); + $this->_xgTable = $xgTable->getName(); + $igTable = CRM_Utils_SQL_TempTable::build()->setCategory('ig')->setMemory(); + $this->_igTable = $igTable->getName(); //grab the contacts added in the date range first - $sql = "CREATE TEMPORARY TABLE dates_{$this->_tableName} ( id int primary key, date_added date ) ENGINE=HEAP"; - if ($this->_debug > 0) { - print "-- Date range query:
";
-      print "$sql;";
-      print "
"; - } - CRM_Core_DAO::executeQuery($sql); + $datesTable->createWithColumns('id int primary key, date_added date'); - $startDate = CRM_Utils_Date::mysqlToIso(CRM_Utils_Date::processDate($this->_formValues['start_date'])); + $startDate = !empty($this->_formValues['start_date']) ? $this->_formValues['start_date'] : date('Y-m-d'); $endDateFix = NULL; if (!empty($this->_formValues['end_date'])) { - $endDate = CRM_Utils_Date::mysqlToIso(CRM_Utils_Date::processDate($this->_formValues['end_date'])); # tack 11:59pm on to make search inclusive of the end date - $endDateFix = "AND date_added <= '" . substr($endDate, 0, 10) . " 23:59:00'"; + $endDateFix = "AND date_added <= '{$this->_formValues['end_date']} 23:59:00'"; } - $dateRange = "INSERT INTO dates_{$this->_tableName} ( id, date_added ) + $dateRange = "INSERT INTO {$this->_datesTable} ( id, date_added ) SELECT civicrm_contact.id, min(civicrm_log.modified_date) AS date_added @@ -208,22 +208,16 @@ public function from() { GROUP BY civicrm_contact.id HAVING - date_added >= '$startDate' + date_added >= '$startDate 00:00:00' $endDateFix"; - if ($this->_debug > 0) { - print "-- Date range query:
";
-      print "$dateRange;";
-      print "
"; - } - CRM_Core_DAO::executeQuery($dateRange, CRM_Core_DAO::$_nullArray); // Only include groups in the search query of one or more Include OR Exclude groups has been selected. // CRM-6356 if ($this->_groups) { //block for Group search - $smartGroup = array(); + $smartGroup = []; $group = new CRM_Contact_DAO_Group(); $group->is_active = 1; $group->find(); @@ -249,16 +243,14 @@ public function from() { $xGroups = 0; } - $sql = "DROP TEMPORARY TABLE IF EXISTS Xg_{$this->_tableName}"; - CRM_Core_DAO::executeQuery($sql, CRM_Core_DAO::$_nullArray); - $sql = "CREATE TEMPORARY TABLE Xg_{$this->_tableName} ( contact_id int primary key) ENGINE=HEAP"; - CRM_Core_DAO::executeQuery($sql, CRM_Core_DAO::$_nullArray); + $xgTable->drop(); + $xgTable->createWithColumns('contact_id int primary key'); //used only when exclude group is selected if ($xGroups != 0) { - $excludeGroup = "INSERT INTO Xg_{$this->_tableName} ( contact_id ) + $excludeGroup = "INSERT INTO {$this->_xgTable} ( contact_id ) SELECT DISTINCT civicrm_group_contact.contact_id - FROM civicrm_group_contact, dates_{$this->_tableName} AS d + FROM civicrm_group_contact, {$this->_datesTable} AS d WHERE d.id = civicrm_group_contact.contact_id AND civicrm_group_contact.status = 'Added' AND @@ -277,31 +269,21 @@ public function from() { SELECT contact_id FROM civicrm_group_contact WHERE civicrm_group_contact.group_id = {$values} AND civicrm_group_contact.status = 'Removed')"; - $smartGroupQuery = " INSERT IGNORE INTO Xg_{$this->_tableName}(contact_id) $smartSql"; + $smartGroupQuery = " INSERT IGNORE INTO {$this->_xgTable}(contact_id) $smartSql"; CRM_Core_DAO::executeQuery($smartGroupQuery, CRM_Core_DAO::$_nullArray); } } } - $sql = "DROP TEMPORARY TABLE IF EXISTS Ig_{$this->_tableName}"; - CRM_Core_DAO::executeQuery($sql, CRM_Core_DAO::$_nullArray); - $sql = "CREATE TEMPORARY TABLE Ig_{$this->_tableName} - ( id int PRIMARY KEY AUTO_INCREMENT, + $igTable->drop(); + $igTable->createWithColumns('id int PRIMARY KEY AUTO_INCREMENT, contact_id int, - group_names varchar(64)) ENGINE=HEAP"; - - if ($this->_debug > 0) { - print "-- Include groups query:
";
-        print "$sql;";
-        print "
"; - } + group_names varchar(64)'); - CRM_Core_DAO::executeQuery($sql, CRM_Core_DAO::$_nullArray); - - $includeGroup = "INSERT INTO Ig_{$this->_tableName} (contact_id, group_names) + $includeGroup = "INSERT INTO {$this->_igTable} (contact_id, group_names) SELECT d.id as contact_id, civicrm_group.name as group_name - FROM dates_{$this->_tableName} AS d + FROM {$this->_datesTable} AS d INNER JOIN civicrm_group_contact ON civicrm_group_contact.contact_id = d.id LEFT JOIN civicrm_group @@ -309,8 +291,8 @@ public function from() { //used only when exclude group is selected if ($xGroups != 0) { - $includeGroup .= " LEFT JOIN Xg_{$this->_tableName} - ON d.id = Xg_{$this->_tableName}.contact_id"; + $includeGroup .= " LEFT JOIN {$this->_xgTable} + ON d.id = {$this->_xgTable}.contact_id"; } $includeGroup .= " WHERE civicrm_group_contact.status = 'Added' AND @@ -318,13 +300,7 @@ public function from() { //used only when exclude group is selected if ($xGroups != 0) { - $includeGroup .= " AND Xg_{$this->_tableName}.contact_id IS null"; - } - - if ($this->_debug > 0) { - print "-- Include groups query:
";
-        print "$includeGroup;";
-        print "
"; + $includeGroup .= " AND {$this->_xgTable}.contact_id IS null"; } CRM_Core_DAO::executeQuery($includeGroup, CRM_Core_DAO::$_nullArray); @@ -339,7 +315,7 @@ public function from() { $smartSql .= " AND contact_a.id IN ( SELECT id AS contact_id - FROM dates_{$this->_tableName} )"; + FROM {$this->_datesTable} )"; $smartSql .= " AND contact_a.id NOT IN ( SELECT contact_id FROM civicrm_group_contact @@ -347,30 +323,20 @@ public function from() { //used only when exclude group is selected if ($xGroups != 0) { - $smartSql .= " AND contact_a.id NOT IN (SELECT contact_id FROM Xg_{$this->_tableName})"; + $smartSql .= " AND contact_a.id NOT IN (SELECT contact_id FROM {$this->_xgTable})"; } $smartGroupQuery = " INSERT IGNORE INTO - Ig_{$this->_tableName}(contact_id) + {$this->_igTable}(contact_id) $smartSql"; CRM_Core_DAO::executeQuery($smartGroupQuery, CRM_Core_DAO::$_nullArray); - if ($this->_debug > 0) { - print "-- Smart group query:
";
-            print "$smartGroupQuery;";
-            print "
"; - } - $insertGroupNameQuery = "UPDATE IGNORE Ig_{$this->_tableName} + $insertGroupNameQuery = "UPDATE IGNORE {$this->_igTable} SET group_names = (SELECT title FROM civicrm_group WHERE civicrm_group.id = $values) - WHERE Ig_{$this->_tableName}.contact_id IS NOT NULL - AND Ig_{$this->_tableName}.group_names IS NULL"; + WHERE {$this->_igTable}.contact_id IS NOT NULL + AND {$this->_igTable}.group_names IS NULL"; CRM_Core_DAO::executeQuery($insertGroupNameQuery, CRM_Core_DAO::$_nullArray); - if ($this->_debug > 0) { - print "-- Smart group query:
";
-            print "$insertGroupNameQuery;";
-            print "
"; - } } } } @@ -380,12 +346,12 @@ public function from() { /* We need to join to this again to get the date_added value */ - $from .= " INNER JOIN dates_{$this->_tableName} d ON (contact_a.id = d.id) {$this->_aclFrom}"; + $from .= " INNER JOIN {$this->_datesTable} d ON (contact_a.id = d.id) {$this->_aclFrom}"; // Only include groups in the search query of one or more Include OR Exclude groups has been selected. // CRM-6356 if ($this->_groups) { - $from .= " INNER JOIN Ig_{$this->_tableName} temptable1 ON (contact_a.id = temptable1.contact_id)"; + $from .= " INNER JOIN {$this->_igTable} temptable1 ON (contact_a.id = temptable1.contact_id)"; } return $from; @@ -437,13 +403,13 @@ public function count() { public function __destruct() { //drop the temp. tables if they exist - if (!empty($this->_includeGroups)) { - $sql = "DROP TEMPORARY TABLE IF EXISTS Ig_{$this->_tableName}"; + if ($this->_igTable && !empty($this->_includeGroups)) { + $sql = "DROP TEMPORARY TABLE IF EXISTS {$this->_igTable}"; CRM_Core_DAO::executeQuery($sql, CRM_Core_DAO::$_nullArray); } - if (!empty($this->_excludeGroups)) { - $sql = "DROP TEMPORARY TABLE IF EXISTS Xg_{$this->_tableName}"; + if ($this->_xgTable && !empty($this->_excludeGroups)) { + $sql = "DROP TEMPORARY TABLE IF EXISTS {$this->_xgTable}"; CRM_Core_DAO::executeQuery($sql, CRM_Core_DAO::$_nullArray); } } @@ -455,4 +421,27 @@ public function buildACLClause($tableAlias = 'contact') { list($this->_aclFrom, $this->_aclWhere) = CRM_Contact_BAO_Contact_Permission::cacheClause($tableAlias); } + /** + * Format saved search fields for this custom group. + * + * Note this is a function to facilitate the transition to jcalendar for + * saved search groups. In time it can be stripped out again. + * + * @param array $formValues + * + * @return array + */ + public static function formatSavedSearchFields($formValues) { + $dateFields = [ + 'start_date', + 'end_date', + ]; + foreach ($formValues as $element => $value) { + if (in_array($element, $dateFields) && !empty($value)) { + $formValues[$element] = date('Y-m-d', strtotime($value)); + } + } + return $formValues; + } + } diff --git a/CRM/Contact/Form/Search/Custom/EventAggregate.php b/CRM/Contact/Form/Search/Custom/EventAggregate.php index e2bb5aa3b76d..441688dab657 100644 --- a/CRM/Contact/Form/Search/Custom/EventAggregate.php +++ b/CRM/Contact/Form/Search/Custom/EventAggregate.php @@ -1,9 +1,9 @@ _formValues = $formValues; - $this->_permissionedComponent = array('CiviContribute', 'CiviEvent'); + $this->_formValues = self::formatSavedSearchFields($formValues); + $this->_permissionedComponent = ['CiviContribute', 'CiviEvent']; /** * Define the columns for search result rows */ - $this->_columns = array( + $this->_columns = [ ts('Event') => 'event_name', ts('Type') => 'event_type', ts('Number of
Participant') => 'participant_count', @@ -57,7 +57,7 @@ public function __construct(&$formValues) { ts('Fee') => 'fee', ts('Net Payment') => 'net_payment', ts('Participant') => 'participant', - ); + ]; } /** @@ -83,23 +83,23 @@ public function buildForm(&$form) { $form->addElement('checkbox', "event_type_id[$eventId]", 'Event Type', $eventName); } $events = CRM_Event_BAO_Event::getEvents(1); - $form->add('select', 'event_id', ts('Event Name'), array('' => ts('- select -')) + $events); + $form->add('select', 'event_id', ts('Event Name'), ['' => ts('- select -')] + $events); - $form->addDate('start_date', ts('Payments Date From'), FALSE, array('formatType' => 'custom')); - $form->addDate('end_date', ts('...through'), FALSE, array('formatType' => 'custom')); + $form->add('datepicker', 'start_date', ts('Payments Date From'), [], FALSE, ['time' => FALSE]); + $form->add('datepicker', 'end_date', ts('...through'), [], FALSE, ['time' => FALSE]); /** * If you are using the sample template, this array tells the template fields to render * for the search form. */ - $form->assign('elements', array( - 'paid_online', - 'start_date', - 'end_date', - 'show_payees', - 'event_type_id', - 'event_id', - )); + $form->assign('elements', [ + 'paid_online', + 'start_date', + 'end_date', + 'show_payees', + 'event_type_id', + 'event_id', + ]); } /** @@ -155,7 +155,7 @@ public function all( on civicrm_contact.id = civicrm_participant.contact_id"; } else { - unset($this->_columns['Participant']); + unset($this->_columns[ts('Participant')]); } $where = $this->where(); @@ -226,7 +226,7 @@ public function from() { * @return string */ public function where($includeContactIDs = FALSE) { - $clauses = array(); + $clauses = []; $clauses[] = "civicrm_participant.status_id in ( 1 )"; $clauses[] = "civicrm_contribution.is_test = 0"; @@ -237,14 +237,13 @@ public function where($includeContactIDs = FALSE) { $clauses[] = "civicrm_contribution.payment_instrument_id <> 0"; } - $startDate = CRM_Utils_Date::processDate($this->_formValues['start_date']); - if ($startDate) { - $clauses[] = "civicrm_contribution.receive_date >= $startDate"; + // As we only allow date to be submitted we need to set default times so midnight for start time and just before midnight for end time. + if ($this->_formValues['start_date']) { + $clauses[] = "civicrm_contribution.receive_date >= '{$this->_formValues['start_date']} 00:00:00'"; } - $endDate = CRM_Utils_Date::processDate($this->_formValues['end_date']); - if ($endDate) { - $clauses[] = "civicrm_contribution.receive_date <= {$endDate}235959"; + if ($this->_formValues['end_date']) { + $clauses[] = "civicrm_contribution.receive_date <= '{$this->_formValues['end_date']} 23:59:59'"; } if (!empty($this->_formValues['event_id'])) { @@ -252,7 +251,7 @@ public function where($includeContactIDs = FALSE) { } if ($includeContactIDs) { - $contactIDs = array(); + $contactIDs = []; foreach ($this->_formValues as $id => $value) { if ($value && substr($id, 0, CRM_Core_Form::CB_PREFIX_LEN) == CRM_Core_Form::CB_PREFIX @@ -277,8 +276,8 @@ public function where($includeContactIDs = FALSE) { return implode(' AND ', $clauses); } - /* This function does a query to get totals for some of the search result columns and returns a totals array. */ + /** * @return array */ @@ -310,7 +309,7 @@ public function summary() { $dao = CRM_Core_DAO::executeQuery($sql, CRM_Core_DAO::$_nullArray ); - $totals = array(); + $totals = []; while ($dao->fetch()) { $totals['payment_amount'] = $dao->payment_amount; $totals['fee'] = $dao->fee; @@ -374,4 +373,28 @@ public function buildACLClause($tableAlias = 'contact') { list($this->_aclFrom, $this->_aclWhere) = CRM_Contact_BAO_Contact_Permission::cacheClause($tableAlias); } + /** + * Format saved search fields for this custom group. + * + * Note this is a function to facilitate the transition to jcalendar for + * saved search groups. In time it can be stripped out again. + * + * @param array $formValues + * + * @return array + */ + public static function formatSavedSearchFields($formValues) { + $dateFields = [ + 'start_date', + 'end_date', + ]; + foreach ($formValues as $element => $value) { + if (in_array($element, $dateFields) && !empty($value)) { + $formValues[$element] = date('Y-m-d', strtotime($value)); + } + } + + return $formValues; + } + } diff --git a/CRM/Contact/Form/Search/Custom/FullText.php b/CRM/Contact/Form/Search/Custom/FullText.php index b8a99931442b..e8868722cb9e 100644 --- a/CRM/Contact/Form/Search/Custom/FullText.php +++ b/CRM/Contact/Form/Search/Custom/FullText.php @@ -1,9 +1,9 @@ _partialQueries = array( + $this->_partialQueries = [ new CRM_Contact_Form_Search_Custom_FullText_Contact(), new CRM_Contact_Form_Search_Custom_FullText_Activity(), new CRM_Contact_Form_Search_Custom_FullText_Case(), new CRM_Contact_Form_Search_Custom_FullText_Contribution(), new CRM_Contact_Form_Search_Custom_FullText_Participant(), new CRM_Contact_Form_Search_Custom_FullText_Membership(), - ); + ]; $formValues['table'] = $this->getFieldValue($formValues, 'table', 'String'); $this->_table = $formValues['table']; @@ -95,8 +99,8 @@ public function __construct(&$formValues) { $this->_text = $formValues['text']; if (!$this->_table) { - $this->_limitClause = array($this->_limitNumberPlus1, NULL); - $this->_limitRowClause = $this->_limitDetailClause = array($this->_limitNumber, NULL); + $this->_limitClause = [$this->_limitNumberPlus1, NULL]; + $this->_limitRowClause = $this->_limitDetailClause = [$this->_limitNumber, NULL]; } else { // when there is table specified, we would like to use the pager. But since @@ -107,8 +111,8 @@ public function __construct(&$formValues) { $pageId = CRM_Utils_Array::value('crmPID', $_REQUEST, 1); $offset = ($pageId - 1) * $rowCount; $this->_limitClause = NULL; - $this->_limitRowClause = array($rowCount, NULL); - $this->_limitDetailClause = array($rowCount, $offset); + $this->_limitRowClause = [$rowCount, NULL]; + $this->_limitDetailClause = [$rowCount, $offset]; } $this->_formValues = $formValues; @@ -147,10 +151,10 @@ public function initialize() { } public function buildTempTable() { - $randomNum = md5(uniqid()); - $this->_tableName = "civicrm_temp_custom_details_{$randomNum}"; + $table = CRM_Utils_SQL_TempTable::build()->setCategory('custom')->setMemory()->setUtf8(); + $this->_tableName = $table->getName(); - $this->_tableFields = array( + $this->_tableFields = [ 'id' => 'int unsigned NOT NULL AUTO_INCREMENT', 'table_name' => 'varchar(16)', 'contact_id' => 'int unsigned', @@ -196,11 +200,11 @@ public function buildTempTable() { 'membership_status' => 'varchar(255)', // We may have multiple files to list on one record. // The temporary-table approach can't store full details for all of them - 'file_ids' => 'varchar(255)', // comma-separate id listing - ); + // comma-separate id listing + 'file_ids' => 'varchar(255)', + ]; $sql = " -CREATE TEMPORARY TABLE {$this->_tableName} ( "; foreach ($this->_tableFields as $name => $desc) { @@ -209,21 +213,23 @@ public function buildTempTable() { $sql .= " PRIMARY KEY ( id ) -) ENGINE=HEAP DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci "; - CRM_Core_DAO::executeQuery($sql); + $table->createWithColumns($sql); - $this->_entityIDTableName = "civicrm_temp_custom_entityID_{$randomNum}"; + $entityIdTable = CRM_Utils_SQL_TempTable::build()->setCategory('custom')->setMemory()->setUtf8(); + $this->_entityIDTableName = $entityIdTable->getName(); $sql = " -CREATE TEMPORARY TABLE {$this->_entityIDTableName} ( id int unsigned NOT NULL AUTO_INCREMENT, entity_id int unsigned NOT NULL, UNIQUE INDEX unique_entity_id ( entity_id ), PRIMARY KEY ( id ) -) ENGINE=HEAP DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci "; - CRM_Core_DAO::executeQuery($sql); + $entityIdTable->createWithColumns($sql); + + if (!empty($this->_formValues['is_unit_test'])) { + $this->_tableNameForTest = $this->_tableName; + } } public function fillTable() { @@ -254,12 +260,12 @@ public function filterACLContacts() { CRM_Contact_BAO_Contact_Permission::cache($contactID); - $params = array(1 => array($contactID, 'Integer')); + $params = [1 => [$contactID, 'Integer']]; $sql = " DELETE t.* FROM {$this->_tableName} t -WHERE NOT EXISTS ( SELECT c.id +WHERE NOT EXISTS ( SELECT c.contact_id FROM civicrm_acl_contact_cache c WHERE c.user_id = %1 AND t.contact_id = c.contact_id ) "; @@ -269,7 +275,7 @@ public function filterACLContacts() { DELETE t.* FROM {$this->_tableName} t WHERE t.table_name = 'Activity' AND - NOT EXISTS ( SELECT c.id + NOT EXISTS ( SELECT c.contact_id FROM civicrm_acl_contact_cache c WHERE c.user_id = %1 AND ( t.target_contact_id = c.contact_id OR t.target_contact_id IS NULL ) ) "; @@ -279,7 +285,7 @@ public function filterACLContacts() { DELETE t.* FROM {$this->_tableName} t WHERE t.table_name = 'Activity' AND - NOT EXISTS ( SELECT c.id + NOT EXISTS ( SELECT c.contact_id FROM civicrm_acl_contact_cache c WHERE c.user_id = %1 AND ( t.assignee_contact_id = c.contact_id OR t.assignee_contact_id IS NULL ) ) "; @@ -300,7 +306,7 @@ public function buildForm(&$form) { ); // also add a select box to allow the search to be constrained - $tables = array('' => ts('All tables')); + $tables = ['' => ts('All tables')]; foreach ($this->_partialQueries as $partialQuery) { /** @var $partialQuery CRM_Contact_Form_Search_Custom_FullText_AbstractPartialQuery */ if ($partialQuery->isActive()) { @@ -317,7 +323,7 @@ public function buildForm(&$form) { // set form defaults if (!empty($form->_formValues)) { - $defaults = array(); + $defaults = []; if (isset($form->_formValues['text'])) { $defaults['text'] = $form->_formValues['text']; @@ -344,10 +350,10 @@ public function buildForm(&$form) { * @return array */ public function &columns() { - $this->_columns = array( + $this->_columns = [ ts('Contact ID') => 'contact_id', ts('Name') => 'sort_name', - ); + ]; return $this->_columns; } @@ -358,10 +364,10 @@ public function &columns() { public function summary() { $this->initialize(); - $summary = array(); + $summary = []; foreach ($this->_partialQueries as $partialQuery) { /** @var $partialQuery CRM_Contact_Form_Search_Custom_FullText_AbstractPartialQuery */ - $summary[$partialQuery->getName()] = array(); + $summary[$partialQuery->getName()] = []; } // now iterate through the table and add entries to the relevant section @@ -374,7 +380,7 @@ public function summary() { $activityTypes = CRM_Core_PseudoConstant::activityType(TRUE, TRUE); $roleIds = CRM_Event_PseudoConstant::participantRole(); while ($dao->fetch()) { - $row = array(); + $row = []; foreach ($this->_tableFields as $name => $dontCare) { if ($name != 'activity_type_id') { $row[$name] = $dao->$name; @@ -385,7 +391,7 @@ public function summary() { } if (isset($row['participant_role'])) { $participantRole = explode(CRM_Core_DAO::VALUE_SEPARATOR, $row['participant_role']); - $viewRoles = array(); + $viewRoles = []; foreach ($participantRole as $v) { $viewRoles[] = $roleIds[$v]; } @@ -405,7 +411,7 @@ public function summary() { $summary[$dao->table_name][] = $row; } - $summary['Count'] = array(); + $summary['Count'] = []; foreach (array_keys($summary) as $table) { $summary['Count'][$table] = CRM_Utils_Array::value($table, $this->_foundRows); if ($summary['Count'][$table] >= self::LIMIT) { @@ -509,7 +515,7 @@ public function templateFile() { * @return array */ public function setDefaultValues() { - return array(); + return []; } /** diff --git a/CRM/Contact/Form/Search/Custom/FullText/AbstractPartialQuery.php b/CRM/Contact/Form/Search/Custom/FullText/AbstractPartialQuery.php index e0e9626049a5..fc4dc667fc5e 100644 --- a/CRM/Contact/Form/Search/Custom/FullText/AbstractPartialQuery.php +++ b/CRM/Contact/Form/Search/Custom/FullText/AbstractPartialQuery.php @@ -1,9 +1,9 @@ fetch()) { if (!array_key_exists($dao->table_name, $tables)) { - $tables[$dao->table_name] = array( + $tables[$dao->table_name] = [ 'id' => 'entity_id', - 'fields' => array(), - ); + 'fields' => [], + ]; } $tables[$dao->table_name]['fields'][$dao->column_name] = NULL; } @@ -175,15 +175,15 @@ public function runQueries($queryText, &$tables, $entityIDTableName, $limit) { continue; } - $query = $tableValues + array( + $query = $tableValues + [ 'text' => CRM_Utils_QueryFormatter::singleton() - ->format($queryText, CRM_Utils_QueryFormatter::LANG_SOLR), - ); + ->format($queryText, CRM_Utils_QueryFormatter::LANG_SOLR), + ]; list($intLimit, $intOffset) = $this->parseLimitOffset($limit); $files = $searcher->search($query, $intLimit, $intOffset); - $matches = array(); + $matches = []; foreach ($files as $file) { - $matches[] = array('entity_id' => $file['xparent_id']); + $matches[] = ['entity_id' => $file['xparent_id']]; } if ($matches) { $insertSql = CRM_Utils_SQL_Insert::into($entityIDTableName)->usingReplace()->rows($matches)->toSQL(); @@ -191,8 +191,10 @@ public function runQueries($queryText, &$tables, $entityIDTableName, $limit) { } } else { - $fullTextFields = array(); // array (string $sqlColumnName) - $clauses = array(); // array (string $sqlExpression) + // array (string $sqlColumnName) + $fullTextFields = []; + // array (string $sqlExpression) + $clauses = []; foreach ($tableValues['fields'] as $fieldName => $fieldType) { if ($fieldType == 'Int') { @@ -242,10 +244,10 @@ public function runQueries($queryText, &$tables, $entityIDTableName, $limit) { } } - return array( + return [ 'count' => CRM_Core_DAO::singleValueQuery("SELECT count(*) FROM {$entityIDTableName}"), 'files' => $files, - ); + ]; } /** @@ -260,47 +262,7 @@ public function runQueries($queryText, &$tables, $entityIDTableName, $limit) { * SQL, eg "MATCH (col1) AGAINST (queryText)" or "col1 LIKE '%queryText%'" */ public function matchText($table, $fullTextFields, $queryText) { - $strtolower = function_exists('mb_strtolower') ? 'mb_strtolower' : 'strtolower'; - - if (strpos($table, ' ') === FALSE) { - $tableName = $tableAlias = $table; - } - else { - list ($tableName, $tableAlias) = explode(' ', $table); - } - if (is_scalar($fullTextFields)) { - $fullTextFields = array($fullTextFields); - } - - $clauses = array(); - if (CRM_Core_InnoDBIndexer::singleton()->hasDeclaredIndex($tableName, $fullTextFields)) { - $formattedQuery = CRM_Utils_QueryFormatter::singleton() - ->format($queryText, CRM_Utils_QueryFormatter::LANG_SQL_FTSBOOL); - - $prefixedFieldNames = array(); - foreach ($fullTextFields as $fieldName) { - $prefixedFieldNames[] = "$tableAlias.$fieldName"; - } - - $clauses[] = sprintf("MATCH (%s) AGAINST ('%s' IN BOOLEAN MODE)", - implode(',', $prefixedFieldNames), - $strtolower(CRM_Core_DAO::escapeString($formattedQuery)) - ); - } - else { - //CRM_Core_Session::setStatus(ts('Cannot use FTS for %1 (%2)', array( - // 1 => $table, - // 2 => implode(', ', $fullTextFields), - //))); - - $formattedQuery = CRM_Utils_QueryFormatter::singleton() - ->format($queryText, CRM_Utils_QueryFormatter::LANG_SQL_LIKE); - $escapedText = $strtolower(CRM_Core_DAO::escapeString($formattedQuery)); - foreach ($fullTextFields as $fieldName) { - $clauses[] = "$tableAlias.$fieldName LIKE '{$escapedText}'"; - } - } - return implode(' OR ', $clauses); + return CRM_Utils_QueryFormatter::singleton()->formatSql($table, $fullTextFields, $queryText); } /** @@ -317,16 +279,16 @@ public function moveFileIDs($toTable, $parentIdColumn, $files) { return; } - $filesIndex = CRM_Utils_Array::index(array('xparent_id', 'file_id'), $files); + $filesIndex = CRM_Utils_Array::index(['xparent_id', 'file_id'], $files); // ex: $filesIndex[$xparent_id][$file_id] = array(...the file record...); $dao = CRM_Core_DAO::executeQuery(" SELECT distinct {$parentIdColumn} FROM {$toTable} WHERE table_name = %1 - ", array( - 1 => array($this->getName(), 'String'), - )); + ", [ + 1 => [$this->getName(), 'String'], + ]); while ($dao->fetch()) { if (empty($filesIndex[$dao->{$parentIdColumn}])) { continue; @@ -335,11 +297,11 @@ public function moveFileIDs($toTable, $parentIdColumn, $files) { CRM_Core_DAO::executeQuery("UPDATE {$toTable} SET file_ids = %1 WHERE table_name = %2 AND {$parentIdColumn} = %3 - ", array( - 1 => array(implode(',', array_keys($filesIndex[$dao->{$parentIdColumn}])), 'String'), - 2 => array($this->getName(), 'String'), - 3 => array($dao->{$parentIdColumn}, 'Int'), - )); + ", [ + 1 => [implode(',', array_keys($filesIndex[$dao->{$parentIdColumn}])), 'String'], + 2 => [$this->getName(), 'String'], + 3 => [$dao->{$parentIdColumn}, 'Int'], + ]); } } @@ -378,7 +340,7 @@ public function parseLimitOffset($limit) { if (!$intOffset) { $intOffset = 0; } - return array($intLimit, $intOffset); + return [$intLimit, $intOffset]; } } diff --git a/CRM/Contact/Form/Search/Custom/FullText/Activity.php b/CRM/Contact/Form/Search/Custom/FullText/Activity.php index e839c0521bd9..9af565f7bb42 100644 --- a/CRM/Contact/Form/Search/Custom/FullText/Activity.php +++ b/CRM/Contact/Form/Search/Custom/FullText/Activity.php @@ -1,9 +1,9 @@ matchText('civicrm_contact c', array('sort_name', 'display_name', 'nick_name'), $queryText)}) + ({$this->matchText('civicrm_contact c', ['sort_name', 'display_name', 'nick_name'], $queryText)}) OR ({$this->matchText('civicrm_email e', 'email', $queryText)} AND ca.activity_type_id = ov.value AND ov.name IN ('Inbound Email', 'Email') ) ) @@ -105,20 +105,20 @@ public function prepareQueries($queryText, $entityIDTableName) { $contactSQL[] = " SELECT distinct ca.id FROM civicrm_activity ca -WHERE ({$this->matchText('civicrm_activity ca', array('subject', 'details'), $queryText)}) +WHERE ({$this->matchText('civicrm_activity ca', ['subject', 'details'], $queryText)}) AND (ca.is_deleted = 0 OR ca.is_deleted IS NULL) "; - $final = array(); + $final = []; - $tables = array( - 'civicrm_activity' => array('fields' => array()), - 'file' => array( + $tables = [ + 'civicrm_activity' => ['fields' => []], + 'file' => [ 'xparent_table' => 'civicrm_activity', - ), + ], 'sql' => $contactSQL, 'final' => $final, - ); + ]; $this->fillCustomInfo($tables, "( 'Activity' )"); return $tables;; diff --git a/CRM/Contact/Form/Search/Custom/FullText/Case.php b/CRM/Contact/Form/Search/Custom/FullText/Case.php index b9e86478dfe5..bf3f7eede706 100644 --- a/CRM/Contact/Form/Search/Custom/FullText/Case.php +++ b/CRM/Contact/Form/Search/Custom/FullText/Case.php @@ -1,9 +1,9 @@ matchText('civicrm_contact c', array('sort_name', 'display_name', 'nick_name'), $queryText)}) +WHERE ({$this->matchText('civicrm_contact c', ['sort_name', 'display_name', 'nick_name'], $queryText)}) AND (cc.is_deleted = 0 OR cc.is_deleted IS NULL) "; @@ -106,13 +106,13 @@ public function prepareQueries($queryText, $entityIDTableName) { GROUP BY et.entity_id "; - $tables = array( - 'civicrm_case' => array('fields' => array()), - 'file' => array( + $tables = [ + 'civicrm_case' => ['fields' => []], + 'file' => [ 'xparent_table' => 'civicrm_case', - ), + ], 'sql' => $contactSQL, - ); + ]; return $tables; } diff --git a/CRM/Contact/Form/Search/Custom/FullText/Contact.php b/CRM/Contact/Form/Search/Custom/FullText/Contact.php index e696e639c16b..4930e99cd160 100644 --- a/CRM/Contact/Form/Search/Custom/FullText/Contact.php +++ b/CRM/Contact/Form/Search/Custom/FullText/Contact.php @@ -1,9 +1,9 @@ array( + $tables = [ + 'civicrm_contact' => [ 'id' => 'id', - 'fields' => array( + 'fields' => [ 'sort_name' => NULL, 'nick_name' => NULL, 'display_name' => NULL, - ), - ), - 'civicrm_address' => array( + ], + ], + 'civicrm_address' => [ 'id' => 'contact_id', - 'fields' => array( + 'fields' => [ 'street_address' => NULL, 'city' => NULL, 'postal_code' => NULL, - ), - ), - 'civicrm_email' => array( + ], + ], + 'civicrm_email' => [ 'id' => 'contact_id', - 'fields' => array('email' => NULL), - ), - 'civicrm_phone' => array( + 'fields' => ['email' => NULL], + ], + 'civicrm_phone' => [ 'id' => 'contact_id', - 'fields' => array('phone' => NULL), - ), - 'civicrm_note' => array( + 'fields' => ['phone' => NULL], + ], + 'civicrm_note' => [ 'id' => 'entity_id', 'entity_table' => 'civicrm_contact', - 'fields' => array( + 'fields' => [ 'subject' => NULL, 'note' => NULL, - ), - ), - 'file' => array( + ], + ], + 'file' => [ 'xparent_table' => 'civicrm_contact', - ), + ], 'sql' => $contactSQL, 'final' => $final, - ); + ]; // get the custom data info $this->fillCustomInfo($tables, diff --git a/CRM/Contact/Form/Search/Custom/FullText/Contribution.php b/CRM/Contact/Form/Search/Custom/FullText/Contribution.php index b180b0d03db5..293b728a6186 100644 --- a/CRM/Contact/Form/Search/Custom/FullText/Contribution.php +++ b/CRM/Contact/Form/Search/Custom/FullText/Contribution.php @@ -1,9 +1,9 @@ matchText('civicrm_contact c', array('sort_name', 'display_name', 'nick_name'), $queryText)}) +WHERE ({$this->matchText('civicrm_contact c', ['sort_name', 'display_name', 'nick_name'], $queryText)}) "; - $tables = array( - 'civicrm_contribution' => array( + $tables = [ + 'civicrm_contribution' => [ 'id' => 'id', - 'fields' => array( + 'fields' => [ 'source' => NULL, 'amount_level' => NULL, 'trxn_Id' => NULL, 'invoice_id' => NULL, - 'check_number' => 'Int', // Odd: This is really a VARCHAR, so why are we searching like an INT? + // Odd: This is really a VARCHAR, so why are we searching like an INT? + 'check_number' => 'Int', 'total_amount' => 'Int', - ), - ), - 'file' => array( + ], + ], + 'file' => [ 'xparent_table' => 'civicrm_contribution', - ), + ], 'sql' => $contactSQL, - 'civicrm_note' => array( + 'civicrm_note' => [ 'id' => 'entity_id', 'entity_table' => 'civicrm_contribution', - 'fields' => array( + 'fields' => [ 'subject' => NULL, 'note' => NULL, - ), - ), - ); + ], + ], + ]; // get the custom data info $this->fillCustomInfo($tables, "( 'Contribution' )"); diff --git a/CRM/Contact/Form/Search/Custom/FullText/Membership.php b/CRM/Contact/Form/Search/Custom/FullText/Membership.php index 9c0a4c2307d1..f81694b6cf50 100644 --- a/CRM/Contact/Form/Search/Custom/FullText/Membership.php +++ b/CRM/Contact/Form/Search/Custom/FullText/Membership.php @@ -1,9 +1,9 @@ matchText('civicrm_contact c', array('sort_name', 'display_name', 'nick_name'), $queryText)}) +WHERE ({$this->matchText('civicrm_contact c', ['sort_name', 'display_name', 'nick_name'], $queryText)}) "; - $tables = array( - 'civicrm_membership' => array( + $tables = [ + 'civicrm_membership' => [ 'id' => 'id', - 'fields' => array('source' => NULL), - ), - 'file' => array( + 'fields' => ['source' => NULL], + ], + 'file' => [ 'xparent_table' => 'civicrm_membership', - ), + ], 'sql' => $contactSQL, - ); + ]; // get the custom data info $this->fillCustomInfo($tables, "( 'Membership' )"); diff --git a/CRM/Contact/Form/Search/Custom/FullText/Participant.php b/CRM/Contact/Form/Search/Custom/FullText/Participant.php index fd46ca274f0f..20bbbd3e4deb 100644 --- a/CRM/Contact/Form/Search/Custom/FullText/Participant.php +++ b/CRM/Contact/Form/Search/Custom/FullText/Participant.php @@ -1,9 +1,9 @@ matchText('civicrm_contact c', array('sort_name', 'display_name', 'nick_name'), $queryText)}) +WHERE ({$this->matchText('civicrm_contact c', ['sort_name', 'display_name', 'nick_name'], $queryText)}) "; - $tables = array( - 'civicrm_participant' => array( + $tables = [ + 'civicrm_participant' => [ 'id' => 'id', - 'fields' => array( + 'fields' => [ 'source' => NULL, 'fee_level' => NULL, 'fee_amount' => 'Int', - ), - ), - 'file' => array( + ], + ], + 'file' => [ 'xparent_table' => 'civicrm_participant', - ), + ], 'sql' => $contactSQL, - 'civicrm_note' => array( + 'civicrm_note' => [ 'id' => 'entity_id', 'entity_table' => 'civicrm_participant', - 'fields' => array( + 'fields' => [ 'subject' => NULL, 'note' => NULL, - ), - ), - ); + ], + ], + ]; // get the custom data info $this->fillCustomInfo($tables, "( 'Participant' )"); diff --git a/CRM/Contact/Form/Search/Custom/Group.php b/CRM/Contact/Form/Search/Custom/Group.php index 8b889dfd0e29..76c7efbc369a 100644 --- a/CRM/Contact/Form/Search/Custom/Group.php +++ b/CRM/Contact/Form/Search/Custom/Group.php @@ -1,9 +1,9 @@ _formValues = $formValues; - $this->_columns = array( + $this->_columns = [ ts('Contact ID') => 'contact_id', ts('Contact Type') => 'contact_type', ts('Name') => 'sort_name', ts('Group Name') => 'gname', ts('Tag Name') => 'tname', - ); + ]; - $this->_includeGroups = CRM_Utils_Array::value('includeGroups', $this->_formValues, array()); - $this->_excludeGroups = CRM_Utils_Array::value('excludeGroups', $this->_formValues, array()); - $this->_includeTags = CRM_Utils_Array::value('includeTags', $this->_formValues, array()); - $this->_excludeTags = CRM_Utils_Array::value('excludeTags', $this->_formValues, array()); + $this->_includeGroups = CRM_Utils_Array::value('includeGroups', $this->_formValues, []); + $this->_excludeGroups = CRM_Utils_Array::value('excludeGroups', $this->_formValues, []); + $this->_includeTags = CRM_Utils_Array::value('includeTags', $this->_formValues, []); + $this->_excludeTags = CRM_Utils_Array::value('excludeTags', $this->_formValues, []); //define variables $this->_allSearch = FALSE; @@ -95,19 +95,19 @@ public function buildForm(&$form) { $groups = CRM_Core_PseudoConstant::nestedGroup(); - $tags = CRM_Core_PseudoConstant::get('CRM_Core_DAO_EntityTag', 'tag_id', array('onlyActive' => FALSE)); + $tags = CRM_Core_PseudoConstant::get('CRM_Core_DAO_EntityTag', 'tag_id', ['onlyActive' => FALSE]); if (count($groups) == 0 || count($tags) == 0) { CRM_Core_Session::setStatus(ts("At least one Group and Tag must be present for Custom Group / Tag search."), ts('Missing Group/Tag')); $url = CRM_Utils_System::url('civicrm/contact/search/custom/list', 'reset=1'); CRM_Utils_System::redirect($url); } - $select2style = array( + $select2style = [ 'multiple' => TRUE, 'style' => 'width: 100%; max-width: 60em;', 'class' => 'crm-select2', 'placeholder' => ts('- select -'), - ); + ]; $form->add('select', 'includeGroups', ts('Include Group(s)'), @@ -123,10 +123,10 @@ public function buildForm(&$form) { $select2style ); - $andOr = array( + $andOr = [ '1' => ts('Show contacts that meet the Groups criteria AND the Tags criteria'), '0' => ts('Show contacts that meet the Groups criteria OR the Tags criteria'), - ); + ]; $form->addRadio('andOr', ts('AND/OR'), $andOr, NULL, '
', TRUE); $form->add('select', 'includeTags', @@ -147,7 +147,7 @@ public function buildForm(&$form) { * if you are using the standard template, this array tells the template what elements * are part of the search criteria */ - $form->assign('elements', array('includeGroups', 'excludeGroups', 'andOr', 'includeTags', 'excludeTags')); + $form->assign('elements', ['includeGroups', 'excludeGroups', 'andOr', 'includeTags', 'excludeTags']); } /** @@ -174,19 +174,19 @@ public function all( //distinguish column according to user selection if (($this->_includeGroups && !$this->_includeTags)) { - unset($this->_columns['Tag Name']); + unset($this->_columns[ts('Tag Name')]); $selectClause .= ", GROUP_CONCAT(DISTINCT group_names ORDER BY group_names ASC ) as gname"; } elseif ($this->_includeTags && (!$this->_includeGroups)) { - unset($this->_columns['Group Name']); + unset($this->_columns[ts('Group Name')]); $selectClause .= ", GROUP_CONCAT(DISTINCT tag_names ORDER BY tag_names ASC ) as tname"; } elseif (!empty($this->_includeTags) && !empty($this->_includeGroups)) { $selectClause .= ", GROUP_CONCAT(DISTINCT group_names ORDER BY group_names ASC ) as gname , GROUP_CONCAT(DISTINCT tag_names ORDER BY tag_names ASC ) as tname"; } else { - unset($this->_columns['Tag Name']); - unset($this->_columns['Group Name']); + unset($this->_columns[ts('Tag Name')]); + unset($this->_columns[ts('Group Name')]); } } @@ -247,7 +247,7 @@ public function from() { $this->_tableName = "civicrm_temp_custom_{$randomNum}"; //block for Group search - $smartGroup = array(); + $smartGroup = []; if ($this->_groups || $this->_allSearch) { $group = new CRM_Contact_DAO_Group(); $group->is_active = 1; @@ -260,6 +260,12 @@ public function from() { } $includedGroups = implode(',', $allGroups); + //CRM-15049 - Include child group ids. + $childGroupIds = CRM_Contact_BAO_Group::getChildGroupIds($this->_includeGroups); + if (count($childGroupIds) > 0) { + $this->_includeGroups = array_merge($this->_includeGroups, $childGroupIds); + } + if (!empty($this->_includeGroups)) { $iGroups = implode(',', $this->_includeGroups); } @@ -274,7 +280,7 @@ public function from() { $xGroups = 0; } - $sql = "CREATE TEMPORARY TABLE Xg_{$this->_tableName} ( contact_id int primary key) ENGINE=MyISAM"; + $sql = "CREATE TEMPORARY TABLE Xg_{$this->_tableName} ( contact_id int primary key) ENGINE=InnoDB"; CRM_Core_DAO::executeQuery($sql); //used only when exclude group is selected @@ -312,7 +318,7 @@ public function from() { $sql = "CREATE TEMPORARY TABLE Ig_{$this->_tableName} ( id int PRIMARY KEY AUTO_INCREMENT, contact_id int, - group_names varchar(64)) ENGINE=MyISAM"; + group_names varchar(64)) ENGINE=InnoDB"; CRM_Core_DAO::executeQuery($sql); @@ -414,7 +420,7 @@ public function from() { $xTags = 0; } - $sql = "CREATE TEMPORARY TABLE Xt_{$this->_tableName} ( contact_id int primary key) ENGINE=MyISAM"; + $sql = "CREATE TEMPORARY TABLE Xt_{$this->_tableName} ( contact_id int primary key) ENGINE=InnoDB"; CRM_Core_DAO::executeQuery($sql); //used only when exclude tag is selected @@ -432,7 +438,7 @@ public function from() { $sql = "CREATE TEMPORARY TABLE It_{$this->_tableName} ( id int PRIMARY KEY AUTO_INCREMENT, contact_id int, - tag_names varchar(64)) ENGINE=MyISAM"; + tag_names varchar(64)) ENGINE=InnoDB"; CRM_Core_DAO::executeQuery($sql); @@ -497,8 +503,8 @@ public function from() { /* * Set from statement depending on array sel */ - $whereitems = array(); - foreach (array('Ig', 'It') as $inc) { + $whereitems = []; + foreach (['Ig', 'It'] as $inc) { if ($this->_andOr == 1) { if ($$inc) { $from .= " INNER JOIN {$inc}_{$this->_tableName} temptable$inc ON (contact_a.id = temptable$inc.contact_id)"; @@ -514,7 +520,7 @@ public function from() { } } $this->_where = $whereitems ? "(" . implode(' OR ', $whereitems) . ')' : '(1)'; - foreach (array('Xg', 'Xt') as $exc) { + foreach (['Xg', 'Xt'] as $exc) { if ($$exc) { $from .= " LEFT JOIN {$exc}_{$this->_tableName} temptable$exc ON (contact_a.id = temptable$exc.contact_id)"; $this->_where .= " AND temptable$exc.contact_id IS NULL"; @@ -541,7 +547,7 @@ public function from() { */ public function where($includeContactIDs = FALSE) { if ($includeContactIDs) { - $contactIDs = array(); + $contactIDs = []; foreach ($this->_formValues as $id => $value) { if ($value && diff --git a/CRM/Contact/Form/Search/Custom/MultipleValues.php b/CRM/Contact/Form/Search/Custom/MultipleValues.php index f5742817f4ff..2737366bdb1e 100644 --- a/CRM/Contact/Form/Search/Custom/MultipleValues.php +++ b/CRM/Contact/Form/Search/Custom/MultipleValues.php @@ -1,9 +1,9 @@ _groupTree = CRM_Core_BAO_CustomGroup::getTree("'Contact', 'Individual', 'Organization', 'Household'", - CRM_Core_DAO::$_nullObject, - NULL, -1 - ); + $this->_groupTree = CRM_Core_BAO_CustomGroup::getTree("'Contact', 'Individual', 'Organization', 'Household'", NULL, NULL, -1); $this->_group = CRM_Utils_Array::value('group', $this->_formValues); $this->_tag = CRM_Utils_Array::value('tag', $this->_formValues); - $this->_columns = array( + $this->_columns = [ ts('Contact ID') => 'contact_id', ts('Contact Type') => 'contact_type', ts('Name') => 'sort_name', - ); + ]; $this->_customGroupIDs = CRM_Utils_Array::value('custom_group', $formValues); @@ -73,7 +70,7 @@ public function __construct(&$formValues) { * Add all the fields for chosen groups */ public function addColumns() { - $this->_tables = array(); + $this->_tables = []; foreach ($this->_groupTree as $groupID => $group) { if (empty($this->_customGroupIDs[$groupID])) { continue; @@ -85,7 +82,7 @@ public function addColumns() { foreach ($group['fields'] as $fieldID => $field) { $this->_columns[$field['label']] = "custom_{$field['id']}"; if (!array_key_exists($group['table_name'], $this->_tables)) { - $this->_tables[$group['table_name']] = array(); + $this->_tables[$group['table_name']] = []; } $this->_tables[$group['table_name']][$field['id']] = $field['column_name']; } @@ -101,16 +98,16 @@ public function buildForm(&$form) { $form->add('text', 'sort_name', ts('Contact Name'), TRUE); - $contactTypes = array('' => ts('- any contact type -')) + CRM_Contact_BAO_ContactType::getSelectElements(); - $form->add('select', 'contact_type', ts('Find...'), $contactTypes, array('class' => 'crm-select2 huge')); + $contactTypes = ['' => ts('- any contact type -')] + CRM_Contact_BAO_ContactType::getSelectElements(); + $form->add('select', 'contact_type', ts('Find...'), $contactTypes, ['class' => 'crm-select2 huge']); // add select for groups - $group = array('' => ts('- any group -')) + CRM_Core_PseudoConstant::group(); - $form->addElement('select', 'group', ts('in'), $group, array('class' => 'crm-select2 huge')); + $group = ['' => ts('- any group -')] + CRM_Core_PseudoConstant::group(); + $form->addElement('select', 'group', ts('in'), $group, ['class' => 'crm-select2 huge']); // add select for tags - $tag = array('' => ts('- any tag -')) + CRM_Core_PseudoConstant::get('CRM_Core_DAO_EntityTag', 'tag_id', array('onlyActive' => FALSE)); - $form->addElement('select', 'tag', ts('Tagged'), $tag, array('class' => 'crm-select2 huge')); + $tag = ['' => ts('- any tag -')] + CRM_Core_PseudoConstant::get('CRM_Core_DAO_EntityTag', 'tag_id', ['onlyActive' => FALSE]); + $form->addElement('select', 'tag', ts('Tagged'), $tag, ['class' => 'crm-select2 huge']); if (empty($this->_groupTree)) { CRM_Core_Error::statusBounce(ts("Atleast one Custom Group must be present, for Custom Group search."), @@ -181,7 +178,7 @@ public function all($offset = 0, $rowcount = 0, $sort = NULL, $includeContactIDs "; } - $customClauses = array(); + $customClauses = []; foreach ($this->_tables as $tableName => $fields) { foreach ($fields as $fieldID => $fieldName) { $customClauses[] = "{$tableName}.{$fieldName} as custom_{$fieldID}"; @@ -201,7 +198,7 @@ public function all($offset = 0, $rowcount = 0, $sort = NULL, $includeContactIDs public function from() { $this->buildACLClause('contact_a'); $from = "FROM civicrm_contact contact_a {$this->_aclFrom}"; - $customFrom = array(); + $customFrom = []; // lets do an INNER JOIN so we get only relevant values rather than all values if (!empty($this->_tables)) { foreach ($this->_tables as $tableName => $fields) { @@ -231,8 +228,8 @@ public function from() { */ public function where($includeContactIDs = FALSE) { $count = 1; - $clause = array(); - $params = array(); + $clause = []; + $params = []; $name = CRM_Utils_Array::value('sort_name', $this->_formValues ); @@ -240,7 +237,7 @@ public function where($includeContactIDs = FALSE) { if (strpos($name, '%') === FALSE) { $name = "%{$name}%"; } - $params[$count] = array($name, 'String'); + $params[$count] = [$name, 'String']; $clause[] = "contact_a.sort_name LIKE %{$count}"; $count++; } @@ -249,7 +246,7 @@ public function where($includeContactIDs = FALSE) { $this->_formValues ); if ($contact_type != NULL) { - $contactType = explode('__', $contact_type); + $contactType = explode('__', $contact_type, 2); if (count($contactType) > 1) { $clause[] = "contact_a.contact_type = '$contactType[0]' AND contact_a.contact_sub_type = '$contactType[1]'"; } diff --git a/CRM/Contact/Form/Search/Custom/PostalMailing.php b/CRM/Contact/Form/Search/Custom/PostalMailing.php index c44fe2741973..fc6998dbe7f7 100644 --- a/CRM/Contact/Form/Search/Custom/PostalMailing.php +++ b/CRM/Contact/Form/Search/Custom/PostalMailing.php @@ -1,9 +1,9 @@ _columns = array( + $this->_columns = [ // If possible, don't use aliases for the columns you select. // You can prefix columns with table aliases, if needed. // @@ -59,21 +60,21 @@ public function __construct(&$formValues) { // If you don't do this, the patch of CRM-16587 might cause database // errors. ts('State') => 'state_province.name', - ); + ]; } /** * @param CRM_Core_Form $form */ public function buildForm(&$form) { - $groups = array('' => ts('- select group -')) + CRM_Core_PseudoConstant::nestedGroup(FALSE); - $form->addElement('select', 'group_id', ts('Group'), $groups, array('class' => 'crm-select2 huge')); + $groups = ['' => ts('- select group -')] + CRM_Core_PseudoConstant::nestedGroup(FALSE); + $form->addElement('select', 'group_id', ts('Group'), $groups, ['class' => 'crm-select2 huge']); /** * if you are using the standard template, this array tells the template what elements * are part of the search criteria */ - $form->assign('elements', array('group_id')); + $form->assign('elements', ['group_id']); } /** @@ -148,15 +149,15 @@ public function from() { * @return string */ public function where($includeContactIDs = FALSE) { - $params = array(); + $params = []; $count = 1; - $clause = array(); + $clause = []; $groupID = CRM_Utils_Array::value('group_id', $this->_formValues ); if ($groupID) { - $params[$count] = array($groupID, 'Integer'); + $params[$count] = [$groupID, 'Integer']; $clause[] = "cgc.group_id = %{$count}"; } diff --git a/CRM/Contact/Form/Search/Custom/PriceSet.php b/CRM/Contact/Form/Search/Custom/PriceSet.php index 5f550adb7cee..3253fffb8131 100644 --- a/CRM/Contact/Form/Search/Custom/PriceSet.php +++ b/CRM/Contact/Form/Search/Custom/PriceSet.php @@ -1,9 +1,9 @@ _columns as $dontCare => $fieldName) { - if (in_array($fieldName, array( + if (in_array($fieldName, [ 'contact_id', 'participant_id', 'display_name', - ))) { + ])) { continue; } $sql .= "{$fieldName} int default 0,\n"; @@ -133,12 +133,12 @@ public function fillTable() { $dao = CRM_Core_DAO::executeQuery($sql); // first store all the information by option value id - $rows = array(); + $rows = []; while ($dao->fetch()) { $contactID = $dao->contact_id; $participantID = $dao->participant_id; if (!isset($rows[$participantID])) { - $rows[$participantID] = array(); + $rows[$participantID] = []; } $rows[$participantID][] = "price_field_{$dao->price_field_value_id} = {$dao->qty}"; @@ -146,12 +146,14 @@ public function fillTable() { foreach (array_keys($rows) as $participantID) { $values = implode(',', $rows[$participantID]); - $sql = " + if ($values) { + $sql = " UPDATE {$this->_tableName} SET $values WHERE participant_id = $participantID; "; - CRM_Core_DAO::executeQuery($sql); + CRM_Core_DAO::executeQuery($sql); + } } } @@ -174,9 +176,9 @@ public function priceSetDAO($eventID = NULL) { AND p.entity_id = e.id "; - $params = array(); + $params = []; if ($eventID) { - $params[1] = array($eventID, 'Integer'); + $params[1] = [$eventID, 'Integer']; $sql .= " AND e.id = $eventID"; } @@ -194,7 +196,7 @@ public function priceSetDAO($eventID = NULL) { public function buildForm(&$form) { $dao = $this->priceSetDAO(); - $event = array(); + $event = []; while ($dao->fetch()) { $event[$dao->id] = $dao->title; } @@ -219,15 +221,15 @@ public function buildForm(&$form) { * if you are using the standard template, this array tells the template what elements * are part of the search criteria */ - $form->assign('elements', array('event_id')); + $form->assign('elements', ['event_id']); } public function setColumns() { - $this->_columns = array( + $this->_columns = [ ts('Contact ID') => 'contact_id', ts('Participant ID') => 'participant_id', ts('Name') => 'display_name', - ); + ]; if (!$this->_eventID) { return; @@ -290,10 +292,10 @@ public function all( contact_a.display_name as display_name"; foreach ($this->_columns as $dontCare => $fieldName) { - if (in_array($fieldName, array( + if (in_array($fieldName, [ 'contact_id', 'display_name', - ))) { + ])) { continue; } $selectClause .= ",\ntempTable.{$fieldName} as {$fieldName}"; diff --git a/CRM/Contact/Form/Search/Custom/Proximity.php b/CRM/Contact/Form/Search/Custom/Proximity.php index c719f131d14a..dc517d92c8ec 100644 --- a/CRM/Contact/Form/Search/Custom/Proximity.php +++ b/CRM/Contact/Form/Search/Custom/Proximity.php @@ -1,9 +1,9 @@ _formValues)) { // add the country and state - if (!empty($this->_formValues['country_id'])) { - $this->_formValues['country'] = CRM_Core_PseudoConstant::country($this->_formValues['country_id']); - } - - if (!empty($this->_formValues['state_province_id'])) { - $this->_formValues['state_province'] = CRM_Core_PseudoConstant::stateProvince($this->_formValues['state_province_id']); - } - - // use the address to get the latitude and longitude - CRM_Utils_Geocode_Google::format($this->_formValues); - - if (!is_numeric(CRM_Utils_Array::value('geo_code_1', $this->_formValues)) || - !is_numeric(CRM_Utils_Array::value('geo_code_2', $this->_formValues)) || - !isset($this->_formValues['distance']) - ) { - CRM_Core_Error::fatal(ts('Could not geocode input')); - } - + self::addGeocodingData($this->_formValues); $this->_latitude = $this->_formValues['geo_code_1']; $this->_longitude = $this->_formValues['geo_code_2']; @@ -88,14 +74,23 @@ public function __construct(&$formValues) { $this->_tag = CRM_Utils_Array::value('tag', $this->_formValues); - $this->_columns = array( + $this->_columns = [ ts('Name') => 'sort_name', ts('Street Address') => 'street_address', ts('City') => 'city', ts('Postal Code') => 'postal_code', ts('State') => 'state_province', ts('Country') => 'country', - ); + ]; + } + + /** + * Get the query object for this selector. + * + * @return CRM_Contact_BAO_Query + */ + public function getQueryObj() { + return $this->_query; } /** @@ -108,7 +103,7 @@ public function buildForm(&$form) { $form->add('text', 'distance', ts('Distance'), NULL, TRUE); - $proxUnits = array('km' => ts('km'), 'miles' => ts('miles')); + $proxUnits = ['km' => ts('km'), 'miles' => ts('miles')]; $form->add('select', 'prox_distance_unit', ts('Units'), $proxUnits, TRUE); $form->add('text', @@ -126,20 +121,23 @@ public function buildForm(&$form) { ts('Postal Code') ); - $defaults = array(); + $defaults = []; if ($countryDefault) { $defaults['country_id'] = $countryDefault; } $form->addChainSelect('state_province_id'); - $country = array('' => ts('- select -')) + CRM_Core_PseudoConstant::country(); - $form->add('select', 'country_id', ts('Country'), $country, TRUE, array('class' => 'crm-select2')); + $country = ['' => ts('- select -')] + CRM_Core_PseudoConstant::country(); + $form->add('select', 'country_id', ts('Country'), $country, TRUE, ['class' => 'crm-select2']); + + $form->add('text', 'geo_code_1', ts('Latitude')); + $form->add('text', 'geo_code_2', ts('Longitude')); - $group = array('' => ts('- any group -')) + CRM_Core_PseudoConstant::nestedGroup(); - $form->addElement('select', 'group', ts('Group'), $group, array('class' => 'crm-select2 huge')); + $group = ['' => ts('- any group -')] + CRM_Core_PseudoConstant::nestedGroup(); + $form->addElement('select', 'group', ts('Group'), $group, ['class' => 'crm-select2 huge']); - $tag = array('' => ts('- any tag -')) + CRM_Core_PseudoConstant::get('CRM_Core_DAO_EntityTag', 'tag_id', array('onlyActive' => FALSE)); - $form->addElement('select', 'tag', ts('Tag'), $tag, array('class' => 'crm-select2 huge')); + $tag = ['' => ts('- any tag -')] + CRM_Core_PseudoConstant::get('CRM_Core_DAO_EntityTag', 'tag_id', ['onlyActive' => FALSE]); + $form->addElement('select', 'tag', ts('Tag'), $tag, ['class' => 'crm-select2 huge']); /** * You can define a custom title for the search form @@ -150,7 +148,7 @@ public function buildForm(&$form) { * if you are using the standard template, this array tells the template what elements * are part of the search criteria */ - $form->assign('elements', array( + $form->assign('elements', [ 'distance', 'prox_distance_unit', 'street_address', @@ -160,7 +158,7 @@ public function buildForm(&$form) { 'state_province_id', 'group', 'tag', - )); + ]); } /** @@ -176,20 +174,7 @@ public function all( $offset = 0, $rowcount = 0, $sort = NULL, $includeContactIDs = FALSE, $justIDs = FALSE ) { - if ($justIDs) { - $selectClause = "contact_a.id as contact_id"; - } - else { - $selectClause = " -contact_a.id as contact_id , -contact_a.sort_name as sort_name , -address.street_address as street_address, -address.city as city , -address.postal_code as postal_code , -state_province.name as state_province, -country.name as country -"; - } + $selectClause = $justIDs ? "contact_a.id as contact_id" : NULL; return $this->sql($selectClause, $offset, $rowcount, $sort, @@ -198,31 +183,70 @@ public function all( } /** + * Override sql() function to use the Query object rather than generating on the form. + * + * @param string $selectClause + * @param int $offset + * @param int $rowcount + * @param null $sort + * @param bool $includeContactIDs + * @param null $groupBy + * * @return string */ - public function from() { - $this->buildACLClause('contact_a'); - $f = " -FROM civicrm_contact contact_a -LEFT JOIN civicrm_address address ON ( address.contact_id = contact_a.id AND - address.is_primary = 1 ) -LEFT JOIN civicrm_state_province state_province ON state_province.id = address.state_province_id -LEFT JOIN civicrm_country country ON country.id = address.country_id {$this->_aclFrom} -"; - - // This prevents duplicate rows when contacts have more than one tag any you select "any tag" - if ($this->_tag) { - $f .= " -LEFT JOIN civicrm_entity_tag t ON (t.entity_table='civicrm_contact' AND contact_a.id = t.entity_id) -"; + public function sql( + $selectClause, + $offset = 0, + $rowcount = 0, + $sort = NULL, + $includeContactIDs = FALSE, + $groupBy = NULL + ) { + + $isCountOnly = FALSE; + if ($selectClause === 'count(distinct contact_a.id) as total') { + $isCountOnly = TRUE; } - if ($this->_group) { - $f .= " -LEFT JOIN civicrm_group_contact cgc ON ( cgc.contact_id = contact_a.id AND cgc.status = 'Added') -"; + + $searchParams = [ + ['prox_distance_unit', '=', $this->_formValues['prox_distance_unit'], 0, 0], + ['prox_distance', '=', $this->_formValues['distance'], 0, 0], + ['prox_geo_code_1', '=', $this->_formValues['geo_code_1'], 0, 0], + ['prox_geo_code_2', '=', $this->_formValues['geo_code_2'], 0, 0], + ]; + if (!empty($this->_formValues['group'])) { + $searchParams[] = ['group', '=', ['IN', (array) $this->_formValues['group']][1], 0, 0]; + } + if (!empty($this->_formValues['tag'])) { + $searchParams[] = ['contact_tags', '=', ['IN', (array) $this->_formValues['tag']][1], 0, 0]; } - return $f; + $display = array_fill_keys(['city', 'state_province', 'country', 'postal_code', 'street_address', 'display_name', 'sort_name'], 1); + if ($selectClause === 'contact_a.id as contact_id') { + // Not sure when this would happen but calling all with 'justIDs' gets us here. + $display = ['contact_id' => 1]; + } + + $this->_query = new CRM_Contact_BAO_Query($searchParams, $display); + return $this->_query->searchQuery( + $offset, + $rowcount, + $sort, + $isCountOnly, + $includeContactIDs, + FALSE, + $isCountOnly, + $returnQuery = TRUE + ); + + } + + /** + * @return string + */ + public function from() { + //unused + return ''; } /** @@ -231,33 +255,8 @@ public function from() { * @return string */ public function where($includeContactIDs = FALSE) { - $params = array(); - $clause = array(); - - $where = CRM_Contact_BAO_ProximityQuery::where($this->_latitude, - $this->_longitude, - $this->_distance, - 'address' - ); - - if ($this->_tag) { - $where .= " -AND t.tag_id = {$this->_tag} -"; - } - if ($this->_group) { - $where .= " -AND cgc.group_id = {$this->_group} - "; - } - - $where .= " AND contact_a.is_deleted != 1 "; - - if ($this->_aclWhere) { - $where .= " AND {$this->_aclWhere} "; - } - - return $this->whereClause($where, $params); + //unused + return ''; } /** @@ -277,7 +276,7 @@ public function setDefaultValues() { $config = CRM_Core_Config::singleton(); $countryDefault = $config->defaultContactCountry; $stateprovinceDefault = $config->defaultContactStateProvince; - $defaults = array(); + $defaults = []; if ($countryDefault) { if ($countryDefault == '1228' || $countryDefault == '1226') { @@ -314,10 +313,42 @@ public function setTitle($title) { } /** - * @param string $tableAlias + * Validate form input. + * + * @param array $fields + * @param array $files + * @param CRM_Core_Form $self + * + * @return array + * Input errors from the form. + */ + public function formRule($fields, $files, $self) { + $this->addGeocodingData($fields); + + if (!is_numeric(CRM_Utils_Array::value('geo_code_1', $fields)) || + !is_numeric(CRM_Utils_Array::value('geo_code_2', $fields)) || + !isset($fields['distance']) + ) { + $errorMessage = ts('Could not determine co-ordinates for provided data'); + return array_fill_keys(['street_address', 'city', 'postal_code', 'country_id', 'state_province_id'], $errorMessage); + } + return []; + } + + /** + * Add the geocoding data to the fields supplied. + * + * @param array $fields */ - public function buildACLClause($tableAlias = 'contact') { - list($this->_aclFrom, $this->_aclWhere) = CRM_Contact_BAO_Contact_Permission::cacheClause($tableAlias); + protected function addGeocodingData(&$fields) { + if (!empty($fields['country_id'])) { + $fields['country'] = CRM_Core_PseudoConstant::country($fields['country_id']); + } + + if (!empty($fields['state_province_id'])) { + $fields['state_province'] = CRM_Core_PseudoConstant::stateProvince($fields['state_province_id']); + } + CRM_Core_BAO_Address::addGeocoderData($fields); } } diff --git a/CRM/Contact/Form/Search/Custom/RandomSegment.php b/CRM/Contact/Form/Search/Custom/RandomSegment.php index 74465434e643..36addc77c6c9 100644 --- a/CRM/Contact/Form/Search/Custom/RandomSegment.php +++ b/CRM/Contact/Form/Search/Custom/RandomSegment.php @@ -1,9 +1,9 @@ _columns = array( + $this->_columns = [ ts('Contact ID') => 'contact_id', ts('Contact Type') => 'contact_type', ts('Name') => 'sort_name', ts('Email') => 'email', - ); + ]; $this->initialize(); } @@ -87,12 +87,12 @@ public function buildForm(&$form) { $groups = CRM_Core_PseudoConstant::nestedGroup(); - $select2style = array( + $select2style = [ 'multiple' => TRUE, 'style' => 'width: 100%; max-width: 60em;', 'class' => 'crm-select2', 'placeholder' => ts('- select -'), - ); + ]; $form->add('select', 'includeGroups', ts('Include Group(s)'), @@ -114,7 +114,7 @@ public function buildForm(&$form) { * if you are using the standard template, this array tells the template what elements * are part of the search criteria */ - $form->assign('elements', array('segmentSize', 'includeGroups', 'excludeGroups')); + $form->assign('elements', ['segmentSize', 'includeGroups', 'excludeGroups']); } /** @@ -162,7 +162,7 @@ public function from() { $this->_tableName = "civicrm_temp_custom_{$randomNum}"; //block for Group search - $smartGroup = array(); + $smartGroup = []; $group = new CRM_Contact_DAO_Group(); $group->is_active = 1; $group->find(); @@ -372,6 +372,7 @@ public function setTitle($title) { /** * @return mixed */ + /** * @return mixed */ diff --git a/CRM/Contact/Form/Search/Custom/Sample.php b/CRM/Contact/Form/Search/Custom/Sample.php index 9ef5b0c07375..a148f5ca3d48 100644 --- a/CRM/Contact/Form/Search/Custom/Sample.php +++ b/CRM/Contact/Form/Search/Custom/Sample.php @@ -1,9 +1,9 @@ _stateID = CRM_Utils_Request::retrieve('stateID', 'Integer', - CRM_Core_DAO::$_nullObject - ); + $this->_stateID = CRM_Utils_Request::retrieve('stateID', 'Integer'); if ($this->_stateID) { $formValues['state_province_id'] = $this->_stateID; } } - $this->_columns = array( + $this->_columns = [ ts('Contact ID') => 'contact_id', ts('Contact Type') => 'contact_type', ts('Name') => 'sort_name', ts('State') => 'state_province', - ); + ]; } /** @@ -71,7 +70,7 @@ public function buildForm(&$form) { TRUE ); - $stateProvince = array('' => ts('- any state/province -')) + CRM_Core_PseudoConstant::stateProvince(); + $stateProvince = ['' => ts('- any state/province -')] + CRM_Core_PseudoConstant::stateProvince(); $form->addElement('select', 'state_province_id', ts('State/Province'), $stateProvince); /** @@ -83,17 +82,17 @@ public function buildForm(&$form) { * if you are using the standard template, this array tells the template what elements * are part of the search criteria */ - $form->assign('elements', array('household_name', 'state_province_id')); + $form->assign('elements', ['household_name', 'state_province_id']); } /** * @return array */ public function summary() { - $summary = array( + $summary = [ 'summary' => 'This is a summary', 'total' => 50.0, - ); + ]; return $summary; } @@ -160,11 +159,11 @@ public function from() { * @return string */ public function where($includeContactIDs = FALSE) { - $params = array(); + $params = []; $where = "contact_a.contact_type = 'Household'"; $count = 1; - $clause = array(); + $clause = []; $name = CRM_Utils_Array::value('household_name', $this->_formValues ); @@ -172,7 +171,7 @@ public function where($includeContactIDs = FALSE) { if (strpos($name, '%') === FALSE) { $name = "%{$name}%"; } - $params[$count] = array($name, 'String'); + $params[$count] = [$name, 'String']; $clause[] = "contact_a.household_name LIKE %{$count}"; $count++; } @@ -187,7 +186,7 @@ public function where($includeContactIDs = FALSE) { } if ($state) { - $params[$count] = array($state, 'Integer'); + $params[$count] = [$state, 'Integer']; $clause[] = "state_province.id = %{$count}"; } @@ -213,7 +212,7 @@ public function templateFile() { * @return array */ public function setDefaultValues() { - return array_merge(array('household_name' => ''), $this->_formValues); + return array_merge(['household_name' => ''], $this->_formValues); } /** diff --git a/CRM/Contact/Form/Search/Custom/TagContributions.php b/CRM/Contact/Form/Search/Custom/TagContributions.php index d7d153f01d05..df0e60488fe1 100644 --- a/CRM/Contact/Form/Search/Custom/TagContributions.php +++ b/CRM/Contact/Form/Search/Custom/TagContributions.php @@ -1,9 +1,9 @@ _formValues = $formValues; + $this->_formValues = self::formatSavedSearchFields($formValues); $this->_permissionedComponent = 'CiviContribute'; /** * Define the columns for search result rows */ - $this->_columns = array( + $this->_columns = [ ts('Contact ID') => 'contact_id', ts('Full Name') => 'sort_name', ts('First Name') => 'first_name', ts('Last Name') => 'last_name', ts('Tag') => 'tag_name', ts('Totals') => 'amount', - ); + ]; } /** @@ -73,16 +73,16 @@ public function buildForm(&$form) { * Define the search form fields here */ - $form->addDate('start_date', ts('Contribution Date From'), FALSE, array('formatType' => 'custom')); - $form->addDate('end_date', ts('...through'), FALSE, array('formatType' => 'custom')); - $tag = array('' => ts('- any tag -')) + CRM_Core_PseudoConstant::get('CRM_Core_DAO_EntityTag', 'tag_id', array('onlyActive' => FALSE)); + $form->add('datepicker', 'start_date', ts('Contribution Date From'), [], FALSE, ['time' => FALSE]); + $form->add('datepicker', 'end_date', ts('...through'), [], FALSE, ['time' => FALSE]); + $tag = ['' => ts('- any tag -')] + CRM_Core_PseudoConstant::get('CRM_Core_DAO_EntityTag', 'tag_id', ['onlyActive' => FALSE]); $form->addElement('select', 'tag', ts('Tagged'), $tag); /** * If you are using the sample template, this array tells the template fields to render * for the search form. */ - $form->assign('elements', array('start_date', 'end_date', 'tag')); + $form->assign('elements', ['start_date', 'end_date', 'tag']); } /** @@ -169,25 +169,24 @@ public function from() { * WHERE clause is an array built from any required JOINS plus conditional filters based on search criteria field values * */ + /** * @param bool $includeContactIDs * * @return string */ public function where($includeContactIDs = FALSE) { - $clauses = array(); + $clauses = []; $clauses[] = "contact_a.contact_type = 'Individual'"; $clauses[] = "civicrm_contribution.contact_id = contact_a.id"; - $startDate = CRM_Utils_Date::processDate($this->_formValues['start_date']); - if ($startDate) { - $clauses[] = "civicrm_contribution.receive_date >= $startDate"; + if ($this->_formValues['start_date']) { + $clauses[] = "civicrm_contribution.receive_date >= '{$this->_formValues['start_date']} 00:00:00'"; } - $endDate = CRM_Utils_Date::processDate($this->_formValues['end_date']); - if ($endDate) { - $clauses[] = "civicrm_contribution.receive_date <= $endDate"; + if ($this->_formValues['end_date']) { + $clauses[] = "civicrm_contribution.receive_date <= '{$this->_formValues['end_date']} 23:59:59'"; } $tag = CRM_Utils_Array::value('tag', $this->_formValues); @@ -200,7 +199,7 @@ public function where($includeContactIDs = FALSE) { } if ($includeContactIDs) { - $contactIDs = array(); + $contactIDs = []; foreach ($this->_formValues as $id => $value) { if ($value && substr($id, 0, CRM_Core_Form::CB_PREFIX_LEN) == CRM_Core_Form::CB_PREFIX @@ -220,7 +219,6 @@ public function where($includeContactIDs = FALSE) { return implode(' AND ', $clauses); } - /* * Functions below generally don't need to be modified */ @@ -282,4 +280,27 @@ public function buildACLClause($tableAlias = 'contact') { list($this->_aclFrom, $this->_aclWhere) = CRM_Contact_BAO_Contact_Permission::cacheClause($tableAlias); } + /** + * Format saved search fields for this custom group. + * + * Note this is a function to facilitate the transition to jcalendar for + * saved search groups. In time it can be stripped out again. + * + * @param array $formValues + * + * @return array + */ + public static function formatSavedSearchFields($formValues) { + $dateFields = [ + 'start_date', + 'end_date', + ]; + foreach ($formValues as $element => $value) { + if (in_array($element, $dateFields) && !empty($value)) { + $formValues[$element] = date('Y-m-d', strtotime($value)); + } + } + return $formValues; + } + } diff --git a/CRM/Contact/Form/Search/Custom/ZipCodeRange.php b/CRM/Contact/Form/Search/Custom/ZipCodeRange.php index 33f26c07b21f..3b341bcb2818 100644 --- a/CRM/Contact/Form/Search/Custom/ZipCodeRange.php +++ b/CRM/Contact/Form/Search/Custom/ZipCodeRange.php @@ -1,9 +1,9 @@ _columns = array( + $this->_columns = [ // If possible, don't use aliases for the columns you select. // You can prefix columns with table aliases, if needed. // @@ -53,7 +54,7 @@ public function __construct(&$formValues) { ts('Name') => 'sort_name', ts('Email') => 'email', ts('Zip') => 'postal_code', - ); + ]; } /** @@ -81,14 +82,14 @@ public function buildForm(&$form) { * if you are using the standard template, this array tells the template what elements * are part of the search criteria */ - $form->assign('elements', array('postal_code_low', 'postal_code_high')); + $form->assign('elements', ['postal_code_low', 'postal_code_high']); } /** * @return array */ public function summary() { - $summary = array(); + $summary = []; return $summary; } @@ -161,7 +162,7 @@ public function from() { * @return string */ public function where($includeContactIDs = FALSE) { - $params = array(); + $params = []; $low = CRM_Utils_Array::value('postal_code_low', $this->_formValues @@ -179,10 +180,10 @@ public function where($includeContactIDs = FALSE) { } $where = "ROUND(address.postal_code) >= %1 AND ROUND(address.postal_code) <= %2"; - $params = array( - 1 => array(trim($low), 'Integer'), - 2 => array(trim($high), 'Integer'), - ); + $params = [ + 1 => [trim($low), 'Integer'], + 2 => [trim($high), 'Integer'], + ]; if ($this->_aclWhere) { $where .= " AND {$this->_aclWhere} "; diff --git a/CRM/Contact/Form/Search/Interface.php b/CRM/Contact/Form/Search/Interface.php index 7b806759fb18..1728708bc7bd 100644 --- a/CRM/Contact/Form/Search/Interface.php +++ b/CRM/Contact/Form/Search/Interface.php @@ -1,9 +1,9 @@ _contactIds = []; + $form->_contactTypes = []; - $form->_contactIds = array(); - $form->_contactTypes = array(); + $useTable = (CRM_Utils_System::getClassName($form->controller->getStateMachine()) == 'CRM_Export_StateMachine_Standalone'); + + $isStandAlone = in_array('task', $form->urlPath) || in_array('standalone', $form->urlPath); + if ($isStandAlone) { + list($form->_task, $title) = CRM_Contact_Task::getTaskAndTitleByClass(get_class($form)); + if (!array_key_exists($form->_task, CRM_Contact_Task::permissionedTaskTitles(CRM_Core_Permission::getPermission()))) { + CRM_Core_Error::statusBounce(ts('You do not have permission to access this page.')); + } + $form->_contactIds = explode(',', CRM_Utils_Request::retrieve('cids', 'CommaSeparatedIntegers', $form, TRUE)); + if (empty($form->_contactIds)) { + CRM_Core_Error::statusBounce(ts('No Contacts Selected')); + } + $form->setTitle($title); + } // get the submitted values of the search form // we'll need to get fv from either search or adv search in the future @@ -116,7 +132,7 @@ public static function preProcessCommon(&$form, $useTable = FALSE) { self::$_searchFormValues = $form->controller->exportValues('Custom'); $fragment .= '/custom'; } - else { + elseif (!$isStandAlone) { self::$_searchFormValues = $form->controller->exportValues('Basic'); } @@ -138,35 +154,30 @@ public static function preProcessCommon(&$form, $useTable = FALSE) { $form->assign('taskName', CRM_Utils_Array::value($form->_task, $crmContactTaskTasks)); if ($useTable) { - $form->_componentTable = CRM_Core_DAO::createTempTableName('civicrm_task_action', TRUE, $qfKey); - $sql = " DROP TABLE IF EXISTS {$form->_componentTable}"; - CRM_Core_DAO::executeQuery($sql); - - $sql = "CREATE TABLE {$form->_componentTable} ( contact_id int primary key) ENGINE=MyISAM DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci"; - CRM_Core_DAO::executeQuery($sql); + $tempTable = CRM_Utils_SQL_TempTable::build()->setCategory('tskact')->setDurable()->setId($qfKey)->setUtf8(); + $form->_componentTable = $tempTable->getName(); + $tempTable->drop(); + $tempTable->createWithColumns('contact_id int primary key'); } // all contacts or action = save a search if ((CRM_Utils_Array::value('radio_ts', self::$_searchFormValues) == 'ts_all') || ($form->_task == CRM_Contact_Task::SAVE_SEARCH) ) { - $sortByCharacter = $form->get('sortByCharacter'); - $cacheKey = ($sortByCharacter && $sortByCharacter != 'all') ? "{$cacheKey}_alphabet" : $cacheKey; - // since we don't store all contacts in prevnextcache, when user selects "all" use query to retrieve contacts // rather than prevnext cache table for most of the task actions except export where we rebuild query to fetch // final result set if ($useTable) { - $allCids = CRM_Core_BAO_PrevNextCache::getSelection($cacheKey, "getall"); + $allCids = Civi::service('prevnext')->getSelection($cacheKey, "getall"); } else { - $allCids[$cacheKey] = $form->getContactIds(); + $allCids[$cacheKey] = self::getContactIds($form); } - $form->_contactIds = array(); + $form->_contactIds = []; if ($useTable) { $count = 0; - $insertString = array(); + $insertString = []; foreach ($allCids[$cacheKey] as $cid => $ignore) { $count++; $insertString[] = " ( {$cid} ) "; @@ -174,7 +185,7 @@ public static function preProcessCommon(&$form, $useTable = FALSE) { $string = implode(',', $insertString); $sql = "REPLACE INTO {$form->_componentTable} ( contact_id ) VALUES $string"; CRM_Core_DAO::executeQuery($sql); - $insertString = array(); + $insertString = []; } } if (!empty($insertString)) { @@ -183,7 +194,7 @@ public static function preProcessCommon(&$form, $useTable = FALSE) { CRM_Core_DAO::executeQuery($sql); } } - else { + elseif (empty($form->_contactIds)) { // filter duplicates here // CRM-7058 // might be better to do this in the query, but that logic is a bit complex @@ -198,7 +209,7 @@ public static function preProcessCommon(&$form, $useTable = FALSE) { elseif (CRM_Utils_Array::value('radio_ts', self::$_searchFormValues) == 'ts_sel') { // selected contacts only // need to perform action on only selected contacts - $insertString = array(); + $insertString = []; // refire sql in case of custom search if ($form->_action == CRM_Core_Action::COPY) { @@ -218,7 +229,7 @@ public static function preProcessCommon(&$form, $useTable = FALSE) { } else { // fetching selected contact ids of passed cache key - $selectedCids = CRM_Core_BAO_PrevNextCache::getSelection($cacheKey); + $selectedCids = Civi::service('prevnext')->getSelection($cacheKey); foreach ($selectedCids[$cacheKey] as $selectedCid => $ignore) { if ($useTable) { $insertString[] = " ( {$selectedCid} ) "; @@ -258,7 +269,7 @@ public static function preProcessCommon(&$form, $useTable = FALSE) { ) { $sel = CRM_Utils_Array::value('radio_ts', self::$_searchFormValues); $form->assign('searchtype', $sel); - $result = CRM_Core_BAO_PrevNextCache::getSelectedContacts(); + $result = self::getSelectedContactNames(); $form->assign("value", $result); } @@ -271,47 +282,50 @@ public static function preProcessCommon(&$form, $useTable = FALSE) { } /** - * Get the contact id for custom search. + * Get the contact ids for: + * - "Select Records: All xx records" + * - custom search (FIXME: does this still apply to custom search?). + * When we call this function we are not using the prev/next cache * - * we are not using prev/next table in case of custom search + * @param $form CRM_Core_Form + * + * @return array $contactIds */ - public function getContactIds() { + public static function getContactIds($form) { // need to perform action on all contacts // fire the query again and get the contact id's + display name $sortID = NULL; - if ($this->get(CRM_Utils_Sort::SORT_ID)) { - $sortID = CRM_Utils_Sort::sortIDValue($this->get(CRM_Utils_Sort::SORT_ID), - $this->get(CRM_Utils_Sort::SORT_DIRECTION) + if ($form->get(CRM_Utils_Sort::SORT_ID)) { + $sortID = CRM_Utils_Sort::sortIDValue($form->get(CRM_Utils_Sort::SORT_ID), + $form->get(CRM_Utils_Sort::SORT_DIRECTION) ); } - $selectorName = $this->controller->selectorName(); - require_once str_replace('_', DIRECTORY_SEPARATOR, $selectorName) . '.php'; + $selectorName = $form->controller->selectorName(); - $fv = $this->get('formValues'); - $customClass = $this->get('customSearchClass'); - require_once 'CRM/Core/BAO/Mapping.php'; + $fv = $form->get('formValues'); + $customClass = $form->get('customSearchClass'); $returnProperties = CRM_Core_BAO_Mapping::returnProperties(self::$_searchFormValues); $selector = new $selectorName($customClass, $fv, NULL, $returnProperties); - $params = $this->get('queryParams'); + $params = $form->get('queryParams'); // fix for CRM-5165 - $sortByCharacter = $this->get('sortByCharacter'); + $sortByCharacter = $form->get('sortByCharacter'); if ($sortByCharacter && $sortByCharacter != 1) { - $params[] = array('sortByCharacter', '=', $sortByCharacter, 0, 0); + $params[] = ['sortByCharacter', '=', $sortByCharacter, 0, 0]; } - $queryOperator = $this->get('queryOperator'); + $queryOperator = $form->get('queryOperator'); if (!$queryOperator) { $queryOperator = 'AND'; } - $dao = $selector->contactIDQuery($params, $this->_action, $sortID, + $dao = $selector->contactIDQuery($params, $sortID, CRM_Utils_Array::value('display_relationship_type', $fv), $queryOperator ); - $contactIds = array(); + $contactIds = []; while ($dao->fetch()) { $contactIds[$dao->contact_id] = $dao->contact_id; } @@ -319,7 +333,6 @@ public function getContactIds() { return $contactIds; } - /** * Set default values for the form. Relationship that in edit/view action. * @@ -328,7 +341,7 @@ public function getContactIds() { * @return array */ public function setDefaultValues() { - $defaults = array(); + $defaults = []; return $defaults; } @@ -364,19 +377,18 @@ public function postProcess() { * @param bool $submitOnce */ public function addDefaultButtons($title, $nextType = 'next', $backType = 'back', $submitOnce = FALSE) { - $this->addButtons(array( - array( - 'type' => $nextType, - 'name' => $title, - 'isDefault' => TRUE, - ), - array( - 'type' => $backType, - 'name' => ts('Cancel'), - 'icon' => 'fa-times', - ), - ) - ); + $this->addButtons([ + [ + 'type' => $nextType, + 'name' => $title, + 'isDefault' => TRUE, + ], + [ + 'type' => $backType, + 'name' => ts('Cancel'), + 'icon' => 'fa-times', + ], + ]); } /** @@ -402,10 +414,10 @@ public function mergeContactIdsByHousehold() { // Get Head of Household & Household Member relationships $relationKeyMOH = CRM_Utils_Array::key('Household Member of', $contactRelationshipTypes); $relationKeyHOH = CRM_Utils_Array::key('Head of Household for', $contactRelationshipTypes); - $householdRelationshipTypes = array( + $householdRelationshipTypes = [ $relationKeyMOH => $contactRelationshipTypes[$relationKeyMOH], $relationKeyHOH => $contactRelationshipTypes[$relationKeyHOH], - ); + ]; $relID = implode(',', $this->_contactIds); @@ -428,7 +440,7 @@ public function mergeContactIdsByHousehold() { $today = date('Ymd'); $relationActive = " AND (crel.is_active = 1 AND ( crel.end_date is NULL OR crel.end_date >= {$today} ) )"; $relationWhere = " WHERE contact_household.is_deleted = 0 AND crel.{$contactA} IN ( {$relID} ) {$relationActive}"; - $relationGroupBy = " GROUP BY crel.{$contactA}"; + $relationGroupBy = " GROUP BY crel.{$contactA}, contact_household.id"; $relationQueryString = "$relationSelect $relationFrom $relationWhere $relationGroupBy"; $householdsDAO = CRM_Core_DAO::executeQuery($relationQueryString); @@ -443,6 +455,43 @@ public function mergeContactIdsByHousehold() { } $householdsDAO->free(); } + + // If contact list has changed, households will probably be at the end of + // the list. Sort it again by sort_name. + if (implode(',', $this->_contactIds) != $relID) { + $result = civicrm_api3('Contact', 'get', [ + 'return' => ['id'], + 'id' => ['IN' => $this->_contactIds], + 'options' => [ + 'limit' => 0, + 'sort' => "sort_name", + ], + ]); + $this->_contactIds = array_keys($result['values']); + } + } + + /** + * @return array + * List of contact names. + * NOTE: These are raw values from the DB. In current data-model, that means + * they are pre-encoded HTML. + */ + private static function getSelectedContactNames() { + $qfKey = CRM_Utils_Request::retrieve('qfKey', 'String'); + $cacheKey = "civicrm search {$qfKey}"; + + $cids = []; + // Gymanstic time! + foreach (Civi::service('prevnext')->getSelection($cacheKey) as $cacheKey => $values) { + $cids = array_unique(array_merge($cids, array_keys($values))); + } + + $result = CRM_Utils_SQL_Select::from('civicrm_contact') + ->where('id IN (#cids)', ['cids' => $cids]) + ->execute() + ->fetchMap('id', 'sort_name'); + return $result; } /** @@ -457,18 +506,18 @@ public function createHiddenGroup() { $searchParams = $this->controller->exportValues(); if ($searchParams['radio_ts'] == 'ts_sel') { // Create a static group. - - $randID = md5(time() . rand(1, 1000)); // groups require a unique name + // groups require a unique name + $randID = md5(time() . rand(1, 1000)); $grpTitle = "Hidden Group {$randID}"; $grpID = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Group', $grpTitle, 'id', 'title'); if (!$grpID) { - $groupParams = array( + $groupParams = [ 'title' => $grpTitle, 'is_active' => 1, 'is_hidden' => 1, - 'group_type' => array('2' => 1), - ); + 'group_type' => ['2' => 1], + ]; $group = CRM_Contact_BAO_Group::create($groupParams); $grpID = $group->id; @@ -476,32 +525,33 @@ public function createHiddenGroup() { CRM_Contact_BAO_GroupContact::addContactsToGroup($this->_contactIds, $group->id); $newGroupTitle = "Hidden Group {$grpID}"; - $groupParams = array( + $groupParams = [ 'id' => $grpID, 'name' => CRM_Utils_String::titleToVar($newGroupTitle), 'title' => $newGroupTitle, - 'group_type' => array('2' => 1), - ); - $group = CRM_Contact_BAO_Group::create($groupParams); + 'group_type' => ['2' => 1], + ]; + CRM_Contact_BAO_Group::create($groupParams); } // note at this point its a static group - return array($grpID, NULL); + return [$grpID, NULL]; } else { // Create a smart group. - $ssId = $this->get('ssID'); - $hiddenSmartParams = array( - 'group_type' => array('2' => 1), - 'form_values' => $this->get('formValues'), + $hiddenSmartParams = [ + 'group_type' => ['2' => 1], + // queryParams have been preprocessed esp WRT any entity reference fields - see + + // https://github.com/civicrm/civicrm-core/pull/13250 + 'form_values' => $this->get('queryParams'), 'saved_search_id' => $ssId, 'search_custom_id' => $this->get('customSearchID'), 'search_context' => $this->get('context'), - ); + ]; list($smartGroupId, $savedSearchId) = CRM_Contact_BAO_Group::createHiddenSmartGroup($hiddenSmartParams); - return array($smartGroupId, $savedSearchId); + return [$smartGroupId, $savedSearchId]; } } diff --git a/CRM/Contact/Form/Task/AddToGroup.php b/CRM/Contact/Form/Task/AddToGroup.php index 74e7a6c4a529..2958f6e98eff 100644 --- a/CRM/Contact/Form/Task/AddToGroup.php +++ b/CRM/Contact/Form/Task/AddToGroup.php @@ -1,9 +1,9 @@ _id) { - $this->addRadio('group_option', ts('Group Options'), $options, array('onclick' => "return showElements();")); + $this->addRadio('group_option', ts('Group Options'), $options, ['onclick' => "return showElements();"]); $this->add('text', 'title', ts('Group Name:') . ' ', CRM_Core_DAO::getAttribute('CRM_Contact_DAO_Group', 'title') ); $this->addRule('title', ts('Name already exists in Database.'), - 'objectExists', array('CRM_Contact_DAO_Group', $this->_id, 'title') + 'objectExists', ['CRM_Contact_DAO_Group', $this->_id, 'title'] ); $this->add('textarea', 'description', ts('Description:') . ' ', @@ -115,9 +115,9 @@ public function buildQuickForm() { } // add select for groups - $group = array('' => ts('- select group -')) + CRM_Core_PseudoConstant::nestedGroup(); + $group = ['' => ts('- select group -')] + CRM_Core_PseudoConstant::nestedGroup(); - $groupElement = $this->add('select', 'group_id', ts('Select Group'), $group, FALSE, array('class' => 'crm-select2 huge')); + $groupElement = $this->add('select', 'group_id', ts('Select Group'), $group, FALSE, ['class' => 'crm-select2 huge']); $this->_title = $group[$this->_id]; @@ -125,13 +125,13 @@ public function buildQuickForm() { $groupElement->freeze(); // also set the group title - $groupValues = array('id' => $this->_id, 'title' => $this->_title); + $groupValues = ['id' => $this->_id, 'title' => $this->_title]; $this->assign_by_ref('group', $groupValues); } // Set dynamic page title for 'Add Members Group (confirm)' if ($this->_id) { - CRM_Utils_System::setTitle(ts('Add Contacts: %1', array(1 => $this->_title))); + CRM_Utils_System::setTitle(ts('Add Contacts: %1', [1 => $this->_title])); } else { CRM_Utils_System::setTitle(ts('Add Contacts to A Group')); @@ -148,7 +148,7 @@ public function buildQuickForm() { * the default array reference */ public function setDefaultValues() { - $defaults = array(); + $defaults = []; if ($this->_context === 'amtg') { $defaults['group_id'] = $this->_id; @@ -162,7 +162,7 @@ public function setDefaultValues() { * Add local and global form rules. */ public function addRules() { - $this->addFormRule(array('CRM_Contact_Form_task_AddToGroup', 'formRule')); + $this->addFormRule(['CRM_Contact_Form_task_AddToGroup', 'formRule']); } /** @@ -174,7 +174,7 @@ public function addRules() { * list of errors to be posted back to the form */ public static function formRule($params) { - $errors = array(); + $errors = []; if (!empty($params['group_option']) && empty($params['title'])) { $errors['title'] = "Group Name is a required field"; @@ -193,7 +193,7 @@ public function postProcess() { $params = $this->controller->exportValues(); $groupOption = CRM_Utils_Array::value('group_option', $params, NULL); if ($groupOption) { - $groupParams = array(); + $groupParams = []; $groupParams['title'] = $params['title']; $groupParams['description'] = $params['description']; $groupParams['visibility'] = "User and User Admin Only"; @@ -219,24 +219,24 @@ public function postProcess() { list($total, $added, $notAdded) = CRM_Contact_BAO_GroupContact::addContactsToGroup($this->_contactIds, $groupID); - $status = array( - ts('%count contact added to group', array( - 'count' => $added, - 'plural' => '%count contacts added to group', - )), - ); + $status = [ + ts('%count contact added to group', [ + 'count' => $added, + 'plural' => '%count contacts added to group', + ]), + ]; if ($notAdded) { - $status[] = ts('%count contact was already in group', array( - 'count' => $notAdded, - 'plural' => '%count contacts were already in group', - )); + $status[] = ts('%count contact was already in group', [ + 'count' => $notAdded, + 'plural' => '%count contacts were already in group', + ]); } $status = '
  • ' . implode('
  • ', $status) . '
'; - CRM_Core_Session::setStatus($status, ts('Added Contact to %1', array( - 1 => $groupName, - 'count' => $added, - 'plural' => 'Added Contacts to %1', - )), 'success', array('expires' => 0)); + CRM_Core_Session::setStatus($status, ts('Added Contact to %1', [ + 1 => $groupName, + 'count' => $added, + 'plural' => 'Added Contacts to %1', + ]), 'success', ['expires' => 0]); if ($this->_context === 'amtg') { CRM_Core_Session::singleton() diff --git a/CRM/Contact/Form/Task/AddToHousehold.php b/CRM/Contact/Form/Task/AddToHousehold.php index 24481b654da8..c72e9a096522 100644 --- a/CRM/Contact/Form/Task/AddToHousehold.php +++ b/CRM/Contact/Form/Task/AddToHousehold.php @@ -1,9 +1,9 @@ addElement('text', 'name', ts('Find Target Household')); - - $this->add('select', 'relationship_type_id', ts('Relationship Type'), - array( - '' => ts('- select -'), - ) + - CRM_Contact_BAO_Relationship::getRelationType("Household"), TRUE - ); - - $searchRows = $this->get('searchRows'); - $searchCount = $this->get('searchCount'); - if ($searchRows) { - $checkBoxes = array(); - $checkFlag = 0; - foreach ($searchRows as $id => $row) { - if (!$checkFlag) { - $checkFlag = $id; - } - $checkBoxes[$id] = $this->createElement('radio', NULL, NULL, NULL, $id); - } - $this->addGroup($checkBoxes, 'contact_check'); - if ($checkFlag) { - $checkBoxes[$checkFlag]->setChecked(TRUE); - } - $this->assign('searchRows', $searchRows); - } - - $this->assign('searchCount', $searchCount); - $this->assign('searchDone', $this->get('searchDone')); - $this->assign('contact_type_display', ts('Household')); - $this->addElement('submit', $this->getButtonName('refresh'), ts('Search'), array('class' => 'crm-form-submit')); - $this->addElement('submit', $this->getButtonName('cancel'), ts('Cancel'), array('class' => 'crm-form-submit')); - - $this->addButtons(array( - array( - 'type' => 'next', - 'name' => ts('Add to Household'), - 'isDefault' => TRUE, - ), - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - ), - ) - ); + $this->set('contactType', 'Household'); + $this->assign('contactType', 'Household'); + parent::buildQuickForm(); } /** * Process the form after the input has been submitted and validated. */ public function postProcess() { - - // store the submitted values in an array - $this->params = $this->controller->exportValues($this->_name); - - $this->set('searchDone', 0); - if (!empty($_POST['_qf_AddToHousehold_refresh'])) { - $searchParams['contact_type'] = 'Household'; - $searchParams['rel_contact'] = $this->params['name']; - $this->search($this, $searchParams); - $this->set('searchDone', 1); - return; - } - - $this->addRelationships(); + parent::postProcess(); } } diff --git a/CRM/Contact/Form/Task/AddToIndividual.php b/CRM/Contact/Form/Task/AddToIndividual.php new file mode 100644 index 000000000000..f8f12aa4d907 --- /dev/null +++ b/CRM/Contact/Form/Task/AddToIndividual.php @@ -0,0 +1,55 @@ +set('contactType', 'Individual'); + $this->assign('contactType', 'Individual'); + parent::buildQuickForm(); + } + + /** + * Process the form after the input has been submitted and validated. + */ + public function postProcess() { + parent::postProcess(); + } + +} diff --git a/CRM/Contact/Form/Task/AddToOrganization.php b/CRM/Contact/Form/Task/AddToOrganization.php index 279eb519eb96..089dbe24dbe7 100644 --- a/CRM/Contact/Form/Task/AddToOrganization.php +++ b/CRM/Contact/Form/Task/AddToOrganization.php @@ -1,9 +1,9 @@ addElement('text', 'name', ts('Find Target Organization')); - - $this->add('select', - 'relationship_type_id', - ts('Relationship Type'), - array( - '' => ts('- select -'), - ) + - CRM_Contact_BAO_Relationship::getRelationType("Organization"), TRUE - ); - - $searchRows = $this->get('searchRows'); - $searchCount = $this->get('searchCount'); - if ($searchRows) { - $checkBoxes = array(); - $chekFlag = 0; - foreach ($searchRows as $id => $row) { - if (!$chekFlag) { - $chekFlag = $id; - } - - $checkBoxes[$id] = $this->createElement('radio', NULL, NULL, NULL, $id); - } - - $this->addGroup($checkBoxes, 'contact_check'); - if ($chekFlag) { - $checkBoxes[$chekFlag]->setChecked(TRUE); - } - $this->assign('searchRows', $searchRows); - } - - $this->assign('searchCount', $searchCount); - $this->assign('searchDone', $this->get('searchDone')); - $this->assign('contact_type_display', ts('Organization')); - $this->addElement('submit', $this->getButtonName('refresh'), ts('Search'), array('class' => 'crm-form-submit')); - $this->addElement('submit', $this->getButtonName('cancel'), ts('Cancel'), array('class' => 'crm-form-submit')); - - $this->addButtons(array( - array( - 'type' => 'next', - 'name' => ts('Add to Organization'), - 'isDefault' => TRUE, - ), - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - ), - ) - ); + $this->set('contactType', 'Organization'); + $this->assign('contactType', 'Organization'); + parent::buildQuickForm(); } /** * Process the form after the input has been submitted and validated. */ public function postProcess() { - // store the submitted values in an array - $this->params = $this->controller->exportValues($this->_name); - - $this->set('searchDone', 0); - if (!empty($_POST['_qf_AddToOrganization_refresh'])) { - $searchParams['contact_type'] = 'Organization'; - $searchParams['rel_contact'] = $this->params['name']; - $this->search($this, $searchParams); - $this->set('searchDone', 1); - return; - } - - $this->addRelationships(); + parent::postProcess(); } } diff --git a/CRM/Contact/Form/Task/AddToParentClass.php b/CRM/Contact/Form/Task/AddToParentClass.php index 43e7c507f4e7..3a1231f5b364 100644 --- a/CRM/Contact/Form/Task/AddToParentClass.php +++ b/CRM/Contact/Form/Task/AddToParentClass.php @@ -1,9 +1,9 @@ get('contactType'); + CRM_Utils_System::setTitle(ts('Add Contacts to %1', [1 => $contactType])); + $this->addElement('text', 'name', ts('Find Target %1', [1 => $contactType])); + + $this->add('select', + 'relationship_type_id', + ts('Relationship Type'), + [ + '' => ts('- select -'), + ] + + CRM_Contact_BAO_Relationship::getRelationType($contactType), TRUE + ); + + $searchRows = $this->get('searchRows'); + $searchCount = $this->get('searchCount'); + if ($searchRows) { + $checkBoxes = []; + $chekFlag = 0; + foreach ($searchRows as $id => $row) { + if (!$chekFlag) { + $chekFlag = $id; + } + + $checkBoxes[$id] = $this->createElement('radio', NULL, NULL, NULL, $id); + } + + $this->addGroup($checkBoxes, 'contact_check'); + if ($chekFlag) { + $checkBoxes[$chekFlag]->setChecked(TRUE); + } + $this->assign('searchRows', $searchRows); + } + + $this->assign('searchCount', $searchCount); + $this->assign('searchDone', $this->get('searchDone')); + $this->assign('contact_type_display', ts($contactType)); + $this->addElement('submit', $this->getButtonName('refresh'), ts('Search'), ['class' => 'crm-form-submit']); + $this->addElement('submit', $this->getButtonName('cancel'), ts('Cancel'), ['class' => 'crm-form-submit']); + $this->addButtons([ + [ + 'type' => 'next', + 'name' => ts('Add to %1', [1 => $contactType]), + 'isDefault' => TRUE, + ], + [ + 'type' => 'cancel', + 'name' => ts('Cancel'), + ], + ]); + } + /** * Add relationships from form. */ @@ -54,10 +106,10 @@ public function addRelationships() { return; } $relationshipTypeParts = explode('_', $this->params['relationship_type_id']); - $params = array( + $params = [ 'relationship_type_id' => $relationshipTypeParts[0], 'is_active' => 1, - ); + ]; $secondaryRelationshipSide = $relationshipTypeParts[1]; $primaryRelationshipSide = $relationshipTypeParts[2]; $primaryFieldName = 'contact_id_' . $primaryRelationshipSide; @@ -73,33 +125,33 @@ public function addRelationships() { $relatedContactName = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $params[$primaryFieldName], 'display_name'); - $status = array( - ts('%count %2 %3 relationship created', array( + $status = [ + ts('%count %2 %3 relationship created', [ 'count' => $outcome['valid'], 'plural' => '%count %2 %3 relationships created', 2 => $relationshipLabel, 3 => $relatedContactName, - )), - ); + ]), + ]; if ($outcome['duplicate']) { - $status[] = ts('%count was skipped because the contact is already %2 %3', array( + $status[] = ts('%count was skipped because the contact is already %2 %3', [ 'count' => $outcome['duplicate'], 'plural' => '%count were skipped because the contacts are already %2 %3', 2 => $relationshipLabel, 3 => $relatedContactName, - )); + ]); } if ($outcome['invalid']) { - $status[] = ts('%count relationship was not created because the contact is not of the right type for this relationship', array( + $status[] = ts('%count relationship was not created because the contact is not of the right type for this relationship', [ 'count' => $outcome['invalid'], 'plural' => '%count relationships were not created because the contact is not of the right type for this relationship', - )); + ]); } $status = '
  • ' . implode('
  • ', $status) . '
'; - CRM_Core_Session::setStatus($status, ts('Relationship created.', array( + CRM_Core_Session::setStatus($status, ts('Relationship created.', [ 'count' => $outcome['valid'], 'plural' => 'Relationships created.', - )), 'success', array('expires' => 0)); + ]), 'success', ['expires' => 0]); } @@ -112,20 +164,20 @@ public function addRelationships() { */ public function search(&$form, &$params) { //max records that will be listed - $searchValues = array(); + $searchValues = []; if (!empty($params['rel_contact'])) { if (isset($params['rel_contact_id']) && is_numeric($params['rel_contact_id']) ) { - $searchValues[] = array('contact_id', '=', $params['rel_contact_id'], 0, 1); + $searchValues[] = ['contact_id', '=', $params['rel_contact_id'], 0, 1]; } else { - $searchValues[] = array('sort_name', 'LIKE', $params['rel_contact'], 0, 1); + $searchValues[] = ['sort_name', 'LIKE', $params['rel_contact'], 0, 1]; } } $contactTypeAdded = FALSE; - $excludedContactIds = array(); + $excludedContactIds = []; if (isset($form->_contactId)) { $excludedContactIds[] = $form->_contactId; } @@ -148,18 +200,18 @@ public function search(&$form, &$params) { $form->set('contact_type', $type); $form->set('contact_sub_type', $subType); if ($type == 'Individual' || $type == 'Organization' || $type == 'Household') { - $searchValues[] = array('contact_type', '=', $type, 0, 0); + $searchValues[] = ['contact_type', '=', $type, 0, 0]; $contactTypeAdded = TRUE; } if ($subType) { - $searchValues[] = array('contact_sub_type', '=', $subType, 0, 0); + $searchValues[] = ['contact_sub_type', '=', $subType, 0, 0]; } } } if (!$contactTypeAdded && !empty($params['contact_type'])) { - $searchValues[] = array('contact_type', '=', $params['contact_type'], 0, 0); + $searchValues[] = ['contact_type', '=', $params['contact_type'], 0, 0]; } // get the count of contact @@ -172,7 +224,7 @@ public function search(&$form, &$params) { $result = $query->searchQuery(0, 50, NULL); $config = CRM_Core_Config::singleton(); - $searchRows = array(); + $searchRows = []; //variable is set if only one record is foun and that record already has relationship with the contact $duplicateRelationship = 0; @@ -210,4 +262,23 @@ public function search(&$form, &$params) { } } + /** + * Process the form after the input has been submitted and validated. + */ + public function postProcess() { + // store the submitted values in an array + $this->params = $this->controller->exportValues($this->_name); + $this->set('searchDone', 0); + $contactType = $this->get('contactType'); + + if (!empty($_POST["_qf_AddTo{$contactType}_refresh"])) { + $searchParams['contact_type'] = $contactType; + $searchParams['rel_contact'] = $this->params['name']; + $this->search($this, $searchParams); + $this->set('searchDone', 1); + return; + } + $this->addRelationships(); + } + } diff --git a/CRM/Contact/Form/Task/AddToTag.php b/CRM/Contact/Form/Task/AddToTag.php index 77af701ff8a5..db80cfc11985 100644 --- a/CRM/Contact/Form/Task/AddToTag.php +++ b/CRM/Contact/Form/Task/AddToTag.php @@ -1,9 +1,9 @@ addFormRule(array('CRM_Contact_Form_Task_AddToTag', 'formRule')); + $this->addFormRule(['CRM_Contact_Form_Task_AddToTag', 'formRule']); } /** @@ -82,7 +82,7 @@ public function addRules() { * @return array */ public static function formRule($form, $rule) { - $errors = array(); + $errors = []; if (empty($form['tag']) && empty($form['contact_taglist'])) { $errors['_qf_default'] = ts("Please select at least one tag."); } @@ -95,7 +95,7 @@ public static function formRule($form, $rule) { public function postProcess() { //get the submitted values in an array $params = $this->controller->exportValues($this->_name); - $contactTags = $tagList = array(); + $contactTags = $tagList = []; // check if contact tags exists if (!empty($params['tag'])) { @@ -132,22 +132,22 @@ public function postProcess() { // merge contact and taglist tags $allTags = CRM_Utils_Array::crmArrayMerge($contactTags, $tagList); - $this->_name = array(); + $this->_name = []; foreach ($allTags as $key => $dnc) { $this->_name[] = $this->_tags[$key]; list($total, $added, $notAdded) = CRM_Core_BAO_EntityTag::addEntitiesToTag($this->_contactIds, $key, 'civicrm_contact', FALSE); - $status = array(ts('%count contact tagged', array('count' => $added, 'plural' => '%count contacts tagged'))); + $status = [ts('%count contact tagged', ['count' => $added, 'plural' => '%count contacts tagged'])]; if ($notAdded) { - $status[] = ts('%count contact already had this tag', array( - 'count' => $notAdded, - 'plural' => '%count contacts already had this tag', - )); + $status[] = ts('%count contact already had this tag', [ + 'count' => $notAdded, + 'plural' => '%count contacts already had this tag', + ]); } $status = '
  • ' . implode('
  • ', $status) . '
'; - CRM_Core_Session::setStatus($status, ts("Added Tag %1", array(1 => $this->_tags[$key])), 'success', array('expires' => 0)); + CRM_Core_Session::setStatus($status, ts("Added Tag %1", [1 => $this->_tags[$key]]), 'success', ['expires' => 0]); } } diff --git a/CRM/Contact/Form/Task/AlterPreferences.php b/CRM/Contact/Form/Task/AlterPreferences.php index a25fbfc93ac0..cd596f0c9822 100644 --- a/CRM/Contact/Form/Task/AlterPreferences.php +++ b/CRM/Contact/Form/Task/AlterPreferences.php @@ -1,9 +1,9 @@ addRadio('actionTypeOption', ts('actionTypeOption'), $options); @@ -57,7 +57,7 @@ public function buildQuickForm() { } public function addRules() { - $this->addFormRule(array('CRM_Contact_Form_Task_AlterPreferences', 'formRule')); + $this->addFormRule(['CRM_Contact_Form_Task_AlterPreferences', 'formRule']); } /** @@ -68,7 +68,7 @@ public function addRules() { * the default array reference */ public function setDefaultValues() { - $defaults = array(); + $defaults = []; $defaults['actionTypeOption'] = 0; return $defaults; @@ -81,7 +81,7 @@ public function setDefaultValues() { * @return array */ public static function formRule($form, $rule) { - $errors = array(); + $errors = []; if (empty($form['pref']) && empty($form['contact_taglist'])) { $errors['_qf_default'] = ts("Please select at least one privacy option."); } @@ -115,19 +115,19 @@ public function postProcess() { } // Status message $privacyOptions = CRM_Core_SelectValues::privacy(); - $status = array(); + $status = []; foreach ($privacyValues as $privacy_key => $privacy_value) { $label = $privacyOptions[$privacy_key]; - $status[] = $privacyValueNew ? ts("Added '%1'", array(1 => $label)) : ts("Removed '%1'", array(1 => $label)); + $status[] = $privacyValueNew ? ts("Added '%1'", [1 => $label]) : ts("Removed '%1'", [1 => $label]); } $status = '
  • ' . implode('
  • ', $status) . '
'; if ($count > 1) { - $title = ts('%1 Contacts Updated', array(1 => $count)); + $title = ts('%1 Contacts Updated', [1 => $count]); } else { $name = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $contact_id, 'display_name'); - $title = ts('%1 Updated', array(1 => $name)); + $title = ts('%1 Updated', [1 => $name]); } CRM_Core_Session::setStatus($status, $title, 'success'); diff --git a/CRM/Contact/Form/Task/Batch.php b/CRM/Contact/Form/Task/Batch.php index a96887d789ee..d0bc8ac47756 100644 --- a/CRM/Contact/Form/Task/Batch.php +++ b/CRM/Contact/Form/Task/Batch.php @@ -1,9 +1,9 @@ _fields as $name => $field) { if ($cfID = CRM_Core_BAO_CustomField::getKeyID($name) && in_array($this->_fields[$name]['html_type'], $removehtmlTypes) @@ -101,25 +105,24 @@ public function buildQuickForm() { //FIX ME: phone ext field is added at the end and it gets removed because of below code //$this->_fields = array_slice($this->_fields, 0, $this->_maxFields); - $this->addButtons(array( - array( - 'type' => 'submit', - 'name' => ts('Update Contact(s)'), - 'isDefault' => TRUE, - ), - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'submit', + 'name' => ts('Update Contact(s)'), + 'isDefault' => TRUE, + ], + [ + 'type' => 'cancel', + 'name' => ts('Cancel'), + ], + ]); $this->assign('profileTitle', $this->_title); $this->assign('componentIds', $this->_contactIds); // if below fields are missing we should not reset sort name / display name // CRM-6794 - $preserveDefaultsArray = array( + $preserveDefaultsArray = [ 'first_name', 'last_name', 'middle_name', @@ -127,7 +130,7 @@ public function buildQuickForm() { 'prefix_id', 'suffix_id', 'household_name', - ); + ]; foreach ($this->_contactIds as $contactId) { $profileFields = $this->_fields; @@ -147,11 +150,11 @@ public function buildQuickForm() { $buttonName = $this->controller->getButtonName('submit'); if ($suppressFields && $buttonName != '_qf_BatchUpdateProfile_next') { - CRM_Core_Session::setStatus(ts("File or Autocomplete-Select type field(s) in the selected profile are not supported for Update multiple contacts."), ts('Some Fields Excluded'), 'info'); + CRM_Core_Session::setStatus(ts("File type field(s) in the selected profile are not supported for Update multiple contacts."), ts('Some Fields Excluded'), 'info'); } $this->addDefaultButtons(ts('Update Contacts')); - $this->addFormRule(array('CRM_Contact_Form_Task_Batch', 'formRule')); + $this->addFormRule(['CRM_Contact_Form_Task_Batch', 'formRule']); } /** @@ -165,9 +168,9 @@ public function setDefaultValues() { return NULL; } - $defaults = $sortName = array(); + $defaults = $sortName = []; foreach ($this->_contactIds as $contactId) { - $details[$contactId] = array(); + $details[$contactId] = []; //build sortname $sortName[$contactId] = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', @@ -193,8 +196,8 @@ public function setDefaultValues() { * true if no errors, else array of errors */ public static function formRule($fields) { - $errors = array(); - $externalIdentifiers = array(); + $errors = []; + $externalIdentifiers = []; foreach ($fields['field'] as $componentId => $field) { foreach ($field as $fieldName => $fieldValue) { if ($fieldName == 'external_identifier') { @@ -217,6 +220,9 @@ public static function formRule($fields) { public function postProcess() { $params = $this->exportValues(); + // @todo extract submit functions & + // extend CRM_Event_Form_Task_BatchTest::testSubmit with a data provider to test + // handling of custom data, specifically checkbox fields. $ufGroupId = $this->get('ufGroupId'); $notify = NULL; $inValidSubtypeCnt = 0; @@ -245,10 +251,10 @@ public function postProcess() { CRM_Core_Session::setStatus('', ts("Updates Saved"), 'success'); if ($inValidSubtypeCnt) { - CRM_Core_Session::setStatus(ts('Contact Subtype field of 1 contact has not been updated.', array( - 'plural' => 'Contact Subtype field of %count contacts has not been updated.', - 'count' => $inValidSubtypeCnt, - )), ts('Invalid Subtype')); + CRM_Core_Session::setStatus(ts('Contact Subtype field of 1 contact has not been updated.', [ + 'plural' => 'Contact Subtype field of %count contacts has not been updated.', + 'count' => $inValidSubtypeCnt, + ]), ts('Invalid Subtype')); } } @@ -286,7 +292,7 @@ public static function parseStreetAddress(&$contactValues, &$form) { return; } - $allParseValues = array(); + $allParseValues = []; foreach ($contactValues as $key => $value) { if (strpos($key, $addressFldKey) !== FALSE) { $locTypeId = substr($key, strlen($addressFldKey) + 1); diff --git a/CRM/Contact/Form/Task/Delete.php b/CRM/Contact/Form/Task/Delete.php index 73e17c64a69a..3411f67d05f0 100644 --- a/CRM/Contact/Form/Task/Delete.php +++ b/CRM/Contact/Form/Task/Delete.php @@ -1,9 +1,9 @@ _contactIds = array($cid); + $this->_contactIds = [$cid]; $this->_single = TRUE; $this->assign('totalSelectedContacts', 1); } @@ -100,7 +101,7 @@ public function preProcess() { $this->_sharedAddressMessage = $this->get('sharedAddressMessage'); if (!$this->_restore && !$this->_sharedAddressMessage) { // we check for each contact for shared contact address - $sharedContactList = array(); + $sharedContactList = []; $sharedAddressCount = 0; foreach ($this->_contactIds as $contactId) { // check if a contact that is being deleted has any shared addresses @@ -114,25 +115,25 @@ public function preProcess() { } } - $this->_sharedAddressMessage = array( + $this->_sharedAddressMessage = [ 'count' => $sharedAddressCount, 'contactList' => $sharedContactList, - ); + ]; if ($sharedAddressCount > 0) { if (count($this->_contactIds) > 1) { // more than one contact deleted - $message = ts('One of the selected contacts has an address record that is shared with 1 other contact.', array( - 'plural' => 'One or more selected contacts have address records which are shared with %count other contacts.', - 'count' => $sharedAddressCount, - )); + $message = ts('One of the selected contacts has an address record that is shared with 1 other contact.', [ + 'plural' => 'One or more selected contacts have address records which are shared with %count other contacts.', + 'count' => $sharedAddressCount, + ]); } else { // only one contact deleted - $message = ts('This contact has an address record which is shared with 1 other contact.', array( - 'plural' => 'This contact has an address record which is shared with %count other contacts.', - 'count' => $sharedAddressCount, - )); + $message = ts('This contact has an address record which is shared with 1 other contact.', [ + 'plural' => 'This contact has an address record which is shared with %count other contacts.', + 'count' => $sharedAddressCount, + ]); } CRM_Core_Session::setStatus($message . ' ' . ts('Shared addresses will not be removed or altered but will no longer be shared.'), ts('Shared Addesses Owner')); } @@ -150,7 +151,7 @@ public function buildQuickForm() { if ($this->_single) { // also fix the user context stack in case the user hits cancel - $context = CRM_Utils_Request::retrieve('context', 'String', $this, FALSE, 'basic'); + $context = CRM_Utils_Request::retrieve('context', 'Alphanumeric', $this, FALSE, 'basic'); if ($context == 'search' && CRM_Utils_Rule::qfKey($this->_searchKey)) { $urlParams = "&context=$context&key=$this->_searchKey"; } @@ -168,7 +169,7 @@ public function buildQuickForm() { $this->addDefaultButtons($label, 'done'); } - $this->addFormRule(array('CRM_Contact_Form_Task_Delete', 'formRule'), $this); + $this->addFormRule(['CRM_Contact_Form_Task_Delete', 'formRule'], $this); } /** @@ -186,7 +187,7 @@ public function buildQuickForm() { */ public static function formRule($fields, $files, $self) { // CRM-12929 - $error = array(); + $error = []; if ($self->_skipUndelete) { CRM_Financial_BAO_FinancialItem::checkContactPresent($self->_contactIds, $error); } @@ -200,7 +201,7 @@ public function postProcess() { $session = CRM_Core_Session::singleton(); $currentUserId = $session->get('userID'); - $context = CRM_Utils_Request::retrieve('context', 'String', $this, FALSE, 'basic'); + $context = CRM_Utils_Request::retrieve('context', 'Alphanumeric', $this, FALSE, 'basic'); $urlParams = 'force=1'; $urlString = "civicrm/contact/search/$context"; @@ -222,15 +223,15 @@ public function postProcess() { // Delete/Restore Contacts. Report errors. $deleted = 0; - $not_deleted = array(); + $not_deleted = []; foreach ($this->_contactIds as $cid) { $name = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $cid, 'display_name'); if (CRM_Contact_BAO_Contact::checkDomainContact($cid)) { - $session->setStatus(ts("'%1' cannot be deleted because the information is used for special system purposes.", array(1 => $name)), 'Cannot Delete Domain Contact', 'error'); + $session->setStatus(ts("'%1' cannot be deleted because the information is used for special system purposes.", [1 => $name]), 'Cannot Delete Domain Contact', 'error'); continue; } if ($currentUserId == $cid && !$this->_restore) { - $session->setStatus(ts("You are currently logged in as '%1'. You cannot delete yourself.", array(1 => $name)), 'Unable To Delete', 'error'); + $session->setStatus(ts("You are currently logged in as '%1'. You cannot delete yourself.", [1 => $name]), 'Unable To Delete', 'error'); continue; } if (CRM_Contact_BAO_Contact::deleteContact($cid, $this->_restore, $this->_skipUndelete)) { @@ -245,25 +246,25 @@ public function postProcess() { $title = ts('Deleted'); if ($this->_restore) { $title = ts('Restored'); - $status = ts('%1 has been restored from the trash.', array( - 1 => $name, - 'plural' => '%count contacts restored from trash.', - 'count' => $deleted, - )); + $status = ts('%1 has been restored from the trash.', [ + 1 => $name, + 'plural' => '%count contacts restored from trash.', + 'count' => $deleted, + ]); } elseif ($this->_skipUndelete) { - $status = ts('%1 has been permanently deleted.', array( - 1 => $name, - 'plural' => '%count contacts permanently deleted.', - 'count' => $deleted, - )); + $status = ts('%1 has been permanently deleted.', [ + 1 => $name, + 'plural' => '%count contacts permanently deleted.', + 'count' => $deleted, + ]); } else { - $status = ts('%1 has been moved to the trash.', array( - 1 => $name, - 'plural' => '%count contacts moved to trash.', - 'count' => $deleted, - )); + $status = ts('%1 has been moved to the trash.', [ + 1 => $name, + 'plural' => '%count contacts moved to trash.', + 'count' => $deleted, + ]); } $session->setStatus($status, $title, 'success'); } @@ -283,7 +284,7 @@ public function postProcess() { } $message .= '
  • ' . implode('
  • ', $this->_sharedAddressMessage['contactList']) . '
'; - $session->setStatus($message, ts('Shared Addesses Owner Deleted'), 'info', array('expires' => 0)); + $session->setStatus($message, ts('Shared Addesses Owner Deleted'), 'info', ['expires' => 0]); $this->set('sharedAddressMessage', NULL); } diff --git a/CRM/Contact/Form/Task/Email.php b/CRM/Contact/Form/Task/Email.php index 3ab2cd522a70..d8dd851e3349 100644 --- a/CRM/Contact/Form/Task/Email.php +++ b/CRM/Contact/Form/Task/Email.php @@ -1,9 +1,9 @@ _caseId = CRM_Utils_Request::retrieve('caseid', 'String', $this, FALSE); - $this->_context = CRM_Utils_Request::retrieve('context', 'String', $this); + $this->_context = CRM_Utils_Request::retrieve('context', 'Alphanumeric', $this); $cid = CRM_Utils_Request::retrieve('cid', 'String', $this, FALSE); // Allow request to specify email id rather than contact id $toEmailId = CRM_Utils_Request::retrieve('email_id', 'String', $this); if ($toEmailId) { - $toEmail = civicrm_api('email', 'getsingle', array('version' => 3, 'id' => $toEmailId)); + $toEmail = civicrm_api('email', 'getsingle', ['version' => 3, 'id' => $toEmailId]); if (!empty($toEmail['email']) && !empty($toEmail['contact_id'])) { $this->_toEmail = $toEmail; } @@ -115,7 +115,7 @@ public function preProcess() { if ($cid) { $cid = explode(',', $cid); - $displayName = array(); + $displayName = []; foreach ($cid as $val) { $displayName[] = CRM_Contact_BAO_Contact::displayName($val); @@ -132,11 +132,6 @@ public function preProcess() { parent::preProcess(); } - //early prevent, CRM-6209 - if (count($this->_contactIds) > CRM_Contact_Form_Task_EmailCommon::MAX_EMAILS_KILL_SWITCH) { - CRM_Core_Error::statusBounce(ts('Please do not use this task to send a lot of emails (greater than %1). We recommend using CiviMail instead.', array(1 => CRM_Contact_Form_Task_EmailCommon::MAX_EMAILS_KILL_SWITCH))); - } - $this->assign('single', $this->_single); if (CRM_Core_Permission::check('administer CiviCRM')) { $this->assign('isAdmin', 1); @@ -168,6 +163,13 @@ public function postProcess() { */ public function listTokens() { $tokens = CRM_Core_SelectValues::contactTokens(); + + if (isset($this->_caseId) || isset($this->_caseIds)) { + // For a single case, list tokens relevant for only that case type + $caseTypeId = isset($this->_caseId) ? CRM_Core_DAO::getFieldValue('CRM_Case_DAO_Case', $this->_caseId, 'case_type_id') : NULL; + $tokens += CRM_Core_SelectValues::caseTokens($caseTypeId); + } + return $tokens; } diff --git a/CRM/Contact/Form/Task/EmailCommon.php b/CRM/Contact/Form/Task/EmailCommon.php index 3fd971a352e0..5006f9d5321c 100644 --- a/CRM/Contact/Form/Task/EmailCommon.php +++ b/CRM/Contact/Form/Task/EmailCommon.php @@ -1,9 +1,9 @@ _single = TRUE; } - $form->_emails = $emails = array(); - - $session = CRM_Core_Session::singleton(); - $contactID = $session->get('userID'); - - $form->_contactIds = array($contactID); - $contactEmails = CRM_Core_BAO_Email::allEmails($contactID); - - $form->_onHold = array(); - - $fromDisplayName = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', - $contactID, 'display_name' - ); + $form->_emails = array(); - foreach ($contactEmails as $emailId => $item) { - $email = $item['email']; - if (!$email && (count($emails) < 1)) { - // set it if no emails are present at all - $form->_noEmails = TRUE; - } - else { - if ($email) { - if (in_array($email, $emails)) { - // CRM-3624 - continue; - } + // @TODO remove these line and to it somewhere more appropriate. Currently some classes (e.g Case + // are having to re-write contactIds afterwards due to this inappropriate variable setting + // If we don't have any contact IDs, use the logged in contact ID + $form->_contactIds = $form->_contactIds ?: [CRM_Core_Session::getLoggedInContactID()]; - $emails[$emailId] = '"' . $fromDisplayName . '" <' . $email . '> '; - $form->_onHold[$emailId] = $item['on_hold']; - $form->_noEmails = FALSE; - } - } - if (!empty($email)) { - $form->_emails[$emailId] = $emails[$emailId]; - $emails[$emailId] .= $item['locationType']; + $fromEmailValues = CRM_Core_BAO_Email::getFromEmail(); - if ($item['is_primary']) { - $emails[$emailId] .= ' ' . ts('(preferred)'); - } - $emails[$emailId] = htmlspecialchars($emails[$emailId]); - } + $form->_noEmails = FALSE; + if (empty($fromEmailValues)) { + $form->_noEmails = TRUE; } - $form->assign('noEmails', $form->_noEmails); if ($bounce) { if ($form->_noEmails) { - CRM_Core_Error::statusBounce(ts('Your user record does not have a valid email address')); + CRM_Core_Error::statusBounce(ts('Your user record does not have a valid email address and no from addresses have been configured.')); } } - // now add domain from addresses - $domainEmails = self::domainEmails(); - foreach ($domainEmails as $domainEmail => $email) { - $form->_emails[$domainEmail] = $domainEmail; + $form->_emails = $fromEmailValues; + $defaults = array(); + $form->_fromEmails = $fromEmailValues; + if (!Civi::settings()->get('allow_mail_from_logged_in_contact')) { + $defaults['from_email_address'] = current(CRM_Core_BAO_Domain::getNameAndEmail(FALSE, TRUE)); } - $form->_fromEmails = CRM_Utils_Array::crmArrayMerge($emails, $domainEmails); - $form->_fromEmails = array_filter($form->_fromEmails); if (is_numeric(key($form->_fromEmails))) { // Add signature $defaultEmail = civicrm_api3('email', 'getsingle', array('id' => key($form->_fromEmails))); @@ -140,8 +104,8 @@ public static function preProcessFromAddress(&$form, $bounce = TRUE) { if (!empty($defaultEmail['signature_text'])) { $defaults['text_message'] = "\n\n--\n" . $defaultEmail['signature_text']; } - $form->setDefaults($defaults); } + $form->setDefaults($defaults); } /** @@ -160,6 +124,7 @@ public static function buildQuickForm(&$form) { if (count($form->_contactIds) > 1) { $form->_single = FALSE; } + CRM_Contact_Form_Task_EmailCommon::bounceIfSimpleMailLimitExceeded(count($form->_contactIds)); $emailAttributes = array( 'class' => 'huge', @@ -291,7 +256,7 @@ public static function buildQuickForm(&$form) { $form->add('text', 'subject', ts('Subject'), 'size=50 maxlength=254', TRUE); - $form->add('select', 'fromEmailAddress', ts('From'), $form->_fromEmails, TRUE, array('class' => 'crm-select2 huge')); + $form->add('select', 'from_email_address', ts('From'), $form->_fromEmails, TRUE); CRM_Mailing_BAO_Mailing::commonCompose($form); @@ -351,7 +316,7 @@ public static function buildQuickForm(&$form) { ); //add followup date - $form->addDateTime('followup_date', ts('in'), FALSE, array('formatType' => 'activityDateTime')); + $form->add('datepicker', 'followup_date', ts('in')); foreach ($fields as $field => $values) { if (!empty($fields[$field])) { @@ -370,6 +335,9 @@ public static function buildQuickForm(&$form) { } } + //Added for CRM-15984: Add campaign field + CRM_Campaign_BAO_Campaign::addCampaign($form); + $form->addFormRule(array('CRM_Contact_Form_Task_EmailCommon', 'formRule'), $form); CRM_Core_Resources::singleton()->addScriptFile('civicrm', 'templates/CRM/Contact/Form/Task/EmailCommon.js', 0, 'html-header'); } @@ -410,16 +378,29 @@ public static function formRule($fields, $dontCare, $self) { * @param CRM_Core_Form $form */ public static function postProcess(&$form) { - if (count($form->_contactIds) > self::MAX_EMAILS_KILL_SWITCH) { - CRM_Core_Error::fatal(ts('Please do not use this task to send a lot of emails (greater than %1). We recommend using CiviMail instead.', - array(1 => self::MAX_EMAILS_KILL_SWITCH) - )); - } + self::bounceIfSimpleMailLimitExceeded(count($form->_contactIds)); // check and ensure that $formValues = $form->controller->exportValues($form->getName()); - $fromEmail = $formValues['fromEmailAddress']; - $from = CRM_Utils_Array::value($fromEmail, $form->_emails); + self::submit($form, $formValues); + } + + /** + * Submit the form values. + * + * This is also accessible for testing. + * + * @param CRM_Core_Form $form + * @param array $formValues + */ + public static function submit(&$form, $formValues) { + self::saveMessageTemplate($formValues); + + $from = CRM_Utils_Array::value('from_email_address', $formValues); + // dev/core#357 User Emails are keyed by their id so that the Signature is able to be added + // If we have had a contact email used here the value returned from the line above will be the + // numerical key where as $from for use in the sendEmail in Activity needs to be of format of "To Name" + $from = CRM_Utils_Mail::formatFromAddress($from); $subject = $formValues['subject']; // CRM-13378: Append CC and BCC information at the end of Activity Details and format cc and bcc fields @@ -463,27 +444,6 @@ public static function postProcess(&$form) { $subject = "[case #$hash] $subject"; } - // process message template - if (!empty($formValues['saveTemplate']) || !empty($formValues['updateTemplate'])) { - $messageTemplate = array( - 'msg_text' => $formValues['text_message'], - 'msg_html' => $formValues['html_message'], - 'msg_subject' => $formValues['subject'], - 'is_active' => TRUE, - ); - - if (!empty($formValues['saveTemplate'])) { - $messageTemplate['msg_title'] = $formValues['saveTemplateName']; - CRM_Core_BAO_MessageTemplate::add($messageTemplate); - } - - if (!empty($formValues['template']) && !empty($formValues['updateTemplate'])) { - $messageTemplate['id'] = $formValues['template']; - unset($messageTemplate['msg_title']); - CRM_Core_BAO_MessageTemplate::add($messageTemplate); - } - } - $attachments = array(); CRM_Core_BAO_File::formatAttachment($formValues, $attachments, @@ -512,6 +472,11 @@ public static function postProcess(&$form) { } } + $contributionIds = array(); + if ($form->getVar('_contributionIds')) { + $contributionIds = $form->getVar('_contributionIds'); + } + // send the mail list($sent, $activityId) = CRM_Activity_BAO_Activity::sendEmail( $formattedContactDetails, @@ -525,7 +490,9 @@ public static function postProcess(&$form) { $cc, $bcc, array_keys($form->_toContactDetails), - $additionalDetails + $additionalDetails, + $contributionIds, + CRM_Utils_Array::value('campaign_id', $formValues) ); $followupStatus = ''; @@ -535,7 +502,6 @@ public static function postProcess(&$form) { $params['followup_activity_type_id'] = $formValues['followup_activity_type_id']; $params['followup_activity_subject'] = $formValues['followup_activity_subject']; $params['followup_date'] = $formValues['followup_date']; - $params['followup_date_time'] = $formValues['followup_date_time']; $params['target_contact_id'] = $form->_contactIds; $params['followup_assignee_contact_id'] = explode(',', $formValues['followup_assignee_contact_id']); $followupActivity = CRM_Activity_BAO_Activity::createFollowupActivity($activityId, $params); @@ -560,9 +526,9 @@ public static function postProcess(&$form) { $count_success = count($form->_toContactDetails); CRM_Core_Session::setStatus(ts('One message was sent successfully. ', array( - 'plural' => '%count messages were sent successfully. ', - 'count' => $count_success, - )) . $followupStatus, ts('Message Sent', array('plural' => 'Messages Sent', 'count' => $count_success)), 'success'); + 'plural' => '%count messages were sent successfully. ', + 'count' => $count_success, + )) . $followupStatus, ts('Message Sent', array('plural' => 'Messages Sent', 'count' => $count_success)), 'success'); } // Display the name and number of contacts for those email is not sent. @@ -581,9 +547,9 @@ public static function postProcess(&$form) { } $status = '(' . ts('because no email address on file or communication preferences specify DO NOT EMAIL or Contact is deceased or Primary email address is On Hold') . ')
  • ' . implode('
  • ', $not_sent) . '
'; CRM_Core_Session::setStatus($status, ts('One Message Not Sent', array( - 'count' => count($emailsNotSent), - 'plural' => '%count Messages Not Sent', - )), 'info'); + 'count' => count($emailsNotSent), + 'plural' => '%count Messages Not Sent', + )), 'info'); } if (isset($form->_caseId)) { @@ -601,4 +567,46 @@ public static function postProcess(&$form) { } } + /** + * Save the template if update selected. + * + * @param array $formValues + */ + protected static function saveMessageTemplate($formValues) { + if (!empty($formValues['saveTemplate']) || !empty($formValues['updateTemplate'])) { + $messageTemplate = array( + 'msg_text' => $formValues['text_message'], + 'msg_html' => $formValues['html_message'], + 'msg_subject' => $formValues['subject'], + 'is_active' => TRUE, + ); + + if (!empty($formValues['saveTemplate'])) { + $messageTemplate['msg_title'] = $formValues['saveTemplateName']; + CRM_Core_BAO_MessageTemplate::add($messageTemplate); + } + + if (!empty($formValues['template']) && !empty($formValues['updateTemplate'])) { + $messageTemplate['id'] = $formValues['template']; + unset($messageTemplate['msg_title']); + CRM_Core_BAO_MessageTemplate::add($messageTemplate); + } + } + } + + /** + * Bounce if there are more emails than permitted. + * + * @param int $count + * The number of emails the user is attempting to send + */ + public static function bounceIfSimpleMailLimitExceeded($count) { + $limit = Civi::settings()->get('simple_mail_limit'); + if ($count > $limit) { + CRM_Core_Error::statusBounce(ts('Please do not use this task to send a lot of emails (greater than %1). Many countries have legal requirements when sending bulk emails and the CiviMail framework has opt out functionality and domain tokens to help meet these.', + array(1 => $limit) + )); + } + } + } diff --git a/CRM/Contact/Form/Task/HookSample.php b/CRM/Contact/Form/Task/HookSample.php index 0fdd74186c51..a93a92a7ed2d 100644 --- a/CRM/Contact/Form/Task/HookSample.php +++ b/CRM/Contact/Form/Task/HookSample.php @@ -1,9 +1,9 @@ fetch()) { - $rows[] = array( + $rows[] = [ 'id' => $dao->contact_id, 'name' => $dao->name, 'contact_type' => $dao->contact_type, 'email' => $dao->email, - ); + ]; } $this->assign('rows', $rows); diff --git a/CRM/Contact/Form/Task/Label.php b/CRM/Contact/Form/Task/Label.php index adf8ef63683e..674d1d1ad310 100644 --- a/CRM/Contact/Form/Task/Label.php +++ b/CRM/Contact/Form/Task/Label.php @@ -1,9 +1,9 @@ add('select', 'label_name', ts('Select Label'), array('' => ts('- select label -')) + $label, TRUE); + $form->add('select', 'label_name', ts('Select Label'), ['' => ts('- select label -')] + $label, TRUE); // add select for Location Type - $this->addElement('select', 'location_type_id', ts('Select Location'), - array( + $form->addElement('select', 'location_type_id', ts('Select Location'), + [ '' => ts('Primary'), - ) + CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id'), TRUE + ] + CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id'), TRUE ); // checkbox for SKIP contacts with Do Not Mail privacy option - $this->addElement('checkbox', 'do_not_mail', ts('Do not print labels for contacts with "Do Not Mail" privacy option checked')); + $form->addElement('checkbox', 'do_not_mail', ts('Do not print labels for contacts with "Do Not Mail" privacy option checked')); - $this->add('checkbox', 'merge_same_address', ts('Merge labels for contacts with the same address'), NULL); - $this->add('checkbox', 'merge_same_household', ts('Merge labels for contacts belonging to the same household'), NULL); + $form->add('checkbox', 'merge_same_address', ts('Merge labels for contacts with the same address'), NULL); + $form->add('checkbox', 'merge_same_household', ts('Merge labels for contacts belonging to the same household'), NULL); - $this->addButtons(array( - array( + $form->addButtons([ + [ 'type' => 'submit', 'name' => ts('Make Mailing Labels'), 'isDefault' => TRUE, - ), - array( + ], + [ 'type' => 'cancel', 'name' => ts('Done'), - ), - )); + ], + ]); } /** @@ -88,7 +97,7 @@ public function buildQuickForm() { * array of default values */ public function setDefaultValues() { - $defaults = array(); + $defaults = []; $format = CRM_Core_BAO_LabelFormat::getDefaultValues(); $defaults['label_name'] = CRM_Utils_Array::value('name', $format); $defaults['do_not_mail'] = 1; @@ -117,10 +126,10 @@ public function postProcess() { } //build the returnproperties - $returnProperties = array('display_name' => 1, 'contact_type' => 1, 'prefix_id' => 1); + $returnProperties = ['display_name' => 1, 'contact_type' => 1, 'prefix_id' => 1]; $mailingFormat = Civi::settings()->get('mailing_format'); - $mailingFormatProperties = array(); + $mailingFormatProperties = []; if ($mailingFormat) { $mailingFormatProperties = CRM_Utils_Token::getReturnProperties($mailingFormat); $returnProperties = array_merge($returnProperties, $mailingFormatProperties); @@ -130,7 +139,7 @@ public function postProcess() { unset($mailingFormatProperties['addressee']); } - $customFormatProperties = array(); + $customFormatProperties = []; if (stristr($mailingFormat, 'custom_')) { foreach ($mailingFormatProperties as $token => $true) { if (substr($token, 0, 7) == 'custom_') { @@ -163,38 +172,38 @@ public function postProcess() { } //get the contacts information - $params = array(); + $params = []; if (!empty($fv['location_type_id'])) { $locType = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id'); $locName = $locType[$fv['location_type_id']]; - $location = array('location' => array("{$locName}" => $address)); + $location = ['location' => ["{$locName}" => $address]]; $returnProperties = array_merge($returnProperties, $location); - $params[] = array('location_type', '=', array(1 => $fv['location_type_id']), 0, 0); + $params[] = ['location_type', '=', [1 => $fv['location_type_id']], 0, 0]; } else { $returnProperties = array_merge($returnProperties, $address); } - $rows = array(); + $rows = []; foreach ($this->_contactIds as $key => $contactID) { - $params[] = array( + $params[] = [ CRM_Core_Form::CB_PREFIX . $contactID, '=', 1, 0, 0, - ); + ]; } // fix for CRM-2651 if (!empty($fv['do_not_mail'])) { - $params[] = array('do_not_mail', '=', 0, 0, 0); + $params[] = ['do_not_mail', '=', 0, 0, 0]; } // fix for CRM-2613 - $params[] = array('is_deceased', '=', 0, 0, 0); + $params[] = ['is_deceased', '=', 0, 0, 0]; - $custom = array(); + $custom = []; foreach ($returnProperties as $name => $dontCare) { $cfID = CRM_Core_BAO_CustomField::getKeyID($name); if ($cfID) { @@ -217,9 +226,9 @@ public function postProcess() { 'CRM_Contact_Form_Task_Label' ); - $tokens = array(); + $tokens = []; CRM_Utils_Hook::tokens($tokens); - $tokenFields = array(); + $tokenFields = []; foreach ($tokens as $category => $catTokens) { foreach ($catTokens as $token => $tokenName) { $tokenFields[] = $token; @@ -260,8 +269,8 @@ public function postProcess() { $rows[$value][$field] = $fieldValue; } - $valuesothers = array(); - $paramsothers = array('contact_id' => $value); + $valuesothers = []; + $paramsothers = ['contact_id' => $value]; $valuesothers = CRM_Core_BAO_Location::getValues($paramsothers, $valuesothers); if (!empty($fv['location_type_id'])) { foreach ($valuesothers as $vals) { @@ -269,12 +278,12 @@ public function postProcess() { CRM_Utils_Array::value('location_type_id', $fv) ) { foreach ($vals as $k => $v) { - if (in_array($k, array( + if (in_array($k, [ 'email', 'phone', 'im', 'openid', - ))) { + ])) { if ($k == 'im') { $rows[$value][$k] = $v['1']['name']; } @@ -317,14 +326,14 @@ public function postProcess() { if ($commMethods = CRM_Utils_Array::value('preferred_communication_method', $row)) { $val = array_filter(explode(CRM_Core_DAO::VALUE_SEPARATOR, $commMethods)); $comm = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'preferred_communication_method'); - $temp = array(); + $temp = []; foreach ($val as $vals) { $temp[] = $comm[$vals]; } $row['preferred_communication_method'] = implode(', ', $temp); } $row['id'] = $id; - $formatted = CRM_Utils_Address::format($row, 'mailing_format', FALSE, TRUE, $individualFormat, $tokenFields); + $formatted = CRM_Utils_Address::format($row, 'mailing_format', FALSE, TRUE, $tokenFields); // CRM-2211: UFPDF doesn't have bidi support; use the PECL fribidi package to fix it. // On Ubuntu (possibly Debian?) be aware of http://pecl.php.net/bugs/bug.php?id=12366 @@ -337,12 +346,12 @@ public function postProcess() { } $formatted = implode("\n", $lines); } - $rows[$id] = array($formatted); + $rows[$id] = [$formatted]; } //call function to create labels self::createLabel($rows, $fv['label_name']); - CRM_Utils_System::civiExit(1); + CRM_Utils_System::civiExit(); } /** diff --git a/CRM/Contact/Form/Task/LabelCommon.php b/CRM/Contact/Form/Task/LabelCommon.php index 0b1b02a55a7e..74bdc83cb185 100644 --- a/CRM/Contact/Form/Task/LabelCommon.php +++ b/CRM/Contact/Form/Task/LabelCommon.php @@ -1,9 +1,9 @@ Output($fileName, 'D'); } - /** * Get the rows for the labels. * @@ -80,21 +79,21 @@ public static function createLabel(&$contactRows, &$format, $fileName = 'Mailing */ public static function getRows($contactIDs, $locationTypeID, $respectDoNotMail, $mergeSameAddress, $mergeSameHousehold) { $locName = NULL; - $rows = array(); + $rows = []; //get the address format sequence from the config file $addressReturnProperties = CRM_Contact_Form_Task_LabelCommon::getAddressReturnProperties(); //build the return properties - $returnProperties = array('display_name' => 1, 'contact_type' => 1, 'prefix_id' => 1); + $returnProperties = ['display_name' => 1, 'contact_type' => 1, 'prefix_id' => 1]; $mailingFormat = Civi::settings()->get('mailing_format'); - $mailingFormatProperties = array(); + $mailingFormatProperties = []; if ($mailingFormat) { $mailingFormatProperties = CRM_Utils_Token::getReturnProperties($mailingFormat); $returnProperties = array_merge($returnProperties, $mailingFormatProperties); } - $customFormatProperties = array(); + $customFormatProperties = []; if (stristr($mailingFormat, 'custom_')) { foreach ($mailingFormatProperties as $token => $true) { if (substr($token, 0, 7) == 'custom_') { @@ -113,30 +112,30 @@ public static function getRows($contactIDs, $locationTypeID, $respectDoNotMail, } //get the contacts information - $params = $custom = array(); + $params = $custom = []; foreach ($contactIDs as $key => $contactID) { - $params[] = array( + $params[] = [ CRM_Core_Form::CB_PREFIX . $contactID, '=', 1, 0, 0, - ); + ]; } // fix for CRM-2651 if (!empty($respectDoNotMail['do_not_mail'])) { - $params[] = array('do_not_mail', '=', 0, 0, 0); + $params[] = ['do_not_mail', '=', 0, 0, 0]; } // fix for CRM-2613 - $params[] = array('is_deceased', '=', 0, 0, 0); + $params[] = ['is_deceased', '=', 0, 0, 0]; if ($locationTypeID) { $locType = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id'); $locName = $locType[$locationTypeID]; - $location = array('location' => array("{$locName}" => $addressReturnProperties)); + $location = ['location' => ["{$locName}" => $addressReturnProperties]]; $returnProperties = array_merge($returnProperties, $location); - $params[] = array('location_type', '=', array($locationTypeID => 1), 0, 0); + $params[] = ['location_type', '=', [$locationTypeID => 1], 0, 0]; } else { $returnProperties = array_merge($returnProperties, $addressReturnProperties); @@ -193,8 +192,8 @@ public static function getRows($contactIDs, $locationTypeID, $respectDoNotMail, $rows[$value][$field] = $fieldValue; } - $valuesothers = array(); - $paramsothers = array('contact_id' => $value); + $valuesothers = []; + $paramsothers = ['contact_id' => $value]; $valuesothers = CRM_Core_BAO_Location::getValues($paramsothers, $valuesothers); if ($locationTypeID) { foreach ($valuesothers as $vals) { @@ -202,12 +201,12 @@ public static function getRows($contactIDs, $locationTypeID, $respectDoNotMail, $locationTypeID ) { foreach ($vals as $k => $v) { - if (in_array($k, array( + if (in_array($k, [ 'email', 'phone', 'im', 'openid', - ))) { + ])) { if ($k == 'im') { $rows[$value][$k] = $v['1']['name']; } @@ -240,7 +239,7 @@ public static function getRows($contactIDs, $locationTypeID, $respectDoNotMail, } } // sigh couldn't extract out tokenfields yet - return array($rows, $tokenFields); + return [$rows, $tokenFields]; } /** @@ -269,7 +268,7 @@ public static function getAddressReturnProperties() { */ public static function getTokenData(&$contacts) { $mailingFormat = Civi::settings()->get('mailing_format'); - $tokens = $tokenFields = array(); + $tokens = $tokenFields = []; $messageToken = CRM_Utils_Token::getTokens($mailingFormat); // also get all token values @@ -298,8 +297,8 @@ public static function getTokenData(&$contacts) { */ public function mergeSameHousehold(&$rows) { // group selected contacts by type - $individuals = array(); - $households = array(); + $individuals = []; + $households = []; foreach ($rows as $contact_id => $row) { if ($row['contact_type'] == 'Household') { $households[$contact_id] = $row; diff --git a/CRM/Contact/Form/Task/Map.php b/CRM/Contact/Form/Task/Map.php index 851f0d7b909f..82e1af323efa 100644 --- a/CRM/Contact/Form/Task/Map.php +++ b/CRM/Contact/Form/Task/Map.php @@ -1,9 +1,9 @@ assign('profileGID', $profileGID); - $context = CRM_Utils_Request::retrieve('context', 'String', $this); + $context = CRM_Utils_Request::retrieve('context', 'Alphanumeric', $this); $type = 'Contact'; if ($cid) { - $ids = array($cid); + $ids = [$cid]; $this->_single = TRUE; if ($profileGID) { // this does a check and ensures that the user has permission on this profile @@ -113,14 +113,13 @@ public function preProcess() { * Build the form object. */ public function buildQuickForm() { - $this->addButtons(array( - array( - 'type' => 'done', - 'name' => ts('Done'), - 'isDefault' => TRUE, - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'done', + 'name' => ts('Done'), + 'isDefault' => TRUE, + ], + ]); } /** @@ -221,14 +220,14 @@ public static function createMapXML($ids, $locationId, &$page, $addBreadCrumb, $ } } - $center = array( + $center = [ 'lat' => (float ) $sumLat / count($locations), 'lng' => (float ) $sumLng / count($locations), - ); - $span = array( + ]; + $span = [ 'lat' => (float ) ($maxLat - $minLat), 'lng' => (float ) ($maxLng - $minLng), - ); + ]; $page->assign_by_ref('center', $center); $page->assign_by_ref('span', $span); } diff --git a/CRM/Contact/Form/Task/Map/Event.php b/CRM/Contact/Form/Task/Map/Event.php index 5f94617f37e6..496e172f6763 100644 --- a/CRM/Contact/Form/Task/Map/Event.php +++ b/CRM/Contact/Form/Task/Map/Event.php @@ -1,9 +1,9 @@ assign('single', FALSE); $this->assign('skipLocationType', TRUE); + $is_public = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_Event', $ids, 'is_public'); + if ($is_public == 0) { + CRM_Utils_System::addHTMLHead(''); + } } /** diff --git a/CRM/Contact/Form/Task/Merge.php b/CRM/Contact/Form/Task/Merge.php index 05834d29450a..80359da7af4a 100644 --- a/CRM/Contact/Form/Task/Merge.php +++ b/CRM/Contact/Form/Task/Merge.php @@ -1,9 +1,9 @@ _contactIds)) { $contactIds = array_unique($this->_contactIds); } @@ -52,7 +51,7 @@ public function preProcess() { } // do check for same contact type. - $contactTypes = array(); + $contactTypes = []; if (!$statusMsg) { $sql = "SELECT contact_type FROM civicrm_contact WHERE id IN (" . implode(',', $contactIds) . ")"; $contact = CRM_Core_DAO::executeQuery($sql); diff --git a/CRM/Contact/Form/Task/PDF.php b/CRM/Contact/Form/Task/PDF.php index fe9089b01aa0..67f42d2c30f3 100644 --- a/CRM/Contact/Form/Task/PDF.php +++ b/CRM/Contact/Form/Task/PDF.php @@ -1,9 +1,9 @@ _caseId = CRM_Utils_Request::retrieve('caseid', 'Positive', $this, FALSE); + $this->_caseId = CRM_Utils_Request::retrieve('caseid', 'CommaSeparatedIntegers', $this, FALSE); + if (!empty($this->_caseId) && strpos($this->_caseId, ',')) { + $this->_caseIds = explode(',', $this->_caseId); + unset($this->_caseId); + } // retrieve contact ID if this is 'single' mode - $cid = CRM_Utils_Request::retrieve('cid', 'Positive', $this, FALSE); + $cid = CRM_Utils_Request::retrieve('cid', 'CommaSeparatedIntegers', $this, FALSE); if ($cid) { // this is true in non-search context / single mode @@ -73,7 +77,6 @@ public function preProcess() { if ($cid) { CRM_Contact_Form_Task_PDFLetterCommon::preProcessSingle($this, $cid); $this->_single = TRUE; - $this->_cid = $cid; } else { parent::preProcess(); @@ -85,9 +88,9 @@ public function preProcess() { * Set default values for the form. */ public function setDefaultValues() { - $defaults = array(); + $defaults = []; if (isset($this->_activityId)) { - $params = array('id' => $this->_activityId); + $params = ['id' => $this->_activityId]; CRM_Activity_BAO_Activity::retrieve($params, $defaults); $defaults['html_message'] = CRM_Utils_Array::value('details', $defaults); } @@ -118,8 +121,9 @@ public function postProcess() { */ public function listTokens() { $tokens = CRM_Core_SelectValues::contactTokens(); - if (isset($this->_caseId)) { - $caseTypeId = CRM_Core_DAO::getFieldValue('CRM_Case_DAO_Case', $this->_caseId, 'case_type_id'); + if (isset($this->_caseId) || isset($this->_caseIds)) { + // For a single case, list tokens relevant for only that case type + $caseTypeId = isset($this->_caseId) ? CRM_Core_DAO::getFieldValue('CRM_Case_DAO_Case', $this->_caseId, 'case_type_id') : NULL; $tokens += CRM_Core_SelectValues::caseTokens($caseTypeId); } return $tokens; diff --git a/CRM/Contact/Form/Task/PDFLetterCommon.php b/CRM/Contact/Form/Task/PDFLetterCommon.php index 116d0115ab5f..b52b24d76745 100644 --- a/CRM/Contact/Form/Task/PDFLetterCommon.php +++ b/CRM/Contact/Form/Task/PDFLetterCommon.php @@ -1,9 +1,9 @@ string $label). + */ + public static function getLoggingOptions() { + return [ + 'none' => ts('Do not record'), + 'multiple' => ts('Multiple activities (one per contact)'), + 'combined' => ts('One combined activity'), + 'combined-attached' => ts('One combined activity plus one file attachment'), + // 'multiple-attached' <== not worth the work + ]; + } /** * Build all the data structures needed to build the form. @@ -42,8 +58,9 @@ class CRM_Contact_Form_Task_PDFLetterCommon { * @param CRM_Core_Form $form */ public static function preProcess(&$form) { - $messageText = array(); - $messageSubject = array(); + CRM_Contact_Form_Task_EmailCommon::preProcessFromAddress($form); + $messageText = []; + $messageSubject = []; $dao = new CRM_Core_BAO_MessageTemplate(); $dao->is_active = 1; $dao->find(); @@ -54,7 +71,7 @@ public static function preProcess(&$form) { $form->assign('message', $messageText); $form->assign('messageSubject', $messageSubject); - CRM_Utils_System::setTitle('Print/Merge Document'); + parent::preProcess($form); } /** @@ -62,193 +79,11 @@ public static function preProcess(&$form) { * @param int $cid */ public static function preProcessSingle(&$form, $cid) { - $form->_contactIds = array($cid); + $form->_contactIds = explode(',', $cid); // put contact display name in title for single contact mode - CRM_Utils_System::setTitle(ts('Print/Merge Document for %1', array(1 => CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $cid, 'display_name')))); - } - - /** - * Build the form object. - * - * @var CRM_Core_Form $form - */ - public static function buildQuickForm(&$form) { - // This form outputs a file so should never be submitted via ajax - $form->preventAjaxSubmit(); - - //Added for CRM-12682: Add activity subject and campaign fields - CRM_Campaign_BAO_Campaign::addCampaign($form); - $form->add( - 'text', - 'subject', - ts('Activity Subject'), - array('size' => 45, 'maxlength' => 255), - FALSE - ); - - $form->add('static', 'pdf_format_header', NULL, ts('Page Format: %1', array(1 => ''))); - $form->addSelect('format_id', array( - 'label' => ts('Select Format'), - 'placeholder' => ts('Default'), - 'entity' => 'message_template', - 'field' => 'pdf_format_id', - 'option_url' => 'civicrm/admin/pdfFormats', - )); - $form->add( - 'select', - 'paper_size', - ts('Paper Size'), - array(0 => ts('- default -')) + CRM_Core_BAO_PaperSize::getList(TRUE), - FALSE, - array('onChange' => "selectPaper( this.value ); showUpdateFormatChkBox();") - ); - $form->add('static', 'paper_dimensions', NULL, ts('Width x Height')); - $form->add( - 'select', - 'orientation', - ts('Orientation'), - CRM_Core_BAO_PdfFormat::getPageOrientations(), - FALSE, - array('onChange' => "updatePaperDimensions(); showUpdateFormatChkBox();") - ); - $form->add( - 'select', - 'metric', - ts('Unit of Measure'), - CRM_Core_BAO_PdfFormat::getUnits(), - FALSE, - array('onChange' => "selectMetric( this.value );") - ); - $form->add( - 'text', - 'margin_left', - ts('Left Margin'), - array('size' => 8, 'maxlength' => 8, 'onkeyup' => "showUpdateFormatChkBox();"), - TRUE - ); - $form->add( - 'text', - 'margin_right', - ts('Right Margin'), - array('size' => 8, 'maxlength' => 8, 'onkeyup' => "showUpdateFormatChkBox();"), - TRUE - ); - $form->add( - 'text', - 'margin_top', - ts('Top Margin'), - array('size' => 8, 'maxlength' => 8, 'onkeyup' => "showUpdateFormatChkBox();"), - TRUE - ); - $form->add( - 'text', - 'margin_bottom', - ts('Bottom Margin'), - array('size' => 8, 'maxlength' => 8, 'onkeyup' => "showUpdateFormatChkBox();"), - TRUE - ); - - $config = CRM_Core_Config::singleton(); - /** CRM-15883 Suppressing Stationery path field until we switch from DOMPDF to a library that supports it. - if ($config->wkhtmltopdfPath == FALSE) { - $form->add( - 'text', - 'stationery', - ts('Stationery (relative path to PDF you wish to use as the background)'), - array('size' => 25, 'maxlength' => 900, 'onkeyup' => "showUpdateFormatChkBox();"), - FALSE - ); - } - */ - $form->add('checkbox', 'bind_format', ts('Always use this Page Format with the selected Template')); - $form->add('checkbox', 'update_format', ts('Update Page Format (this will affect all templates that use this format)')); - - $form->assign('useThisPageFormat', ts('Always use this Page Format with the new template?')); - $form->assign('useSelectedPageFormat', ts('Should the new template always use the selected Page Format?')); - $form->assign('totalSelectedContacts', count($form->_contactIds)); - - $form->add('select', 'document_type', ts('Document Type'), CRM_Core_SelectValues::documentFormat()); - - $documentTypes = implode(',', CRM_Core_SelectValues::documentApplicationType()); - $form->addElement('file', "document_file", 'Upload Document', 'size=30 maxlength=255 accept="' . $documentTypes . '"'); - $form->addUploadElement("document_file"); - - CRM_Mailing_BAO_Mailing::commonCompose($form); - - $buttons = array(); - if ($form->get('action') != CRM_Core_Action::VIEW) { - $buttons[] = array( - 'type' => 'upload', - 'name' => ts('Download Document'), - 'isDefault' => TRUE, - 'icon' => 'fa-download', - ); - $buttons[] = array( - 'type' => 'submit', - 'name' => ts('Preview'), - 'subName' => 'preview', - 'icon' => 'fa-search', - 'isDefault' => FALSE, - ); - } - $buttons[] = array( - 'type' => 'cancel', - 'name' => $form->get('action') == CRM_Core_Action::VIEW ? ts('Done') : ts('Cancel'), - ); - $form->addButtons($buttons); - - $form->addFormRule(array('CRM_Contact_Form_Task_PDFLetterCommon', 'formRule'), $form); - } - - /** - * Set default values. - */ - public static function setDefaultValues() { - $defaultFormat = CRM_Core_BAO_PdfFormat::getDefaultValues(); - $defaultFormat['format_id'] = $defaultFormat['id']; - return $defaultFormat; - } - - /** - * Form rule. - * - * @param array $fields - * The input form values. - * @param array $files - * @param array $self - * Additional values form 'this'. - * - * @return bool - * TRUE if no errors, else array of errors. - */ - public static function formRule($fields, $files, $self) { - $errors = array(); - $template = CRM_Core_Smarty::singleton(); - - // If user uploads non-document file other than odt/docx - if (empty($fields['template']) && - !empty($files['document_file']['tmp_name']) && - array_search($files['document_file']['type'], CRM_Core_SelectValues::documentApplicationType()) == NULL - ) { - $errors['document_file'] = ts('Invalid document file format'); - } - //Added for CRM-1393 - if (!empty($fields['saveTemplate']) && empty($fields['saveTemplateName'])) { - $errors['saveTemplateName'] = ts("Enter name to save message template"); - } - if (!is_numeric($fields['margin_left'])) { - $errors['margin_left'] = 'Margin must be numeric'; - } - if (!is_numeric($fields['margin_right'])) { - $errors['margin_right'] = 'Margin must be numeric'; - } - if (!is_numeric($fields['margin_top'])) { - $errors['margin_top'] = 'Margin must be numeric'; + if (count($form->_contactIds) === 1) { + CRM_Utils_System::setTitle(ts('Print/Merge Document for %1', [1 => CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $cid, 'display_name')])); } - if (!is_numeric($fields['margin_bottom'])) { - $errors['margin_bottom'] = 'Margin must be numeric'; - } - return empty($errors) ? TRUE : $errors; } /** @@ -261,62 +96,9 @@ public static function formRule($fields, $files, $self) { * [$categories, $html_message, $messageToken, $returnProperties] */ public static function processMessageTemplate($formValues) { - $html_message = CRM_Utils_Array::value('html_message', $formValues); - - // process message template - if (!empty($formValues['saveTemplate']) || !empty($formValues['updateTemplate'])) { - $messageTemplate = array( - 'msg_text' => NULL, - 'msg_html' => $formValues['html_message'], - 'msg_subject' => NULL, - 'is_active' => TRUE, - ); - - $messageTemplate['pdf_format_id'] = 'null'; - if (!empty($formValues['bind_format']) && $formValues['format_id']) { - $messageTemplate['pdf_format_id'] = $formValues['format_id']; - } - if (!empty($formValues['saveTemplate']) && $formValues['saveTemplate']) { - $messageTemplate['msg_title'] = $formValues['saveTemplateName']; - CRM_Core_BAO_MessageTemplate::add($messageTemplate); - } - - if (!empty($formValues['updateTemplate']) && $formValues['template'] && $formValues['updateTemplate']) { - $messageTemplate['id'] = $formValues['template']; - - unset($messageTemplate['msg_title']); - CRM_Core_BAO_MessageTemplate::add($messageTemplate); - } - } - elseif (CRM_Utils_Array::value('template', $formValues) > 0) { - if (!empty($formValues['bind_format']) && $formValues['format_id']) { - $query = "UPDATE civicrm_msg_template SET pdf_format_id = {$formValues['format_id']} WHERE id = {$formValues['template']}"; - } - else { - $query = "UPDATE civicrm_msg_template SET pdf_format_id = NULL WHERE id = {$formValues['template']}"; - } - CRM_Core_DAO::executeQuery($query); + $html_message = self::processTemplate($formValues); - $documentInfo = CRM_Core_BAO_File::getEntityFile('civicrm_msg_template', $formValues['template']); - foreach ((array) $documentInfo as $info) { - list($html_message, $formValues['document_type']) = CRM_Utils_PDF_Document::docReader($info['fullPath'], $info['mime_type']); - $formValues['document_file_path'] = $info['fullPath']; - } - } - // extract the content of uploaded document file - elseif (!empty($formValues['document_file'])) { - list($html_message, $formValues['document_type']) = CRM_Utils_PDF_Document::docReader($formValues['document_file']['name'], $formValues['document_file']['type']); - $formValues['document_file_path'] = $formValues['document_file']['name']; - } - - if (!empty($formValues['update_format'])) { - $bao = new CRM_Core_BAO_PdfFormat(); - $bao->savePdfFormat($formValues, $formValues['format_id']); - } - - $tokens = array(); - CRM_Utils_Hook::tokens($tokens); - $categories = array_keys($tokens); + $categories = self::getTokenCategories(); //time being hack to strip ' ' //from particular letter line, CRM-6798 @@ -324,20 +106,22 @@ public static function processMessageTemplate($formValues) { $messageToken = CRM_Utils_Token::getTokens($html_message); - $returnProperties = array(); + $returnProperties = []; if (isset($messageToken['contact'])) { foreach ($messageToken['contact'] as $key => $value) { $returnProperties[$value] = 1; } } - return array($formValues, $categories, $html_message, $messageToken, $returnProperties); + return [$formValues, $categories, $html_message, $messageToken, $returnProperties]; } /** * Process the form after the input has been submitted and validated. * * @param CRM_Core_Form $form + * + * @throws \CRM_Core_Exception */ public static function postProcess(&$form) { $formValues = $form->controller->exportValues($form->getName()); @@ -345,11 +129,15 @@ public static function postProcess(&$form) { $buttonName = $form->controller->getButtonName(); $skipOnHold = isset($form->skipOnHold) ? $form->skipOnHold : FALSE; $skipDeceased = isset($form->skipDeceased) ? $form->skipDeceased : TRUE; - $html = $document = array(); + $html = $activityIds = []; + + // CRM-21255 - Hrm, CiviCase 4+5 seem to report buttons differently... + $c = $form->controller->container(); + $isLiveMode = ($buttonName == '_qf_PDF_upload') || isset($c['values']['PDF']['buttons']['_qf_PDF_upload']); // CRM-16725 Skip creation of activities if user is previewing their PDF letter(s) - if ($buttonName == '_qf_PDF_upload') { - self::createActivities($form, $html_message, $form->_contactIds); + if ($isLiveMode) { + $activityIds = self::createActivities($form, $html_message, $form->_contactIds, $formValues['subject'], CRM_Utils_Array::value('campaign_id', $formValues)); } if (!empty($formValues['document_file_path'])) { @@ -358,7 +146,7 @@ public static function postProcess(&$form) { foreach ($form->_contactIds as $item => $contactId) { $caseId = NULL; - $params = array('contact_id' => $contactId); + $params = ['contact_id' => $contactId]; list($contact) = CRM_Utils_Token::getTokenDetails($params, $returnProperties, @@ -396,123 +184,168 @@ public static function postProcess(&$form) { $html[] = $tokenHtml; } + $tee = NULL; + if ($isLiveMode && Civi::settings()->get('recordGeneratedLetters') === 'combined-attached') { + if (count($activityIds) !== 1) { + throw new CRM_Core_Exception("When recordGeneratedLetters=combined-attached, there should only be one activity."); + } + $tee = CRM_Utils_ConsoleTee::create()->start(); + } + $type = $formValues['document_type']; + $mimeType = self::getMimeType($type); + // ^^ Useful side-effect: consistently throws error for unrecognized types. if ($type == 'pdf') { - CRM_Utils_PDF_Utils::html2pdf($html, "CiviLetter.pdf", FALSE, $formValues); + $fileName = "CiviLetter.$type"; + CRM_Utils_PDF_Utils::html2pdf($html, $fileName, FALSE, $formValues); } elseif (!empty($formValues['document_file_path'])) { - CRM_Utils_PDF_Document::printDocuments($formValues['document_file_path'], $html, $type, $zip); + $fileName = pathinfo($formValues['document_file_path'], PATHINFO_FILENAME) . '.' . $type; + CRM_Utils_PDF_Document::printDocuments($html, $fileName, $type, $zip); } else { - CRM_Utils_PDF_Document::html2doc($html, "CiviLetter.$type", $formValues); + $fileName = "CiviLetter.$type"; + CRM_Utils_PDF_Document::html2doc($html, $fileName, $formValues); + } + + if ($tee) { + $tee->stop(); + $content = file_get_contents($tee->getFileName(), NULL, NULL, NULL, 5); + if (empty($content)) { + throw new \CRM_Core_Exception("Failed to capture document content (type=$type)!"); + } + foreach ($activityIds as $activityId) { + civicrm_api3('Attachment', 'create', [ + 'entity_table' => 'civicrm_activity', + 'entity_id' => $activityId, + 'name' => $fileName, + 'mime_type' => $mimeType, + 'options' => [ + 'move-file' => $tee->getFileName(), + ], + ]); + } } $form->postProcessHook(); - CRM_Utils_System::civiExit(1); + CRM_Utils_System::civiExit(); } /** * @param CRM_Core_Form $form - * @param $html_message - * @param $contactIds + * @param string $html_message + * @param array $contactIds + * @param string $subject + * @param int $campaign_id + * @param array $perContactHtml + * + * @return array + * List of activity IDs. + * There may be 1 or more, depending on the system-settings + * and use-case. * * @throws CRM_Core_Exception */ - public static function createActivities($form, $html_message, $contactIds) { - //Added for CRM-12682: Add activity subject and campaign fields - $formValues = $form->controller->exportValues($form->getName()); + public static function createActivities($form, $html_message, $contactIds, $subject, $campaign_id, $perContactHtml = []) { - $session = CRM_Core_Session::singleton(); - $userID = $session->get('userID'); - $activityTypeID = CRM_Core_OptionGroup::getValue( - 'activity_type', - 'Print PDF Letter', - 'name' - ); - $activityParams = array( - 'subject' => $formValues['subject'], - 'campaign_id' => CRM_Utils_Array::value('campaign_id', $formValues), - 'source_contact_id' => $userID, - 'activity_type_id' => $activityTypeID, + $activityParams = [ + 'subject' => $subject, + 'campaign_id' => $campaign_id, + 'source_contact_id' => CRM_Core_Session::singleton()->getLoggedInContactID(), + 'activity_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Print PDF Letter'), 'activity_date_time' => date('YmdHis'), 'details' => $html_message, - ); + ]; if (!empty($form->_activityId)) { - $activityParams += array('id' => $form->_activityId); + $activityParams += ['id' => $form->_activityId]; } - if ($form->_cid) { - $activity = CRM_Activity_BAO_Activity::create($activityParams); - if (!empty($form->_caseId)) { - $caseActivityParams = array('activity_id' => $activity->id, 'case_id' => $form->_caseId); - CRM_Case_BAO_Case::processCaseActivity($caseActivityParams); - } - } - else { - // create Print PDF activity for each selected contact. CRM-6886 - $activityIds = array(); - foreach ($contactIds as $contactId) { - $activityID = CRM_Activity_BAO_Activity::create($activityParams); - $activityIds[$contactId] = $activityID->id; - } + + $activityIds = []; + switch (Civi::settings()->get('recordGeneratedLetters')) { + case 'none': + return []; + + case 'multiple': + // One activity per contact. + foreach ($contactIds as $i => $contactId) { + $fullParams = [ + 'target_contact_id' => $contactId, + ] + $activityParams; + if (!empty($form->_caseId)) { + $fullParams['case_id'] = $form->_caseId; + } + elseif (!empty($form->_caseIds[$i])) { + $fullParams['case_id'] = $form->_caseIds[$i]; + } + + if (isset($perContactHtml[$contactId])) { + $fullParams['details'] = implode('
', $perContactHtml[$contactId]); + } + $activity = civicrm_api3('Activity', 'create', $fullParams); + $activityIds[$contactId] = $activity['id']; + } + + break; + + case 'combined': + case 'combined-attached': + // One activity with all contacts. + $fullParams = [ + 'target_contact_id' => $contactIds, + ] + $activityParams; + if (!empty($form->_caseId)) { + $fullParams['case_id'] = $form->_caseId; + } + elseif (!empty($form->_caseIds)) { + $fullParams['case_id'] = $form->_caseIds; + } + $activity = civicrm_api3('Activity', 'create', $fullParams); + $activityIds[] = $activity['id']; + break; + + default: + throw new CRM_Core_Exception("Unrecognized option in recordGeneratedLetters: " . Civi::settings()->get('recordGeneratedLetters')); } - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); - $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts); + return $activityIds; + } - //@todo why are we using $form->_contactIds here & contactIds above - need comment - foreach ($form->_contactIds as $contactId) { - $activityTargetParams = array( - 'activity_id' => empty($activity->id) ? $activityIds[$contactId] : $activity->id, - 'contact_id' => $contactId, - 'record_type_id' => $targetID, - ); - CRM_Activity_BAO_ActivityContact::create($activityTargetParams); + /** + * Convert from a vague-type/file-extension to mime-type. + * + * @param string $type + * @return string + * @throws \CRM_Core_Exception + */ + private static function getMimeType($type) { + $mimeTypes = [ + 'pdf' => 'application/pdf', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'odt' => 'application/vnd.oasis.opendocument.text', + 'html' => 'text/html', + ]; + if (isset($mimeTypes[$type])) { + return $mimeTypes[$type]; + } + else { + throw new \CRM_Core_Exception("Cannot determine mime type"); } } /** - * @param $message + * Get the categories required for rendering tokens. + * + * @return array */ - public static function formatMessage(&$message) { - $newLineOperators = array( - 'p' => array( - 'oper' => '

', - 'pattern' => '/<(\s+)?p(\s+)?>/m', - ), - 'br' => array( - 'oper' => '
', - 'pattern' => '/<(\s+)?br(\s+)?\/>/m', - ), - ); - - $htmlMsg = preg_split($newLineOperators['p']['pattern'], $message); - foreach ($htmlMsg as $k => & $m) { - $messages = preg_split($newLineOperators['br']['pattern'], $m); - foreach ($messages as $key => & $msg) { - $msg = trim($msg); - $matches = array(); - if (preg_match('/^( )+/', $msg, $matches)) { - $spaceLen = strlen($matches[0]) / 6; - $trimMsg = ltrim($msg, '  '); - $charLen = strlen($trimMsg); - $totalLen = $charLen + $spaceLen; - if ($totalLen > 100) { - $spacesCount = 10; - if ($spaceLen > 50) { - $spacesCount = 20; - } - if ($charLen > 100) { - $spacesCount = 1; - } - $msg = str_repeat(' ', $spacesCount) . $trimMsg; - } - } - } - $m = implode($newLineOperators['br']['oper'], $messages); + protected static function getTokenCategories() { + if (!isset(Civi::$statics[__CLASS__]['token_categories'])) { + $tokens = []; + CRM_Utils_Hook::tokens($tokens); + Civi::$statics[__CLASS__]['token_categories'] = array_keys($tokens); } - $message = implode($newLineOperators['p']['oper'], $htmlMsg); + return Civi::$statics[__CLASS__]['token_categories']; } } diff --git a/CRM/Contact/Form/Task/PickProfile.php b/CRM/Contact/Form/Task/PickProfile.php index 63b1d88de22a..a3a061b8dde3 100644 --- a/CRM/Contact/Form/Task/PickProfile.php +++ b/CRM/Contact/Form/Task/PickProfile.php @@ -1,9 +1,9 @@ _contactIds) > $this->_maxContacts) { - CRM_Core_Session::setStatus(ts("The maximum number of contacts you can select for Update multiple contacts is %1. You have selected %2. Please select fewer contacts from your search results and try again.", array( - 1 => $this->_maxContacts, - 2 => count($this->_contactIds), - )), ts('Maximum Exceeded'), 'error'); + CRM_Core_Session::setStatus(ts("The maximum number of contacts you can select for Update multiple contacts is %1. You have selected %2. Please select fewer contacts from your search results and try again.", [ + 1 => $this->_maxContacts, + 2 => count($this->_contactIds), + ]), ts('Maximum Exceeded'), 'error'); $validate = TRUE; } @@ -106,10 +109,10 @@ public function buildQuickForm() { if (empty($profiles)) { $types = implode(' ' . ts('or') . ' ', $this->_contactTypes); - CRM_Core_Session::setStatus(ts("The contact type selected for Update multiple contacts does not have a corresponding profile. Please set up a profile for %1s and try again.", array(1 => $types)), ts('No Profile Available'), 'error'); + CRM_Core_Session::setStatus(ts("The contact type selected for Update multiple contacts does not have a corresponding profile. Please set up a profile for %1s and try again.", [1 => $types]), ts('No Profile Available'), 'error'); CRM_Utils_System::redirect($this->_userContext); } - $ufGroupElement = $this->add('select', 'uf_group_id', ts('Select Profile'), array('' => ts('- select profile -')) + $profiles, TRUE, array('class' => 'crm-select2 huge')); + $ufGroupElement = $this->add('select', 'uf_group_id', ts('Select Profile'), ['' => ts('- select profile -')] + $profiles, TRUE, ['class' => 'crm-select2 huge']); $this->addDefaultButtons(ts('Continue')); } @@ -118,7 +121,7 @@ public function buildQuickForm() { * Add local and global form rules. */ public function addRules() { - $this->addFormRule(array('CRM_Contact_Form_Task_PickProfile', 'formRule')); + $this->addFormRule(['CRM_Contact_Form_Task_PickProfile', 'formRule']); } /** diff --git a/CRM/Contact/Form/Task/Print.php b/CRM/Contact/Form/Task/Print.php index 8882d123a9d2..911af3dd8257 100644 --- a/CRM/Contact/Form/Task/Print.php +++ b/CRM/Contact/Form/Task/Print.php @@ -1,9 +1,9 @@ _contactIds as $contactId) { - $params[] = array( + $params[] = [ CRM_Core_Form::CB_PREFIX . $contactId, '=', 1, 0, 0, - ); + ]; } } @@ -82,8 +82,9 @@ public function preProcess() { $selectorName = $this->controller->selectorName(); require_once str_replace('_', DIRECTORY_SEPARATOR, $selectorName) . '.php'; - $returnP = isset($returnPropeties) ? $returnPropeties : ""; + $returnP = isset($returnProperties) ? $returnProperties : ""; $customSearchClass = $this->get('customSearchClass'); + $this->assign('customSearchID', $this->get('customSearchID')); $selector = new $selectorName($customSearchClass, $fv, $params, @@ -111,19 +112,18 @@ public function buildQuickForm() { // // just need to add a javacript to popup the window for printing // - $this->addButtons(array( - array( - 'type' => 'next', - 'name' => ts('Print Contact List'), - 'js' => array('onclick' => 'window.print()'), - 'isDefault' => TRUE, - ), - array( - 'type' => 'back', - 'name' => ts('Done'), - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'next', + 'name' => ts('Print Contact List'), + 'js' => ['onclick' => 'window.print()'], + 'isDefault' => TRUE, + ], + [ + 'type' => 'back', + 'name' => ts('Done'), + ], + ]); } /** diff --git a/CRM/Contact/Form/Task/ProximityCommon.php b/CRM/Contact/Form/Task/ProximityCommon.php index 5bb7418e7401..6f632cb9de6e 100644 --- a/CRM/Contact/Form/Task/ProximityCommon.php +++ b/CRM/Contact/Form/Task/ProximityCommon.php @@ -1,9 +1,9 @@ assign('proximity_search', TRUE); @@ -74,18 +74,18 @@ static public function buildQuickForm($form, $proxSearch) { $form->add('text', 'prox_postal_code', ts('Postal Code'), NULL, FALSE); - $form->addChainSelect('prox_state_province_id', array('required' => $proxRequired)); + $form->addChainSelect('prox_state_province_id', ['required' => $proxRequired]); - $country = array('' => ts('- select -')) + CRM_Core_PseudoConstant::country(); + $country = ['' => ts('- select -')] + CRM_Core_PseudoConstant::country(); $form->add('select', 'prox_country_id', ts('Country'), $country, $proxRequired); $form->add('text', 'prox_distance', ts('Distance'), NULL, $proxRequired); - $proxUnits = array('km' => ts('km'), 'miles' => ts('miles')); + $proxUnits = ['km' => ts('km'), 'miles' => ts('miles')]; $form->add('select', 'prox_distance_unit', ts('Units'), $proxUnits, $proxRequired); // prox_distance_unit - $form->addFormRule(array('CRM_Contact_Form_Task_ProximityCommon', 'formRule'), $form); + $form->addFormRule(['CRM_Contact_Form_Task_ProximityCommon', 'formRule'], $form); } /** @@ -101,13 +101,13 @@ static public function buildQuickForm($form, $proxSearch) { * true if no errors, else array of errors */ public static function formRule($fields, $files, $form) { - $errors = array(); + $errors = []; // If Distance is present, make sure state, country and city or postal code are populated. if (!empty($fields['prox_distance'])) { if (empty($fields['prox_state_province_id']) || empty($fields['prox_country_id'])) { $errors["prox_state_province_id"] = ts("Country AND State/Province are required to search by distance."); } - if (!CRM_Utils_Array::value('prox_postal_code', $fields) AND + if (!CRM_Utils_Array::value('prox_postal_code', $fields) and !CRM_Utils_Array::value('prox_city', $fields) ) { $errors["prox_distance"] = ts("City OR Postal Code are required to search by distance."); @@ -125,8 +125,8 @@ public static function formRule($fields, $files, $form) { * @return array * the default array reference */ - static public function setDefaultValues($form) { - $defaults = array(); + public static function setDefaultValues($form) { + $defaults = []; $config = CRM_Core_Config::singleton(); $countryDefault = $config->defaultContactCountry; diff --git a/CRM/Contact/Form/Task/RemoveFromGroup.php b/CRM/Contact/Form/Task/RemoveFromGroup.php index 72b41e51382e..d803f436e7b6 100644 --- a/CRM/Contact/Form/Task/RemoveFromGroup.php +++ b/CRM/Contact/Form/Task/RemoveFromGroup.php @@ -1,9 +1,9 @@ ts('- select group -')) + CRM_Core_PseudoConstant::nestedGroup(); - $groupElement = $this->add('select', 'group_id', ts('Select Group'), $group, TRUE, array('class' => 'crm-select2 huge')); + $group = ['' => ts('- select group -')] + CRM_Core_PseudoConstant::nestedGroup(); + $groupElement = $this->add('select', 'group_id', ts('Select Group'), $group, TRUE, ['class' => 'crm-select2 huge']); CRM_Utils_System::setTitle(ts('Remove Contacts from Group')); $this->addDefaultButtons(ts('Remove from Group')); @@ -58,7 +58,7 @@ public function buildQuickForm() { * the default array reference */ public function setDefaultValues() { - $defaults = array(); + $defaults = []; if ($this->get('context') === 'smog') { $defaults['group_id'] = $this->get('gid'); @@ -75,24 +75,24 @@ public function postProcess() { list($total, $removed, $notRemoved) = CRM_Contact_BAO_GroupContact::removeContactsFromGroup($this->_contactIds, $groupId); - $status = array( - ts("%count contact removed from '%2'", array( + $status = [ + ts("%count contact removed from '%2'", [ 'count' => $removed, 'plural' => "%count contacts removed from '%2'", 2 => $group[$groupId], - )), - ); + ]), + ]; if ($notRemoved) { - $status[] = ts('1 contact was already not in this group', array( - 'count' => $notRemoved, - 'plural' => '%count contacts were already not in this group', - )); + $status[] = ts('1 contact was already not in this group', [ + 'count' => $notRemoved, + 'plural' => '%count contacts were already not in this group', + ]); } $status = '

  • ' . implode('
  • ', $status) . '
'; - CRM_Core_Session::setStatus($status, ts("Removed Contact From Group", array( - 'plural' => "Removed Contacts From Group", - 'count' => $removed, - )), 'success', array('expires' => 0)); + CRM_Core_Session::setStatus($status, ts("Removed Contact From Group", [ + 'plural' => "Removed Contacts From Group", + 'count' => $removed, + ]), 'success', ['expires' => 0]); } } diff --git a/CRM/Contact/Form/Task/RemoveFromTag.php b/CRM/Contact/Form/Task/RemoveFromTag.php index 5857e816621a..3c18e5b3e2e3 100644 --- a/CRM/Contact/Form/Task/RemoveFromTag.php +++ b/CRM/Contact/Form/Task/RemoveFromTag.php @@ -1,9 +1,9 @@ addFormRule(array('CRM_Contact_Form_Task_RemoveFromTag', 'formRule')); + $this->addFormRule(['CRM_Contact_Form_Task_RemoveFromTag', 'formRule']); } /** @@ -77,7 +77,7 @@ public function addRules() { * @return array */ public static function formRule($form, $rule) { - $errors = array(); + $errors = []; if (empty($form['tag']) && empty($form['contact_taglist'])) { $errors['_qf_default'] = "Please select atleast one tag."; } @@ -91,7 +91,7 @@ public function postProcess() { //get the submitted values in an array $params = $this->controller->exportValues($this->_name); - $contactTags = $tagList = array(); + $contactTags = $tagList = []; // check if contact tags exists if (!empty($params['tag'])) { @@ -120,27 +120,27 @@ public function postProcess() { // merge contact and taglist tags $allTags = CRM_Utils_Array::crmArrayMerge($contactTags, $tagList); - $this->_name = array(); + $this->_name = []; foreach ($allTags as $key => $dnc) { $this->_name[] = $this->_tags[$key]; list($total, $removed, $notRemoved) = CRM_Core_BAO_EntityTag::removeEntitiesFromTag($this->_contactIds, $key, 'civicrm_contact', FALSE); - $status = array( - ts('%count contact un-tagged', array( - 'count' => $removed, - 'plural' => '%count contacts un-tagged', - )), - ); + $status = [ + ts('%count contact un-tagged', [ + 'count' => $removed, + 'plural' => '%count contacts un-tagged', + ]), + ]; if ($notRemoved) { - $status[] = ts('1 contact already did not have this tag', array( - 'count' => $notRemoved, - 'plural' => '%count contacts already did not have this tag', - )); + $status[] = ts('1 contact already did not have this tag', [ + 'count' => $notRemoved, + 'plural' => '%count contacts already did not have this tag', + ]); } $status = '
  • ' . implode('
  • ', $status) . '
'; - CRM_Core_Session::setStatus($status, ts("Removed Tag %1", array(1 => $this->_tags[$key])), 'success', array('expires' => 0)); + CRM_Core_Session::setStatus($status, ts("Removed Tag %1", [1 => $this->_tags[$key]]), 'success', ['expires' => 0]); } } diff --git a/CRM/Contact/Form/Task/Result.php b/CRM/Contact/Form/Task/Result.php index f062bbfbc066..22c8bea27382 100644 --- a/CRM/Contact/Form/Task/Result.php +++ b/CRM/Contact/Form/Task/Result.php @@ -1,9 +1,9 @@ set('searchRows', ''); $context = $this->get('context'); - if (in_array($context, array( + if (in_array($context, [ 'smog', 'amtg', - ))) { + ])) { $urlParams = 'reset=1&force=1&context=smog&gid='; $urlParams .= ($context == 'smog') ? $this->get('gid') : $this->get('amtgID'); $session->replaceUserContext(CRM_Utils_System::url('civicrm/group/search', $urlParams)); @@ -94,14 +94,13 @@ public function preProcess() { * Build the form object. */ public function buildQuickForm() { - $this->addButtons(array( - array( - 'type' => 'done', - 'name' => ts('Done'), - 'isDefault' => TRUE, - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'done', + 'name' => ts('Done'), + 'isDefault' => TRUE, + ], + ]); } } diff --git a/CRM/Contact/Form/Task/SMS.php b/CRM/Contact/Form/Task/SMS.php index ae629e3945bb..113c763cd218 100644 --- a/CRM/Contact/Form/Task/SMS.php +++ b/CRM/Contact/Form/Task/SMS.php @@ -1,9 +1,9 @@ _context = CRM_Utils_Request::retrieve('context', 'String', $this); + $this->_context = CRM_Utils_Request::retrieve('context', 'Alphanumeric', $this); $cid = CRM_Utils_Request::retrieve('cid', 'Positive', $this, FALSE); diff --git a/CRM/Contact/Form/Task/SMSCommon.php b/CRM/Contact/Form/Task/SMSCommon.php index 17e990386902..eba06f8006ed 100644 --- a/CRM/Contact/Form/Task/SMSCommon.php +++ b/CRM/Contact/Form/Task/SMSCommon.php @@ -1,9 +1,9 @@ _activityHolderIds)) { CRM_Core_Error::statusBounce(ts("The Reply SMS Could only be sent for activities with '%1' subject.", - array(1 => self::RECIEVED_SMS_ACTIVITY_SUBJECT) + [1 => self::RECIEVED_SMS_ACTIVITY_SUBJECT] )); } } @@ -88,11 +87,11 @@ public static function preProcessProvider(&$form) { */ public static function buildQuickForm(&$form) { - $toArray = array(); + $toArray = []; $providers = CRM_SMS_BAO_Provider::getProviders(NULL, NULL, TRUE, 'is_default desc'); - $providerSelect = array(); + $providerSelect = []; foreach ($providers as $provider) { $providerSelect[$provider['id']] = $provider['title']; } @@ -101,11 +100,11 @@ public static function buildQuickForm(&$form) { $cid = $form->get('cid'); if ($cid) { - $form->_contactIds = array($cid); + $form->_contactIds = [$cid]; } - $to = $form->add('text', 'to', ts('To'), array('class' => 'huge'), TRUE); - $form->add('text', 'activity_subject', ts('Name The SMS'), array('class' => 'huge'), TRUE); + $to = $form->add('text', 'to', ts('To'), ['class' => 'huge'], TRUE); + $form->add('text', 'activity_subject', ts('Name The SMS'), ['class' => 'huge'], TRUE); $toSetDefault = TRUE; if (property_exists($form, '_context') && $form->_context == 'standalone') { @@ -113,11 +112,11 @@ public static function buildQuickForm(&$form) { } // when form is submitted recompute contactIds - $allToSMS = array(); + $allToSMS = []; if ($to->getValue()) { $allToPhone = explode(',', $to->getValue()); - $form->_contactIds = array(); + $form->_contactIds = []; foreach ($allToPhone as $value) { list($contactId, $phone) = explode('::', $value); if ($contactId) { @@ -140,7 +139,7 @@ public static function buildQuickForm(&$form) { continue; } - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts); //target contacts limit check $ids = array_keys(CRM_Activity_BAO_ActivityContact::getNames($id, $targetID)); @@ -156,30 +155,30 @@ public static function buildQuickForm(&$form) { if (!$validActivities) { $errorMess = ""; if ($extendTargetContacts) { - $errorMess = ts('One selected activity consists of more than one target contact.', array( + $errorMess = ts('One selected activity consists of more than one target contact.', [ 'count' => $extendTargetContacts, 'plural' => '%count selected activities consist of more than one target contact.', - )); + ]); } if ($invalidActivity) { $errorMess = ($errorMess ? ' ' : ''); - $errorMess .= ts('The selected activity is invalid.', array( + $errorMess .= ts('The selected activity is invalid.', [ 'count' => $invalidActivity, 'plural' => '%count selected activities are invalid.', - )); + ]); } - CRM_Core_Error::statusBounce(ts("%1: SMS Reply will not be sent.", array(1 => $errorMess))); + CRM_Core_Error::statusBounce(ts("%1: SMS Reply will not be sent.", [1 => $errorMess])); } } if (is_array($form->_contactIds) && !empty($form->_contactIds) && $toSetDefault) { - $returnProperties = array( + $returnProperties = [ 'sort_name' => 1, 'phone' => 1, 'do_not_sms' => 1, 'is_deceased' => 1, 'display_name' => 1, - ); + ]; list($form->_contactDetails) = CRM_Utils_Token::getTokenDetails($form->_contactIds, $returnProperties, @@ -198,6 +197,7 @@ public static function buildQuickForm(&$form) { if (CRM_Utils_System::getClassName($form) == 'CRM_Activity_Form_Task_SMS') { //to check for "if the contact id belongs to a specified activity type" + // @todo use the api instead - function is deprecated. $actDetails = CRM_Activity_BAO_Activity::getContactActivity($contactId); if (self::RECIEVED_SMS_ACTIVITY_SUBJECT != CRM_Utils_Array::retrieveValueRecursive($actDetails, 'subject') @@ -214,7 +214,7 @@ public static function buildQuickForm(&$form) { if (!empty($value['phone']) && $value['phone_type_id'] != CRM_Utils_Array::value('Mobile', $phoneTypes) && empty($value['is_deceased']) ) { - $filter = array('do_not_sms' => 0); + $filter = ['do_not_sms' => 0]; $contactPhones = CRM_Core_BAO_Phone::allPhones($contactId, FALSE, 'Mobile', $filter); if (count($contactPhones) > 0) { $mobilePhone = CRM_Utils_Array::retrieveValueRecursive($contactPhones, 'phone'); @@ -246,10 +246,10 @@ public static function buildQuickForm(&$form) { } if ($phone) { - $toArray[] = array( + $toArray[] = [ 'text' => '"' . $value['sort_name'] . '" (' . $phone . ')', 'id' => "$contactId::{$phone}", - ); + ]; } } @@ -293,7 +293,7 @@ public static function buildQuickForm(&$form) { $form->addDefaultButtons(ts('Send SMS'), 'upload'); } - $form->addFormRule(array('CRM_Contact_Form_Task_SMSCommon', 'formRule'), $form); + $form->addFormRule(['CRM_Contact_Form_Task_SMSCommon', 'formRule'], $form); } /** @@ -309,7 +309,7 @@ public static function buildQuickForm(&$form) { * true if no errors, else array of errors */ public static function formRule($fields, $dontCare, $self) { - $errors = array(); + $errors = []; $template = CRM_Core_Smarty::singleton(); @@ -321,7 +321,7 @@ public static function formRule($fields, $dontCare, $self) { $messageCheck = CRM_Utils_Array::value('sms_text_message', $fields); $messageCheck = str_replace("\r\n", "\n", $messageCheck); if ($messageCheck && (strlen($messageCheck) > CRM_SMS_Provider::MAX_SMS_CHAR)) { - $errors['sms_text_message'] = ts("You can configure the SMS message body up to %1 characters", array(1 => CRM_SMS_Provider::MAX_SMS_CHAR)); + $errors['sms_text_message'] = ts("You can configure the SMS message body up to %1 characters", [1 => CRM_SMS_Provider::MAX_SMS_CHAR]); } } } @@ -348,11 +348,11 @@ public static function postProcess(&$form) { // process message template if (!empty($thisValues['SMSsaveTemplate']) || !empty($thisValues['SMSupdateTemplate'])) { - $messageTemplate = array( + $messageTemplate = [ 'msg_text' => $thisValues['sms_text_message'], 'is_active' => TRUE, 'is_sms' => TRUE, - ); + ]; if (!empty($thisValues['SMSsaveTemplate'])) { $messageTemplate['msg_title'] = $thisValues['SMSsaveTemplateName']; @@ -367,8 +367,8 @@ public static function postProcess(&$form) { } // format contact details array to handle multiple sms from same contact - $formattedContactDetails = array(); - $tempPhones = array(); + $formattedContactDetails = []; + $tempPhones = []; foreach ($form->_contactIds as $key => $contactId) { $phone = $form->_toContactPhone[$key]; @@ -399,10 +399,10 @@ public static function postProcess(&$form) { ); if ($countSuccess > 0) { - CRM_Core_Session::setStatus(ts('One message was sent successfully.', array( - 'plural' => '%count messages were sent successfully.', - 'count' => $countSuccess, - )), ts('Message Sent', array('plural' => 'Messages Sent', 'count' => $countSuccess)), 'success'); + CRM_Core_Session::setStatus(ts('One message was sent successfully.', [ + 'plural' => '%count messages were sent successfully.', + 'count' => $countSuccess, + ]), ts('Message Sent', ['plural' => 'Messages Sent', 'count' => $countSuccess]), 'success'); } if (is_array($sent)) { @@ -413,17 +413,17 @@ public static function postProcess(&$form) { $status .= '
  • ' . $errMsg . '
  • '; } $status .= ''; - CRM_Core_Session::setStatus($status, ts('One Message Not Sent', array( - 'count' => count($sent), - 'plural' => '%count Messages Not Sent', - )), 'info'); + CRM_Core_Session::setStatus($status, ts('One Message Not Sent', [ + 'count' => count($sent), + 'plural' => '%count Messages Not Sent', + ]), 'info'); } else { //Display the name and number of contacts for those sms is not sent. $smsNotSent = array_diff_assoc($allContactIds, $contactIds); if (!empty($smsNotSent)) { - $not_sent = array(); + $not_sent = []; foreach ($smsNotSent as $index => $contactId) { $displayName = $form->_allContactDetails[$contactId]['display_name']; $phone = $form->_allContactDetails[$contactId]['phone']; @@ -432,13 +432,13 @@ public static function postProcess(&$form) { } $status = '(' . ts('because no phone number on file or communication preferences specify DO NOT SMS or Contact is deceased'); if (CRM_Utils_System::getClassName($form) == 'CRM_Activity_Form_Task_SMS') { - $status .= ' ' . ts("or the contact is not part of the activity '%1'", array(1 => self::RECIEVED_SMS_ACTIVITY_SUBJECT)); + $status .= ' ' . ts("or the contact is not part of the activity '%1'", [1 => self::RECIEVED_SMS_ACTIVITY_SUBJECT]); } $status .= ')
    • ' . implode('
    • ', $not_sent) . '
    '; - CRM_Core_Session::setStatus($status, ts('One Message Not Sent', array( - 'count' => count($smsNotSent), - 'plural' => '%count Messages Not Sent', - )), 'info'); + CRM_Core_Session::setStatus($status, ts('One Message Not Sent', [ + 'count' => count($smsNotSent), + 'plural' => '%count Messages Not Sent', + ]), 'info'); } } } diff --git a/CRM/Contact/Form/Task/SaveSearch.php b/CRM/Contact/Form/Task/SaveSearch.php index e4ef5cb0202b..f871162a3433 100644 --- a/CRM/Contact/Form/Task/SaveSearch.php +++ b/CRM/Contact/Form/Task/SaveSearch.php @@ -1,9 +1,9 @@ controller->exportValues('Basic'); } + // Get Task name + $modeValue = CRM_Contact_Form_Search::getModeValue($values['component_mode']); + $className = $modeValue['taskClassName']; + $taskList = $className::taskTitles(); $this->_task = CRM_Utils_Array::value('task', $values); - $crmContactTaskTasks = CRM_Contact_Task::taskTitles(); - $this->assign('taskName', CRM_Utils_Array::value($this->_task, $crmContactTaskTasks)); + $this->assign('taskName', CRM_Utils_Array::value($this->_task, $taskList)); } /** @@ -118,6 +121,7 @@ public function buildQuickForm() { //CRM-14190 CRM_Group_Form_Edit::buildParentGroups($this); + CRM_Group_Form_Edit::buildGroupOrganizations($this); // get the group id for the saved search $groupID = NULL; @@ -134,7 +138,7 @@ public function buildQuickForm() { $this->assign('partiallySelected', $formValues['radio_ts'] != 'ts_all'); } $this->addRule('title', ts('Name already exists in Database.'), - 'objectExists', array('CRM_Contact_DAO_Group', $groupID, 'title') + 'objectExists', ['CRM_Contact_DAO_Group', $groupID, 'title'] ); } @@ -156,9 +160,10 @@ public function postProcess() { if (!$this->_id) { //save record in mapping table - $mappingParams = array('mapping_type' => 'Search Builder'); - $temp = array(); - $mapping = CRM_Core_BAO_Mapping::add($mappingParams, $temp); + $mappingParams = [ + 'mapping_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_Mapping', 'mapping_type_id', 'Search Builder'), + ]; + $mapping = CRM_Core_BAO_Mapping::add($mappingParams); $mappingId = $mapping->id; } else { @@ -178,10 +183,7 @@ public function postProcess() { $savedSearch = new CRM_Contact_BAO_SavedSearch(); $savedSearch->id = $this->_id; $queryParams = $this->get('queryParams'); - // CRM-18585 include selected operator in $savedSearch->form_values - if (!empty($formValues['operator'])) { - $queryParams[] = array('operator', '=', $formValues['operator'], 0, 0); - } + // Use the query parameters rather than the form values - these have already been assessed / converted // with the extra knowledge that the form has. // Note that we want to move towards a standardised way of saving the query that is not @@ -189,6 +191,8 @@ public function postProcess() { // Ideally per CRM-17075 we will use entity reference fields heavily in the form layer & convert to the // sql operator syntax at the query layer. if (!$isSearchBuilder) { + CRM_Contact_BAO_SavedSearch::saveRelativeDates($queryParams, $formValues); + CRM_Contact_BAO_SavedSearch::saveSkippedElement($queryParams, $formValues); $savedSearch->form_values = serialize($queryParams); } else { @@ -201,18 +205,15 @@ public function postProcess() { $savedSearch->search_custom_id = $this->get('customSearchID'); $savedSearch->save(); $this->set('ssID', $savedSearch->id); - CRM_Core_Session::setStatus(ts("Your smart group has been saved as '%1'.", array(1 => $formValues['title'])), ts('Group Saved'), 'success'); + CRM_Core_Session::setStatus(ts("Your smart group has been saved as '%1'.", [1 => $formValues['title']]), ts('Group Saved'), 'success'); // also create a group that is associated with this saved search only if new saved search - $params = array(); + $params = []; $params['title'] = $formValues['title']; $params['description'] = $formValues['description']; - if (isset($formValues['group_type']) && - is_array($formValues['group_type']) - ) { + if (isset($formValues['group_type']) && is_array($formValues['group_type']) && count($formValues['group_type'])) { $params['group_type'] = CRM_Core_DAO::VALUE_SEPARATOR . implode(CRM_Core_DAO::VALUE_SEPARATOR, - array_keys($formValues['group_type']) - ) . CRM_Core_DAO::VALUE_SEPARATOR; + array_keys($formValues['group_type'])) . CRM_Core_DAO::VALUE_SEPARATOR; } else { $params['group_type'] = ''; @@ -228,7 +229,18 @@ public function postProcess() { $params['id'] = CRM_Contact_BAO_SavedSearch::getName($this->_id, 'id'); } - CRM_Contact_BAO_Group::create($params); + $group = CRM_Contact_BAO_Group::create($params); + + // Update mapping with the name and description of the group. + if ($mappingId && $group) { + $mappingParams = [ + 'id' => $mappingId, + 'name' => CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Group', $group->id, 'name', 'id'), + 'description' => CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Group', $group->id, 'description', 'id'), + 'mapping_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_Mapping', 'mapping_type_id', 'Search Builder'), + ]; + CRM_Core_BAO_Mapping::add($mappingParams); + } // CRM-9464 $this->_id = $savedSearch->id; @@ -239,4 +251,17 @@ public function postProcess() { } } + /** + * Set form defaults. + * + * return array + */ + public function setDefaultValues() { + $defaults = []; + if (empty($defaults['parents'])) { + $defaults['parents'] = CRM_Core_BAO_Domain::getGroupId(); + } + return $defaults; + } + } diff --git a/CRM/Contact/Form/Task/SaveSearch/Update.php b/CRM/Contact/Form/Task/SaveSearch/Update.php index 3f1be4fc0bf8..4d6e7446a719 100644 --- a/CRM/Contact/Form/Task/SaveSearch/Update.php +++ b/CRM/Contact/Form/Task/SaveSearch/Update.php @@ -1,9 +1,9 @@ $this->_id); + $params = ['saved_search_id' => $this->_id]; CRM_Contact_BAO_Group::retrieve($params, $defaults); return $defaults; diff --git a/CRM/Contact/Form/Task/Unhold.php b/CRM/Contact/Form/Task/Unhold.php index 3da999ed314b..add289176475 100644 --- a/CRM/Contact/Form/Task/Unhold.php +++ b/CRM/Contact/Form/Task/Unhold.php @@ -27,15 +27,15 @@ public function postProcess() { $rowCount = $result->affectedRows(); if ($rowCount) { - CRM_Core_Session::setStatus(ts('%count email was found on hold and updated.', array( - 'count' => $rowCount, - 'plural' => '%count emails were found on hold and updated.', - )), ts('Emails Restored'), 'success'); + CRM_Core_Session::setStatus(ts('%count email was found on hold and updated.', [ + 'count' => $rowCount, + 'plural' => '%count emails were found on hold and updated.', + ]), ts('Emails Restored'), 'success'); } else { - CRM_Core_Session::setStatus(ts('The selected contact does not have an email on hold.', array( - 'plural' => 'None of the selected contacts have an email on hold.', - )), ts('No Emails to Restore'), 'info'); + CRM_Core_Session::setStatus(ts('The selected contact does not have an email on hold.', [ + 'plural' => 'None of the selected contacts have an email on hold.', + ]), ts('No Emails to Restore'), 'info'); } } else { diff --git a/CRM/Contact/Form/Task/Useradd.php b/CRM/Contact/Form/Task/Useradd.php index a13941cfc6a3..303fee2ad5b6 100644 --- a/CRM/Contact/Form/Task/Useradd.php +++ b/CRM/Contact/Form/Task/Useradd.php @@ -1,9 +1,9 @@ _contactId = CRM_Utils_Request::retrieve('cid', 'Positive', $this, TRUE); $params['id'] = $params['contact_id'] = $this->_contactId; $contact = CRM_Contact_BAO_Contact::retrieve($params, $defaults, $ids); $this->_displayName = $contact->display_name; $this->_email = $contact->email; - CRM_Utils_System::setTitle(ts('Create User Record for %1', array(1 => $this->_displayName))); + CRM_Utils_System::setTitle(ts('Create User Record for %1', [1 => $this->_displayName])); } /** * Set default values for the form. */ public function setDefaultValues() { - $defaults = array(); + $defaults = []; $defaults['contactID'] = $this->_contactId; $defaults['name'] = $this->_displayName; if (!empty($this->_email)) { @@ -80,32 +80,32 @@ public function setDefaultValues() { * Build the form object. */ public function buildQuickForm() { - $element = $this->add('text', 'name', ts('Full Name'), array('class' => 'huge')); + $element = $this->add('text', 'name', ts('Full Name'), ['class' => 'huge']); $element->freeze(); - $this->add('text', 'cms_name', ts('Username'), array('class' => 'huge')); + $this->add('text', 'cms_name', ts('Username'), ['class' => 'huge']); $this->addRule('cms_name', 'Username is required', 'required'); - $this->add('password', 'cms_pass', ts('Password'), array('class' => 'huge')); - $this->add('password', 'cms_confirm_pass', ts('Confirm Password'), array('class' => 'huge')); + $this->add('password', 'cms_pass', ts('Password'), ['class' => 'huge']); + $this->add('password', 'cms_confirm_pass', ts('Confirm Password'), ['class' => 'huge']); $this->addRule('cms_pass', 'Password is required', 'required'); - $this->addRule(array('cms_pass', 'cms_confirm_pass'), 'ERROR: Password mismatch', 'compare'); - $this->add('text', 'email', ts('Email:'), array('class' => 'huge'))->freeze(); + $this->addRule(['cms_pass', 'cms_confirm_pass'], 'ERROR: Password mismatch', 'compare'); + $this->add('text', 'email', ts('Email:'), ['class' => 'huge'])->freeze(); $this->add('hidden', 'contactID'); //add a rule to check username uniqueness - $this->addFormRule(array('CRM_Contact_Form_Task_Useradd', 'usernameRule')); + $this->addFormRule(['CRM_Contact_Form_Task_Useradd', 'usernameRule']); $this->addButtons( - array( - array( + [ + [ 'type' => 'next', 'name' => ts('Add'), 'isDefault' => TRUE, - ), - array( + ], + [ 'type' => 'cancel', 'name' => ts('Cancel'), - ), - ) + ], + ] ); $this->setDefaults($this->setDefaultValues()); } @@ -130,11 +130,11 @@ public function postProcess() { */ public static function usernameRule($params) { $config = CRM_Core_Config::singleton(); - $errors = array(); - $check_params = array( + $errors = []; + $check_params = [ 'name' => $params['cms_name'], 'mail' => $params['email'], - ); + ]; $config->userSystem->checkUserNameEmailExists($check_params, $errors); return empty($errors) ? TRUE : $errors; diff --git a/CRM/Contact/Import/Controller.php b/CRM/Contact/Import/Controller.php index ca610bb72dea..b483639ce785 100644 --- a/CRM/Contact/Import/Controller.php +++ b/CRM/Contact/Import/Controller.php @@ -1,9 +1,9 @@ addActions($config->uploadDir, array('uploadFile')); + $this->addActions($config->uploadDir, ['uploadFile']); } } diff --git a/CRM/Contact/Import/Field.php b/CRM/Contact/Import/Field.php index 95b01f49bf15..a1abc023439e 100644 --- a/CRM/Contact/Import/Field.php +++ b/CRM/Contact/Import/Field.php @@ -1,9 +1,9 @@ uploadDir); - $errorFiles = array('sqlImport.errors', 'sqlImport.conflicts', 'sqlImport.duplicates', 'sqlImport.mismatch'); + $errorFiles = ['sqlImport.errors', 'sqlImport.conflicts', 'sqlImport.duplicates', 'sqlImport.mismatch']; // check for post max size avoid when called twice $snippet = CRM_Utils_Array::value('snippet', $_GET, 0); @@ -81,10 +81,10 @@ public function preProcess() { } closedir($handler); if (!empty($results)) { - CRM_Core_Error::fatal(ts('%1 file(s) in %2 directory are not writable. Listed file(s) might be used during the import to log the errors occurred during Import process. Contact your site administrator for assistance.', array( - 1 => implode(', ', $results), - 2 => $config->uploadDir, - ))); + CRM_Core_Error::fatal(ts('%1 file(s) in %2 directory are not writable. Listed file(s) might be used during the import to log the errors occurred during Import process. Contact your site administrator for assistance.', [ + 1 => implode(', ', $results), + 2 => $config->uploadDir, + ])); } $this->_dataSourceIsValid = FALSE; @@ -146,11 +146,11 @@ public function buildQuickForm() { $this->assign('urlPathVar', 'snippet=4'); $this->add('select', 'dataSource', ts('Data Source'), $dataSources, TRUE, - array('onchange' => 'buildDataSourceFormBlock(this.value);') + ['onchange' => 'buildDataSourceFormBlock(this.value);'] ); // duplicate handling options - $duplicateOptions = array(); + $duplicateOptions = []; $duplicateOptions[] = $this->createElement('radio', NULL, NULL, ts('Skip'), CRM_Import_Parser::DUPLICATE_SKIP ); @@ -168,17 +168,14 @@ public function buildQuickForm() { ts('For Duplicate Contacts') ); - $mappingArray = CRM_Core_BAO_Mapping::getMappings(CRM_Core_OptionGroup::getValue('mapping_type', - 'Import Contact', - 'name' - )); + $mappingArray = CRM_Core_BAO_Mapping::getMappings('Import Contact'); $this->assign('savedMapping', $mappingArray); - $this->addElement('select', 'savedMapping', ts('Mapping Option'), array('' => ts('- select -')) + $mappingArray); + $this->addElement('select', 'savedMapping', ts('Mapping Option'), ['' => ts('- select -')] + $mappingArray); - $js = array('onClick' => "buildSubTypes();buildDedupeRules();"); + $js = ['onClick' => "buildSubTypes();buildDedupeRules();"]; // contact types option - $contactOptions = array(); + $contactOptions = []; if (CRM_Contact_BAO_ContactType::isActive('Individual')) { $contactOptions[] = $this->createElement('radio', NULL, NULL, ts('Individual'), CRM_Import_Parser::CONTACT_INDIVIDUAL, $js @@ -206,31 +203,30 @@ public function buildQuickForm() { $config = CRM_Core_Config::singleton(); $geoCode = FALSE; - if (!empty($config->geocodeMethod)) { + if (CRM_Utils_GeocodeProvider::getUsableClassName()) { $geoCode = TRUE; - $this->addElement('checkbox', 'doGeocodeAddress', ts('Lookup mapping info during import?')); + $this->addElement('checkbox', 'doGeocodeAddress', ts('Geocode addresses during import?')); } $this->assign('geoCode', $geoCode); - $this->addElement('text', 'fieldSeparator', ts('Import Field Separator'), array('size' => 2)); + $this->addElement('text', 'fieldSeparator', ts('Import Field Separator'), ['size' => 2]); if (Civi::settings()->get('address_standardization_provider') == 'USPS') { $this->addElement('checkbox', 'disableUSPS', ts('Disable USPS address validation during import?')); } - $this->addButtons(array( - array( - 'type' => 'upload', - 'name' => ts('Continue'), - 'spacing' => '          ', - 'isDefault' => TRUE, - ), - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'upload', + 'name' => ts('Continue'), + 'spacing' => '          ', + 'isDefault' => TRUE, + ], + [ + 'type' => 'cancel', + 'name' => ts('Cancel'), + ], + ]); } /** @@ -243,12 +239,12 @@ public function buildQuickForm() { */ public function setDefaultValues() { $config = CRM_Core_Config::singleton(); - $defaults = array( + $defaults = [ 'dataSource' => 'CRM_Import_DataSource_CSV', 'onDuplicate' => CRM_Import_Parser::DUPLICATE_SKIP, 'contactType' => CRM_Import_Parser::CONTACT_INDIVIDUAL, 'fieldSeparator' => $config->fieldSeparator, - ); + ]; if ($loadeMapping = $this->get('loadedMapping')) { $this->assign('loadedMapping', $loadeMapping); @@ -271,7 +267,7 @@ private function _getDataSources() { // Open the data source dir and scan it for class files global $civicrm_root; $dataSourceDir = $civicrm_root . DIRECTORY_SEPARATOR . 'CRM' . DIRECTORY_SEPARATOR . 'Import' . DIRECTORY_SEPARATOR . 'DataSource' . DIRECTORY_SEPARATOR; - $dataSources = array(); + $dataSources = []; if (!is_dir($dataSourceDir)) { CRM_Core_Error::fatal("Import DataSource directory $dataSourceDir does not exist"); } @@ -281,7 +277,7 @@ private function _getDataSources() { while (($dataSourceFile = readdir($dataSourceHandle)) !== FALSE) { $fileType = filetype($dataSourceDir . $dataSourceFile); - $matches = array(); + $matches = []; if (($fileType == 'file' || $fileType == 'link') && preg_match('/^(.+)\.php$/', $dataSourceFile, $matches) ) { @@ -310,14 +306,14 @@ public function postProcess() { // Setup the params array $this->_params = $this->controller->exportValues($this->_name); - $storeParams = array( + $storeParams = [ 'onDuplicate' => 'onDuplicate', 'dedupe' => 'dedupe', 'contactType' => 'contactType', 'contactSubType' => 'subType', 'dateFormats' => 'dateFormats', 'savedMapping' => 'savedMapping', - ); + ]; foreach ($storeParams as $storeName => $storeValueName) { $$storeName = $this->exportValue($storeValueName); @@ -346,7 +342,7 @@ public function postProcess() { // We should have the data in the DB now, parse it $importTableName = $this->get('importTableName'); $fieldNames = $this->_prepareImportTable($db, $importTableName); - $mapper = array(); + $mapper = []; $parser = new CRM_Contact_Import_Parser_Contact($mapper); $parser->setMaxLinesToProcess(100); @@ -407,7 +403,7 @@ private function _prepareImportTable($db, $importTableName) { AUTO_INCREMENT"; $db->query($alterQuery); - return array('status' => $statusFieldName, 'pk' => $primaryKeyName); + return ['status' => $statusFieldName, 'pk' => $primaryKeyName]; } /** diff --git a/CRM/Contact/Import/Form/MapField.php b/CRM/Contact/Import/Form/MapField.php index 61722c9810b9..fa24b121655c 100644 --- a/CRM/Contact/Import/Form/MapField.php +++ b/CRM/Contact/Import/Form/MapField.php @@ -1,9 +1,9 @@ _onDuplicate != CRM_Import_Parser::DUPLICATE_NOCHECK) { //Mark Dedupe Rule Fields as required, since it's used in matching contact foreach (array( - 'Individual', - 'Household', - 'Organization', - ) as $cType) { + 'Individual', + 'Household', + 'Organization', + ) as $cType) { $ruleParams = array( 'contact_type' => $cType, 'used' => 'Unsupervised', @@ -209,11 +209,10 @@ public function buildQuickForm() { else { $savedMapping = $this->get('savedMapping'); - list($mappingName, $mappingContactType, $mappingLocation, $mappingPhoneType, $mappingImProvider, $mappingRelation, $mappingOperator, $mappingValue, $mappingWebsiteType) = CRM_Core_BAO_Mapping::getMappingFields($savedMapping); + list($mappingName, $mappingContactType, $mappingLocation, $mappingPhoneType, $mappingImProvider, $mappingRelation, $mappingOperator, $mappingValue, $mappingWebsiteType) = CRM_Core_BAO_Mapping::getMappingFields($savedMapping, TRUE); //get loaded Mapping Fields $mappingName = CRM_Utils_Array::value(1, $mappingName); - $mappingContactType = CRM_Utils_Array::value(1, $mappingContactType); $mappingLocation = CRM_Utils_Array::value(1, $mappingLocation); $mappingPhoneType = CRM_Utils_Array::value(1, $mappingPhoneType); $mappingImProvider = CRM_Utils_Array::value(1, $mappingImProvider); @@ -250,8 +249,7 @@ public function buildQuickForm() { $dataPatterns = $this->get('dataPatterns'); $hasLocationTypes = $this->get('fieldTypes'); - $this->_location_types = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id'); - + $this->_location_types = array('Primary' => ts('Primary')) + CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id'); $defaultLocationType = CRM_Core_BAO_LocationType::getDefault(); // Pass default location to js @@ -320,7 +318,6 @@ public function buildQuickForm() { $cType = 'All'; } - $relatedFields = array(); $relatedFields = CRM_Contact_BAO_Contact::importableFields($cType); unset($relatedFields['']); $values = array(); @@ -611,7 +608,7 @@ public function buildQuickForm() { 'type' => 'cancel', 'name' => ts('Cancel'), ), - ) + ) ); } @@ -632,7 +629,7 @@ public static function formRule($fields) { $errors['saveMappingName'] = ts('Name is required to save Import Mapping'); } else { - $mappingTypeId = CRM_Core_OptionGroup::getValue('mapping_type', 'Import Contact', 'name'); + $mappingTypeId = CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_Mapping', 'mapping_type_id', 'Import Contact'); if (CRM_Core_BAO_Mapping::checkMapping($nameField, $mappingTypeId)) { $errors['saveMappingName'] = ts('Duplicate Import Mapping Name'); } @@ -666,43 +663,68 @@ public function postProcess() { $this->controller->resetPage($this->_name); return; } - - $mapper = array(); - $mapperKeys = array(); $mapperKeys = $this->controller->exportValue($this->_name, 'mapper'); - $mapperKeysMain = array(); + + $parser = $this->submit($params, $mapperKeys); + + // add all the necessary variables to the form + $parser->set($this); + } + + /** + * Format custom field name. + * + * Combine group and field name to avoid conflict. + * + * @param array $fields + * + * @return array + */ + public function formatCustomFieldName(&$fields) { + //CRM-2676, replacing the conflict for same custom field name from different custom group. + $fieldIds = $formattedFieldNames = array(); + foreach ($fields as $key => $value) { + if ($customFieldId = CRM_Core_BAO_CustomField::getKeyID($key)) { + $fieldIds[] = $customFieldId; + } + } + + if (!empty($fieldIds) && is_array($fieldIds)) { + $groupTitles = CRM_Core_BAO_CustomGroup::getGroupTitles($fieldIds); + + if (!empty($groupTitles)) { + foreach ($groupTitles as $fId => $values) { + $key = "custom_{$fId}"; + $groupTitle = $values['groupTitle']; + $formattedFieldNames[$key] = $fields[$key] . ' :: ' . $groupTitle; + } + } + } + + return $formattedFieldNames; + } + + /** + * Main submit function. + * + * Extracted to add testing & start refactoring. + * + * @param $params + * @param $mapperKeys + * + * @return \CRM_Contact_Import_Parser_Contact + */ + public function submit($params, $mapperKeys) { + $mapper = $mapperKeysMain = $locations = array(); + $parserParameters = CRM_Contact_Import_Parser_Contact::getParameterForParser($this->_columnCount); $phoneTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Phone', 'phone_type_id'); $imProviders = CRM_Core_PseudoConstant::get('CRM_Core_DAO_IM', 'provider_id'); $websiteTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Website', 'website_type_id'); $locationTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id'); - - //these mapper params need to set key as array and val as null. - $mapperParams = array( - 'related' => 'relatedVal', - 'locations' => 'locationsVal', - 'mapperLocType' => 'mapperLocTypeVal', - 'mapperPhoneType' => 'mapperPhoneTypeVal', - 'mapperImProvider' => 'mapperImProviderVal', - 'mapperWebsiteType' => 'mapperWebsiteTypeVal', - 'relatedContactType' => 'relatedContactTypeVal', - 'relatedContactDetails' => 'relatedContactDetailsVal', - 'relatedContactLocType' => 'relatedContactLocTypeVal', - 'relatedContactPhoneType' => 'relatedContactPhoneTypeVal', - 'relatedContactImProvider' => 'relatedContactImProviderVal', - 'relatedContactWebsiteType' => 'relatedContactWebsiteTypeVal', - ); - - //set respective mapper params to array. - foreach (array_keys($mapperParams) as $mapperParam) { - $$mapperParam = array(); - } + $locationTypes['Primary'] = ts('Primary'); for ($i = 0; $i < $this->_columnCount; $i++) { - //set respective mapper value to null - foreach (array_values($mapperParams) as $mapperParam) { - $$mapperParam = NULL; - } $fldName = CRM_Utils_Array::value(0, $mapperKeys[$i]); $selOne = CRM_Utils_Array::value(1, $mapperKeys[$i]); @@ -712,19 +734,19 @@ public function postProcess() { $mapperKeysMain[$i] = $fldName; //need to differentiate non location elements. - if ($selOne && is_numeric($selOne)) { + if ($selOne && (is_numeric($selOne) || $selOne === 'Primary')) { if ($fldName == 'url') { - $mapperWebsiteTypeVal = $websiteTypes[$selOne]; + $parserParameters['mapperWebsiteType'][$i] = $websiteTypes[$selOne]; } else { - $locationsVal = $locationTypes[$selOne]; - $mapperLocTypeVal = $selOne; + $locations[$i] = $locationTypes[$selOne]; + $parserParameters['mapperLocType'][$i] = $selOne; if ($selTwo && is_numeric($selTwo)) { if ($fldName == 'phone') { - $mapperPhoneTypeVal = $phoneTypes[$selTwo]; + $parserParameters['mapperPhoneType'][$i] = $phoneTypes[$selTwo]; } elseif ($fldName == 'im') { - $mapperImProviderVal = $imProviders[$selTwo]; + $parserParameters['mapperImProvider'][$i] = $imProviders[$selTwo]; } } } @@ -735,19 +757,19 @@ public function postProcess() { if (($first == 'a' && $second == 'b') || ($first == 'b' && $second == 'a') ) { - $relatedVal = $this->_mapperFields[$fldName]; + $parserParameters['mapperRelated'][$i] = $this->_mapperFields[$fldName]; if ($selOne) { if ($selOne == 'url') { - $relatedContactWebsiteTypeVal = $websiteTypes[$selTwo]; + $parserParameters['relatedContactWebsiteType'][$i] = $websiteTypes[$selTwo]; } else { - $relatedContactLocTypeVal = CRM_Utils_Array::value($selTwo, $locationTypes); + $parserParameters['relatedContactLocType'][$i] = CRM_Utils_Array::value($selTwo, $locationTypes); if ($selThree) { if ($selOne == 'phone') { - $relatedContactPhoneTypeVal = $phoneTypes[$selThree]; + $parserParameters['relatedContactPhoneType'][$i] = $phoneTypes[$selThree]; } elseif ($selOne == 'im') { - $relatedContactImProviderVal = $imProviders[$selThree]; + $parserParameters['relatedContactImProvider'][$i] = $imProviders[$selThree]; } } } @@ -756,44 +778,25 @@ public function postProcess() { $relationType = new CRM_Contact_DAO_RelationshipType(); $relationType->id = $id; $relationType->find(TRUE); - $relatedContactTypeVal = $relationType->{"contact_type_$second"}; - $relatedContactDetailsVal = $this->_formattedFieldNames[$relatedContactTypeVal][$selOne]; + $parserParameters['relatedContactType'][$i] = $relationType->{"contact_type_$second"}; + $parserParameters['relatedContactDetails'][$i] = $this->_formattedFieldNames[$parserParameters['relatedContactType'][$i]][$selOne]; } } - - //set the respective mapper param array values. - foreach ($mapperParams as $mapperParamKey => $mapperParamVal) { - ${$mapperParamKey}[$i] = $$mapperParamVal; - } } $this->set('columnNames', $this->_columnNames); - - //set main contact properties. - $properties = array( - 'ims' => 'mapperImProvider', - 'mapper' => 'mapper', - 'phones' => 'mapperPhoneType', - 'websites' => 'mapperWebsiteType', - 'locations' => 'locations', - ); - foreach ($properties as $propertyName => $propertyVal) { - $this->set($propertyName, $$propertyVal); - } - - //set related contact propeties. - $relProperties = array( - 'related', - 'relatedContactType', - 'relatedContactDetails', - 'relatedContactLocType', - 'relatedContactPhoneType', - 'relatedContactImProvider', - 'relatedContactWebsiteType', - ); - foreach ($relProperties as $relProperty) { - $this->set($relProperty, $$relProperty); - } + $this->set('websites', $parserParameters['mapperWebsiteType']); + $this->set('locations', $locations); + $this->set('phones', $parserParameters['mapperPhoneType']); + $this->set('ims', $parserParameters['mapperImProvider']); + $this->set('related', $parserParameters['mapperRelated']); + $this->set('relatedContactType', $parserParameters['relatedContactType']); + $this->set('relatedContactDetails', $parserParameters['relatedContactDetails']); + $this->set('relatedContactLocType', $parserParameters['relatedContactLocType']); + $this->set('relatedContactPhoneType', $parserParameters['relatedContactPhoneType']); + $this->set('relatedContactImProvider', $parserParameters['relatedContactImProvider']); + $this->set('relatedContactWebsiteType', $parserParameters['relatedContactWebsiteType']); + $this->set('mapper', $mapper); // store mapping Id to display it in the preview page $this->set('loadMappingId', CRM_Utils_Array::value('mappingId', $params)); @@ -801,8 +804,6 @@ public function postProcess() { //Updating Mapping Records if (!empty($params['updateMapping'])) { - $locationTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id'); - $mappingFields = new CRM_Core_DAO_MappingField(); $mappingFields->mapping_id = $params['mappingId']; $mappingFields->find(); @@ -859,8 +860,9 @@ public function postProcess() { elseif (CRM_Utils_Array::value('0', $mapperKeys[$i]) == 'im') { $updateMappingFields->im_provider_id = isset($mapperKeys[$i][2]) ? $mapperKeys[$i][2] : NULL; } - $location = array_keys($locationTypes, $locations[$i]); - $updateMappingFields->location_type_id = (isset($location) && isset($location[0])) ? $location[0] : NULL; + $locationTypeID = $parserParameters['mapperLocType'][$i]; + // location_type_id is NULL for non-location fields, and for Primary location. + $updateMappingFields->location_type_id = is_numeric($locationTypeID) ? $locationTypeID : 'null'; } } $updateMappingFields->save(); @@ -872,15 +874,11 @@ public function postProcess() { $mappingParams = array( 'name' => $params['saveMappingName'], 'description' => $params['saveMappingDesc'], - 'mapping_type_id' => CRM_Core_OptionGroup::getValue('mapping_type', - 'Import Contact', - 'name' - ), + 'mapping_type_id' => 'Import Contact', ); - $saveMapping = CRM_Core_BAO_Mapping::add($mappingParams); + $saveMapping = civicrm_api3('Mapping', 'create', $mappingParams); - $locationTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id'); $contactType = $this->get('contactType'); switch ($contactType) { case CRM_Import_Parser::CONTACT_INDIVIDUAL: @@ -897,7 +895,7 @@ public function postProcess() { for ($i = 0; $i < $this->_columnCount; $i++) { $saveMappingFields = new CRM_Core_DAO_MappingField(); - $saveMappingFields->mapping_id = $saveMapping->id; + $saveMappingFields->mapping_id = $saveMapping['id']; $saveMappingFields->contact_type = $cType; $saveMappingFields->column_number = $i; @@ -921,12 +919,12 @@ public function postProcess() { elseif (CRM_Utils_Array::value('1', $mapperKeys[$i]) == 'im') { $saveMappingFields->im_provider_id = isset($mapperKeys[$i][3]) ? $mapperKeys[$i][3] : NULL; } - $saveMappingFields->location_type_id = isset($mapperKeys[$i][2]) ? $mapperKeys[$i][2] : NULL; + $saveMappingFields->location_type_id = (isset($mapperKeys[$i][2]) && $mapperKeys[$i][2] !== 'Primary') ? $mapperKeys[$i][2] : NULL; } } else { $saveMappingFields->name = $mapper[$i]; - $location_id = array_keys($locationTypes, $locations[$i]); + $locationTypeID = $parserParameters['mapperLocType'][$i]; // to get phoneType id and provider id separately // before saving mappingFields of phone and IM, CRM-3140 if (CRM_Utils_Array::value('0', $mapperKeys[$i]) == 'url') { @@ -939,7 +937,7 @@ public function postProcess() { elseif (CRM_Utils_Array::value('0', $mapperKeys[$i]) == 'im') { $saveMappingFields->im_provider_id = isset($mapperKeys[$i][2]) ? $mapperKeys[$i][2] : NULL; } - $saveMappingFields->location_type_id = isset($location_id[0]) ? $location_id[0] : NULL; + $saveMappingFields->location_type_id = is_numeric($locationTypeID) ? $locationTypeID : NULL; } $saveMappingFields->relationship_type_id = NULL; } @@ -948,11 +946,11 @@ public function postProcess() { $this->set('savedMapping', $saveMappingFields->mapping_id); } - $parser = new CRM_Contact_Import_Parser_Contact($mapperKeysMain, $mapperLocType, $mapperPhoneType, - $mapperImProvider, $related, $relatedContactType, - $relatedContactDetails, $relatedContactLocType, - $relatedContactPhoneType, $relatedContactImProvider, - $mapperWebsiteType, $relatedContactWebsiteType + $parser = new CRM_Contact_Import_Parser_Contact($mapperKeysMain, $parserParameters['mapperLocType'], $parserParameters['mapperPhoneType'], + $parserParameters['mapperImProvider'], $parserParameters['mapperRelated'], $parserParameters['relatedContactType'], + $parserParameters['relatedContactDetails'], $parserParameters['relatedContactLocType'], + $parserParameters['relatedContactPhoneType'], $parserParameters['relatedContactImProvider'], + $parserParameters['mapperWebsiteType'], $parserParameters['relatedContactWebsiteType'] ); $primaryKeyName = $this->get('primaryKeyName'); @@ -969,42 +967,7 @@ public function postProcess() { $this->get('contactSubType'), $this->get('dedupe') ); - - // add all the necessary variables to the form - $parser->set($this); - } - - /** - * Format custom field name. - * - * Combine group and field name to avoid conflict. - * - * @param array $fields - * - * @return array - */ - public function formatCustomFieldName(&$fields) { - //CRM-2676, replacing the conflict for same custom field name from different custom group. - $fieldIds = $formattedFieldNames = array(); - foreach ($fields as $key => $value) { - if ($customFieldId = CRM_Core_BAO_CustomField::getKeyID($key)) { - $fieldIds[] = $customFieldId; - } - } - - if (!empty($fieldIds) && is_array($fieldIds)) { - $groupTitles = CRM_Core_BAO_CustomGroup::getGroupTitles($fieldIds); - - if (!empty($groupTitles)) { - foreach ($groupTitles as $fId => $values) { - $key = "custom_{$fId}"; - $groupTitle = $values['groupTitle']; - $formattedFieldNames[$key] = $fields[$key] . ' :: ' . $groupTitle; - } - } - } - - return $formattedFieldNames; + return $parser; } } diff --git a/CRM/Contact/Import/Form/Preview.php b/CRM/Contact/Import/Form/Preview.php index 57f8e08aaa98..e47a5a10474a 100644 --- a/CRM/Contact/Import/Form/Preview.php +++ b/CRM/Contact/Import/Form/Preview.php @@ -1,9 +1,9 @@ assign($property, $this->get($property)); } - $statusID = $this->get('statusID'); - if (!$statusID) { - $statusID = md5(uniqid(rand(), TRUE)); - $this->set('statusID', $statusID); - } - $statusUrl = CRM_Utils_System::url('civicrm/ajax/status', "id={$statusID}", FALSE, NULL, FALSE); - $this->assign('statusUrl', $statusUrl); + $this->setStatusUrl(); $showColNames = TRUE; if ('CRM_Import_DataSource_CSV' == $this->get('dataSource') && @@ -157,9 +151,9 @@ public function buildQuickForm() { if (!empty($groups)) { $this->addElement('select', 'groups', ts('Add imported records to existing group(s)'), $groups, array( - 'multiple' => "multiple", - 'class' => 'crm-select2', - )); + 'multiple' => "multiple", + 'class' => 'crm-select2', + )); } //display new tag @@ -193,7 +187,6 @@ public function buildQuickForm() { 'name' => ts('Import Now'), 'spacing' => '          ', 'isDefault' => TRUE, - 'js' => array('onclick' => "return verify( );"), ), array( 'type' => 'cancel', @@ -304,13 +297,9 @@ public function postProcess() { // run the import $importJob->runImport($this); - // update cache after we done with runImport - if (!CRM_Core_Permission::check('view all contacts')) { - CRM_ACL_BAO_Cache::updateEntry($userID); - } - - // clear all caches - CRM_Contact_BAO_Contact_Utils::clearContactCaches(); + // Clear all caches, forcing any searches to recheck the ACLs or group membership as the import + // may have changed it. + CRM_Contact_BAO_Contact_Utils::clearContactCaches(TRUE); // add all the necessary variables to the form $importJob->setFormVariables($this); diff --git a/CRM/Contact/Import/Form/Summary.php b/CRM/Contact/Import/Form/Summary.php index 506f439df221..811b3f62e22f 100644 --- a/CRM/Contact/Import/Form/Summary.php +++ b/CRM/Contact/Import/Form/Summary.php @@ -1,9 +1,9 @@ assign('dupeActionString', $dupeActionString); - $properties = array( + $properties = [ 'totalRowCount', 'validRowCount', 'invalidRowCount', @@ -110,7 +110,7 @@ public function preProcess() { 'tagAdditions', 'unMatchCount', 'unparsedAddressCount', - ); + ]; foreach ($properties as $property) { $this->assign($property, $this->get($property)); } diff --git a/CRM/Contact/Import/ImportJob.php b/CRM/Contact/Import/ImportJob.php index 5d206bc5415c..1cec44c09e8d 100644 --- a/CRM/Contact/Import/ImportJob.php +++ b/CRM/Contact/Import/ImportJob.php @@ -1,9 +1,9 @@ _tableName = $tableName; - - //initialize the properties. - $properties = array( - 'mapperKeys', - 'mapperRelated', - 'mapperLocTypes', - 'mapperPhoneTypes', - 'mapperImProviders', - 'mapperWebsiteTypes', - 'mapperRelatedContactType', - 'mapperRelatedContactDetails', - 'mapperRelatedContactLocType', - 'mapperRelatedContactPhoneType', - 'mapperRelatedContactImProvider', - 'mapperRelatedContactWebsiteType', - ); - foreach ($properties as $property) { - $this->{"_$property"} = array(); - } } /** @@ -169,31 +139,13 @@ public function setJobParams(&$params) { public function runImport(&$form, $timeout = 55) { $mapper = $this->_mapper; $mapperFields = array(); + $parserParameters = CRM_Contact_Import_Parser_Contact::getParameterForParser(count($mapper)); $phoneTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Phone', 'phone_type_id'); $imProviders = CRM_Core_PseudoConstant::get('CRM_Core_DAO_IM', 'provider_id'); $websiteTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Website', 'website_type_id'); - $locationTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id'); - - //initialize mapper perperty value. - $mapperPeroperties = array( - 'mapperRelated' => 'mapperRelatedVal', - 'mapperLocTypes' => 'mapperLocTypesVal', - 'mapperPhoneTypes' => 'mapperPhoneTypesVal', - 'mapperImProviders' => 'mapperImProvidersVal', - 'mapperWebsiteTypes' => 'mapperWebsiteTypesVal', - 'mapperRelatedContactType' => 'mapperRelatedContactTypeVal', - 'mapperRelatedContactDetails' => 'mapperRelatedContactDetailsVal', - 'mapperRelatedContactLocType' => 'mapperRelatedContactLocTypeVal', - 'mapperRelatedContactPhoneType' => 'mapperRelatedContactPhoneTypeVal', - 'mapperRelatedContactImProvider' => 'mapperRelatedContactImProviderVal', - 'mapperRelatedContactWebsiteType' => 'mapperRelatedContactWebsiteTypeVal', - ); + $locationTypes = array('Primary' => ts('Primary')) + CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id'); foreach ($mapper as $key => $value) { - //set respective mapper value to null. - foreach (array_values($mapperPeroperties) as $perpertyVal) { - $$perpertyVal = NULL; - } $fldName = CRM_Utils_Array::value(0, $mapper[$key]); $header = array($this->_mapFields[$fldName]); @@ -203,22 +155,23 @@ public function runImport(&$form, $timeout = 55) { $this->_mapperKeys[$key] = $fldName; //need to differentiate non location elements. - if ($selOne && is_numeric($selOne)) { + // @todo merge this with duplicate code on MapField class. + if ($selOne && (is_numeric($selOne) || $selOne === 'Primary')) { if ($fldName == 'url') { $header[] = $websiteTypes[$selOne]; - $mapperWebsiteTypesVal = $selOne; + $parserParameters['mapperWebsiteType'][$key] = $selOne; } else { $header[] = $locationTypes[$selOne]; - $mapperLocTypesVal = $selOne; + $parserParameters['mapperLocType'][$key] = $selOne; if ($selTwo && is_numeric($selTwo)) { if ($fldName == 'phone') { $header[] = $phoneTypes[$selTwo]; - $mapperPhoneTypesVal = $selTwo; + $parserParameters['mapperPhoneType'][$key] = $selTwo; } elseif ($fldName == 'im') { $header[] = $imProviders[$selTwo]; - $mapperImProvidersVal = $selTwo; + $parserParameters['mapperImProvider'][$key] = $selTwo; } } } @@ -237,27 +190,27 @@ public function runImport(&$form, $timeout = 55) { $relationType = new CRM_Contact_DAO_RelationshipType(); $relationType->id = $id; $relationType->find(TRUE); - $mapperRelatedContactTypeVal = $relationType->{"contact_type_$second"}; + $parserParameters['relatedContactType'][$key] = $relationType->{"contact_type_$second"}; - $mapperRelatedVal = $fldName; + $parserParameters['mapperRelated'][$key] = $fldName; if ($selOne) { - $mapperRelatedContactDetailsVal = $selOne; + $parserParameters['relatedContactDetails'][$key] = $selOne; if ($selTwo) { if ($selOne == 'url') { $header[] = $websiteTypes[$selTwo]; - $mapperRelatedContactWebsiteTypeVal = $selTwo; + $parserParameters[$key]['relatedContactWebsiteType'][$key] = $selTwo; } else { $header[] = $locationTypes[$selTwo]; - $mapperRelatedContactLocTypeVal = $selTwo; + $parserParameters['relatedContactLocType'][$key] = $selTwo; if ($selThree) { if ($selOne == 'phone') { $header[] = $phoneTypes[$selThree]; - $mapperRelatedContactPhoneTypeVal = $selThree; + $parserParameters['relatedContactPhoneType'][$key] = $selThree; } elseif ($selOne == 'im') { $header[] = $imProviders[$selThree]; - $mapperRelatedContactImProviderVal = $selThree; + $parserParameters['relatedContactImProvider'][$key] = $selThree; } } } @@ -265,26 +218,21 @@ public function runImport(&$form, $timeout = 55) { } } $mapperFields[] = implode(' - ', $header); - - //set the respective mapper param array values. - foreach ($mapperPeroperties as $mapperProKey => $mapperProVal) { - $this->{"_$mapperProKey"}[$key] = $$mapperProVal; - } } $this->_parser = new CRM_Contact_Import_Parser_Contact( $this->_mapperKeys, - $this->_mapperLocTypes, - $this->_mapperPhoneTypes, - $this->_mapperImProviders, - $this->_mapperRelated, - $this->_mapperRelatedContactType, - $this->_mapperRelatedContactDetails, - $this->_mapperRelatedContactLocType, - $this->_mapperRelatedContactPhoneType, - $this->_mapperRelatedContactImProvider, - $this->_mapperWebsiteTypes, - $this->_mapperRelatedContactWebsiteType + $parserParameters['mapperLocType'], + $parserParameters['mapperPhoneType'], + $parserParameters['mapperImProvider'], + $parserParameters['mapperRelated'], + $parserParameters['relatedContactType'], + $parserParameters['relatedContactDetails'], + $parserParameters['relatedContactLocType'], + $parserParameters['relatedContactPhoneType'], + $parserParameters['relatedContactImProvider'], + $parserParameters['mapperWebsiteType'], + $parserParameters['relatedContactWebsiteType'] ); $this->_parser->run($this->_tableName, $mapperFields, @@ -323,7 +271,7 @@ public function runImport(&$form, $timeout = 55) { } } - if ($this->_newTagName || count($this->_tag)) { + if ($this->_newTagName || !empty($this->_tag)) { $tagAdditions = $this->_tagImportedContactsWithNewTag($contactIds, $this->_newTagName, $this->_newTagDesc @@ -342,9 +290,12 @@ public function setFormVariables($form) { } /** - * @param $contactIds + * Add imported contacts. + * + * @param array $contactIds * @param string $newGroupName - * @param $newGroupDesc + * @param string $newGroupDesc + * @param string $newGroupType * * @return array|bool */ diff --git a/CRM/Contact/Import/Importer.php b/CRM/Contact/Import/Importer.php index 3001c7ebca5e..779d994b6ebe 100644 --- a/CRM/Contact/Import/Importer.php +++ b/CRM/Contact/Import/Importer.php @@ -1,9 +1,9 @@   " . ts('No processing status reported yet.') . "
    "; - echo json_encode(array(0, $status)); + echo json_encode([0, $status]); } CRM_Utils_System::civiExit(); } diff --git a/CRM/Contact/Import/Parser.php b/CRM/Contact/Import/Parser.php index ebb786e8ef03..8979fb64f6a2 100644 --- a/CRM/Contact/Import/Parser.php +++ b/CRM/Contact/Import/Parser.php @@ -1,9 +1,9 @@ _invalidRowCount = $this->_validCount = 0; $this->_totalCount = $this->_conflictCount = 0; - $this->_errors = array(); - $this->_warnings = array(); - $this->_conflicts = array(); - $this->_unparsedAddresses = array(); + $this->_errors = []; + $this->_warnings = []; + $this->_conflicts = []; + $this->_unparsedAddresses = []; $this->_tableName = $tableName; $this->_primaryKeyName = $primaryKeyName; $this->_statusFieldName = $statusFieldName; if ($mode == self::MODE_MAPFIELD) { - $this->_rows = array(); + $this->_rows = []; } else { $this->_activeFieldCount = count($this->_activeFields); @@ -167,20 +170,9 @@ public function run( } if ($statusID) { - $skip = 50; - // $skip = 1; - $config = CRM_Core_Config::singleton(); - $statusFile = "{$config->uploadDir}status_{$statusID}.txt"; - $status = "
      " . ts('No processing status reported yet.') . "
    "; - - //do not force the browser to display the save dialog, CRM-7640 - $contents = json_encode(array(0, $status)); - - file_put_contents($statusFile, $contents); - + $this->progressImport($statusID); $startTimestamp = $currTimestamp = $prevTimestamp = time(); } - // get the contents of the temp. import table $query = "SELECT * FROM $tableName"; if ($mode == self::MODE_IMPORT) { @@ -215,39 +207,9 @@ public function run( elseif ($mode == self::MODE_IMPORT) { //print "Running parser in import mode
    \n"; $returnCode = $this->import($onDuplicate, $values, $doGeocodeAddress); - if ($statusID && (($this->_rowCount % $skip) == 0)) { - $currTimestamp = time(); - $totalTime = ($currTimestamp - $startTimestamp); - $time = ($currTimestamp - $prevTimestamp); - $recordsLeft = $totalRowCount - $this->_rowCount; - if ($recordsLeft < 0) { - $recordsLeft = 0; - } - $estimatedTime = ($recordsLeft / $skip) * $time; - $estMinutes = floor($estimatedTime / 60); - $timeFormatted = ''; - if ($estMinutes > 1) { - $timeFormatted = $estMinutes . ' ' . ts('minutes') . ' '; - $estimatedTime = $estimatedTime - ($estMinutes * 60); - } - $timeFormatted .= round($estimatedTime) . ' ' . ts('seconds'); - $processedPercent = (int ) (($this->_rowCount * 100) / $totalRowCount); - $statusMsg = ts('%1 of %2 records - %3 remaining', - array(1 => $this->_rowCount, 2 => $totalRowCount, 3 => $timeFormatted) - ); - $status = " -
    {$statusMsg} -
    -"; - - $contents = json_encode(array($processedPercent, $status)); - - file_put_contents($statusFile, $contents); - - $prevTimestamp = $currTimestamp; + if ($statusID && (($this->_rowCount % 50) == 0)) { + $prevTimestamp = $this->progressImport($statusID, FALSE, $startTimestamp, $prevTimestamp, $totalRowCount); } - // sleep(1); } else { $returnCode = self::ERROR; @@ -271,10 +233,8 @@ public function run( if ($returnCode & self::ERROR) { $this->_invalidRowCount++; - if ($this->_invalidRowCount < $this->_maxErrorCount) { - array_unshift($values, $this->_rowCount); - $this->_errors[] = $values; - } + array_unshift($values, $this->_rowCount); + $this->_errors[] = $values; } if ($returnCode & self::CONFLICT) { @@ -318,9 +278,6 @@ public function run( break; } - // clean up memory from dao's - CRM_Core_DAO::freeResult(); - // see if we've hit our timeout yet /* if ( $the_thing_with_the_stuff ) { do_something( ); @@ -339,54 +296,44 @@ public function run( if ($this->_invalidRowCount) { // removed view url for invlaid contacts - $headers = array_merge(array( - ts('Line Number'), - ts('Reason'), - ), - $customHeaders - ); + $headers = array_merge([ + ts('Line Number'), + ts('Reason'), + ], $customHeaders); $this->_errorFileName = self::errorFileName(self::ERROR); self::exportCSV($this->_errorFileName, $headers, $this->_errors); } if ($this->_conflictCount) { - $headers = array_merge(array( - ts('Line Number'), - ts('Reason'), - ), - $customHeaders - ); + $headers = array_merge([ + ts('Line Number'), + ts('Reason'), + ], $customHeaders); $this->_conflictFileName = self::errorFileName(self::CONFLICT); self::exportCSV($this->_conflictFileName, $headers, $this->_conflicts); } if ($this->_duplicateCount) { - $headers = array_merge(array( - ts('Line Number'), - ts('View Contact URL'), - ), - $customHeaders - ); + $headers = array_merge([ + ts('Line Number'), + ts('View Contact URL'), + ], $customHeaders); $this->_duplicateFileName = self::errorFileName(self::DUPLICATE); self::exportCSV($this->_duplicateFileName, $headers, $this->_duplicates); } if ($this->_unMatchCount) { - $headers = array_merge(array( - ts('Line Number'), - ts('Reason'), - ), - $customHeaders - ); + $headers = array_merge([ + ts('Line Number'), + ts('Reason'), + ], $customHeaders); $this->_misMatchFilemName = self::errorFileName(self::NO_MATCH); self::exportCSV($this->_misMatchFilemName, $headers, $this->_unMatch); } if ($this->_unparsedAddressCount) { - $headers = array_merge(array( - ts('Line Number'), - ts('Contact Edit URL'), - ), - $customHeaders - ); + $headers = array_merge([ + ts('Line Number'), + ts('Contact Edit URL'), + ], $customHeaders); $this->_errorFileName = self::errorFileName(self::UNPARSED_ADDRESS_WARNING); self::exportCSV($this->_errorFileName, $headers, $this->_unparsedAddresses); } @@ -426,6 +373,7 @@ public function setActiveFieldLocationTypes($elements) { /** * @param $elements */ + /** * @param $elements */ @@ -531,7 +479,7 @@ public function setActiveFieldRelatedContactImProvider($elements) { * (reference ) associative array of name/value pairs */ public function &getActiveFieldParams() { - $params = array(); + $params = []; for ($i = 0; $i < $this->_activeFieldCount; $i++) { if ($this->_activeFields[$i]->_name == 'do_not_import') { @@ -541,13 +489,13 @@ public function &getActiveFieldParams() { if (isset($this->_activeFields[$i]->_value)) { if (isset($this->_activeFields[$i]->_hasLocationType)) { if (!isset($params[$this->_activeFields[$i]->_name])) { - $params[$this->_activeFields[$i]->_name] = array(); + $params[$this->_activeFields[$i]->_name] = []; } - $value = array( + $value = [ $this->_activeFields[$i]->_name => $this->_activeFields[$i]->_value, 'location_type_id' => $this->_activeFields[$i]->_hasLocationType, - ); + ]; if (isset($this->_activeFields[$i]->_phoneType)) { $value['phone_type_id'] = $this->_activeFields[$i]->_phoneType; @@ -561,10 +509,10 @@ public function &getActiveFieldParams() { $params[$this->_activeFields[$i]->_name][] = $value; } elseif (isset($this->_activeFields[$i]->_websiteType)) { - $value = array( + $value = [ $this->_activeFields[$i]->_name => $this->_activeFields[$i]->_value, 'website_type_id' => $this->_activeFields[$i]->_websiteType, - ); + ]; $params[$this->_activeFields[$i]->_name][] = $value; } @@ -578,7 +526,7 @@ public function &getActiveFieldParams() { //minor fix for CRM-4062 if (isset($this->_activeFields[$i]->_related)) { if (!isset($params[$this->_activeFields[$i]->_related])) { - $params[$this->_activeFields[$i]->_related] = array(); + $params[$this->_activeFields[$i]->_related] = []; } if (!isset($params[$this->_activeFields[$i]->_related]['contact_type']) && !empty($this->_activeFields[$i]->_relatedContactType)) { @@ -589,12 +537,12 @@ public function &getActiveFieldParams() { if (!empty($params[$this->_activeFields[$i]->_related][$this->_activeFields[$i]->_relatedContactDetails]) && !is_array($params[$this->_activeFields[$i]->_related][$this->_activeFields[$i]->_relatedContactDetails]) ) { - $params[$this->_activeFields[$i]->_related][$this->_activeFields[$i]->_relatedContactDetails] = array(); + $params[$this->_activeFields[$i]->_related][$this->_activeFields[$i]->_relatedContactDetails] = []; } - $value = array( + $value = [ $this->_activeFields[$i]->_relatedContactDetails => $this->_activeFields[$i]->_value, 'location_type_id' => $this->_activeFields[$i]->_relatedContactLocType, - ); + ]; if (isset($this->_activeFields[$i]->_relatedContactPhoneType)) { $value['phone_type_id'] = $this->_activeFields[$i]->_relatedContactPhoneType; @@ -608,10 +556,10 @@ public function &getActiveFieldParams() { $params[$this->_activeFields[$i]->_related][$this->_activeFields[$i]->_relatedContactDetails][] = $value; } elseif (isset($this->_activeFields[$i]->_relatedContactWebsiteType)) { - $params[$this->_activeFields[$i]->_related][$this->_activeFields[$i]->_relatedContactDetails][] = array( + $params[$this->_activeFields[$i]->_related][$this->_activeFields[$i]->_relatedContactDetails][] = [ 'url' => $this->_activeFields[$i]->_value, 'website_type_id' => $this->_activeFields[$i]->_relatedContactWebsiteType, - ); + ]; } else { $params[$this->_activeFields[$i]->_related][$this->_activeFields[$i]->_relatedContactDetails] = $this->_activeFields[$i]->_value; @@ -627,7 +575,7 @@ public function &getActiveFieldParams() { * @return array */ public function getColumnPatterns() { - $values = array(); + $values = []; foreach ($this->_fields as $name => $field) { $values[$name] = $field->_columnPattern; } @@ -728,8 +676,8 @@ public static function exportCSV($fileName, $header, $data) { CRM_Core_Error::movedSiteError($fileName); } //hack to remove '_status', '_statusMsg' and '_id' from error file - $errorValues = array(); - $dbRecordStatus = array('IMPORTED', 'ERROR', 'DUPLICATE', 'INVALID', 'NEW'); + $errorValues = []; + $dbRecordStatus = ['IMPORTED', 'ERROR', 'DUPLICATE', 'INVALID', 'NEW']; foreach ($data as $rowCount => $rowValues) { $count = 0; foreach ($rowValues as $key => $val) { @@ -742,7 +690,7 @@ public static function exportCSV($fileName, $header, $data) { } $data = $errorValues; - $output = array(); + $output = []; $fd = fopen($fileName, 'w'); foreach ($header as $key => $value) { @@ -779,11 +727,11 @@ public function updateImportRecord($id, &$params) { SET $statusFieldName = ?, ${statusFieldName}Msg = ? WHERE $primaryKeyName = ?"; - $args = array( + $args = [ $params[$statusFieldName], CRM_Utils_Array::value("${statusFieldName}Msg", $params), $id, - ); + ]; //print "Running query: $query
    With arguments: ".$params[$statusFieldName].", ".$params["${statusFieldName}Msg"].", $id
    "; @@ -791,4 +739,581 @@ public function updateImportRecord($id, &$params) { } } + /** + * Format common params data to proper format to store. + * + * @param array $params + * Contain record values. + * @param array $formatted + * Array of formatted data. + * @param array $contactFields + * Contact DAO fields. + */ + public function formatCommonData($params, &$formatted, &$contactFields) { + $csType = [ + CRM_Utils_Array::value('contact_type', $formatted), + ]; + + //CRM-5125 + //add custom fields for contact sub type + if (!empty($this->_contactSubType)) { + $csType = $this->_contactSubType; + } + + if ($relCsType = CRM_Utils_Array::value('contact_sub_type', $formatted)) { + $csType = $relCsType; + } + + $customFields = CRM_Core_BAO_CustomField::getFields($formatted['contact_type'], FALSE, FALSE, $csType); + + $addressCustomFields = CRM_Core_BAO_CustomField::getFields('Address'); + $customFields = $customFields + $addressCustomFields; + + //if a Custom Email Greeting, Custom Postal Greeting or Custom Addressee is mapped, and no "Greeting / Addressee Type ID" is provided, then automatically set the type = Customized, CRM-4575 + $elements = [ + 'email_greeting_custom' => 'email_greeting', + 'postal_greeting_custom' => 'postal_greeting', + 'addressee_custom' => 'addressee', + ]; + foreach ($elements as $k => $v) { + if (array_key_exists($k, $params) && !(array_key_exists($v, $params))) { + $label = key(CRM_Core_OptionGroup::values($v, TRUE, NULL, NULL, 'AND v.name = "Customized"')); + $params[$v] = $label; + } + } + + //format date first + $session = CRM_Core_Session::singleton(); + $dateType = $session->get("dateTypes"); + foreach ($params as $key => $val) { + $customFieldID = CRM_Core_BAO_CustomField::getKeyID($key); + if ($customFieldID && + !array_key_exists($customFieldID, $addressCustomFields) + ) { + //we should not update Date to null, CRM-4062 + if ($val && ($customFields[$customFieldID]['data_type'] == 'Date')) { + //CRM-21267 + CRM_Contact_Import_Parser_Contact::formatCustomDate($params, $formatted, $dateType, $key); + } + elseif ($customFields[$customFieldID]['data_type'] == 'Boolean') { + if (empty($val) && !is_numeric($val) && $this->_onDuplicate == CRM_Import_Parser::DUPLICATE_FILL) { + //retain earlier value when Import mode is `Fill` + unset($params[$key]); + } + else { + $params[$key] = CRM_Utils_String::strtoboolstr($val); + } + } + } + + if ($key == 'birth_date' && $val) { + CRM_Utils_Date::convertToDefaultDate($params, $dateType, $key); + } + elseif ($key == 'deceased_date' && $val) { + CRM_Utils_Date::convertToDefaultDate($params, $dateType, $key); + $params['is_deceased'] = 1; + } + elseif ($key == 'is_deceased' && $val) { + $params[$key] = CRM_Utils_String::strtoboolstr($val); + } + elseif ($key == 'gender') { + //CRM-4360 + $params[$key] = $this->checkGender($val); + } + } + + //now format custom data. + foreach ($params as $key => $field) { + if (is_array($field)) { + $isAddressCustomField = FALSE; + foreach ($field as $value) { + $break = FALSE; + if (is_array($value)) { + foreach ($value as $name => $testForEmpty) { + if ($addressCustomFieldID = CRM_Core_BAO_CustomField::getKeyID($name)) { + $isAddressCustomField = TRUE; + break; + } + // check if $value does not contain IM provider or phoneType + if (($name !== 'phone_type_id' || $name !== 'provider_id') && ($testForEmpty === '' || $testForEmpty == NULL)) { + $break = TRUE; + break; + } + } + } + else { + $break = TRUE; + } + + if (!$break) { + $this->formatContactParameters($value, $formatted); + } + } + if (!$isAddressCustomField) { + continue; + } + } + + $formatValues = [ + $key => $field, + ]; + + if (($key !== 'preferred_communication_method') && (array_key_exists($key, $contactFields))) { + // due to merging of individual table and + // contact table, we need to avoid + // preferred_communication_method forcefully + $formatValues['contact_type'] = $formatted['contact_type']; + } + + if ($key == 'id' && isset($field)) { + $formatted[$key] = $field; + } + $this->formatContactParameters($formatValues, $formatted); + + //Handling Custom Data + // note: Address custom fields will be handled separately inside formatContactParameters + if (($customFieldID = CRM_Core_BAO_CustomField::getKeyID($key)) && + array_key_exists($customFieldID, $customFields) && + !array_key_exists($customFieldID, $addressCustomFields) + ) { + + $extends = CRM_Utils_Array::value('extends', $customFields[$customFieldID]); + $htmlType = CRM_Utils_Array::value('html_type', $customFields[$customFieldID]); + switch ($htmlType) { + case 'Select': + case 'Radio': + case 'Autocomplete-Select': + if ($customFields[$customFieldID]['data_type'] == 'String' || $customFields[$customFieldID]['data_type'] == 'Int') { + $customOption = CRM_Core_BAO_CustomOption::getCustomOption($customFieldID, TRUE); + foreach ($customOption as $customValue) { + $val = CRM_Utils_Array::value('value', $customValue); + $label = CRM_Utils_Array::value('label', $customValue); + $label = strtolower($label); + $value = strtolower(trim($formatted[$key])); + if (($value == $label) || ($value == strtolower($val))) { + $params[$key] = $formatted[$key] = $val; + } + } + } + break; + + case 'CheckBox': + case 'Multi-Select': + + if (!empty($formatted[$key]) && !empty($params[$key])) { + $mulValues = explode(',', $formatted[$key]); + $customOption = CRM_Core_BAO_CustomOption::getCustomOption($customFieldID, TRUE); + $formatted[$key] = []; + $params[$key] = []; + foreach ($mulValues as $v1) { + foreach ($customOption as $v2) { + if ((strtolower($v2['label']) == strtolower(trim($v1))) || + (strtolower($v2['value']) == strtolower(trim($v1))) + ) { + if ($htmlType == 'CheckBox') { + $params[$key][$v2['value']] = $formatted[$key][$v2['value']] = 1; + } + else { + $params[$key][] = $formatted[$key][] = $v2['value']; + } + } + } + } + } + break; + } + } + } + + if (!empty($key) && ($customFieldID = CRM_Core_BAO_CustomField::getKeyID($key)) && array_key_exists($customFieldID, $customFields) && + !array_key_exists($customFieldID, $addressCustomFields) + ) { + // @todo calling api functions directly is not supported + _civicrm_api3_custom_format_params($params, $formatted, $extends); + } + + // to check if not update mode and unset the fields with empty value. + if (!$this->_updateWithId && array_key_exists('custom', $formatted)) { + foreach ($formatted['custom'] as $customKey => $customvalue) { + if (empty($formatted['custom'][$customKey][-1]['is_required'])) { + $formatted['custom'][$customKey][-1]['is_required'] = $customFields[$customKey]['is_required']; + } + $emptyValue = CRM_Utils_Array::value('value', $customvalue[-1]); + if (!isset($emptyValue)) { + unset($formatted['custom'][$customKey]); + } + } + } + + // parse street address, CRM-5450 + if ($this->_parseStreetAddress) { + if (array_key_exists('address', $formatted) && is_array($formatted['address'])) { + foreach ($formatted['address'] as $instance => & $address) { + $streetAddress = CRM_Utils_Array::value('street_address', $address); + if (empty($streetAddress)) { + continue; + } + // parse address field. + $parsedFields = CRM_Core_BAO_Address::parseStreetAddress($streetAddress); + + //street address consider to be parsed properly, + //If we get street_name and street_number. + if (empty($parsedFields['street_name']) || empty($parsedFields['street_number'])) { + $parsedFields = array_fill_keys(array_keys($parsedFields), ''); + } + + // merge parse address w/ main address block. + $address = array_merge($address, $parsedFields); + } + } + } + } + + /** + * Format contact parameters. + * + * @todo this function needs re-writing & re-merging into the main function. + * + * Here be dragons. + * + * @param array $values + * @param array $params + * + * @return bool + */ + protected function formatContactParameters(&$values, &$params) { + // Crawl through the possible classes: + // Contact + // Individual + // Household + // Organization + // Location + // Address + // Email + // Phone + // IM + // Note + // Custom + + // Cache the various object fields + static $fields = []; + + // first add core contact values since for other Civi modules they are not added + $contactFields = CRM_Contact_DAO_Contact::fields(); + _civicrm_api3_store_values($contactFields, $values, $params); + + if (isset($values['contact_type'])) { + // we're an individual/household/org property + + $fields[$values['contact_type']] = CRM_Contact_DAO_Contact::fields(); + + _civicrm_api3_store_values($fields[$values['contact_type']], $values, $params); + return TRUE; + } + + if (isset($values['individual_prefix'])) { + if (!empty($params['prefix_id'])) { + $prefixes = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'prefix_id'); + $params['prefix'] = $prefixes[$params['prefix_id']]; + } + else { + $params['prefix'] = $values['individual_prefix']; + } + return TRUE; + } + + if (isset($values['individual_suffix'])) { + if (!empty($params['suffix_id'])) { + $suffixes = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'suffix_id'); + $params['suffix'] = $suffixes[$params['suffix_id']]; + } + else { + $params['suffix'] = $values['individual_suffix']; + } + return TRUE; + } + + // CRM-4575 + if (isset($values['email_greeting'])) { + if (!empty($params['email_greeting_id'])) { + $emailGreetingFilter = [ + 'contact_type' => CRM_Utils_Array::value('contact_type', $params), + 'greeting_type' => 'email_greeting', + ]; + $emailGreetings = CRM_Core_PseudoConstant::greeting($emailGreetingFilter); + $params['email_greeting'] = $emailGreetings[$params['email_greeting_id']]; + } + else { + $params['email_greeting'] = $values['email_greeting']; + } + + return TRUE; + } + + if (isset($values['postal_greeting'])) { + if (!empty($params['postal_greeting_id'])) { + $postalGreetingFilter = [ + 'contact_type' => CRM_Utils_Array::value('contact_type', $params), + 'greeting_type' => 'postal_greeting', + ]; + $postalGreetings = CRM_Core_PseudoConstant::greeting($postalGreetingFilter); + $params['postal_greeting'] = $postalGreetings[$params['postal_greeting_id']]; + } + else { + $params['postal_greeting'] = $values['postal_greeting']; + } + return TRUE; + } + + if (isset($values['addressee'])) { + if (!empty($params['addressee_id'])) { + $addresseeFilter = [ + 'contact_type' => CRM_Utils_Array::value('contact_type', $params), + 'greeting_type' => 'addressee', + ]; + $addressee = CRM_Core_PseudoConstant::addressee($addresseeFilter); + $params['addressee'] = $addressee[$params['addressee_id']]; + } + else { + $params['addressee'] = $values['addressee']; + } + return TRUE; + } + + if (isset($values['gender'])) { + if (!empty($params['gender_id'])) { + $genders = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'gender_id'); + $params['gender'] = $genders[$params['gender_id']]; + } + else { + $params['gender'] = $values['gender']; + } + return TRUE; + } + + if (!empty($values['preferred_communication_method'])) { + $comm = []; + $pcm = array_change_key_case(array_flip(CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'preferred_communication_method')), CASE_LOWER); + + $preffComm = explode(',', $values['preferred_communication_method']); + foreach ($preffComm as $v) { + $v = strtolower(trim($v)); + if (array_key_exists($v, $pcm)) { + $comm[$pcm[$v]] = 1; + } + } + + $params['preferred_communication_method'] = $comm; + return TRUE; + } + + // format the website params. + if (!empty($values['url'])) { + static $websiteFields; + if (!is_array($websiteFields)) { + $websiteFields = CRM_Core_DAO_Website::fields(); + } + if (!array_key_exists('website', $params) || + !is_array($params['website']) + ) { + $params['website'] = []; + } + + $websiteCount = count($params['website']); + _civicrm_api3_store_values($websiteFields, $values, + $params['website'][++$websiteCount] + ); + + return TRUE; + } + + // get the formatted location blocks into params - w/ 3.0 format, CRM-4605 + if (!empty($values['location_type_id'])) { + $blockTypes = [ + 'phone' => 'Phone', + 'email' => 'Email', + 'im' => 'IM', + 'openid' => 'OpenID', + 'phone_ext' => 'Phone', + ]; + foreach ($blockTypes as $blockFieldName => $block) { + if (!array_key_exists($blockFieldName, $values)) { + continue; + } + + // block present in value array. + if (!array_key_exists($blockFieldName, $params) || !is_array($params[$blockFieldName])) { + $params[$blockFieldName] = []; + } + + if (!array_key_exists($block, $fields)) { + $className = "CRM_Core_DAO_$block"; + $fields[$block] = $className::fields(); + } + + $blockCnt = count($params[$blockFieldName]); + + // copy value to dao field name. + if ($blockFieldName == 'im') { + $values['name'] = $values[$blockFieldName]; + } + + _civicrm_api3_store_values($fields[$block], $values, + $params[$blockFieldName][++$blockCnt] + ); + + if ($values['location_type_id'] === 'Primary') { + if (!empty($params['id'])) { + $primary = civicrm_api3($block, 'get', ['return' => 'location_type_id', 'contact_id' => $params['id'], 'is_primary' => 1, 'sequential' => 1]); + } + $defaultLocationType = CRM_Core_BAO_LocationType::getDefault(); + $values['location_type_id'] = (isset($primary) && $primary['count']) ? $primary['values'][0]['location_type_id'] : $defaultLocationType->id; + $values['is_primary'] = 1; + } + + if (empty($params['id']) && ($blockCnt == 1)) { + $params[$blockFieldName][$blockCnt]['is_primary'] = TRUE; + } + + // we only process single block at a time. + return TRUE; + } + + // handle address fields. + if (!array_key_exists('address', $params) || !is_array($params['address'])) { + $params['address'] = []; + } + + if (!array_key_exists('Address', $fields)) { + $fields['Address'] = CRM_Core_DAO_Address::fields(); + } + + // Note: we doing multiple value formatting here for address custom fields, plus putting into right format. + // The actual formatting (like date, country ..etc) for address custom fields is taken care of while saving + // the address in CRM_Core_BAO_Address::create method + if (!empty($values['location_type_id'])) { + static $customFields = []; + if (empty($customFields)) { + $customFields = CRM_Core_BAO_CustomField::getFields('Address'); + } + // make a copy of values, as we going to make changes + $newValues = $values; + foreach ($values as $key => $val) { + $customFieldID = CRM_Core_BAO_CustomField::getKeyID($key); + if ($customFieldID && array_key_exists($customFieldID, $customFields)) { + // mark an entry in fields array since we want the value of custom field to be copied + $fields['Address'][$key] = NULL; + + $htmlType = CRM_Utils_Array::value('html_type', $customFields[$customFieldID]); + switch ($htmlType) { + case 'CheckBox': + case 'Multi-Select': + if ($val) { + $mulValues = explode(',', $val); + $customOption = CRM_Core_BAO_CustomOption::getCustomOption($customFieldID, TRUE); + $newValues[$key] = []; + foreach ($mulValues as $v1) { + foreach ($customOption as $v2) { + if ((strtolower($v2['label']) == strtolower(trim($v1))) || + (strtolower($v2['value']) == strtolower(trim($v1))) + ) { + if ($htmlType == 'CheckBox') { + $newValues[$key][$v2['value']] = 1; + } + else { + $newValues[$key][] = $v2['value']; + } + } + } + } + } + break; + } + } + } + // consider new values + $values = $newValues; + } + + _civicrm_api3_store_values($fields['Address'], $values, $params['address'][$values['location_type_id']]); + + $addressFields = [ + 'county', + 'country', + 'state_province', + 'supplemental_address_1', + 'supplemental_address_2', + 'supplemental_address_3', + 'StateProvince.name', + ]; + + foreach ($addressFields as $field) { + if (array_key_exists($field, $values)) { + if (!array_key_exists('address', $params)) { + $params['address'] = []; + } + $params['address'][$values['location_type_id']][$field] = $values[$field]; + } + } + + if ($values['location_type_id'] === 'Primary') { + if (!empty($params['id'])) { + $primary = civicrm_api3('Address', 'get', ['return' => 'location_type_id', 'contact_id' => $params['id'], 'is_primary' => 1, 'sequential' => 1]); + } + $defaultLocationType = CRM_Core_BAO_LocationType::getDefault(); + $params['address'][$values['location_type_id']]['location_type_id'] = (isset($primary) && $primary['count']) ? $primary['values'][0]['location_type_id'] : $defaultLocationType->id; + $params['address'][$values['location_type_id']]['is_primary'] = 1; + + } + return TRUE; + } + + if (isset($values['note'])) { + // add a note field + if (!isset($params['note'])) { + $params['note'] = []; + } + $noteBlock = count($params['note']) + 1; + + $params['note'][$noteBlock] = []; + if (!isset($fields['Note'])) { + $fields['Note'] = CRM_Core_DAO_Note::fields(); + } + + // get the current logged in civicrm user + $session = CRM_Core_Session::singleton(); + $userID = $session->get('userID'); + + if ($userID) { + $values['contact_id'] = $userID; + } + + _civicrm_api3_store_values($fields['Note'], $values, $params['note'][$noteBlock]); + + return TRUE; + } + + // Check for custom field values + + if (empty($fields['custom'])) { + $fields['custom'] = &CRM_Core_BAO_CustomField::getFields(CRM_Utils_Array::value('contact_type', $values), + FALSE, FALSE, NULL, NULL, FALSE, FALSE, FALSE + ); + } + + foreach ($values as $key => $value) { + if ($customFieldID = CRM_Core_BAO_CustomField::getKeyID($key)) { + // check if it's a valid custom field id + + if (!array_key_exists($customFieldID, $fields['custom'])) { + return civicrm_api3_create_error('Invalid custom field ID'); + } + else { + $params[$key] = $value; + } + } + } + return TRUE; + } + } diff --git a/CRM/Contact/Import/Parser/Contact.php b/CRM/Contact/Import/Parser/Contact.php index 2cffc5dc911f..663aa55101cf 100644 --- a/CRM/Contact/Import/Parser/Contact.php +++ b/CRM/Contact/Import/Parser/Contact.php @@ -1,9 +1,9 @@ _mapperKeys = &$mapperKeys; @@ -180,18 +182,18 @@ public function init() { } if (!empty($relationshipType)) { - $fields = array_merge($fields, array( - 'related' => array( + $fields = array_merge($fields, [ + 'related' => [ 'title' => ts('- related contact info -'), - ), - ), $relationshipType); + ], + ], $relationshipType); } foreach ($fields as $name => $field) { $this->addField($name, $field['title'], CRM_Utils_Array::value('type', $field), CRM_Utils_Array::value('headerPattern', $field), CRM_Utils_Array::value('dataPattern', $field), CRM_Utils_Array::value('hasLocationType', $field)); } - $this->_newContacts = array(); + $this->_newContacts = []; $this->setActiveFields($this->_mapperKeys); $this->setActiveFieldLocationTypes($this->_mapperLocType); @@ -222,7 +224,7 @@ public function init() { foreach ($this->_mapperKeys as $key) { if (substr($key, 0, 5) == 'email' && substr($key, 0, 14) != 'email_greeting') { $this->_emailIndex = $index; - $this->_allEmails = array(); + $this->_allEmails = []; } if (substr($key, 0, 5) == 'phone') { $this->_phoneIndex = $index; @@ -242,17 +244,16 @@ public function init() { if ($key == 'external_identifier') { $this->_externalIdentifierIndex = $index; - $this->_allExternalIdentifiers = array(); + $this->_allExternalIdentifiers = []; } $index++; } $this->_updateWithId = FALSE; - if (in_array('id', $this->_mapperKeys) || ($this->_externalIdentifierIndex >= 0 && in_array($this->_onDuplicate, array( - CRM_Import_Parser::DUPLICATE_UPDATE, - CRM_Import_Parser::DUPLICATE_FILL, - ))) - ) { + if (in_array('id', $this->_mapperKeys) || ($this->_externalIdentifierIndex >= 0 && in_array($this->_onDuplicate, [ + CRM_Import_Parser::DUPLICATE_UPDATE, + CRM_Import_Parser::DUPLICATE_FILL, + ]))) { $this->_updateWithId = TRUE; } @@ -301,7 +302,7 @@ public function summary(&$values) { $errorRequired = FALSE; switch ($this->_contactType) { case 'Individual': - $missingNames = array(); + $missingNames = []; if ($this->_firstNameIndex < 0 || empty($values[$this->_firstNameIndex])) { $errorRequired = TRUE; $missingNames[] = ts('First Name'); @@ -345,10 +346,10 @@ public function summary(&$values) { $errorMessage = ts('Missing required field:') . ' ' . ts('Email Address'); } array_unshift($values, $errorMessage); - $importRecordParams = array( + $importRecordParams = [ $statusFieldName => 'ERROR', "${statusFieldName}Msg" => $errorMessage, - ); + ]; $this->updateImportRecord($values[count($values) - 1], $importRecordParams); return CRM_Import_Parser::ERROR; @@ -362,10 +363,10 @@ public function summary(&$values) { if (!CRM_Utils_Rule::email($email)) { $errorMessage = ts('Invalid Email address'); array_unshift($values, $errorMessage); - $importRecordParams = array( + $importRecordParams = [ $statusFieldName => 'ERROR', "${statusFieldName}Msg" => $errorMessage, - ); + ]; $this->updateImportRecord($values[count($values) - 1], $importRecordParams); return CRM_Import_Parser::ERROR; @@ -383,10 +384,10 @@ public function summary(&$values) { $errorMessage = ts('Missing required field:') . ' ' . ts('Email Address'); } array_unshift($values, $errorMessage); - $importRecordParams = array( + $importRecordParams = [ $statusFieldName => 'ERROR', "${statusFieldName}Msg" => $errorMessage, - ); + ]; $this->updateImportRecord($values[count($values) - 1], $importRecordParams); return CRM_Import_Parser::ERROR; @@ -398,12 +399,12 @@ public function summary(&$values) { /* If it's a dupe,external Identifier */ if ($externalDupe = CRM_Utils_Array::value($externalID, $this->_allExternalIdentifiers)) { - $errorMessage = ts('External ID conflicts with record %1', array(1 => $externalDupe)); + $errorMessage = ts('External ID conflicts with record %1', [1 => $externalDupe]); array_unshift($values, $errorMessage); - $importRecordParams = array( + $importRecordParams = [ $statusFieldName => 'ERROR', "${statusFieldName}Msg" => $errorMessage, - ); + ]; $this->updateImportRecord($values[count($values) - 1], $importRecordParams); return CRM_Import_Parser::ERROR; } @@ -433,10 +434,10 @@ public function summary(&$values) { if ($errorMessage) { $tempMsg = "Invalid value for field(s) : $errorMessage"; // put the error message in the import record in the DB - $importRecordParams = array( + $importRecordParams = [ $statusFieldName => 'ERROR', "${statusFieldName}Msg" => $tempMsg, - ); + ]; $this->updateImportRecord($values[count($values) - 1], $importRecordParams); array_unshift($values, $tempMsg); $errorMessage = NULL; @@ -446,14 +447,24 @@ public function summary(&$values) { //if user correcting errors by walking back //need to reset status ERROR msg to null //now currently we are having valid data. - $importRecordParams = array( + $importRecordParams = [ $statusFieldName => 'NEW', - ); + ]; $this->updateImportRecord($values[count($values) - 1], $importRecordParams); return CRM_Import_Parser::VALID; } + /** + * Get Array of all the fields that could potentially be part + * import process + * + * @return array + */ + public function getAllFields() { + return $this->_fields; + } + /** * Handle the values in import mode. * @@ -469,10 +480,10 @@ public function summary(&$values) { */ public function import($onDuplicate, &$values, $doGeocodeAddress = FALSE) { $config = CRM_Core_Config::singleton(); - $this->_unparsedStreetAddressContacts = array(); + $this->_unparsedStreetAddressContacts = []; if (!$doGeocodeAddress) { // CRM-5854, reset the geocode method to null to prevent geocoding - $config->geocodeMethod = NULL; + CRM_Utils_GeocodeProvider::disableForSession(); } // first make sure this is a valid line @@ -481,18 +492,18 @@ public function import($onDuplicate, &$values, $doGeocodeAddress = FALSE) { $statusFieldName = $this->_statusFieldName; if ($response != CRM_Import_Parser::VALID) { - $importRecordParams = array( + $importRecordParams = [ $statusFieldName => 'INVALID', "${statusFieldName}Msg" => "Invalid (Error Code: $response)", - ); + ]; $this->updateImportRecord($values[count($values) - 1], $importRecordParams); return $response; } $params = &$this->getActiveFieldParams(); - $formatted = array( + $formatted = [ 'contact_type' => $this->_contactType, - ); + ]; static $contactFields = NULL; if ($contactFields == NULL) { @@ -500,22 +511,41 @@ public function import($onDuplicate, &$values, $doGeocodeAddress = FALSE) { } //check if external identifier exists in database - if (!empty($params['external_identifier']) && (!empty($params['id']) || in_array($onDuplicate, array( - CRM_Import_Parser::DUPLICATE_SKIP, - CRM_Import_Parser::DUPLICATE_NOCHECK, - ))) - ) { + if (!empty($params['external_identifier']) && (!empty($params['id']) || in_array($onDuplicate, [ + CRM_Import_Parser::DUPLICATE_SKIP, + CRM_Import_Parser::DUPLICATE_NOCHECK, + ]))) { - if ($internalCid = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $params['external_identifier'], 'id', 'external_identifier')) { + $extIDResult = civicrm_api3('Contact', 'get', [ + 'external_identifier' => $params['external_identifier'], + 'showAll' => 'all', + 'return' => ['id', 'contact_is_deleted'], + ]); + if (isset($extIDResult['id'])) { + // record with matching external identifier does exist. + $internalCid = $extIDResult['id']; if ($internalCid != CRM_Utils_Array::value('id', $params)) { - $errorMessage = ts('External ID already exists in Database.'); - array_unshift($values, $errorMessage); - $importRecordParams = array( - $statusFieldName => 'ERROR', - "${statusFieldName}Msg" => $errorMessage, - ); - $this->updateImportRecord($values[count($values) - 1], $importRecordParams); - return CRM_Import_Parser::DUPLICATE; + if ($extIDResult['values'][$internalCid]['contact_is_deleted'] == 1) { + // And it is deleted. What to do? If we skip it, they user + // will be under the impression that the record exists in + // the database, yet they won't be able to find it. If we + // don't skip it, the database will try to insert a new record + // with an external_identifier that is non-unique. So... + // we will update this contact to remove the external_identifier + // and let a new record be created. + $update_params = ['id' => $internalCid, 'external_identifier' => '']; + civicrm_api3('Contact', 'create', $update_params); + } + else { + $errorMessage = ts('External ID already exists in Database.'); + array_unshift($values, $errorMessage); + $importRecordParams = [ + $statusFieldName => 'ERROR', + "${statusFieldName}Msg" => $errorMessage, + ]; + $this->updateImportRecord($values[count($values) - 1], $importRecordParams); + return CRM_Import_Parser::DUPLICATE; + } } } } @@ -539,7 +569,20 @@ public function import($onDuplicate, &$values, $doGeocodeAddress = FALSE) { // Get contact id to format common data in update/fill mode, // prioritising a dedupe rule check over an external_identifier check, but falling back on ext id. if ($this->_updateWithId && empty($params['id'])) { - $possibleMatches = $this->getPossibleContactMatches($params); + try { + $possibleMatches = $this->getPossibleContactMatches($params); + } + catch (CRM_Core_Exception $e) { + $errorMessage = $e->getMessage(); + array_unshift($values, $errorMessage); + + $importRecordParams = [ + $statusFieldName => 'ERROR', + "${statusFieldName}Msg" => $errorMessage, + ]; + $this->updateImportRecord($values[count($values) - 1], $importRecordParams); + return CRM_Import_Parser::ERROR; + } foreach ($possibleMatches as $possibleID) { $params['id'] = $formatted['id'] = $possibleID; } @@ -558,7 +601,12 @@ public function import($onDuplicate, &$values, $doGeocodeAddress = FALSE) { // check for the contact_id & type. $error = _civicrm_api3_deprecated_duplicate_formatted_contact($formatted); if (CRM_Core_Error::isAPIError($error, CRM_Core_ERROR::DUPLICATE_CONTACT)) { - $matchedIDs = explode(',', $error['error_message']['params'][0]); + if (is_array($error['error_message']['params'][0])) { + $matchedIDs = $error['error_message']['params'][0]; + } + else { + $matchedIDs = explode(',', $error['error_message']['params'][0]); + } if (count($matchedIDs) >= 1) { $updateflag = TRUE; foreach ($matchedIDs as $contactId) { @@ -661,7 +709,7 @@ public function import($onDuplicate, &$values, $doGeocodeAddress = FALSE) { if ($createNewContact || ($this->_retCode != CRM_Import_Parser::NO_MATCH && $this->_updateWithId)) { //CRM-4430, don't carry if not submitted. - foreach (array('prefix_id', 'suffix_id', 'gender_id') as $name) { + foreach (['prefix_id', 'suffix_id', 'gender_id'] as $name) { if (!empty($formatted[$name])) { $options = CRM_Contact_BAO_Contact::buildOptions($name, 'get'); if (!isset($options[$formatted[$name]])) { @@ -686,22 +734,24 @@ public function import($onDuplicate, &$values, $doGeocodeAddress = FALSE) { $this->_retCode = CRM_Import_Parser::VALID; } } - elseif (isset($newContact) && CRM_Core_Error::isAPIError($newContact, CRM_Core_ERROR::DUPLICATE_CONTACT)) { + elseif (isset($newContact) && CRM_Core_Error::isAPIError($newContact, CRM_Core_Error::DUPLICATE_CONTACT)) { // if duplicate, no need of further processing if ($onDuplicate == CRM_Import_Parser::DUPLICATE_SKIP) { $errorMessage = "Skipping duplicate record"; array_unshift($values, $errorMessage); - $importRecordParams = array( + $importRecordParams = [ $statusFieldName => 'DUPLICATE', "${statusFieldName}Msg" => $errorMessage, - ); + ]; $this->updateImportRecord($values[count($values) - 1], $importRecordParams); return CRM_Import_Parser::DUPLICATE; } $relationship = TRUE; - # see CRM-10433 - might return comma separate list of all dupes - $dupeContactIDs = explode(',', $newContact['error_message']['params'][0]); + // CRM-10433/CRM-20739 - IDs could be string or array; handle accordingly + if (!is_array($dupeContactIDs = $newContact['error_message']['params'][0])) { + $dupeContactIDs = explode(',', $dupeContactIDs); + } $dupeCount = count($dupeContactIDs); $contactID = array_pop($dupeContactIDs); // check to see if we had more than one duplicate contact id. @@ -718,13 +768,13 @@ public function import($onDuplicate, &$values, $doGeocodeAddress = FALSE) { // call import hook $currentImportID = end($values); - $hookParams = array( + $hookParams = [ 'contactID' => $contactID, 'importID' => $currentImportID, 'importTempTable' => $this->_tableName, 'fieldHeaders' => $this->_mapperKeys, 'fields' => $this->_activeFields, - ); + ]; CRM_Utils_Hook::import('Contact', 'process', $this, $hookParams); } @@ -732,8 +782,8 @@ public function import($onDuplicate, &$values, $doGeocodeAddress = FALSE) { if ($relationship) { $primaryContactId = NULL; if (CRM_Core_Error::isAPIError($newContact, CRM_Core_ERROR::DUPLICATE_CONTACT)) { - if (CRM_Utils_Rule::integer($newContact['error_message']['params'][0])) { - $primaryContactId = $newContact['error_message']['params'][0]; + if ($dupeCount == 1 && CRM_Utils_Rule::integer($contactID)) { + $primaryContactId = $contactID; } } else { @@ -754,9 +804,9 @@ public function import($onDuplicate, &$values, $doGeocodeAddress = FALSE) { $relationType->find(TRUE); $direction = "contact_sub_type_$second"; - $formatting = array( + $formatting = [ 'contact_type' => $params[$key]['contact_type'], - ); + ]; //set subtype for related contact CRM-5125 if (isset($relationType->$direction)) { @@ -780,14 +830,13 @@ public function import($onDuplicate, &$values, $doGeocodeAddress = FALSE) { $params[$key]['id'] = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $params[$key]['external_identifier'], 'id', 'external_identifier'); } // check for valid related contact id in update/fill mode, CRM-4424 - if (in_array($onDuplicate, array( - CRM_Import_Parser::DUPLICATE_UPDATE, - CRM_Import_Parser::DUPLICATE_FILL, - )) && !empty($params[$key]['id']) - ) { + if (in_array($onDuplicate, [ + CRM_Import_Parser::DUPLICATE_UPDATE, + CRM_Import_Parser::DUPLICATE_FILL, + ]) && !empty($params[$key]['id'])) { $relatedContactType = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $params[$key]['id'], 'contact_type'); if (!$relatedContactType) { - $errorMessage = ts("No contact found for this related contact ID: %1", array(1 => $params[$key]['id'])); + $errorMessage = ts("No contact found for this related contact ID: %1", [1 => $params[$key]['id']]); array_unshift($values, $errorMessage); return CRM_Import_Parser::NO_MATCH; } @@ -802,7 +851,7 @@ public function import($onDuplicate, &$values, $doGeocodeAddress = FALSE) { if (!empty($relatedCsType) && (!CRM_Contact_BAO_ContactType::isAllowEdit($params[$key]['id'], $relatedCsType) && $relatedCsType != CRM_Utils_Array::value('contact_sub_type', $formatting)) ) { - $errorMessage = ts("Mismatched or Invalid contact subtype found for this related contact.") . ' ' . ts("ID: %1", array(1 => $params[$key]['id'])); + $errorMessage = ts("Mismatched or Invalid contact subtype found for this related contact.") . ' ' . ts("ID: %1", [1 => $params[$key]['id']]); array_unshift($values, $errorMessage); return CRM_Import_Parser::NO_MATCH; } @@ -828,10 +877,10 @@ public function import($onDuplicate, &$values, $doGeocodeAddress = FALSE) { //fixed for CRM-4148 if (!empty($params[$key]['id'])) { - $contact = array( + $contact = [ 'contact_id' => $params[$key]['id'], - ); - $defaults = array(); + ]; + $defaults = []; $relatedNewContact = CRM_Contact_BAO_Contact::retrieve($contact, $defaults); } else { @@ -842,20 +891,23 @@ public function import($onDuplicate, &$values, $doGeocodeAddress = FALSE) { $relatedNewContact = clone($relatedNewContact); } - $matchedIDs = array(); + $matchedIDs = []; // To update/fill contact, get the matching contact Ids if duplicate contact found // otherwise get contact Id from object of related contact if (is_array($relatedNewContact) && civicrm_error($relatedNewContact)) { if (CRM_Core_Error::isAPIError($relatedNewContact, CRM_Core_ERROR::DUPLICATE_CONTACT)) { - $matchedIDs = explode(',', $relatedNewContact['error_message']['params'][0]); + $matchedIDs = $relatedNewContact['error_message']['params'][0]; + if (!is_array($matchedIDs)) { + $matchedIDs = explode(',', $matchedIDs); + } } else { $errorMessage = $relatedNewContact['error_message']; array_unshift($values, $errorMessage); - $importRecordParams = array( + $importRecordParams = [ $statusFieldName => 'ERROR', "${statusFieldName}Msg" => $errorMessage, - ); + ]; $this->updateImportRecord($values[count($values) - 1], $importRecordParams); return CRM_Import_Parser::ERROR; } @@ -864,10 +916,10 @@ public function import($onDuplicate, &$values, $doGeocodeAddress = FALSE) { $matchedIDs[] = $relatedNewContact->id; } // update/fill related contact after getting matching Contact Ids, CRM-4424 - if (in_array($onDuplicate, array( + if (in_array($onDuplicate, [ CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::DUPLICATE_FILL, - ))) { + ])) { //validation of related contact subtype for update mode //CRM-5125 $relatedCsType = NULL; @@ -884,7 +936,7 @@ public function import($onDuplicate, &$values, $doGeocodeAddress = FALSE) { $updatedContact = $this->createContact($formatting, $contactFields, $onDuplicate, $matchedIDs[0]); } } - static $relativeContact = array(); + static $relativeContact = []; if (CRM_Core_Error::isAPIError($relatedNewContact, CRM_Core_ERROR::DUPLICATE_CONTACT)) { if (count($matchedIDs) >= 1) { $relContactId = $matchedIDs[0]; @@ -913,21 +965,21 @@ public function import($onDuplicate, &$values, $doGeocodeAddress = FALSE) { //if more than one duplicate contact //found, create relationship with first contact // now create the relationship record - $relationParams = array(); - $relationParams = array( + $relationParams = []; + $relationParams = [ 'relationship_type_id' => $key, - 'contact_check' => array( + 'contact_check' => [ $relContactId => 1, - ), + ], 'is_active' => 1, 'skipRecentView' => TRUE, - ); + ]; // we only handle related contact success, we ignore failures for now // at some point wold be nice to have related counts as separate - $relationIds = array( + $relationIds = [ 'contact' => $primaryContactId, - ); + ]; list($valid, $invalid, $duplicate, $saved, $relationshipIds) = CRM_Contact_BAO_Relationship::legacyCreateMultiple($relationParams, $relationIds); @@ -940,13 +992,13 @@ public function import($onDuplicate, &$values, $doGeocodeAddress = FALSE) { //handle current employer, CRM-3532 if ($valid) { $allRelationships = CRM_Core_PseudoConstant::relationshipType('name'); - $relationshipTypeId = str_replace(array( + $relationshipTypeId = str_replace([ '_a_b', '_b_a', - ), array( + ], [ '', '', - ), $key); + ], $key); $relationshipType = str_replace($relationshipTypeId . '_', '', $key); $orgId = $individualId = NULL; if ($allRelationships[$relationshipTypeId]["name_{$relationshipType}"] == 'Employee of') { @@ -976,7 +1028,7 @@ public function import($onDuplicate, &$values, $doGeocodeAddress = FALSE) { $code = NULL; if (($code = CRM_Utils_Array::value('code', $newContact['error_message'])) && ($code == CRM_Core_Error::DUPLICATE_CONTACT)) { - $urls = array(); + $urls = []; // need to fix at some stage and decide if the error will return an // array or string, crude hack for now if (is_array($newContact['error_message']['params'][0])) { @@ -995,10 +1047,10 @@ public function import($onDuplicate, &$values, $doGeocodeAddress = FALSE) { // If we duplicate more than one record, skip no matter what if (count($cids) > 1) { $errorMessage = ts('Record duplicates multiple contacts'); - $importRecordParams = array( + $importRecordParams = [ $statusFieldName => 'ERROR', "${statusFieldName}Msg" => $errorMessage, - ); + ]; //combine error msg to avoid mismatch between error file columns. $errorMessage .= "\n" . $url_string; @@ -1011,7 +1063,7 @@ public function import($onDuplicate, &$values, $doGeocodeAddress = FALSE) { $contactId = array_shift($cids); $cid = NULL; - $vals = array('contact_id' => $contactId); + $vals = ['contact_id' => $contactId]; if ($onDuplicate == CRM_Import_Parser::DUPLICATE_REPLACE) { civicrm_api('contact', 'delete', $vals); @@ -1025,10 +1077,10 @@ public function import($onDuplicate, &$values, $doGeocodeAddress = FALSE) { } // else skip does nothing and just returns an error code. if ($cid) { - $contact = array( + $contact = [ 'contact_id' => $cid, - ); - $defaults = array(); + ]; + $defaults = []; $newContact = CRM_Contact_BAO_Contact::retrieve($contact, $defaults); } @@ -1037,15 +1089,18 @@ public function import($onDuplicate, &$values, $doGeocodeAddress = FALSE) { // different kind of error other than DUPLICATE $errorMessage = $newContact['error_message']; array_unshift($values, $errorMessage); - $importRecordParams = array( + $importRecordParams = [ $statusFieldName => 'ERROR', "${statusFieldName}Msg" => $errorMessage, - ); + ]; $this->updateImportRecord($values[count($values) - 1], $importRecordParams); return CRM_Import_Parser::ERROR; } $contactID = $newContact['error_message']['params'][0]; + if (is_array($contactID)) { + $contactID = array_pop($contactID); + } if (!in_array($contactID, $this->_newContacts)) { $this->_newContacts[] = $contactID; } @@ -1053,17 +1108,17 @@ public function import($onDuplicate, &$values, $doGeocodeAddress = FALSE) { //CRM-262 No Duplicate Checking if ($onDuplicate == CRM_Import_Parser::DUPLICATE_SKIP) { array_unshift($values, $url_string); - $importRecordParams = array( + $importRecordParams = [ $statusFieldName => 'DUPLICATE', "${statusFieldName}Msg" => "Skipping duplicate record", - ); + ]; $this->updateImportRecord($values[count($values) - 1], $importRecordParams); return CRM_Import_Parser::DUPLICATE; } - $importRecordParams = array( + $importRecordParams = [ $statusFieldName => 'IMPORTED', - ); + ]; $this->updateImportRecord($values[count($values) - 1], $importRecordParams); //return warning if street address is not parsed, CRM-5886 return $this->processMessage($values, $statusFieldName, CRM_Import_Parser::VALID); @@ -1072,10 +1127,10 @@ public function import($onDuplicate, &$values, $doGeocodeAddress = FALSE) { // Not a dupe, so we had an error $errorMessage = $newContact['error_message']; array_unshift($values, $errorMessage); - $importRecordParams = array( + $importRecordParams = [ $statusFieldName => 'ERROR', "${statusFieldName}Msg" => $errorMessage, - ); + ]; $this->updateImportRecord($values[count($values) - 1], $importRecordParams); return CRM_Import_Parser::ERROR; } @@ -1119,8 +1174,7 @@ public function fini() { * @param null $relationships */ public static function isErrorInCustomData($params, &$errorMessage, $csType = NULL, $relationships = NULL) { - $session = CRM_Core_Session::singleton(); - $dateType = $session->get("dateTypes"); + $dateType = CRM_Core_Session::singleton()->get("dateTypes"); if (!empty($params['contact_sub_type'])) { $csType = CRM_Utils_Array::value('contact_sub_type', $params); @@ -1131,14 +1185,14 @@ public static function isErrorInCustomData($params, &$errorMessage, $csType = NU } // get array of subtypes - CRM-18708 - if (in_array($csType, array('Individual', 'Organization', 'Household'))) { + if (in_array($csType, ['Individual', 'Organization', 'Household'])) { $csType = self::getSubtypes($params['contact_type']); } if (is_array($csType)) { // fetch custom fields for every subtype and add it to $customFields array // CRM-18708 - $customFields = array(); + $customFields = []; foreach ($csType as $cType) { $customFields += CRM_Core_BAO_CustomField::getFields($params['contact_type'], FALSE, FALSE, $cType); } @@ -1186,15 +1240,14 @@ public static function isErrorInCustomData($params, &$errorMessage, $csType = NU } } // need not check for label filed import - $htmlType = array( + $htmlType = [ 'CheckBox', 'Multi-Select', - 'AdvMulti-Select', 'Select', 'Radio', 'Multi-Select State/Province', 'Multi-Select Country', - ); + ]; if (!in_array($customFields[$customFieldID]['html_type'], $htmlType) || $customFields[$customFieldID]['data_type'] == 'Boolean' || $customFields[$customFieldID]['data_type'] == 'ContactReference') { $valid = CRM_Core_BAO_CustomValue::typecheck($customFields[$customFieldID]['data_type'], $value); if (!$valid) { @@ -1203,7 +1256,7 @@ public static function isErrorInCustomData($params, &$errorMessage, $csType = NU } // check for values for custom fields for checkboxes and multiselect - if ($customFields[$customFieldID]['html_type'] == 'CheckBox' || $customFields[$customFieldID]['html_type'] == 'AdvMulti-Select' || $customFields[$customFieldID]['html_type'] == 'Multi-Select') { + if ($customFields[$customFieldID]['html_type'] == 'CheckBox' || $customFields[$customFieldID]['html_type'] == 'Multi-Select') { $value = trim($value); $value = str_replace('|', ',', $value); $mulValues = explode(',', $value); @@ -1259,11 +1312,11 @@ public static function isErrorInCustomData($params, &$errorMessage, $csType = NU $limitCodes = CRM_Core_BAO_Country::countryLimit(); $error = TRUE; - foreach (array( - $countryNames, - $countryIsoCodes, - $limitCodes, - ) as $values) { + foreach ([ + $countryNames, + $countryIsoCodes, + $limitCodes, + ] as $values) { if (in_array(trim($countryValue), $values)) { $error = FALSE; break; @@ -1393,7 +1446,7 @@ public function isErrorInCoreData($params, &$errorMessage) { break; case 'preferred_communication_method': - $preffComm = array(); + $preffComm = []; $preffComm = explode(',', $value); foreach ($preffComm as $v) { if (!self::in_value(trim($v), CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'preferred_communication_method'))) { @@ -1515,30 +1568,30 @@ public function isErrorInCoreData($params, &$errorMessage) { //custom email/postal greeting, custom addressee, CRM-4575 case 'email_greeting': - $emailGreetingFilter = array( + $emailGreetingFilter = [ 'contact_type' => $this->_contactType, 'greeting_type' => 'email_greeting', - ); + ]; if (!self::in_value($value, CRM_Core_PseudoConstant::greeting($emailGreetingFilter))) { self::addToErrorMsg(ts('Email Greeting must be one of the configured format options. Check Administer >> System Settings >> Option Groups >> Email Greetings for valid values'), $errorMessage); } break; case 'postal_greeting': - $postalGreetingFilter = array( + $postalGreetingFilter = [ 'contact_type' => $this->_contactType, 'greeting_type' => 'postal_greeting', - ); + ]; if (!self::in_value($value, CRM_Core_PseudoConstant::greeting($postalGreetingFilter))) { self::addToErrorMsg(ts('Postal Greeting must be one of the configured format options. Check Administer >> System Settings >> Option Groups >> Postal Greetings for valid values'), $errorMessage); } break; case 'addressee': - $addresseeFilter = array( + $addresseeFilter = [ 'contact_type' => $this->_contactType, 'greeting_type' => 'addressee', - ); + ]; if (!self::in_value($value, CRM_Core_PseudoConstant::greeting($addresseeFilter))) { self::addToErrorMsg(ts('Addressee must be one of the configured format options. Check Administer >> System Settings >> Option Groups >> Addressee for valid values'), $errorMessage); } @@ -1635,6 +1688,10 @@ public static function in_value($value, $valueArray) { /** * Build error-message containing error-fields * + * Once upon a time there was a dev who hadn't heard of implode. That dev wrote this function. + * + * @todo just say no! + * * @param string $errorName * A string containing error-field name. * @param string $errorMessage @@ -1673,11 +1730,10 @@ public function createContact(&$formatted, &$contactFields, $onDuplicate, $conta //get the prefix id etc if exists CRM_Contact_BAO_Contact::resolveDefaults($formatted, TRUE); - require_once 'CRM/Utils/DeprecatedUtils.php'; //@todo direct call to API function not supported. // setting required check to false, CRM-2839 // plus we do our own required check in import - $error = _civicrm_api3_deprecated_contact_check_params($formatted, $dupeCheck, TRUE, FALSE, $dedupeRuleGroupID); + $error = _civicrm_api3_deprecated_contact_check_params($formatted, $dupeCheck, $dedupeRuleGroupID); if ((is_null($error)) && (civicrm_error(_civicrm_api3_deprecated_validate_formatted_contact($formatted)))) { $error = _civicrm_api3_deprecated_validate_formatted_contact($formatted); @@ -1690,17 +1746,16 @@ public function createContact(&$formatted, &$contactFields, $onDuplicate, $conta $this->formatParams($formatted, $onDuplicate, (int) $contactId); } - // pass doNotResetCache flag since resetting and rebuilding cache could be expensive. - $config = CRM_Core_Config::singleton(); - $config->doNotResetCache = 1; + // Resetting and rebuilding cache could be expensive. + CRM_Core_Config::setPermitCacheFlushMode(FALSE); $cid = CRM_Contact_BAO_Contact::createProfileContact($formatted, $contactFields, $contactId, NULL, NULL, $formatted['contact_type']); - $config->doNotResetCache = 0; + CRM_Core_Config::setPermitCacheFlushMode(TRUE); - $contact = array( + $contact = [ 'contact_id' => $cid, - ); + ]; - $defaults = array(); + $defaults = []; $newContact = CRM_Contact_BAO_Contact::retrieve($contact, $defaults); } @@ -1708,10 +1763,10 @@ public function createContact(&$formatted, &$contactFields, $onDuplicate, $conta if ($this->_parseStreetAddress && is_object($newContact) && property_exists($newContact, 'address') && $newContact->address) { foreach ($newContact->address as $address) { if (!empty($address['street_address']) && (empty($address['street_number']) || empty($address['street_name']))) { - $this->_unparsedStreetAddressContacts[] = array( + $this->_unparsedStreetAddressContacts[] = [ 'id' => $newContact->id, 'streetAddress' => $address['street_address'], - ); + ]; } } } @@ -1733,11 +1788,11 @@ public function formatParams(&$params, $onDuplicate, $cid) { return; } - $contactParams = array( + $contactParams = [ 'contact_id' => $cid, - ); + ]; - $defaults = array(); + $defaults = []; $contactObj = CRM_Contact_BAO_Contact::retrieve($contactParams, $defaults); $modeUpdate = $modeFill = FALSE; @@ -1750,16 +1805,16 @@ public function formatParams(&$params, $onDuplicate, $cid) { $modeFill = TRUE; } - $groupTree = CRM_Core_BAO_CustomGroup::getTree($params['contact_type'], CRM_Core_DAO::$_nullObject, $cid, 0, NULL); + $groupTree = CRM_Core_BAO_CustomGroup::getTree($params['contact_type'], NULL, $cid, 0, NULL); CRM_Core_BAO_CustomGroup::setDefaults($groupTree, $defaults, FALSE, FALSE); - $locationFields = array( + $locationFields = [ 'email' => 'email', 'phone' => 'phone', 'im' => 'name', 'website' => 'website', 'address' => 'address', - ); + ]; $contact = get_object_vars($contactObj); @@ -1771,23 +1826,28 @@ public function formatParams(&$params, $onDuplicate, $cid) { if (array_key_exists($key, $locationFields)) { continue; } - elseif (in_array($key, array( + elseif (in_array($key, [ 'email_greeting', 'postal_greeting', 'addressee', - ))) { + ])) { // CRM-4575, need to null custom if ($params["{$key}_id"] != 4) { $params["{$key}_custom"] = 'null'; } unset($params[$key]); } - elseif ($customFieldId = CRM_Core_BAO_CustomField::getKeyID($key)) { - $custom = TRUE; - } else { - $getValue = CRM_Utils_Array::retrieveValueRecursive($contact, $key); - + if ($customFieldId = CRM_Core_BAO_CustomField::getKeyID($key)) { + $custom_params = ['id' => $contact['id'], 'return' => $key]; + $getValue = civicrm_api3('Contact', 'getvalue', $custom_params); + if (empty($getValue)) { + unset($getValue); + } + } + else { + $getValue = CRM_Utils_Array::retrieveValueRecursive($contact, $key); + } if ($key == 'contact_source') { $params['source'] = $params[$key]; unset($params[$key]); @@ -1795,6 +1855,11 @@ public function formatParams(&$params, $onDuplicate, $cid) { if ($modeFill && isset($getValue)) { unset($params[$key]); + if ($customFieldId) { + // Extra values must be unset to ensure the values are not + // imported. + unset($params['custom'][$customFieldId]); + } } } } @@ -1846,237 +1911,6 @@ public static function formatCustomDate(&$params, &$formatted, $dateType, $dateP $formatted[$dateParam] = CRM_Utils_Date::processDate($params[$dateParam]); } - /** - * Format common params data to proper format to store. - * - * @param array $params - * Contain record values. - * @param array $formatted - * Array of formatted data. - * @param array $contactFields - * Contact DAO fields. - */ - public function formatCommonData($params, &$formatted, &$contactFields) { - $csType = array( - CRM_Utils_Array::value('contact_type', $formatted), - ); - - //CRM-5125 - //add custom fields for contact sub type - if (!empty($this->_contactSubType)) { - $csType = $this->_contactSubType; - } - - if ($relCsType = CRM_Utils_Array::value('contact_sub_type', $formatted)) { - $csType = $relCsType; - } - - $customFields = CRM_Core_BAO_CustomField::getFields($formatted['contact_type'], FALSE, FALSE, $csType); - - $addressCustomFields = CRM_Core_BAO_CustomField::getFields('Address'); - $customFields = $customFields + $addressCustomFields; - - //if a Custom Email Greeting, Custom Postal Greeting or Custom Addressee is mapped, and no "Greeting / Addressee Type ID" is provided, then automatically set the type = Customized, CRM-4575 - $elements = array( - 'email_greeting_custom' => 'email_greeting', - 'postal_greeting_custom' => 'postal_greeting', - 'addressee_custom' => 'addressee', - ); - foreach ($elements as $k => $v) { - if (array_key_exists($k, $params) && !(array_key_exists($v, $params))) { - $label = key(CRM_Core_OptionGroup::values($v, TRUE, NULL, NULL, 'AND v.name = "Customized"')); - $params[$v] = $label; - } - } - - //format date first - $session = CRM_Core_Session::singleton(); - $dateType = $session->get("dateTypes"); - foreach ($params as $key => $val) { - $customFieldID = CRM_Core_BAO_CustomField::getKeyID($key); - if ($customFieldID && - !array_key_exists($customFieldID, $addressCustomFields) - ) { - //we should not update Date to null, CRM-4062 - if ($val && ($customFields[$customFieldID]['data_type'] == 'Date')) { - self::formatCustomDate($params, $formatted, $dateType, $key); - } - elseif ($customFields[$customFieldID]['data_type'] == 'Boolean') { - if (empty($val) && !is_numeric($val) && $this->_onDuplicate == CRM_Import_Parser::DUPLICATE_FILL) { - //retain earlier value when Import mode is `Fill` - unset($params[$key]); - } - else { - $params[$key] = CRM_Utils_String::strtoboolstr($val); - } - } - } - - if ($key == 'birth_date' && $val) { - CRM_Utils_Date::convertToDefaultDate($params, $dateType, $key); - } - elseif ($key == 'deceased_date' && $val) { - CRM_Utils_Date::convertToDefaultDate($params, $dateType, $key); - } - elseif ($key == 'is_deceased' && $val) { - $params[$key] = CRM_Utils_String::strtoboolstr($val); - } - elseif ($key == 'gender') { - //CRM-4360 - $params[$key] = $this->checkGender($val); - } - } - - //now format custom data. - foreach ($params as $key => $field) { - if (is_array($field)) { - $isAddressCustomField = FALSE; - foreach ($field as $value) { - $break = FALSE; - if (is_array($value)) { - foreach ($value as $name => $testForEmpty) { - if ($addressCustomFieldID = CRM_Core_BAO_CustomField::getKeyID($name)) { - $isAddressCustomField = TRUE; - break; - } - // check if $value does not contain IM provider or phoneType - if (($name !== 'phone_type_id' || $name !== 'provider_id') && ($testForEmpty === '' || $testForEmpty == NULL)) { - $break = TRUE; - break; - } - } - } - else { - $break = TRUE; - } - - if (!$break) { - require_once 'CRM/Utils/DeprecatedUtils.php'; - _civicrm_api3_deprecated_add_formatted_param($value, $formatted); - } - } - if (!$isAddressCustomField) { - continue; - } - } - - $formatValues = array( - $key => $field, - ); - - if (($key !== 'preferred_communication_method') && (array_key_exists($key, $contactFields))) { - // due to merging of individual table and - // contact table, we need to avoid - // preferred_communication_method forcefully - $formatValues['contact_type'] = $formatted['contact_type']; - } - - if ($key == 'id' && isset($field)) { - $formatted[$key] = $field; - } - require_once 'CRM/Utils/DeprecatedUtils.php'; - _civicrm_api3_deprecated_add_formatted_param($formatValues, $formatted); - - //Handling Custom Data - // note: Address custom fields will be handled separately inside _civicrm_api3_deprecated_add_formatted_param - if (($customFieldID = CRM_Core_BAO_CustomField::getKeyID($key)) && - array_key_exists($customFieldID, $customFields) && - !array_key_exists($customFieldID, $addressCustomFields) - ) { - - $extends = CRM_Utils_Array::value('extends', $customFields[$customFieldID]); - $htmlType = CRM_Utils_Array::value('html_type', $customFields[$customFieldID]); - switch ($htmlType) { - case 'Select': - case 'Radio': - case 'Autocomplete-Select': - if ($customFields[$customFieldID]['data_type'] == 'String') { - $customOption = CRM_Core_BAO_CustomOption::getCustomOption($customFieldID, TRUE); - foreach ($customOption as $customValue) { - $val = CRM_Utils_Array::value('value', $customValue); - $label = CRM_Utils_Array::value('label', $customValue); - $label = strtolower($label); - $value = strtolower(trim($formatted[$key])); - if (($value == $label) || ($value == strtolower($val))) { - $params[$key] = $formatted[$key] = $val; - } - } - } - break; - - case 'CheckBox': - case 'AdvMulti-Select': - case 'Multi-Select': - - if (!empty($formatted[$key]) && !empty($params[$key])) { - $mulValues = explode(',', $formatted[$key]); - $customOption = CRM_Core_BAO_CustomOption::getCustomOption($customFieldID, TRUE); - $formatted[$key] = array(); - $params[$key] = array(); - foreach ($mulValues as $v1) { - foreach ($customOption as $v2) { - if ((strtolower($v2['label']) == strtolower(trim($v1))) || - (strtolower($v2['value']) == strtolower(trim($v1))) - ) { - if ($htmlType == 'CheckBox') { - $params[$key][$v2['value']] = $formatted[$key][$v2['value']] = 1; - } - else { - $params[$key][] = $formatted[$key][] = $v2['value']; - } - } - } - } - } - break; - } - } - } - - if (!empty($key) && ($customFieldID = CRM_Core_BAO_CustomField::getKeyID($key)) && array_key_exists($customFieldID, $customFields) && - !array_key_exists($customFieldID, $addressCustomFields) - ) { - // @todo calling api functions directly is not supported - _civicrm_api3_custom_format_params($params, $formatted, $extends); - } - - // to check if not update mode and unset the fields with empty value. - if (!$this->_updateWithId && array_key_exists('custom', $formatted)) { - foreach ($formatted['custom'] as $customKey => $customvalue) { - if (empty($formatted['custom'][$customKey][-1]['is_required'])) { - $formatted['custom'][$customKey][-1]['is_required'] = $customFields[$customKey]['is_required']; - } - $emptyValue = CRM_Utils_Array::value('value', $customvalue[-1]); - if (!isset($emptyValue)) { - unset($formatted['custom'][$customKey]); - } - } - } - - // parse street address, CRM-5450 - if ($this->_parseStreetAddress) { - if (array_key_exists('address', $formatted) && is_array($formatted['address'])) { - foreach ($formatted['address'] as $instance => & $address) { - $streetAddress = CRM_Utils_Array::value('street_address', $address); - if (empty($streetAddress)) { - continue; - } - // parse address field. - $parsedFields = CRM_Core_BAO_Address::parseStreetAddress($streetAddress); - - //street address consider to be parsed properly, - //If we get street_name and street_number. - if (empty($parsedFields['street_name']) || empty($parsedFields['street_number'])) { - $parsedFields = array_fill_keys(array_keys($parsedFields), ''); - } - - // merge parse address w/ main address block. - $address = array_merge($address, $parsedFields); - } - } - } - } - /** * Generate status and error message for unparsed street address records. * @@ -2090,9 +1924,9 @@ public function formatCommonData($params, &$formatted, &$contactFields) { */ public function processMessage(&$values, $statusFieldName, $returnCode) { if (empty($this->_unparsedStreetAddressContacts)) { - $importRecordParams = array( + $importRecordParams = [ $statusFieldName => 'IMPORTED', - ); + ]; } else { $errorMessage = ts("Record imported successfully but unable to parse the street address: "); @@ -2101,10 +1935,10 @@ public function processMessage(&$values, $statusFieldName, $returnCode) { $errorMessage .= "\n Contact ID:" . $contactValue['id'] . " " . $contactValue['streetAddress'] . ""; } array_unshift($values, $errorMessage); - $importRecordParams = array( + $importRecordParams = [ $statusFieldName => 'ERROR', "${statusFieldName}Msg" => $errorMessage, - ); + ]; $returnCode = CRM_Import_Parser::UNPARSED_ADDRESS_WARNING; } $this->updateImportRecord($values[count($values) - 1], $importRecordParams); @@ -2122,7 +1956,7 @@ public function checkRelatedContactFields($relKey, $params) { $allowToCreate = FALSE; //build the mapper field array. - static $relatedContactFields = array(); + static $relatedContactFields = []; if (!isset($relatedContactFields[$relKey])) { foreach ($this->_mapperRelated as $key => $name) { if (!$name) { @@ -2130,7 +1964,7 @@ public function checkRelatedContactFields($relKey, $params) { } if (!empty($relatedContactFields[$name]) && !is_array($relatedContactFields[$name])) { - $relatedContactFields[$name] = array(); + $relatedContactFields[$name] = []; } $fldName = CRM_Utils_Array::value($key, $this->_mapperRelatedContactDetails); if ($fldName == 'url') { @@ -2162,7 +1996,7 @@ public function checkRelatedContactFields($relKey, $params) { * @return array $subTypes */ public static function getSubtypes($contactType) { - $subTypes = array(); + $subTypes = []; $types = CRM_Contact_BAO_ContactType::subTypeInfo($contactType); if (count($types) > 0) { @@ -2193,15 +2027,27 @@ protected function getPossibleContactMatches($params) { $extIDMatch = NULL; if (!empty($params['external_identifier'])) { - $extIDContact = civicrm_api3('Contact', 'get', array( + // Check for any match on external id, deleted or otherwise. + $extIDContact = civicrm_api3('Contact', 'get', [ 'external_identifier' => $params['external_identifier'], - 'return' => 'id', - )); + 'showAll' => 'all', + 'return' => ['id', 'contact_is_deleted'], + ]); if (isset($extIDContact['id'])) { $extIDMatch = $extIDContact['id']; + + if ($extIDContact['values'][$extIDMatch]['contact_is_deleted'] == 1) { + // If the contact is deleted, update external identifier to be blank + // to avoid key error from MySQL. + $params = ['id' => $extIDMatch, 'external_identifier' => '']; + civicrm_api3('Contact', 'create', $params); + + // And now it is no longer a match. + $extIDMatch = NULL; + } } } - $checkParams = array('check_permissions' => FALSE, 'match' => $params); + $checkParams = ['check_permissions' => FALSE, 'match' => $params]; $checkParams['match']['contact_type'] = $this->_contactType; $possibleMatches = civicrm_api3('Contact', 'duplicatecheck', $checkParams); @@ -2210,14 +2056,43 @@ protected function getPossibleContactMatches($params) { } if ($possibleMatches['count']) { if (in_array($extIDMatch, array_keys($possibleMatches['values']))) { - return array($extIDMatch); + return [$extIDMatch]; } else { throw new CRM_Core_Exception(ts( 'Matching this contact based on the de-dupe rule would cause an external ID conflict')); } } - return array($extIDMatch); + return [$extIDMatch]; + } + + /** + * Format the form mapping parameters ready for the parser. + * + * @param int $count + * Number of rows. + * + * @return array $parserParameters + */ + public static function getParameterForParser($count) { + $baseArray = []; + for ($i = 0; $i < $count; $i++) { + $baseArray[$i] = NULL; + } + $parserParameters['mapperLocType'] = $baseArray; + $parserParameters['mapperPhoneType'] = $baseArray; + $parserParameters['mapperImProvider'] = $baseArray; + $parserParameters['mapperWebsiteType'] = $baseArray; + $parserParameters['mapperRelated'] = $baseArray; + $parserParameters['relatedContactType'] = $baseArray; + $parserParameters['relatedContactDetails'] = $baseArray; + $parserParameters['relatedContactLocType'] = $baseArray; + $parserParameters['relatedContactPhoneType'] = $baseArray; + $parserParameters['relatedContactImProvider'] = $baseArray; + $parserParameters['relatedContactWebsiteType'] = $baseArray; + + return $parserParameters; + } } diff --git a/CRM/Contact/Page/AJAX.php b/CRM/Contact/Page/AJAX.php index 342e4883c878..f354d12bea9d 100644 --- a/CRM/Contact/Page/AJAX.php +++ b/CRM/Contact/Page/AJAX.php @@ -1,9 +1,9 @@ $cfID); - $returnProperties = array('filter', 'data_type', 'is_active'); - $cf = array(); + $params = ['id' => $cfID]; + $returnProperties = ['filter', 'data_type', 'is_active']; + $cf = []; CRM_Core_DAO::commonRetrieve('CRM_Core_DAO_CustomField', $params, $cf, $returnProperties); if (!$cf['id'] || !$cf['is_active'] || $cf['data_type'] != 'ContactReference') { - CRM_Utils_System::civiExit('error'); + CRM_Utils_System::civiExit(1); } if (!empty($cf['filter'])) { - $filterParams = array(); + $filterParams = []; parse_str($cf['filter'], $filterParams); $action = CRM_Utils_Array::value('action', $filterParams); - if (!empty($action) && !in_array($action, array('get', 'lookup'))) { - CRM_Utils_System::civiExit('error'); + if (!empty($action) && !in_array($action, ['get', 'lookup'])) { + CRM_Utils_System::civiExit(1); } if (!empty($filterParams['group'])) { @@ -82,17 +84,17 @@ public static function contactReference() { 'contact_reference_options' ), '1'); - $return = array_unique(array_merge(array('sort_name'), $list)); + $return = array_unique(array_merge(['sort_name'], $list)); $limit = Civi::settings()->get('search_autocomplete_count'); - $params = array('offset' => 0, 'rowCount' => $limit, 'version' => 3); + $params = ['offset' => 0, 'rowCount' => $limit, 'version' => 3]; foreach ($return as $fld) { $params["return.{$fld}"] = 1; } if (!empty($action)) { - $excludeGet = array( + $excludeGet = [ 'reset', 'key', 'className', @@ -106,7 +108,7 @@ public static function contactReference() { 's', 'q', 'action', - ); + ]; foreach ($_GET as $param => $val) { if (empty($val) || in_array($param, $excludeGet) || @@ -136,18 +138,18 @@ public static function contactReference() { $contact = civicrm_api('Contact', 'Get', $params); if (!empty($contact['is_error'])) { - CRM_Utils_System::civiExit('error'); + CRM_Utils_System::civiExit(1); } - $contactList = array(); + $contactList = []; foreach ($contact['values'] as $value) { - $view = array(); + $view = []; foreach ($return as $fld) { if (!empty($value[$fld])) { $view[] = $value[$fld]; } } - $contactList[] = array('id' => $value['id'], 'text' => implode(' :: ', $view)); + $contactList[] = ['id' => $value['id'], 'text' => implode(' :: ', $view)]; } if (!empty($_GET['is_unit_test'])) { @@ -211,33 +213,30 @@ public static function getPCPList() { "; $dao = CRM_Core_DAO::executeQuery($query); - $output = array('results' => array(), 'more' => FALSE); + $output = ['results' => [], 'more' => FALSE]; while ($dao->fetch()) { if (++$count > $max) { $output['more'] = TRUE; } else { - $output['results'][] = array('id' => $dao->id, 'text' => $dao->data); + $output['results'][] = ['id' => $dao->id, 'text' => $dao->data]; } } CRM_Utils_JSON::output($output); } - /** - * @throws \CiviCRM_API3_Exception - */ public static function relationship() { $relType = CRM_Utils_Request::retrieve('rel_type', 'String', CRM_Core_DAO::$_nullObject, TRUE); $relContactID = CRM_Utils_Request::retrieve('rel_contact', 'Positive', CRM_Core_DAO::$_nullObject, TRUE); - $originalCid = CRM_Utils_Request::retrieve('cid', 'Positive', CRM_Core_DAO::$_nullObject); - $relationshipID = CRM_Utils_Request::retrieve('rel_id', 'Positive', CRM_Core_DAO::$_nullObject); + $originalCid = CRM_Utils_Request::retrieve('cid', 'Positive'); + $relationshipID = CRM_Utils_Request::retrieve('rel_id', 'Positive'); $caseID = CRM_Utils_Request::retrieve('case_id', 'Positive', CRM_Core_DAO::$_nullObject, TRUE); if (!CRM_Case_BAO_Case::accessCase($caseID)) { CRM_Utils_System::permissionDenied(); } - $ret = array('is_error' => 0); + $ret = ['is_error' => 0]; list($relTypeId, $b, $a) = explode('_', $relType); @@ -249,18 +248,36 @@ public static function relationship() { // Loop through multiple case clients foreach ($clientList as $i => $sourceContactID) { - $result = civicrm_api3('relationship', 'create', array( - 'case_id' => $caseID, - 'relationship_type_id' => $relTypeId, - "contact_id_$a" => $relContactID, - "contact_id_$b" => $sourceContactID, - 'start_date' => 'now', - )); + try { + $params = [ + 'case_id' => $caseID, + 'relationship_type_id' => $relTypeId, + "contact_id_$a" => $relContactID, + "contact_id_$b" => $sourceContactID, + 'sequential' => TRUE, + ]; + // first check if there is any existing relationship present with same parameters. + // If yes then update the relationship by setting active and start date to current time + $relationship = civicrm_api3('Relationship', 'get', $params)['values']; + $params = array_merge(CRM_Utils_Array::value(0, $relationship, $params), [ + 'start_date' => 'now', + 'is_active' => TRUE, + 'end_date' => '', + ]); + $result = civicrm_api3('relationship', 'create', $params); + } + catch (CiviCRM_API3_Exception $e) { + $ret['is_error'] = 1; + $ret['error_message'] = $e->getMessage(); + } // Save activity only for the primary (first) client if ($i == 0 && empty($result['is_error'])) { - CRM_Case_BAO_Case::createCaseRoleActivity($caseID, $result['id'], $relContactID); + CRM_Case_BAO_Case::createCaseRoleActivity($caseID, $result['id'], $relContactID, $sourceContactID); } } + if (!empty($_REQUEST['is_unit_test'])) { + return $ret; + } CRM_Utils_JSON::output($ret); } @@ -270,9 +287,9 @@ public static function relationship() { */ public static function customField() { $fieldId = CRM_Utils_Type::escape($_REQUEST['id'], 'Integer'); - $params = array('id' => $fieldId); - $returnProperties = array('help_pre', 'help_post'); - $values = array(); + $params = ['id' => $fieldId]; + $returnProperties = ['help_pre', 'help_post']; + $values = []; CRM_Core_DAO::commonRetrieve('CRM_Core_DAO_CustomField', $params, $values, $returnProperties); CRM_Utils_JSON::output($values); @@ -292,7 +309,7 @@ public static function deleteCustomValue() { CRM_Utils_System::setHttpHeader('Content-Type', 'text/plain'); $customValueID = CRM_Utils_Type::escape($_REQUEST['valueID'], 'Positive'); $customGroupID = CRM_Utils_Type::escape($_REQUEST['groupID'], 'Positive'); - $contactId = CRM_Utils_Request::retrieve('contactId', 'Positive', CRM_Core_DAO::$_nullObject); + $contactId = CRM_Utils_Request::retrieve('contactId', 'Positive'); CRM_Core_BAO_CustomValue::deleteCustomValue($customValueID, $customGroupID); if ($contactId) { echo CRM_Contact_BAO_Contact::getCountComponent('custom_' . $customGroupID, $contactId); @@ -305,35 +322,35 @@ public static function deleteCustomValue() { /** * check the CMS username. */ - static public function checkUserName() { - $signer = new CRM_Utils_Signer(CRM_Core_Key::privateKey(), array('for', 'ts')); - $sig = CRM_Utils_Request::retrieve('sig', 'String', CRM_Core_DAO::$_nullObject); - $for = CRM_Utils_Request::retrieve('for', 'String', CRM_Core_DAO::$_nullObject); + public static function checkUserName() { + $signer = new CRM_Utils_Signer(CRM_Core_Key::privateKey(), ['for', 'ts']); + $sig = CRM_Utils_Request::retrieve('sig', 'String'); + $for = CRM_Utils_Request::retrieve('for', 'String'); if ( CRM_Utils_Time::getTimeRaw() > $_REQUEST['ts'] + self::CHECK_USERNAME_TTL || $for != 'civicrm/ajax/cmsuser' || !$signer->validate($sig, $_REQUEST) ) { - $user = array('name' => 'error'); + $user = ['name' => 'error']; CRM_Utils_JSON::output($user); } $config = CRM_Core_Config::singleton(); $username = trim(CRM_Utils_Array::value('cms_name', $_REQUEST)); - $params = array('name' => $username); + $params = ['name' => $username]; - $errors = array(); + $errors = []; $config->userSystem->checkUserNameEmailExists($params, $errors); if (isset($errors['cms_name']) || isset($errors['name'])) { //user name is not available - $user = array('name' => 'no'); + $user = ['name' => 'no']; CRM_Utils_JSON::output($user); } else { //user name is available - $user = array('name' => 'yes'); + $user = ['name' => 'yes']; CRM_Utils_JSON::output($user); } @@ -385,6 +402,7 @@ public static function getContactEmail() { } if ($queryString) { + $result = []; $offset = CRM_Utils_Array::value('offset', $_GET, 0); $rowCount = Civi::settings()->get('search_autocomplete_count'); @@ -408,16 +426,16 @@ public static function getContactEmail() { // send query to hook to be modified if needed CRM_Utils_Hook::contactListQuery($query, $name, - CRM_Utils_Request::retrieve('context', 'String', CRM_Core_DAO::$_nullObject), - CRM_Utils_Request::retrieve('cid', 'Positive', CRM_Core_DAO::$_nullObject) + CRM_Utils_Request::retrieve('context', 'Alphanumeric'), + CRM_Utils_Request::retrieve('cid', 'Positive') ); $dao = CRM_Core_DAO::executeQuery($query); while ($dao->fetch()) { - $result[] = array( + $result[] = [ 'id' => $dao->id, 'text' => $dao->name, - ); + ]; } } else { @@ -433,23 +451,21 @@ public static function getContactEmail() { // send query to hook to be modified if needed CRM_Utils_Hook::contactListQuery($query, $name, - CRM_Utils_Request::retrieve('context', 'String', CRM_Core_DAO::$_nullObject), - CRM_Utils_Request::retrieve('cid', 'Positive', CRM_Core_DAO::$_nullObject) + CRM_Utils_Request::retrieve('context', 'Alphanumeric'), + CRM_Utils_Request::retrieve('cid', 'Positive') ); $dao = CRM_Core_DAO::executeQuery($query); while ($dao->fetch()) { //working here - $result[] = array( + $result[] = [ 'text' => '"' . $dao->name . '" <' . $dao->email . '>', 'id' => (CRM_Utils_Array::value('id', $_GET)) ? "{$dao->id}::{$dao->email}" : '"' . $dao->name . '" <' . $dao->email . '>', - ); + ]; } } - if ($result) { - CRM_Utils_JSON::output($result); - } + CRM_Utils_JSON::output($result); } } CRM_Utils_System::civiExit(); @@ -458,33 +474,28 @@ public static function getContactEmail() { public static function getContactPhone() { $queryString = NULL; + $sqlParmas = []; //check for mobile type $phoneTypes = CRM_Core_OptionGroup::values('phone_type', TRUE, FALSE, FALSE, NULL, 'name'); $mobileType = CRM_Utils_Array::value('Mobile', $phoneTypes); - $name = CRM_Utils_Array::value('name', $_GET); + $name = CRM_Utils_Request::retrieveValue('name', 'String', NULL, FALSE, 'GET'); if ($name) { - $name = CRM_Utils_Type::escape($name, 'String'); - $queryString = " ( cc.sort_name LIKE '%$name%' OR cp.phone LIKE '%$name%' ) "; + $key = (int) count(array_keys($sqlParmas)) + 1; + $queryString = " ( cc.sort_name LIKE %{$key} OR cp.phone LIKE %{$key} ) "; + $sqlParams[$key] = ['%' . $name . '%', 'String']; } else { - $cid = CRM_Utils_Array::value('cid', $_GET); + $cid = CRM_Utils_Request::retrieveValue('cid', 'CommaSeparatedIntegers', NULL, FALSE, 'GET'); if ($cid) { - //check cid for integer - $contIDS = explode(',', $cid); - foreach ($contIDS as $contID) { - CRM_Utils_Type::escape($contID, 'Integer'); - } $queryString = " cc.id IN ( $cid )"; } } if ($queryString) { - $offset = CRM_Utils_Array::value('offset', $_GET, 0); - $rowCount = CRM_Utils_Array::value('rowcount', $_GET, 20); - - $offset = CRM_Utils_Type::escape($offset, 'Int'); - $rowCount = CRM_Utils_Type::escape($rowCount, 'Int'); + $result = []; + $offset = (int) CRM_Utils_Request::retrieveValue('offset', 'Integer', 0, FALSE, 'GET'); + $rowCount = (int) CRM_Utils_Request::retrieveValue('rowcount', 'Integer', 20, FALSE, 'GET'); // add acl clause here list($aclFrom, $aclWhere) = CRM_Contact_BAO_Contact_Permission::cacheClause('cc'); @@ -504,29 +515,25 @@ public static function getContactPhone() { // send query to hook to be modified if needed CRM_Utils_Hook::contactListQuery($query, $name, - CRM_Utils_Request::retrieve('context', 'String', CRM_Core_DAO::$_nullObject), - CRM_Utils_Request::retrieve('cid', 'Positive', CRM_Core_DAO::$_nullObject) + CRM_Utils_Request::retrieve('context', 'Alphanumeric'), + CRM_Utils_Request::retrieve('cid', 'Positive') ); - $dao = CRM_Core_DAO::executeQuery($query); + $dao = CRM_Core_DAO::executeQuery($query, $sqlParams); while ($dao->fetch()) { - $result[] = array( + $result[] = [ 'text' => '"' . $dao->name . '" (' . $dao->phone . ')', 'id' => (CRM_Utils_Array::value('id', $_GET)) ? "{$dao->id}::{$dao->phone}" : '"' . $dao->name . '" <' . $dao->phone . '>', - ); + ]; } - } - - if ($result) { CRM_Utils_JSON::output($result); } CRM_Utils_System::civiExit(); } - public static function buildSubTypes() { - $parent = CRM_Utils_Request::retrieve('parentId', 'Positive', CRM_Core_DAO::$_nullObject); + $parent = CRM_Utils_Request::retrieve('parentId', 'Positive'); switch ($parent) { case 1: @@ -548,7 +555,7 @@ public static function buildSubTypes() { } public static function buildDedupeRules() { - $parent = CRM_Utils_Request::retrieve('parentId', 'Positive', CRM_Core_DAO::$_nullObject); + $parent = CRM_Utils_Request::retrieve('parentId', 'Positive'); switch ($parent) { case 1: @@ -594,12 +601,12 @@ public static function getSignature() { $query = "SELECT signature_text, signature_html FROM civicrm_email WHERE id = {$emailID}"; $dao = CRM_Core_DAO::executeQuery($query); - $signatures = array(); + $signatures = []; while ($dao->fetch()) { - $signatures = array( + $signatures = [ 'signature_text' => $dao->signature_text, 'signature_html' => $dao->signature_html, - ); + ]; } CRM_Utils_JSON::output($signatures); @@ -634,7 +641,7 @@ public static function processDupes() { $status = $exception->delete(); } - CRM_Utils_JSON::output(array('status' => ($status) ? $oper : $status)); + CRM_Utils_JSON::output(['status' => ($status) ? $oper : $status]); } /** @@ -646,25 +653,28 @@ public static function getDedupes() { $gid = CRM_Utils_Request::retrieve('gid', 'Positive'); $rgid = CRM_Utils_Request::retrieve('rgid', 'Positive'); - $selected = isset($_REQUEST['selected']) ? CRM_Utils_Type::escape($_REQUEST['selected'], 'Integer') : 0; + $null = NULL; + $criteria = CRM_Utils_Request::retrieve('criteria', 'Json', $null, FALSE, '{}'); + $selected = CRM_Utils_Request::retrieveValue('selected', 'Boolean'); if ($rowCount < 0) { $rowCount = 0; } $whereClause = $orderByClause = ''; - $cacheKeyString = CRM_Dedupe_Merger::getMergeCacheKeyString($rgid, $gid); - $searchRows = array(); + $cacheKeyString = CRM_Dedupe_Merger::getMergeCacheKeyString($rgid, $gid, json_decode($criteria, TRUE)); + + $searchRows = []; $searchParams = self::getSearchOptionsFromRequest(); - $queryParams = array(); + $queryParams = []; $join = ''; - $where = array(); + $where = []; $isOrQuery = self::isOrQuery(); $nextParamKey = 3; - $mappings = array( + $mappings = [ 'dst' => 'cc1.display_name', 'src' => 'cc2.display_name', 'dst_email' => 'ce1.email', @@ -673,22 +683,24 @@ public static function getDedupes() { 'src_postcode' => 'ca2.postal_code', 'dst_street' => 'ca1.street', 'src_street' => 'ca2.street', - ); + ]; foreach ($mappings as $key => $dbName) { if (!empty($searchParams[$key])) { - $queryParams[$nextParamKey] = array('%' . $searchParams[$key] . '%', 'String'); + // CRM-18694. + $wildcard = strstr($key, 'postcode') ? '' : '%'; + $queryParams[$nextParamKey] = [$wildcard . $searchParams[$key] . '%', 'String']; $where[] = $dbName . " LIKE %{$nextParamKey} "; $nextParamKey++; } } if ($isOrQuery) { - $whereClause = ' ( ' . implode(' OR ', $where) . ' ) '; + $whereClause = ' ( ' . implode(' OR ', $where) . ' ) '; } else { if (!empty($where)) { - $whereClause = implode(' AND ', $where); + $whereClause = implode(' AND ', $where); } } $whereClause .= $whereClause ? ' AND de.id IS NULL' : ' de.id IS NULL'; @@ -698,7 +710,7 @@ public static function getDedupes() { } $join .= CRM_Dedupe_Merger::getJoinOnDedupeTable(); - $select = array( + $select = [ 'cc1.contact_type' => 'dst_contact_type', 'cc1.display_name' => 'dst_display_name', 'cc1.contact_sub_type' => 'dst_contact_sub_type', @@ -711,7 +723,7 @@ public static function getDedupes() { 'ca2.postal_code' => 'src_postcode', 'ca1.street_address' => 'dst_street', 'ca2.street_address' => 'src_street', - ); + ]; if ($select) { $join .= " INNER JOIN civicrm_contact cc1 ON cc1.id = pn.entity_id1"; @@ -806,9 +818,17 @@ public static function getDedupes() { $searchRows[$count]['weight'] = CRM_Utils_Array::value('weight', $pair); if (!empty($pairInfo['data']['canMerge'])) { - $mergeParams = "reset=1&cid={$pairInfo['entity_id1']}&oid={$pairInfo['entity_id2']}&action=update&rgid={$rgid}&limit=" . CRM_Utils_Request::retrieve('limit', 'Integer'); + $mergeParams = [ + 'reset' => 1, + 'cid' => $pairInfo['entity_id1'], + 'oid' => $pairInfo['entity_id2'], + 'action' => 'update', + 'rgid' => $rgid, + 'criteria' => $criteria, + 'limit' => CRM_Utils_Request::retrieve('limit', 'Integer'), + ]; if ($gid) { - $mergeParams .= "&gid={$gid}"; + $mergeParams['gid'] = $gid; } $searchRows[$count]['actions'] = "" . ts('flip') . " | "; @@ -821,11 +841,11 @@ public static function getDedupes() { $count++; } - $dupePairs = array( + $dupePairs = [ 'data' => $searchRows, 'recordsTotal' => $iTotal, 'recordsFiltered' => $iFilteredTotal, - ); + ]; if (!empty($_REQUEST['is_unit_test'])) { return $dupePairs; } @@ -838,10 +858,10 @@ public static function getDedupes() { * @return array */ public static function getSearchOptionsFromRequest() { - $searchParams = array(); + $searchParams = []; $searchData = CRM_Utils_Array::value('search', $_REQUEST); $searchData['value'] = CRM_Utils_Type::escape($searchData['value'], 'String'); - $selectorElements = array( + $selectorElements = [ 'is_selected', 'is_selected_input', 'src_image', @@ -857,7 +877,7 @@ public static function getSearchOptionsFromRequest() { 'conflicts', 'weight', 'actions', - ); + ]; $columns = $_REQUEST['columns']; foreach ($columns as $column) { @@ -950,22 +970,22 @@ public static function selectUnselectContacts() { $elements[$key] = self::_convertToId($element); } CRM_Utils_Type::escapeAll($elements, 'Integer'); - CRM_Core_BAO_PrevNextCache::markSelection($cacheKey, $actionToPerform, $elements); + Civi::service('prevnext')->markSelection($cacheKey, $actionToPerform, $elements); } else { - CRM_Core_BAO_PrevNextCache::markSelection($cacheKey, $actionToPerform); + Civi::service('prevnext')->markSelection($cacheKey, $actionToPerform); } } elseif ($variableType == 'single') { $cId = self::_convertToId($name); CRM_Utils_Type::escape($cId, 'Integer'); $action = ($state == 'checked') ? 'select' : 'unselect'; - CRM_Core_BAO_PrevNextCache::markSelection($cacheKey, $action, $cId); + Civi::service('prevnext')->markSelection($cacheKey, $action, $cId); } - $contactIds = CRM_Core_BAO_PrevNextCache::getSelection($cacheKey); + $contactIds = Civi::service('prevnext')->getSelection($cacheKey); $countSelectionCids = count($contactIds[$cacheKey]); - $arrRet = array('getCount' => $countSelectionCids); + $arrRet = ['getCount' => $countSelectionCids]; CRM_Utils_JSON::output($arrRet); } @@ -982,15 +1002,15 @@ public static function _convertToId($name) { } public static function getAddressDisplay() { - $contactId = CRM_Utils_Request::retrieve('contact_id', 'Positive', CRM_Core_DAO::$_nullObject); + $contactId = CRM_Utils_Request::retrieve('contact_id', 'Positive'); if (!$contactId) { $addressVal["error_message"] = "no contact id found"; } else { - $entityBlock = array( + $entityBlock = [ 'contact_id' => $contactId, 'entity_id' => $contactId, - ); + ]; $addressVal = CRM_Core_BAO_Address::getValues($entityBlock); } @@ -1001,17 +1021,15 @@ public static function getAddressDisplay() { * Mark dupe pairs as selected from un-selected state or vice-versa, in dupe cache table. */ public static function toggleDedupeSelect() { - $rgid = CRM_Utils_Type::escape($_REQUEST['rgid'], 'Integer'); - $gid = CRM_Utils_Type::escape($_REQUEST['gid'], 'Integer'); $pnid = $_REQUEST['pnid']; $isSelected = CRM_Utils_Type::escape($_REQUEST['is_selected'], 'Boolean'); + $cacheKeyString = CRM_Utils_Request::retrieve('cacheKey', 'Alphanumeric', $null, FALSE); - $cacheKeyString = CRM_Dedupe_Merger::getMergeCacheKeyString($rgid, $gid); - - $params = array( - 1 => array($isSelected, 'Boolean'), - 3 => array("$cacheKeyString%", 'String'), // using % to address rows with conflicts as well - ); + $params = [ + 1 => [$isSelected, 'Boolean'], + // using % to address rows with conflicts as well + 3 => ["$cacheKeyString%", 'String'], + ]; //check pnid is_array or integer $whereClause = NULL; @@ -1023,7 +1041,7 @@ public static function toggleDedupeSelect() { else { $pnid = CRM_Utils_Type::escape($pnid, 'Integer'); $whereClause = " id = %2"; - $params[2] = array($pnid, 'Integer'); + $params[2] = [$pnid, 'Integer']; } $sql = "UPDATE civicrm_prevnext_cache SET is_selected = %1 WHERE {$whereClause} AND cacheKey LIKE %3"; @@ -1037,7 +1055,7 @@ public static function toggleDedupeSelect() { */ public static function getContactRelationships() { $contactID = CRM_Utils_Type::escape($_GET['cid'], 'Integer'); - $context = CRM_Utils_Type::escape($_GET['context'], 'String'); + $context = CRM_Utils_Request::retrieve('context', 'Alphanumeric'); $relationship_type_id = CRM_Utils_Type::escape(CRM_Utils_Array::value('relationship_type_id', $_GET), 'Integer', FALSE); if (!CRM_Contact_BAO_Contact_Permission::allow($contactID)) { diff --git a/CRM/Contact/Page/CustomSearch.php b/CRM/Contact/Page/CustomSearch.php index 76765c0898d5..15b313f23182 100644 --- a/CRM/Contact/Page/CustomSearch.php +++ b/CRM/Contact/Page/CustomSearch.php @@ -1,9 +1,9 @@ fetch()) { if (trim($dao->description)) { $rows[$dao->value] = $dao->description; diff --git a/CRM/Contact/Page/DashBoard.php b/CRM/Contact/Page/DashBoard.php index 3f5f09a2e68e..3047958b975a 100644 --- a/CRM/Contact/Page/DashBoard.php +++ b/CRM/Contact/Page/DashBoard.php @@ -1,9 +1,9 @@ assign('contactDashlets', CRM_Core_BAO_Dashboard::getContactDashletsForJS()); CRM_Utils_System::setTitle(ts('CiviCRM Home')); - $session = CRM_Core_Session::singleton(); - $contactID = $session->get('userID'); + $contactID = CRM_Core_Session::getLoggedInContactID(); // call hook to get html from other modules // ignored but needed to prevent warnings diff --git a/CRM/Contact/Page/Dashlet.php b/CRM/Contact/Page/Dashlet.php index 50b185d03b54..3444e1ae993a 100644 --- a/CRM/Contact/Page/Dashlet.php +++ b/CRM/Contact/Page/Dashlet.php @@ -1,9 +1,9 @@ $item['label'], 'is_reserved' => $allDashlets[$item['dashboard_id']]['is_reserved'], - ); + ]; unset($allDashlets[$item['dashboard_id']]); } foreach ($allDashlets as $dashletID => $values) { $key = "{$dashletID}-0"; - $availableDashlets[$key] = array( + $availableDashlets[$key] = [ 'label' => $values['label'], 'is_reserved' => $values['is_reserved'], - ); + ]; } $this->assign('contactDashlets', $contactDashlets); diff --git a/CRM/Contact/Page/DedupeException.php b/CRM/Contact/Page/DedupeException.php index f1fa49554236..cb73cb3c2039 100644 --- a/CRM/Contact/Page/DedupeException.php +++ b/CRM/Contact/Page/DedupeException.php @@ -1,9 +1,9 @@ find(); - $contactIds = array(); - while ($exception->fetch()) { - $key = "{$exception->contact_id1}_{$exception->contact_id2}"; - $contactIds[$exception->contact_id1] = $exception->contact_id1; - $contactIds[$exception->contact_id2] = $exception->contact_id2; - $dedupeExceptions[$key] = array( - 'main' => array('id' => $exception->contact_id1), - 'other' => array('id' => $exception->contact_id2), - ); - } - //get the dupe contacts display names. - if (!empty($dedupeExceptions)) { - $sql = 'select id, display_name from civicrm_contact where id IN ( ' . implode(', ', $contactIds) . ' )'; - $contact = CRM_Core_DAO::executeQuery($sql); - $displayNames = array(); - while ($contact->fetch()) { - $displayNames[$contact->id] = $contact->display_name; - } - foreach ($dedupeExceptions as $key => & $values) { - $values['main']['name'] = CRM_Utils_Array::value($values['main']['id'], $displayNames); - $values['other']['name'] = CRM_Utils_Array::value($values['other']['id'], $displayNames); - } + public function run() { + $this->initializePager(); + $this->assign('exceptions', $this->getExceptions()); + return parent::run(); + } + + /** + * Method to initialize pager + * + * @access protected + */ + protected function initializePager() { + $params = []; + + $contactOneQ = CRM_Utils_Request::retrieve('crmContact1Q', 'String'); + + if ($contactOneQ) { + $params['contact_id1.display_name'] = ['LIKE' => '%' . $contactOneQ . '%']; + $params['contact_id2.display_name'] = ['LIKE' => '%' . $contactOneQ . '%']; + + $params['options']['or'] = [["contact_id1.display_name", "contact_id2.display_name"]]; } - $this->assign('dedupeExceptions', $dedupeExceptions); + + $totalitems = civicrm_api3('Exception', "getcount", $params); + $params = [ + 'total' => $totalitems, + 'rowCount' => CRM_Utils_Pager::ROWCOUNT, + 'status' => ts('Dedupe Exceptions %%StatusMessage%%'), + 'buttonBottom' => 'PagerBottomButton', + 'buttonTop' => 'PagerTopButton', + 'pageID' => $this->get(CRM_Utils_Pager::PAGE_ID), + ]; + $this->_pager = new CRM_Utils_Pager($params); + $this->assign_by_ref('pager', $this->_pager); } /** - * the main function that is called when the page loads, - * it decides the which action has to be taken for the page. + * Function to get the exceptions * - * @return null + * @return array $exceptions + * @access protected */ - public function run() { - $this->preProcess(); - return parent::run(); + protected function getExceptions() { + list($offset, $limit) = $this->_pager->getOffsetAndRowCount(); + $contactOneQ = CRM_Utils_Request::retrieve('crmContact1Q', 'String'); + + if (!$contactOneQ) { + $contactOneQ = ''; + } + + $this->assign('searchcontact1', $contactOneQ); + + $params = [ + "options" => ['limit' => $limit, 'offset' => $offset], + 'return' => ["contact_id1.display_name", "contact_id2.display_name", "contact_id1", "contact_id2"], + ]; + + if ($contactOneQ != '') { + $params['contact_id1.display_name'] = ['LIKE' => '%' . $contactOneQ . '%']; + $params['contact_id2.display_name'] = ['LIKE' => '%' . $contactOneQ . '%']; + + $params['options']['or'] = [["contact_id1.display_name", "contact_id2.display_name"]]; + } + + $exceptions = civicrm_api3("Exception", "get", $params); + $exceptions = $exceptions["values"]; + return $exceptions; } } diff --git a/CRM/Contact/Page/DedupeFind.php b/CRM/Contact/Page/DedupeFind.php index 1fa51fdd1832..56eba2c9846b 100644 --- a/CRM/Contact/Page/DedupeFind.php +++ b/CRM/Contact/Page/DedupeFind.php @@ -1,9 +1,9 @@ selected === NULL) ? NULL : (int) $this->selected; + } /** * Get BAO Name. @@ -53,36 +70,66 @@ public function getBAOName() { public function &links() { } + /** + * Initialize properties from input. + */ + protected function initialize() { + $this->selected = CRM_Utils_Request::retrieveValue('selected', 'Boolean'); + } + /** * Browse all rule groups. */ public function run() { + $this->initialize(); $gid = CRM_Utils_Request::retrieve('gid', 'Positive', $this, FALSE, 0); $action = CRM_Utils_Request::retrieve('action', 'String', $this, FALSE, 0); - $context = CRM_Utils_Request::retrieve('context', 'String', $this); + $context = CRM_Utils_Request::retrieve('context', 'Alphanumeric', $this); $limit = CRM_Utils_Request::retrieve('limit', 'Integer', $this); - $rgid = CRM_Utils_Request::retrieve('rgid', 'Positive'); - $urlQry = "reset=1&rgid={$rgid}&gid={$gid}&limit={$limit}"; - $this->assign('urlQuery', $urlQry); + $rgid = CRM_Utils_Request::retrieve('rgid', 'Positive', $this); + $cid = CRM_Utils_Request::retrieve('cid', 'Positive', $this, FALSE, 0); + + $criteria = CRM_Utils_Request::retrieve('criteria', 'Json', $this, FALSE, '{}'); + $this->assign('criteria', $criteria); + + $isConflictMode = ($context == 'conflicts'); + if ($cid) { + $this->_cid = $cid; + } + if ($gid) { + $this->_gid = $gid; + } + $this->_rgid = $rgid; - $session = CRM_Core_Session::singleton(); - $contactIds = $session->get('selectedSearchContactIds'); - if ($context == 'search' || !empty($contactIds)) { + $urlQry = [ + 'reset' => 1, + 'rgid' => $rgid, + 'gid' => $gid, + 'limit' => $limit, + 'criteria' => $criteria, + ]; + $this->assign('urlQuery', CRM_Utils_System::makeQueryString($urlQry)); + $this->assign('isSelected', $this->isSelected()); + $criteria = json_decode($criteria, TRUE); + $cacheKeyString = CRM_Dedupe_Merger::getMergeCacheKeyString($rgid, $gid, $criteria); + $this->assign('cacheKey', $cacheKeyString); + + if ($context == 'search') { $context = 'search'; - $this->assign('backURL', $session->readUserContext()); + $this->assign('backURL', CRM_Core_Session::singleton()->readUserContext()); } if ($action & CRM_Core_Action::RENEW) { // empty cache - if ($rgid) { - CRM_Core_BAO_PrevNextCache::deleteItem(NULL, CRM_Dedupe_Merger::getMergeCacheKeyString($rgid, $gid)); + CRM_Core_BAO_PrevNextCache::deleteItem(NULL, $cacheKeyString); } - CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contact/dedupefind', $urlQry . "&action=update")); + $urlQry['action'] = 'update'; + CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contact/dedupefind', $urlQry)); } elseif ($action & CRM_Core_Action::MAP) { // do a batch merge if requested - $result = CRM_Dedupe_Merger::batchMerge($rgid, $gid, 'safe', TRUE, 75); + $result = CRM_Dedupe_Merger::batchMerge($rgid, $gid, 'safe', 75, 2, $criteria); $skippedCount = CRM_Utils_Request::retrieve('skipped', 'Positive', $this, FALSE, 0); $skippedCount = $skippedCount + count($result['skipped']); @@ -92,20 +139,23 @@ public function run() { if (empty($result['merged']) && empty($result['skipped'])) { $message = ''; if ($mergedCount >= 1) { - $message = ts("%1 pairs of duplicates were merged", array(1 => $mergedCount)); + $message = ts("%1 pairs of duplicates were merged", [1 => $mergedCount]); } if ($skippedCount >= 1) { $message = $message ? "{$message} and " : ''; $message .= ts("%1 pairs of duplicates were skipped due to conflict", - array(1 => $skippedCount) + [1 => $skippedCount] ); } $message .= ts(" during the batch merge process with safe mode."); CRM_Core_Session::setStatus($message, ts('Merge Complete'), 'success'); - CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contact/dedupefind', $urlQry . "&action=update")); + $urlQry['action'] = 'update'; + CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contact/dedupefind', $urlQry)); } else { - $urlQry .= "&action=map&skipped={$skippedCount}&merged={$mergedCount}"; + $urlQry['action'] = 'map'; + $urlQry['skipped'] = $skippedCount; + $urlQry['merged'] = $mergedCount; CRM_Utils_System::jsRedirect( CRM_Utils_System::url('civicrm/contact/dedupefind', $urlQry), ts('Batch Merge Task in progress'), @@ -117,103 +167,51 @@ public function run() { if ($action & CRM_Core_Action::UPDATE || $action & CRM_Core_Action::BROWSE ) { - $cid = CRM_Utils_Request::retrieve('cid', 'Positive', $this, FALSE, 0); $this->action = CRM_Core_Action::UPDATE; - $urlQry .= '&snippet=4'; - if ($context == 'conflicts') { - $urlQry .= "&selected=1"; - } + $urlQry['snippet'] = 4; $this->assign('sourceUrl', CRM_Utils_System::url('civicrm/ajax/dedupefind', $urlQry, FALSE, NULL, FALSE)); - //reload from cache table - $cacheKeyString = CRM_Dedupe_Merger::getMergeCacheKeyString($rgid, $gid); - - $stats = CRM_Dedupe_Merger::getMergeStatsMsg($cacheKeyString); + $stats = CRM_Dedupe_Merger::getMergeStats($cacheKeyString); if ($stats) { - CRM_Core_Session::setStatus($stats); + $message = CRM_Dedupe_Merger::getMergeStatsMsg($stats); + $status = empty($stats['skipped']) ? 'success' : 'alert'; + CRM_Core_Session::setStatus($message, ts('Batch Complete'), $status, ['expires' => 0]); // reset so we not displaying same message again CRM_Dedupe_Merger::resetMergeStats($cacheKeyString); } - $join = CRM_Dedupe_Merger::getJoinOnDedupeTable(); - $where = "de.id IS NULL"; - if ($context == 'conflicts') { - $where .= " AND pn.is_selected = 1"; - } - $this->_mainContacts = CRM_Core_BAO_PrevNextCache::retrieve($cacheKeyString, $join, $where); + + $this->_mainContacts = CRM_Dedupe_Merger::getDuplicatePairs($rgid, $gid, !$isConflictMode, 0, $this->isSelected(), '', $isConflictMode, $criteria, TRUE, $limit); + if (empty($this->_mainContacts)) { - if ($context == 'conflicts') { + if ($isConflictMode) { // if the current screen was intended to list only selected contacts, move back to full dupe list - CRM_Utils_System::redirect(CRM_Utils_System::url(CRM_Utils_System::currentPath(), $urlQry . '&action=update')); + $urlQry['action'] = 'update'; + unset($urlQry['snippet']); + CRM_Utils_System::redirect(CRM_Utils_System::url(CRM_Utils_System::currentPath(), $urlQry)); } - if ($gid) { - $foundDupes = $this->get("dedupe_dupes_$gid"); - if (!$foundDupes) { - $foundDupes = CRM_Dedupe_Finder::dupesInGroup($rgid, $gid, $limit); - } - $this->set("dedupe_dupes_$gid", $foundDupes); - } - elseif (!empty($contactIds)) { - $foundDupes = $this->get("search_dedupe_dupes_$gid"); - if (!$foundDupes) { - $foundDupes = CRM_Dedupe_Finder::dupes($rgid, $contactIds); - } - $this->set("search_dedupe_dupes_$gid", $foundDupes); - } - else { - $foundDupes = $this->get('dedupe_dupes'); - if (!$foundDupes) { - $foundDupes = CRM_Dedupe_Finder::dupes($rgid, array(), TRUE, $limit); - } - $this->set('dedupe_dupes', $foundDupes); - } - if (!$foundDupes) { - $ruleGroup = new CRM_Dedupe_BAO_RuleGroup(); - $ruleGroup->id = $rgid; - $ruleGroup->find(TRUE); - - $session = CRM_Core_Session::singleton(); - $session->setStatus(ts('No possible duplicates were found using %1 rule.', array(1 => $ruleGroup->name)), ts('None Found'), 'info'); - $url = CRM_Utils_System::url('civicrm/contact/deduperules', 'reset=1'); - if ($context == 'search') { - $url = $session->readUserContext(); - } - CRM_Utils_System::redirect($url); - } - else { - $mainContacts = CRM_Dedupe_Finder::parseAndStoreDupePairs($foundDupes, $cacheKeyString); - - if ($cid) { - $this->_cid = $cid; - } - if ($gid) { - $this->_gid = $gid; - } - $this->_rgid = $rgid; - $this->_mainContacts = $mainContacts; - - $session = CRM_Core_Session::singleton(); - if ($this->_cid) { - $session->pushUserContext(CRM_Utils_System::url('civicrm/contact/deduperules', - $urlQry . "&action=update&cid={$this->_cid}" - )); - } - else { - $session->pushUserContext(CRM_Utils_System::url('civicrm/contact/dedupefind', - $urlQry . "&action=update" - )); - } + $ruleGroupName = civicrm_api3('RuleGroup', 'getvalue', ['id' => $rgid, 'return' => 'name']); + CRM_Core_Session::singleton()->setStatus(ts('No possible duplicates were found using %1 rule.', [1 => $ruleGroupName]), ts('None Found'), 'info'); + $url = CRM_Utils_System::url('civicrm/contact/deduperules', 'reset=1'); + if ($context == 'search') { + $url = CRM_Core_Session::singleton()->readUserContext(); } + CRM_Utils_System::redirect($url); } else { - if ($cid) { - $this->_cid = $cid; + $urlQry['action'] = 'update'; + if ($this->_cid) { + $urlQry['cid'] = $this->_cid; + CRM_Core_Session::singleton()->pushUserContext(CRM_Utils_System::url('civicrm/contact/deduperules', + $urlQry + )); } - if ($gid) { - $this->_gid = $gid; + else { + CRM_Core_Session::singleton()->pushUserContext(CRM_Utils_System::url('civicrm/contact/dedupefind', + $urlQry + )); } - $this->_rgid = $rgid; } $this->assign('action', $this->action); @@ -226,7 +224,6 @@ public function run() { } $this->assign('context', $context); - // parent run return parent::run(); } diff --git a/CRM/Contact/Page/DedupeMerge.php b/CRM/Contact/Page/DedupeMerge.php index 7968d853e895..1dddf5619470 100644 --- a/CRM/Contact/Page/DedupeMerge.php +++ b/CRM/Contact/Page/DedupeMerge.php @@ -1,9 +1,9 @@ runAllViaWeb(); } else { CRM_Core_Session::setStatus(ts('Nothing to merge.')); } - - // parent run return parent::run(); } @@ -55,38 +52,41 @@ public function run() { * Build a queue of tasks by dividing dupe pairs in batches. */ public static function getRunner() { - $rgid = CRM_Utils_Request::retrieve('rgid', 'Positive'); - $gid = CRM_Utils_Request::retrieve('gid', 'Positive'); - $limit = CRM_Utils_Request::retrieve('limit', 'Positive'); - $action = CRM_Utils_Request::retrieve('action', 'String', CRM_Core_DAO::$_nullObject); - $mode = CRM_Utils_Request::retrieve('mode', 'String', CRM_Core_DAO::$_nullObject, FALSE, 'safe'); - - $cacheKeyString = CRM_Dedupe_Merger::getMergeCacheKeyString($rgid, $gid); - $urlQry = "reset=1&action=update&rgid={$rgid}&gid={$gid}&limit={$limit}"; + $rgid = CRM_Utils_Request::retrieveValue('rgid', 'Positive'); + $gid = CRM_Utils_Request::retrieveValue('gid', 'Positive'); + $limit = CRM_Utils_Request::retrieveValue('limit', 'Positive'); + $action = CRM_Utils_Request::retrieveValue('action', 'String'); + $mode = CRM_Utils_Request::retrieveValue('mode', 'String', 'safe'); + $criteria = CRM_Utils_Request::retrieve('criteria', 'Json', $null, FALSE, '{}'); + + $urlQry = [ + 'reset' => 1, + 'action' => 'update', + 'rgid' => $rgid, + 'gid' => $gid, + 'limit' => $limit, + 'criteria' => $criteria, + ]; + + $criteria = json_decode($criteria, TRUE); + $cacheKeyString = CRM_Dedupe_Merger::getMergeCacheKeyString($rgid, $gid, $criteria); if ($mode == 'aggressive' && !CRM_Core_Permission::check('force merge duplicate contacts')) { CRM_Core_Session::setStatus(ts('You do not have permission to force merge duplicate contact records'), ts('Permission Denied'), 'error'); CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contact/dedupefind', $urlQry)); } // Setup the Queue - $queue = CRM_Queue_Service::singleton()->create(array( + $queue = CRM_Queue_Service::singleton()->create([ 'name' => $cacheKeyString, 'type' => 'Sql', 'reset' => TRUE, - )); + ]); $where = NULL; - if ($action == CRM_Core_Action::MAP) { - $where = "pn.is_selected = 1"; - $isSelected = 1; - } - else { - // else merge all (2) - $isSelected = 2; - } + $onlyProcessSelected = ($action == CRM_Core_Action::MAP) ? 1 : 0; - $total = CRM_Core_BAO_PrevNextCache::getCount($cacheKeyString, NULL, $where); + $total = CRM_Core_BAO_PrevNextCache::getCount($cacheKeyString, NULL, ($onlyProcessSelected ? "pn.is_selected = 1" : NULL)); if ($total <= 0) { // Nothing to do. CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contact/dedupefind', $urlQry)); @@ -96,9 +96,9 @@ public static function getRunner() { CRM_Dedupe_Merger::resetMergeStats($cacheKeyString); for ($i = 1; $i <= ceil($total / self::BATCHLIMIT); $i++) { - $task = new CRM_Queue_Task( - array('CRM_Contact_Page_DedupeMerge', 'callBatchMerge'), - array($rgid, $gid, $mode, FALSE, self::BATCHLIMIT, $isSelected), + $task = new CRM_Queue_Task( + ['CRM_Contact_Page_DedupeMerge', 'callBatchMerge'], + [$rgid, $gid, $mode, self::BATCHLIMIT, $onlyProcessSelected, $criteria], "Processed " . $i * self::BATCHLIMIT . " pair of duplicates out of " . $total ); @@ -107,13 +107,16 @@ public static function getRunner() { } // Setup the Runner - $urlQry .= "&context=conflicts"; - $runner = new CRM_Queue_Runner(array( + $urlQry['context'] = "conflicts"; + if ($onlyProcessSelected) { + $urlQry['selected'] = 1; + } + $runner = new CRM_Queue_Runner([ 'title' => ts('Merging Duplicates..'), 'queue' => $queue, 'errorMode' => CRM_Queue_Runner::ERROR_ABORT, 'onEndUrl' => CRM_Utils_System::url('civicrm/contact/dedupefind', $urlQry, TRUE, NULL, FALSE), - )); + ]); return $runner; } @@ -126,17 +129,14 @@ public static function getRunner() { * @param int $gid * @param string $mode * 'safe' mode or 'force' mode. - * @param bool $autoFlip - * Override the values in the prevnext table & use the lowest value? - * As the form offers the user to flip the values themselves this should - * only be TRUE if you wish to ignore the user. * @param int $batchLimit * @param int $isSelected + * @param array $criteria * * @return int */ - public static function callBatchMerge(CRM_Queue_TaskContext $ctx, $rgid, $gid, $mode = 'safe', $autoFlip, $batchLimit, $isSelected) { - CRM_Dedupe_Merger::batchMerge($rgid, $gid, $mode, $autoFlip, $batchLimit, $isSelected); + public static function callBatchMerge(CRM_Queue_TaskContext $ctx, $rgid, $gid, $mode = 'safe', $batchLimit, $isSelected, $criteria) { + CRM_Dedupe_Merger::batchMerge($rgid, $gid, $mode, $batchLimit, $isSelected, $criteria, TRUE, FALSE); return CRM_Queue_Task::TASK_SUCCESS; } diff --git a/CRM/Contact/Page/DedupeRules.php b/CRM/Contact/Page/DedupeRules.php index 34059066a4a8..aa5fcfb11739 100644 --- a/CRM/Contact/Page/DedupeRules.php +++ b/CRM/Contact/Page/DedupeRules.php @@ -1,9 +1,9 @@ ts('Use Rule'), 'url' => 'civicrm/contact/dedupefind', 'qs' => 'reset=1&rgid=%%id%%&action=preview', 'title' => ts('Use DedupeRule'), - ); + ]; } if (CRM_Core_Permission::check('administer dedupe rules')) { - $links[CRM_Core_Action::UPDATE] = array( + $links[CRM_Core_Action::UPDATE] = [ 'name' => ts('Edit Rule'), 'url' => 'civicrm/contact/deduperules', 'qs' => 'action=update&id=%%id%%', 'title' => ts('Edit DedupeRule'), - ); - $links[CRM_Core_Action::DELETE] = array( + ]; + $links[CRM_Core_Action::DELETE] = [ 'name' => ts('Delete'), 'url' => 'civicrm/contact/deduperules', 'qs' => 'action=delete&id=%%id%%', 'extra' => 'onclick = "return confirm(\'' . $deleteExtra . '\');"', 'title' => ts('Delete DedupeRule'), - ); + ]; } self::$_links = $links; @@ -99,14 +99,9 @@ public function &links() { * method. */ public function run() { - // get the requested action, default to 'browse' - $action = CRM_Utils_Request::retrieve('action', 'String', $this, FALSE, 'browse'); + $id = $this->getIdAndAction(); - // assign vars to templates - $this->assign('action', $action); - $id = CRM_Utils_Request::retrieve('id', 'Positive', $this, FALSE, 0); - - $context = CRM_Utils_Request::retrieve('context', 'String', $this, FALSE); + $context = CRM_Utils_Request::retrieve('context', 'Alphanumeric', $this, FALSE); if ($context == 'nonDupe') { CRM_Core_Session::setStatus(ts('Selected contacts have been marked as not duplicates'), ts('Changes Saved'), 'success'); } @@ -116,18 +111,18 @@ public function run() { $this->assign('hasperm_merge_duplicate_contacts', CRM_Core_Permission::check('merge duplicate contacts')); // which action to take? - if ($action & (CRM_Core_Action::UPDATE | CRM_Core_Action::ADD)) { - $this->edit($action, $id); + if ($this->_action & (CRM_Core_Action::UPDATE | CRM_Core_Action::ADD)) { + $this->edit($this->_action, $id); } - if ($action & CRM_Core_Action::DELETE) { + if ($this->_action & CRM_Core_Action::DELETE) { $this->delete($id); } // browse the rules $this->browse(); - // parent run - return parent::run(); + // This replaces parent run, but do parent's parent run + return CRM_Core_Page::run(); } /** @@ -135,14 +130,14 @@ public function run() { */ public function browse() { // get all rule groups - $ruleGroups = array(); + $ruleGroups = []; $dao = new CRM_Dedupe_DAO_RuleGroup(); - $dao->orderBy('contact_type,used ASC'); + $dao->orderBy('contact_type ASC, used ASC, title ASC'); $dao->find(); $dedupeRuleTypes = CRM_Core_SelectValues::getDedupeRuleTypes(); while ($dao->fetch()) { - $ruleGroups[$dao->contact_type][$dao->id] = array(); + $ruleGroups[$dao->contact_type][$dao->id] = []; CRM_Core_DAO::storeValues($dao, $ruleGroups[$dao->contact_type][$dao->id]); // form all action links @@ -160,7 +155,7 @@ public function browse() { $ruleGroups[$dao->contact_type][$dao->id]['action'] = CRM_Core_Action::formLink( $links, $action, - array('id' => $dao->id), + ['id' => $dao->id], ts('more'), FALSE, 'dedupeRule.manage.action', @@ -215,7 +210,11 @@ public function delete($id) { $rgDao = new CRM_Dedupe_DAO_RuleGroup(); $rgDao->id = $id; - $rgDao->delete(); + if ($rgDao->find(TRUE)) { + $rgDao->delete(); + CRM_Core_Session::setStatus(ts("The rule '%1' has been deleted.", [1 => $rgDao->title]), ts('Rule Deleted'), 'success'); + CRM_Utils_System::redirect(CRM_Utils_System::url($this->userContext(), 'reset=1')); + } } } diff --git a/CRM/Contact/Page/ImageFile.php b/CRM/Contact/Page/ImageFile.php index 0c2dd66e8268..9ea30c409b34 100644 --- a/CRM/Contact/Page/ImageFile.php +++ b/CRM/Contact/Page/ImageFile.php @@ -1,9 +1,9 @@ array("%" . $_GET['photo'], 'String'), - ); + $params = [ + 1 => ["%" . $_GET['photo'], 'String'], + ]; $dao = CRM_Core_DAO::executeQuery($sql, $params); $cid = NULL; while ($dao->fetch()) { @@ -69,7 +69,7 @@ public function run() { CRM_Utils_System::civiExit(); } else { - CRM_Core_Error::fatal('Photo does not exist'); + throw new CRM_Core_Exception(ts('Photo does not exist')); } } diff --git a/CRM/Contact/Page/Inline/Actions.php b/CRM/Contact/Page/Inline/Actions.php index 0c5bd99dcb89..b1d63401702a 100644 --- a/CRM/Contact/Page/Inline/Actions.php +++ b/CRM/Contact/Page/Inline/Actions.php @@ -1,9 +1,9 @@ 0) { - $locationTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id', array('labelColumn' => 'display_name')); + $locationTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id', ['labelColumn' => 'display_name']); - $entityBlock = array('id' => $addressId); + $entityBlock = ['id' => $addressId]; $address = CRM_Core_BAO_Address::getValues($entityBlock, FALSE, 'id'); if (!empty($address)) { foreach ($address as $key => & $value) { @@ -65,23 +65,25 @@ public function run() { if (!empty($currentAddressBlock['address'][$locBlockNo])) { // get contact name of shared contact names - $sharedAddresses = array(); + $sharedAddresses = []; $shareAddressContactNames = CRM_Contact_BAO_Contact_Utils::getAddressShareContactNames($currentAddressBlock['address']); foreach ($currentAddressBlock['address'] as $key => $addressValue) { if (!empty($addressValue['master_id']) && !$shareAddressContactNames[$addressValue['master_id']]['is_deleted'] ) { - $sharedAddresses[$key]['shared_address_display'] = array( + $sharedAddresses[$key]['shared_address_display'] = [ 'address' => $addressValue['display'], 'name' => $shareAddressContactNames[$addressValue['master_id']]['name'], - ); + ]; } } + $idValue = $currentAddressBlock['address'][$locBlockNo]['id']; + if (!empty($currentAddressBlock['address'][$locBlockNo]['master_id'])) { + $idValue = $currentAddressBlock['address'][$locBlockNo]['master_id']; + } // add custom data of type address - $groupTree = CRM_Core_BAO_CustomGroup::getTree('Address', - $this, $currentAddressBlock['address'][$locBlockNo]['id'] - ); + $groupTree = CRM_Core_BAO_CustomGroup::getTree('Address', NULL, $idValue); // we setting the prefix to dnc_ below so that we don't overwrite smarty's grouptree var. $currentAddressBlock['address'][$locBlockNo]['custom'] = CRM_Core_BAO_CustomGroup::buildCustomDataView($this, $groupTree, FALSE, NULL, "dnc_"); @@ -93,7 +95,7 @@ public function run() { $contact = new CRM_Contact_BAO_Contact(); $contact->id = $contactId; $contact->find(TRUE); - $privacy = array(); + $privacy = []; foreach (CRM_Contact_BAO_Contact::$_commPrefs as $name) { if (isset($contact->$name)) { $privacy[$name] = $contact->$name; diff --git a/CRM/Contact/Page/Inline/CommunicationPreferences.php b/CRM/Contact/Page/Inline/CommunicationPreferences.php index a04a08262e3e..c6d480a90282 100644 --- a/CRM/Contact/Page/Inline/CommunicationPreferences.php +++ b/CRM/Contact/Page/Inline/CommunicationPreferences.php @@ -1,9 +1,9 @@ $contactId); + $params = ['id' => $contactId]; - $defaults = array(); + $defaults = []; CRM_Contact_BAO_Contact::getValues($params, $defaults); $defaults['privacy_values'] = CRM_Core_SelectValues::privacy(); diff --git a/CRM/Contact/Page/Inline/ContactInfo.php b/CRM/Contact/Page/Inline/ContactInfo.php index 79937542365a..3435e1935660 100644 --- a/CRM/Contact/Page/Inline/ContactInfo.php +++ b/CRM/Contact/Page/Inline/ContactInfo.php @@ -1,9 +1,9 @@ $contactId); + $params = ['id' => $contactId]; - $defaults = array(); + $defaults = []; CRM_Contact_BAO_Contact::getValues($params, $defaults); //get the current employer name diff --git a/CRM/Contact/Page/Inline/ContactName.php b/CRM/Contact/Page/Inline/ContactName.php index fd48196b6afd..ba9cfc187470 100644 --- a/CRM/Contact/Page/Inline/ContactName.php +++ b/CRM/Contact/Page/Inline/ContactName.php @@ -1,9 +1,9 @@ $contactId); + $params = ['id' => $contactId]; - $defaults = array(); + $defaults = []; CRM_Contact_BAO_Contact::getValues($params, $defaults); if (!empty($defaults['gender_id'])) { $gender = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'gender_id'); $defaults['gender_display'] = $gender[CRM_Utils_Array::value('gender_id', $defaults)]; } + $this->assignFieldMetadataToTemplate('Contact'); $this->assign('contactId', $contactId); $this->assign($defaults); - //for birthdate format with respect to birth format set - $this->assign('birthDateViewFormat', CRM_Utils_Array::value('qfMapping', CRM_Utils_Date::checkBirthDateFormat())); - // check logged in user permission CRM_Contact_Page_View::checkUserPermission($this, $contactId); diff --git a/CRM/Contact/Page/Inline/Email.php b/CRM/Contact/Page/Inline/Email.php index 4b46a6a9e846..9a257e1a3bd1 100644 --- a/CRM/Contact/Page/Inline/Email.php +++ b/CRM/Contact/Page/Inline/Email.php @@ -1,9 +1,9 @@ 'display_name')); + $locationTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id', ['labelColumn' => 'display_name']); - $entityBlock = array('contact_id' => $contactId); + $entityBlock = ['contact_id' => $contactId]; $emails = CRM_Core_BAO_Email::getValues($entityBlock); if (!empty($emails)) { - foreach ($emails as $key => & $value) { + foreach ($emails as &$value) { $value['location_type'] = $locationTypes[$value['location_type_id']]; } } @@ -58,7 +58,7 @@ public function run() { $contact = new CRM_Contact_BAO_Contact(); $contact->id = $contactId; $contact->find(TRUE); - $privacy = array(); + $privacy = []; foreach (CRM_Contact_BAO_Contact::$_commPrefs as $name) { if (isset($contact->$name)) { $privacy[$name] = $contact->$name; diff --git a/CRM/Contact/Page/Inline/IM.php b/CRM/Contact/Page/Inline/IM.php index 42fa7c886a93..8b0fd8d52ce7 100644 --- a/CRM/Contact/Page/Inline/IM.php +++ b/CRM/Contact/Page/Inline/IM.php @@ -1,9 +1,9 @@ 'display_name')); + $locationTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id', ['labelColumn' => 'display_name']); $IMProviders = CRM_Core_PseudoConstant::get('CRM_Core_DAO_IM', 'provider_id'); - $entityBlock = array('contact_id' => $contactId); + $entityBlock = ['contact_id' => $contactId]; $ims = CRM_Core_BAO_IM::getValues($entityBlock); if (!empty($ims)) { foreach ($ims as $key => & $value) { diff --git a/CRM/Contact/Page/Inline/OpenID.php b/CRM/Contact/Page/Inline/OpenID.php index fa0bc8394208..b20baf6e5db9 100644 --- a/CRM/Contact/Page/Inline/OpenID.php +++ b/CRM/Contact/Page/Inline/OpenID.php @@ -1,9 +1,9 @@ 'display_name')); + $locationTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id', ['labelColumn' => 'display_name']); - $entityBlock = array('contact_id' => $contactId); + $entityBlock = ['contact_id' => $contactId]; $openids = CRM_Core_BAO_OpenID::getValues($entityBlock); if (!empty($openids)) { foreach ($openids as $key => & $value) { diff --git a/CRM/Contact/Page/Inline/Phone.php b/CRM/Contact/Page/Inline/Phone.php index f252adc6fa84..4e78071fe3b7 100644 --- a/CRM/Contact/Page/Inline/Phone.php +++ b/CRM/Contact/Page/Inline/Phone.php @@ -1,9 +1,9 @@ 'display_name')); + $locationTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id', ['labelColumn' => 'display_name']); $phoneTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Phone', 'phone_type_id'); - $entityBlock = array('contact_id' => $contactId); + $entityBlock = ['contact_id' => $contactId]; $phones = CRM_Core_BAO_Phone::getValues($entityBlock); if (!empty($phones)) { foreach ($phones as $key => & $value) { @@ -60,7 +60,7 @@ public function run() { $contact = new CRM_Contact_BAO_Contact(); $contact->id = $contactId; $contact->find(TRUE); - $privacy = array(); + $privacy = []; foreach (CRM_Contact_BAO_Contact::$_commPrefs as $name) { if (isset($contact->$name)) { $privacy[$name] = $contact->$name; diff --git a/CRM/Contact/Page/Inline/Website.php b/CRM/Contact/Page/Inline/Website.php index 3ca350973114..929ec1e5091c 100644 --- a/CRM/Contact/Page/Inline/Website.php +++ b/CRM/Contact/Page/Inline/Website.php @@ -1,9 +1,9 @@ $contactId); + $params = ['contact_id' => $contactId]; $websites = CRM_Core_BAO_Website::getValues($params, CRM_Core_DAO::$_nullArray); if (!empty($websites)) { foreach ($websites as $key => & $value) { diff --git a/CRM/Contact/Page/SavedSearch.php b/CRM/Contact/Page/SavedSearch.php index 82507cd77358..a101e86283ba 100644 --- a/CRM/Contact/Page/SavedSearch.php +++ b/CRM/Contact/Page/SavedSearch.php @@ -1,9 +1,9 @@ is_active = 1; $savedSearch->selectAdd(); $savedSearch->selectAdd('id, form_values'); $savedSearch->find(); - $properties = array('id', 'name', 'description'); + $properties = ['id', 'name', 'description']; while ($savedSearch->fetch()) { // get name and description from group object $group = new CRM_Contact_DAO_Group(); $group->saved_search_id = $savedSearch->id; if ($group->find(TRUE)) { - $permissions = CRM_Group_Page_Group::checkPermission($group->id, $group->title); + $permissions = CRM_Contact_BAO_Group::checkPermission($group->id, TRUE); if (!CRM_Utils_System::isNull($permissions)) { - $row = array(); + $row = []; $row['name'] = $group->title; $row['description'] = $group->description; @@ -100,7 +100,7 @@ public function browse() { $row['action'] = CRM_Core_Action::formLink( self::links(), $action, - array('id' => $row['id']), + ['id' => $row['id']], ts('more'), FALSE, 'savedSearch.manage.action', @@ -148,20 +148,20 @@ public static function &links() { $deleteExtra = ts('Do you really want to remove this Smart Group?'); - self::$_links = array( - CRM_Core_Action::VIEW => array( + self::$_links = [ + CRM_Core_Action::VIEW => [ 'name' => ts('Search'), 'url' => 'civicrm/contact/search/advanced', 'qs' => 'reset=1&force=1&ssID=%%id%%', 'title' => ts('Search'), - ), - CRM_Core_Action::DELETE => array( + ], + CRM_Core_Action::DELETE => [ 'name' => ts('Delete'), 'url' => 'civicrm/contact/search/saved', 'qs' => 'action=delete&id=%%id%%', 'extra' => 'onclick="return confirm(\'' . $deleteExtra . '\');"', - ), - ); + ], + ]; } return self::$_links; } diff --git a/CRM/Contact/Page/Task.php b/CRM/Contact/Page/Task.php index fd4d5ef523c1..af6ce6cf9e28 100644 --- a/CRM/Contact/Page/Task.php +++ b/CRM/Contact/Page/Task.php @@ -1,9 +1,9 @@ _contactId = CRM_Utils_Request::retrieve('cid', 'Positive', $this, TRUE); + $this->_contactId = CRM_Utils_Request::retrieve('cid', 'Positive', $this); } else { $this->_contactId = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_GroupContact', $gcid, 'contact_id'); @@ -101,7 +101,7 @@ public function preProcess() { // ensure that the id does exist if (CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $this->_contactId, 'id') != $this->_contactId) { CRM_Core_Error::statusBounce( - ts('A Contact with that ID does not exist: %1', array(1 => $this->_contactId)), + ts('A Contact with that ID does not exist: %1', [1 => $this->_contactId]), CRM_Utils_System::url('civicrm/dashboard', 'reset=1') ); } @@ -109,15 +109,15 @@ public function preProcess() { $this->assign('contactId', $this->_contactId); // see if we can get prev/next positions from qfKey - $navContacts = array( + $navContacts = [ 'prevContactID' => NULL, 'prevContactName' => NULL, 'nextContactID' => NULL, 'nextContactName' => NULL, 'nextPrevError' => 0, - ); + ]; if ($qfKey) { - $pos = CRM_Core_BAO_PrevNextCache::getPositions("civicrm search $qfKey", + $pos = Civi::service('prevnext')->getPositions("civicrm search $qfKey", $this->_contactId, $this->_contactId ); @@ -146,34 +146,21 @@ public function preProcess() { } elseif ($context) { $this->assign('context', $context); - CRM_Utils_System::appendBreadCrumb(array( - array( + CRM_Utils_System::appendBreadCrumb([ + [ 'title' => ts('Search Results'), - 'url' => CRM_Utils_System::url("civicrm/contact/search/$context", array('qfKey' => $qfKey)), - ), - )); + 'url' => CRM_Utils_System::url("civicrm/contact/search/$context", ['qfKey' => $qfKey]), + ], + ]); } } $this->assign($navContacts); $path = CRM_Utils_System::url('civicrm/contact/view', 'reset=1&cid=' . $this->_contactId); - CRM_Utils_System::appendBreadCrumb(array(array('title' => ts('View Contact'), 'url' => $path))); + CRM_Utils_System::appendBreadCrumb([['title' => ts('View Contact'), 'url' => $path]]); if ($image_URL = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $this->_contactId, 'image_URL')) { - //CRM-7265 --time being fix. - $config = CRM_Core_Config::singleton(); - $image_URL = str_replace('https://', 'http://', $image_URL); - if (Civi::settings()->get('enableSSL')) { - $image_URL = str_replace('http://', 'https://', $image_URL); - } - - list($imageWidth, $imageHeight) = getimagesize(CRM_Utils_String::unstupifyUrl($image_URL)); - list($imageThumbWidth, $imageThumbHeight) = CRM_Contact_BAO_Contact::getThumbSize($imageWidth, $imageHeight); - $this->assign("imageWidth", $imageWidth); - $this->assign("imageHeight", $imageHeight); - $this->assign("imageThumbWidth", $imageThumbWidth); - $this->assign("imageThumbHeight", $imageThumbHeight); - $this->assign("imageURL", $image_URL); + $this->assign("imageURL", CRM_Utils_File::getImageURL($image_URL)); } // also store in session for future use @@ -197,11 +184,11 @@ public function preProcess() { // add to recently viewed block $isDeleted = (bool) CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $this->_contactId, 'is_deleted'); - $recentOther = array( + $recentOther = [ 'imageUrl' => $contactImageUrl, 'subtype' => $contactSubtype, 'isDeleted' => $isDeleted, - ); + ]; if (CRM_Contact_BAO_Contact_Permission::allow($this->_contactId, CRM_Core_Permission::EDIT)) { $recentOther['editUrl'] = CRM_Utils_System::url('civicrm/contact/add', "reset=1&action=update&cid={$this->_contactId}"); @@ -319,14 +306,14 @@ public static function checkUserPermission($page, $contactID = NULL) { */ public static function setTitle($contactId, $isDeleted = FALSE) { static $contactDetails; - $displayName = $contactImage = NULL; + $contactImage = NULL; if (!isset($contactDetails[$contactId])) { list($displayName, $contactImage) = self::getContactDetails($contactId); - $contactDetails[$contactId] = array( + $contactDetails[$contactId] = [ 'displayName' => $displayName, 'contactImage' => $contactImage, 'isDeceased' => (bool) CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $contactId, 'is_deceased'), - ); + ]; } else { $displayName = $contactDetails[$contactId]['displayName']; @@ -340,6 +327,15 @@ public static function setTitle($contactId, $isDeleted = FALSE) { } if ($isDeleted) { $title = "{$title}"; + $mergedTo = civicrm_api3('Contact', 'getmergedto', ['contact_id' => $contactId, 'api.Contact.get' => ['return' => 'display_name']]); + if ($mergedTo['count']) { + $mergedToContactID = $mergedTo['id']; + $mergedToDisplayName = $mergedTo['values'][$mergedToContactID]['api.Contact.get']['values'][0]['display_name']; + $title .= ' ' . ts('(This contact has been merged to %2)', [ + 1 => CRM_Utils_System::url('civicrm/contact/view', ['reset' => 1, 'cid' => $mergedToContactID]), + 2 => $mergedToDisplayName, + ]); + } } // Inline-edit places its own title on the page @@ -374,13 +370,11 @@ public static function addUrls(&$obj, $cid) { } // See if other modules want to add links to the activtity bar - $hookLinks = array(); + $hookLinks = []; CRM_Utils_Hook::links('view.contact.activity', 'Contact', $cid, - $hookLinks, - CRM_Core_DAO::$_nullObject, - CRM_Core_DAO::$_nullObject + $hookLinks ); if (is_array($hookLinks)) { $obj->assign('hookLinks', $hookLinks); diff --git a/CRM/Contact/Page/View/ContactSmartGroup.php b/CRM/Contact/Page/View/ContactSmartGroup.php index 2bf4f5ef9e0f..d018f62402dd 100644 --- a/CRM/Contact/Page/View/ContactSmartGroup.php +++ b/CRM/Contact/Page/View/ContactSmartGroup.php @@ -1,9 +1,9 @@ assign('groupParent', NULL); if (!empty($allGroup)) { - $smart = $parent = array(); + $smart = $parent = []; foreach ($allGroup['group'] as $group) { // delete all smart groups which are also in static groups if (isset($staticGroups[$group['id']])) { diff --git a/CRM/Contact/Page/View/CustomData.php b/CRM/Contact/Page/View/CustomData.php index d8d5633751de..73d975338209 100644 --- a/CRM/Contact/Page/View/CustomData.php +++ b/CRM/Contact/Page/View/CustomData.php @@ -1,9 +1,9 @@ assign('editOwnCustomData', $editOwnCustomData); if ($this->_action == CRM_Core_Action::BROWSE) { - //Custom Groups Inline - $entityType = CRM_Contact_BAO_Contact::getContactType($this->_contactId); - $entitySubType = CRM_Contact_BAO_Contact::getContactSubType($this->_contactId); - $groupTree = CRM_Core_BAO_CustomGroup::getTree($entityType, $this, $this->_contactId, - $this->_groupId, $entitySubType - ); $displayStyle = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $this->_groupId, @@ -115,7 +109,8 @@ public function run() { if ($this->_multiRecordDisplay != 'single') { $id = "custom_{$this->_groupId}"; - $this->ajaxResponse['tabCount'] = CRM_Contact_BAO_Contact::getCountComponent($id, $this->_contactId, $groupTree[$this->_groupId]['table_name']); + $tableName = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $this->_groupId, 'table_name'); + $this->ajaxResponse['tabCount'] = CRM_Contact_BAO_Contact::getCountComponent($id, $this->_contactId, $tableName); } if ($displayStyle === 'Tab with table' && $this->_multiRecordDisplay != 'single') { @@ -134,23 +129,30 @@ public function run() { $page->set('multiRecordFieldListing', $multiRecordFieldListing); $page->set('pageViewType', 'customDataView'); $page->set('contactType', $ctype); - $page->assign('viewCustomData', array( - $this->_groupId => array( - $this->_groupId => $groupTree[$this->_groupId], - ), - )); + $page->_headersOnly = TRUE; $page->run(); } else { + //Custom Groups Inline + $entityType = CRM_Contact_BAO_Contact::getContactType($this->_contactId); + $entitySubType = CRM_Contact_BAO_Contact::getContactSubType($this->_contactId); $recId = NULL; if ($this->_multiRecordDisplay == 'single') { $groupTitle = CRM_Core_BAO_CustomGroup::getTitle($this->_groupId); - CRM_Utils_System::setTitle(ts('View %1 Record', array(1 => $groupTitle))); + CRM_Utils_System::setTitle(ts('View %1 Record', [1 => $groupTitle])); + $groupTree = CRM_Core_BAO_CustomGroup::getTree($entityType, NULL, $this->_contactId, + $this->_groupId, $entitySubType, NULL, TRUE, NULL, FALSE, TRUE, $this->_cgcount + ); $recId = $this->_recId; $this->assign('multiRecordDisplay', $this->_multiRecordDisplay); $this->assign('skipTitle', 1); } + else { + $groupTree = CRM_Core_BAO_CustomGroup::getTree($entityType, NULL, $this->_contactId, + $this->_groupId, $entitySubType + ); + } CRM_Core_BAO_CustomGroup::buildCustomDataView($this, $groupTree, FALSE, NULL, NULL, $recId, $this->_contactId); } } diff --git a/CRM/Contact/Page/View/GroupContact.php b/CRM/Contact/Page/View/GroupContact.php index ac8c20d51ca9..88178d4b4764 100644 --- a/CRM/Contact/Page/View/GroupContact.php +++ b/CRM/Contact/Page/View/GroupContact.php @@ -1,9 +1,9 @@ _contactId, NULL, NULL, TRUE); + $count = CRM_Contact_BAO_GroupContact::getContactGroup($this->_contactId, NULL, NULL, TRUE, FALSE, FALSE, TRUE, NULL, TRUE); - $in = CRM_Contact_BAO_GroupContact::getContactGroup($this->_contactId, 'Added'); - $pending = CRM_Contact_BAO_GroupContact::getContactGroup($this->_contactId, 'Pending'); - $out = CRM_Contact_BAO_GroupContact::getContactGroup($this->_contactId, 'Removed'); + $in = CRM_Contact_BAO_GroupContact::getContactGroup($this->_contactId, 'Added', NULL, FALSE, FALSE, FALSE, TRUE, NULL, TRUE); + $pending = CRM_Contact_BAO_GroupContact::getContactGroup($this->_contactId, 'Pending', NULL, FALSE, FALSE, FALSE, TRUE, NULL, TRUE); + $out = CRM_Contact_BAO_GroupContact::getContactGroup($this->_contactId, 'Removed', NULL, FALSE, FALSE, FALSE, TRUE, NULL, TRUE); // keep track of all 'added' contact groups so we can remove them from the smart group // section - $staticGroups = array(); + $staticGroups = []; if (!empty($in)) { foreach ($in as $group) { $staticGroups[$group['group_id']] = 1; @@ -176,7 +176,7 @@ public static function del($groupContactId, $status, $contactID) { return FALSE; } - $ids = array($contactID); + $ids = [$contactID]; $method = 'Admin'; $session = CRM_Core_Session::singleton(); diff --git a/CRM/Contact/Page/View/Log.php b/CRM/Contact/Page/View/Log.php index 17efe79c3ac8..493148f4db6d 100644 --- a/CRM/Contact/Page/View/Log.php +++ b/CRM/Contact/Page/View/Log.php @@ -1,9 +1,9 @@ orderBy('modified_date desc'); $log->find(); - $logEntries = array(); + $logEntries = []; while ($log->fetch()) { list($displayName, $contactImage) = CRM_Contact_BAO_Contact::getDisplayAndImage($log->modified_id); - $logEntries[] = array( + $logEntries[] = [ 'id' => $log->modified_id, 'name' => $displayName, 'image' => $contactImage, 'date' => $log->modified_date, - ); + ]; } $this->assign('logCount', count($logEntries)); diff --git a/CRM/Contact/Page/View/Note.php b/CRM/Contact/Page/View/Note.php index 1b9a134504f8..2930341be709 100644 --- a/CRM/Contact/Page/View/Note.php +++ b/CRM/Contact/Page/View/Note.php @@ -1,9 +1,9 @@ id = $this->_id; if ($note->find(TRUE)) { - $values = array(); + $values = []; CRM_Core_DAO::storeValues($note, $values); $values['privacy'] = CRM_Core_PseudoConstant::getLabel('CRM_Core_BAO_Note', 'privacy', $values['privacy']); @@ -86,7 +86,7 @@ public function browse() { $note->orderBy('modified_date desc'); //CRM-4418, handling edit and delete separately. - $permissions = array($this->_permission); + $permissions = [$this->_permission]; if ($this->_permission == CRM_Core_Permission::EDIT) { //previously delete was subset of edit //so for consistency lets grant delete also. @@ -94,7 +94,9 @@ public function browse() { } $mask = CRM_Core_Action::mask($permissions); - $values = array(); + $this->assign('canAddNotes', CRM_Core_Permission::check('add contact notes')); + + $values = []; $links = self::links(); $action = array_sum(array_keys($links)) & $mask; @@ -105,21 +107,23 @@ public function browse() { $values[$note->id]['action'] = CRM_Core_Action::formLink($links, $action, - array( + [ 'id' => $note->id, 'cid' => $this->_contactId, - ), + ], ts('more'), FALSE, 'note.selector.row', 'Note', $note->id ); - $contact = new CRM_Contact_DAO_Contact(); - $contact->id = $note->contact_id; - $contact->find(); - $contact->fetch(); - $values[$note->id]['createdBy'] = $contact->display_name; + if (!empty($note->contact_id)) { + $contact = new CRM_Contact_DAO_Contact(); + $contact->id = $note->contact_id; + $contact->find(); + $contact->fetch(); + $values[$note->id]['createdBy'] = $contact->display_name; + } $values[$note->id]['comment_count'] = CRM_Core_BAO_Note::getChildCount($note->id); // paper icon view for attachments part @@ -136,11 +140,11 @@ public function browse() { $commentAction = CRM_Core_Action::formLink($commentLinks, $action, - array( + [ 'id' => $note->id, 'pid' => $note->entity_id, 'cid' => $note->entity_id, - ), + ], ts('more'), FALSE, 'note.comment.action', @@ -166,10 +170,7 @@ public function edit() { ); $session->pushUserContext($url); - if (CRM_Utils_Request::retrieve('confirmed', 'Boolean', - CRM_Core_DAO::$_nullObject - ) - ) { + if (CRM_Utils_Request::retrieve('confirmed', 'Boolean')) { CRM_Core_BAO_Note::del($this->_id); CRM_Utils_System::redirect($url); } @@ -215,10 +216,27 @@ public function run() { if ($this->_action & CRM_Core_Action::VIEW) { $this->view(); } - elseif ($this->_action & (CRM_Core_Action::UPDATE | CRM_Core_Action::ADD)) { + elseif ($this->_action & CRM_Core_Action::ADD) { + if ( + $this->_permission != CRM_Core_Permission::EDIT && + !CRM_Core_Permission::check('add contact notes') + ) { + CRM_Core_Error::statusBounce(ts('You do not have access to add notes.')); + } + + $this->edit(); + } + elseif ($this->_action & CRM_Core_Action::UPDATE) { + if ($this->_permission != CRM_Core_Permission::EDIT) { + CRM_Core_Error::statusBounce(ts('You do not have access to edit this note.')); + } + $this->edit(); } elseif ($this->_action & CRM_Core_Action::DELETE) { + if ($this->_permission != CRM_Core_Permission::EDIT) { + CRM_Core_Error::statusBounce(ts('You do not have access to delete this note.')); + } // we use the edit screen the confirm the delete $this->edit(); } @@ -244,32 +262,32 @@ public static function &links() { if (!(self::$_links)) { $deleteExtra = ts('Are you sure you want to delete this note?'); - self::$_links = array( - CRM_Core_Action::VIEW => array( + self::$_links = [ + CRM_Core_Action::VIEW => [ 'name' => ts('View'), 'url' => 'civicrm/contact/view/note', 'qs' => 'action=view&reset=1&cid=%%cid%%&id=%%id%%&selectedChild=note', 'title' => ts('View Note'), - ), - CRM_Core_Action::UPDATE => array( + ], + CRM_Core_Action::UPDATE => [ 'name' => ts('Edit'), 'url' => 'civicrm/contact/view/note', 'qs' => 'action=update&reset=1&cid=%%cid%%&id=%%id%%&selectedChild=note', 'title' => ts('Edit Note'), - ), - CRM_Core_Action::ADD => array( + ], + CRM_Core_Action::ADD => [ 'name' => ts('Comment'), 'url' => 'civicrm/contact/view/note', 'qs' => 'action=add&reset=1&cid=%%cid%%&parentId=%%id%%&selectedChild=note', 'title' => ts('Add Comment'), - ), - CRM_Core_Action::DELETE => array( + ], + CRM_Core_Action::DELETE => [ 'name' => ts('Delete'), 'url' => 'civicrm/contact/view/note', 'qs' => 'action=delete&reset=1&cid=%%cid%%&id=%%id%%&selectedChild=note', 'title' => ts('Delete Note'), - ), - ); + ], + ]; } return self::$_links; } @@ -282,26 +300,26 @@ public static function &links() { */ public static function &commentLinks() { if (!(self::$_commentLinks)) { - self::$_commentLinks = array( - CRM_Core_Action::VIEW => array( + self::$_commentLinks = [ + CRM_Core_Action::VIEW => [ 'name' => ts('View'), 'url' => 'civicrm/contact/view/note', 'qs' => 'action=view&reset=1&cid=%%cid%%&id={id}&selectedChild=note', 'title' => ts('View Comment'), - ), - CRM_Core_Action::UPDATE => array( + ], + CRM_Core_Action::UPDATE => [ 'name' => ts('Edit'), 'url' => 'civicrm/contact/view/note', 'qs' => 'action=update&reset=1&cid=%%cid%%&id={id}&parentId=%%pid%%&selectedChild=note', 'title' => ts('Edit Comment'), - ), - CRM_Core_Action::DELETE => array( + ], + CRM_Core_Action::DELETE => [ 'name' => ts('Delete'), 'url' => 'civicrm/contact/view/note', 'qs' => 'action=delete&reset=1&cid=%%cid%%&id={id}&selectedChild=note', 'title' => ts('Delete Comment'), - ), - ); + ], + ]; } return self::$_commentLinks; } diff --git a/CRM/Contact/Page/View/Print.php b/CRM/Contact/Page/View/Print.php index a02912aac49a..7ebbd4042704 100644 --- a/CRM/Contact/Page/View/Print.php +++ b/CRM/Contact/Page/View/Print.php @@ -1,9 +1,9 @@ _contactId; $contact = CRM_Contact_BAO_Contact::retrieve($params, $defaults, $ids); diff --git a/CRM/Contact/Page/View/Relationship.php b/CRM/Contact/Page/View/Relationship.php index 1e44f43e7ca3..8e7006369a4a 100644 --- a/CRM/Contact/Page/View/Relationship.php +++ b/CRM/Contact/Page/View/Relationship.php @@ -1,9 +1,9 @@ _id); $this->assign('viewNote', $viewNote); - $groupTree = CRM_Core_BAO_CustomGroup::getTree('Relationship', $this, $this->_id, 0, $relType); + $groupTree = CRM_Core_BAO_CustomGroup::getTree('Relationship', NULL, $this->_id, 0, $relType); CRM_Core_BAO_CustomGroup::buildCustomDataView($this, $groupTree, FALSE, NULL, NULL, NULL, $this->_id); $rType = CRM_Utils_Array::value('rtype', $viewRelationship[$this->_id]); @@ -157,10 +157,7 @@ public function edit() { $session->pushUserContext($url); - if (CRM_Utils_Request::retrieve('confirmed', 'Boolean', - CRM_Core_DAO::$_nullObject - ) - ) { + if (CRM_Utils_Request::retrieve('confirmed', 'Boolean')) { if ($this->_caseId) { //create an activity for case role removal.CRM-4480 CRM_Case_BAO_Case::createCaseRoleActivity($this->_caseId, $this->_id); @@ -220,7 +217,7 @@ public function run() { } public function setContext() { - $context = CRM_Utils_Request::retrieve('context', 'String', + $context = CRM_Utils_Request::retrieve('context', 'Alphanumeric', $this, FALSE, 'search' ); diff --git a/CRM/Contact/Page/View/Summary.php b/CRM/Contact/Page/View/Summary.php index 02fe9db092c2..39594e4073dd 100644 --- a/CRM/Contact/Page/View/Summary.php +++ b/CRM/Contact/Page/View/Summary.php @@ -1,9 +1,9 @@ _contactId, NULL, $entitySubType @@ -119,21 +119,19 @@ public function view() { CRM_Core_Resources::singleton() ->addScriptFile('civicrm', 'templates/CRM/Contact/Page/View/Summary.js', 2, 'html-header') ->addStyleFile('civicrm', 'css/contactSummary.css', 2, 'html-header') - ->addScriptFile('civicrm', 'packages/jquery/plugins/jstree/jquery.jstree.js', 0, 'html-header', FALSE) - ->addStyleFile('civicrm', 'packages/jquery/plugins/jstree/themes/default/style.css', 0, 'html-header') ->addScriptFile('civicrm', 'templates/CRM/common/TabHeader.js', 1, 'html-header') ->addSetting(array( 'summaryPrint' => array('mode' => $this->_print), - 'tabSettings' => array('active' => CRM_Utils_Request::retrieve('selectedChild', 'String', $this, FALSE, 'summary')), + 'tabSettings' => array('active' => CRM_Utils_Request::retrieve('selectedChild', 'Alphanumeric', $this, FALSE, 'summary')), )); $this->assign('summaryPrint', $this->_print); $session = CRM_Core_Session::singleton(); $url = CRM_Utils_System::url('civicrm/contact/view', 'reset=1&cid=' . $this->_contactId); $session->pushUserContext($url); + $this->assignFieldMetadataToTemplate('Contact'); $params = array(); $defaults = array(); - $ids = array(); $params['id'] = $params['contact_id'] = $this->_contactId; $params['noRelationships'] = $params['noNotes'] = $params['noGroups'] = TRUE; @@ -184,10 +182,7 @@ public function view() { $idValue = $blockVal['master_id']; } } - $groupTree = CRM_Core_BAO_CustomGroup::getTree(ucfirst($key), - $this, - $idValue - ); + $groupTree = CRM_Core_BAO_CustomGroup::getTree(ucfirst($key), NULL, $idValue); // we setting the prefix to dnc_ below so that we don't overwrite smarty's grouptree var. $defaults[$key][$blockId]['custom'] = CRM_Core_BAO_CustomGroup::buildCustomDataView($this, $groupTree, FALSE, NULL, "dnc_"); } @@ -220,7 +215,8 @@ public function view() { $contactTags = CRM_Core_BAO_EntityTag::getContactTags($this->_contactId); if (!empty($contactTags)) { - $defaults['contactTag'] = implode(', ', $contactTags); + $defaults['contactTag'] = $contactTags; + $defaults['allTags'] = CRM_Core_BAO_Tag::getTagsUsedFor('civicrm_contact', FALSE); } $defaults['privacy_values'] = CRM_Core_SelectValues::privacy(); @@ -258,9 +254,6 @@ public function view() { $defaults['current_employer'] = $contact->organization_name; $defaults['current_employer_id'] = $contact->employer_id; } - - //for birthdate format with respect to birth format set - $this->assign('birthDateViewFormat', CRM_Utils_Array::value('qfMapping', CRM_Utils_Date::checkBirthDateFormat())); } $defaults['external_identifier'] = $contact->external_identifier; @@ -271,23 +264,112 @@ public function view() { $lastModified = CRM_Core_BAO_Log::lastModified($this->_contactId, 'civicrm_contact'); $this->assign_by_ref('lastModified', $lastModified); - $allTabs = array(); - $weight = 10; - $this->_viewOptions = CRM_Core_BAO_Setting::valueOptions( CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'contact_view_options', TRUE ); - // show the tabs only if user has generic access to CiviCRM - $accessCiviCRM = CRM_Core_Permission::check('access CiviCRM'); - $changeLog = $this->_viewOptions['log']; $this->assign_by_ref('changeLog', $changeLog); - $components = CRM_Core_Component::getEnabledComponents(); - foreach ($components as $name => $component) { + $this->assign('allTabs', $this->getTabs()); + + // hook for contact summary + // ignored but needed to prevent warnings + $contentPlacement = CRM_Utils_Hook::SUMMARY_BELOW; + CRM_Utils_Hook::summary($this->_contactId, $content, $contentPlacement); + if ($content) { + $this->assign_by_ref('hookContent', $content); + $this->assign('hookContentPlacement', $contentPlacement); + } + } + + /** + * @return string + */ + public function getTemplateFileName() { + if ($this->_contactId) { + $contactSubtypes = $this->get('contactSubtype') ? explode(CRM_Core_DAO::VALUE_SEPARATOR, $this->get('contactSubtype')) : array(); + + // there could be multiple subtypes. We check templates for each of the subtype, and return the first one found. + foreach ($contactSubtypes as $csType) { + if ($csType) { + $templateFile = "CRM/Contact/Page/View/SubType/{$csType}.tpl"; + $template = CRM_Core_Page::getTemplate(); + if ($template->template_exists($templateFile)) { + return $templateFile; + } + } + } + } + return parent::getTemplateFileName(); + } + + /** + * @return array + */ + public static function basicTabs() { + return [ + [ + 'id' => 'summary', + 'url' => '#contact-summary', + 'title' => ts('Summary'), + 'weight' => 0, + 'icon' => 'crm-i fa-address-card-o', + ], + [ + 'id' => 'activity', + 'title' => ts('Activities'), + 'class' => 'livePage', + 'weight' => 70, + 'icon' => 'crm-i fa-tasks', + ], + [ + 'id' => 'rel', + 'title' => ts('Relationships'), + 'class' => 'livePage', + 'weight' => 80, + 'icon' => 'crm-i fa-handshake-o', + ], + [ + 'id' => 'group', + 'title' => ts('Groups'), + 'class' => 'ajaxForm', + 'weight' => 90, + 'icon' => 'crm-i fa-users', + ], + [ + 'id' => 'note', + 'title' => ts('Notes'), + 'class' => 'livePage', + 'weight' => 100, + 'icon' => 'crm-i fa-sticky-note-o', + ], + [ + 'id' => 'tag', + 'title' => ts('Tags'), + 'weight' => 110, + 'icon' => 'crm-i fa-tags', + ], + [ + 'id' => 'log', + 'title' => ts('Change Log'), + 'weight' => 120, + 'icon' => 'crm-i fa-history', + ], + ]; + } + + /** + * @return array + * @throws \CRM_Core_Exception + */ + public function getTabs() { + $allTabs = []; + $weight = 10; + + foreach (CRM_Core_Component::getEnabledComponents() as $name => $component) { if (!empty($this->_viewOptions[$name]) && CRM_Core_Permission::access($component->name) ) { @@ -309,59 +391,30 @@ public function view() { if (CRM_Utils_Request::retrieve('isTest', 'Positive', $this)) { $q .= "&isTest=1"; } - $allTabs[] = array( + $allTabs[] = [ 'id' => $i, 'url' => CRM_Utils_System::url("civicrm/contact/view/$u", $q), 'title' => $elem['title'], 'weight' => $elem['weight'], 'count' => CRM_Contact_BAO_Contact::getCountComponent($u, $this->_contactId), 'class' => 'livePage', - ); - // make sure to get maximum weight, rest of tabs go after - // FIXME: not very elegant again - if ($weight < $elem['weight']) { - $weight = $elem['weight']; - } + 'icon' => $component->getIcon(), + ]; } } - $rest = array( - 'activity' => array( - 'title' => ts('Activities'), - 'class' => 'livePage', - ), - 'rel' => array( - 'title' => ts('Relationships'), - 'class' => 'livePage', - ), - 'group' => array( - 'title' => ts('Groups'), - 'class' => 'ajaxForm', - ), - 'note' => array( - 'title' => ts('Notes'), - 'class' => 'livePage', - ), - 'tag' => array( - 'title' => ts('Tags'), - ), - 'log' => array( - 'title' => ts('Change Log'), - ), - ); - - foreach ($rest as $k => $v) { - if ($accessCiviCRM && !empty($this->_viewOptions[$k])) { - $allTabs[] = $v + array( - 'id' => $k, - 'url' => CRM_Utils_System::url( - "civicrm/contact/view/$k", - "reset=1&cid={$this->_contactId}" - ), - 'weight' => $weight, - 'count' => CRM_Contact_BAO_Contact::getCountComponent($k, $this->_contactId), - ); - $weight += 10; + // show the tabs only if user has generic access to CiviCRM + $accessCiviCRM = CRM_Core_Permission::check('access CiviCRM'); + foreach (self::basicTabs() as $tab) { + if ($tab['id'] == 'summary') { + $allTabs[] = $tab; + } + elseif ($accessCiviCRM && !empty($this->_viewOptions[$tab['id']])) { + $allTabs[] = $tab + [ + 'url' => CRM_Utils_System::url("civicrm/contact/view/{$tab['id']}", "reset=1&cid={$this->_contactId}"), + 'count' => CRM_Contact_BAO_Contact::getCountComponent($tab['id'], $this->_contactId), + ]; + $weight = $tab['weight'] + 10; } } @@ -375,7 +428,7 @@ public function view() { foreach ($activeGroups as $group) { $id = "custom_{$group['id']}"; - $allTabs[] = array( + $allTabs[] = [ 'id' => $id, 'url' => CRM_Utils_System::url($group['path'], $group['query'] . "&selectedChild=$id"), 'title' => $group['title'], @@ -383,56 +436,19 @@ public function view() { 'count' => CRM_Contact_BAO_Contact::getCountComponent($id, $this->_contactId, $group['table_name']), 'hideCount' => !$group['is_multiple'], 'class' => 'livePage', - ); + 'icon' => 'crm-i fa-gear', + ]; $weight += 10; } - $context = array('contact_id' => $this->_contactId); + $context = ['contact_id' => $this->_contactId]; // see if any other modules want to add any tabs CRM_Utils_Hook::tabs($allTabs, $this->_contactId); CRM_Utils_Hook::tabset('civicrm/contact/view', $allTabs, $context); - $allTabs[] = array( - 'id' => 'summary', - 'url' => '#contact-summary', - 'title' => ts('Summary'), - 'weight' => 0, - ); - // now sort the tabs based on weight - usort($allTabs, array('CRM_Utils_Sort', 'cmpFunc')); - - $this->assign('allTabs', $allTabs); - - // hook for contact summary - // ignored but needed to prevent warnings - $contentPlacement = CRM_Utils_Hook::SUMMARY_BELOW; - CRM_Utils_Hook::summary($this->_contactId, $content, $contentPlacement); - if ($content) { - $this->assign_by_ref('hookContent', $content); - $this->assign('hookContentPlacement', $contentPlacement); - } - } - - /** - * @return string - */ - public function getTemplateFileName() { - if ($this->_contactId) { - $contactSubtypes = $this->get('contactSubtype') ? explode(CRM_Core_DAO::VALUE_SEPARATOR, $this->get('contactSubtype')) : array(); - - // there could be multiple subtypes. We check templates for each of the subtype, and return the first one found. - foreach ($contactSubtypes as $csType) { - if ($csType) { - $templateFile = "CRM/Contact/Page/View/SubType/{$csType}.tpl"; - $template = CRM_Core_Page::getTemplate(); - if ($template->template_exists($templateFile)) { - return $templateFile; - } - } - } - } - return parent::getTemplateFileName(); + usort($allTabs, ['CRM_Utils_Sort', 'cmpFunc']); + return $allTabs; } } diff --git a/CRM/Contact/Page/View/Tag.php b/CRM/Contact/Page/View/Tag.php index b1a668f2d008..009147271782 100644 --- a/CRM/Contact/Page/View/Tag.php +++ b/CRM/Contact/Page/View/Tag.php @@ -1,9 +1,9 @@ _contactId = CRM_Utils_Request::retrieve('id', 'Positive', $this); - - $session = CRM_Core_Session::singleton(); - $userID = $session->get('userID'); + $userID = CRM_Core_Session::singleton()->getLoggedInContactID(); + + $userChecksum = $this->getUserChecksum(); + $validUser = FALSE; + if ($userChecksum) { + $this->assign('userChecksum', $userChecksum); + $validUser = CRM_Contact_BAO_Contact_Utils::validChecksum($this->_contactId, $userChecksum); + $this->_isChecksumUser = $validUser; + } if (!$this->_contactId) { $this->_contactId = $userID; } - elseif ($this->_contactId != $userID) { + elseif ($this->_contactId != $userID && !$validUser) { if (!CRM_Contact_BAO_Contact_Permission::allow($this->_contactId, CRM_Core_Permission::VIEW)) { CRM_Core_Error::fatal(ts('You do not have permission to access this contact.')); } @@ -97,7 +101,7 @@ public function preProcess() { $this->set('displayName', $displayName); $this->set('contactImage', $contactImage); - CRM_Utils_System::setTitle(ts('Dashboard - %1', array(1 => $displayName))); + CRM_Utils_System::setTitle(ts('Dashboard - %1', [1 => $displayName])); $this->assign('recentlyViewed', FALSE); } @@ -107,10 +111,9 @@ public function preProcess() { */ public function buildUserDashBoard() { //build component selectors - $dashboardElements = array(); - $config = CRM_Core_Config::singleton(); + $dashboardElements = []; - $this->_userOptions = CRM_Core_BAO_Setting::valueOptions(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, + $dashboardOptions = CRM_Core_BAO_Setting::valueOptions(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'user_dashboard_options' ); @@ -122,65 +125,62 @@ public function buildUserDashBoard() { continue; } - if (!empty($this->_userOptions[$name]) && + if (!empty($dashboardOptions[$name]) && (CRM_Core_Permission::access($component->name) || CRM_Core_Permission::check($elem['perm'][0]) ) ) { $userDashboard = $component->getUserDashboardObject(); - $dashboardElements[] = array( + $dashboardElements[] = [ 'class' => 'crm-dashboard-' . strtolower($component->name), 'sectionTitle' => $elem['title'], 'templatePath' => $userDashboard->getTemplateFileName(), 'weight' => $elem['weight'], - ); + ]; $userDashboard->run(); } } // CRM-16512 - Hide related contact table if user lacks permission to view self - if (!empty($this->_userOptions['Permissioned Orgs']) && CRM_Core_Permission::check('view my contact')) { - $dashboardElements[] = array( + if (!empty($dashboardOptions['Permissioned Orgs']) && CRM_Core_Permission::check('view my contact')) { + $dashboardElements[] = [ 'class' => 'crm-dashboard-permissionedOrgs', 'templatePath' => 'CRM/Contact/Page/View/RelationshipSelector.tpl', 'sectionTitle' => ts('Your Contacts / Organizations'), 'weight' => 40, - ); + ]; } - if (!empty($this->_userOptions['PCP'])) { - $dashboardElements[] = array( + if (!empty($dashboardOptions['PCP'])) { + $dashboardElements[] = [ 'class' => 'crm-dashboard-pcp', 'templatePath' => 'CRM/Contribute/Page/PcpUserDashboard.tpl', 'sectionTitle' => ts('Personal Campaign Pages'), 'weight' => 40, - ); + ]; list($pcpBlock, $pcpInfo) = CRM_PCP_BAO_PCP::getPcpDashboardInfo($this->_contactId); $this->assign('pcpBlock', $pcpBlock); $this->assign('pcpInfo', $pcpInfo); } - if (!empty($this->_userOptions['Assigned Activities'])) { + if (!empty($dashboardOptions['Assigned Activities']) && empty($this->_isChecksumUser)) { // Assigned Activities section - $dashboardElements[] = array( + $dashboardElements[] = [ 'class' => 'crm-dashboard-assignedActivities', 'templatePath' => 'CRM/Activity/Page/UserDashboard.tpl', 'sectionTitle' => ts('Your Assigned Activities'), 'weight' => 5, - ); + ]; $userDashboard = new CRM_Activity_Page_UserDashboard(); $userDashboard->run(); } - usort($dashboardElements, array('CRM_Utils_Sort', 'cmpFunc')); + usort($dashboardElements, ['CRM_Utils_Sort', 'cmpFunc']); $this->assign('dashboardElements', $dashboardElements); - // return true when 'Invoices / Credit Notes' checkbox is checked - $this->assign('invoices', $this->_userOptions['Invoices / Credit Notes']); - - if (!empty($this->_userOptions['Groups'])) { + if (!empty($dashboardOptions['Groups'])) { $this->assign('showGroup', TRUE); //build group selector $gContact = new CRM_Contact_Page_View_UserDashBoard_GroupContact(); @@ -210,32 +210,32 @@ public static function &links() { if (!(self::$_links)) { $disableExtra = ts('Are you sure you want to disable this relationship?'); - self::$_links = array( - CRM_Core_Action::UPDATE => array( + self::$_links = [ + CRM_Core_Action::UPDATE => [ 'name' => ts('Edit Contact Information'), 'url' => 'civicrm/contact/relatedcontact', 'qs' => 'action=update&reset=1&cid=%%cbid%%&rcid=%%cid%%', - 'title' => ts('Edit Relationship'), - ), - CRM_Core_Action::VIEW => array( + 'title' => ts('Edit Contact Information'), + ], + CRM_Core_Action::VIEW => [ 'name' => ts('Dashboard'), 'url' => 'civicrm/user', 'class' => 'no-popup', 'qs' => 'reset=1&id=%%cbid%%', - 'title' => ts('View Relationship'), - ), - ); + 'title' => ts('View Contact Dashboard'), + ], + ]; if (CRM_Core_Permission::check('access CiviCRM')) { - self::$_links = array_merge(self::$_links, array( - CRM_Core_Action::DISABLE => array( + self::$_links += [ + CRM_Core_Action::DISABLE => [ 'name' => ts('Disable'), 'url' => 'civicrm/contact/view/rel', 'qs' => 'action=disable&reset=1&cid=%%cid%%&id=%%id%%&rtype=%%rtype%%&selectedChild=rel&context=dashboard', 'extra' => 'onclick = "return confirm(\'' . $disableExtra . '\');"', 'title' => ts('Disable Relationship'), - ), - )); + ], + ]; } } @@ -243,11 +243,22 @@ public static function &links() { CRM_Utils_Hook::links('view.contact.userDashBoard', 'Contact', CRM_Core_DAO::$_nullObject, - self::$_links, - CRM_Core_DAO::$_nullObject, - CRM_Core_DAO::$_nullObject + self::$_links ); return self::$_links; } + /** + * Get the user checksum from the url to use in links. + * + * @return string + */ + protected function getUserChecksum() { + $userChecksum = CRM_Utils_Request::retrieve('cs', 'String', $this); + if (empty($userID) && $this->_contactId) { + return $userChecksum; + } + return FALSE; + } + } diff --git a/CRM/Contact/Page/View/UserDashBoard/GroupContact.php b/CRM/Contact/Page/View/UserDashBoard/GroupContact.php index 605a32f1abd3..167618a4c048 100644 --- a/CRM/Contact/Page/View/UserDashBoard/GroupContact.php +++ b/CRM/Contact/Page/View/UserDashBoard/GroupContact.php @@ -1,9 +1,9 @@ _contactId, NULL, NULL, TRUE, TRUE, - $this->_onlyPublicGroups + $this->_onlyPublicGroups, + NULL, NULL, TRUE ); $in = CRM_Contact_BAO_GroupContact::getContactGroup( $this->_contactId, 'Added', NULL, FALSE, TRUE, - $this->_onlyPublicGroups + $this->_onlyPublicGroups, + NULL, NULL, TRUE ); $pending = CRM_Contact_BAO_GroupContact::getContactGroup( $this->_contactId, 'Pending', NULL, FALSE, TRUE, - $this->_onlyPublicGroups + $this->_onlyPublicGroups, + NULL, NULL, TRUE ); $out = CRM_Contact_BAO_GroupContact::getContactGroup( $this->_contactId, 'Removed', NULL, FALSE, TRUE, - $this->_onlyPublicGroups + $this->_onlyPublicGroups, + NULL, NULL, TRUE ); $this->assign('groupCount', $count); diff --git a/CRM/Contact/Page/View/Useradd.php b/CRM/Contact/Page/View/Useradd.php index 8a917c979457..aa79fc702246 100644 --- a/CRM/Contact/Page/View/Useradd.php +++ b/CRM/Contact/Page/View/Useradd.php @@ -1,9 +1,9 @@ preProcess(); - $params = array(); - $defaults = array(); - $ids = array(); + $params = []; + $defaults = []; + $ids = []; $params['id'] = $params['contact_id'] = $this->_contactId; $contact = CRM_Contact_BAO_Contact::retrieve($params, $defaults, $ids); // now that we have the contact's data - let's build the vCard // TODO: non-US-ASCII support (requires changes to the Contact_Vcard_Build class) - $vcardNames = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id', array('labelColumn' => 'vcard_name')); + $vcardNames = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id', ['labelColumn' => 'vcard_name']); $vcard = new Contact_Vcard_Build('2.1'); if ($defaults['contact_type'] == 'Individual') { @@ -88,8 +88,8 @@ public function run() { $vcard->setTitle($defaults['job_title']); } - if (!empty($defaults['birth_date_display'])) { - $vcard->setBirthday(CRM_Utils_Array::value('birth_date_display', $defaults)); + if (!empty($defaults['birth_date'])) { + $vcard->setBirthday(CRM_Utils_Array::value('birth_date', $defaults)); } if (!empty($defaults['home_URL'])) { @@ -107,6 +107,9 @@ public function run() { if (!empty($location['supplemental_address_2'])) { $extend .= ', ' . $location['supplemental_address_2']; } + if (!empty($location['supplemental_address_3'])) { + $extend .= ', ' . $location['supplemental_address_3']; + } $street = CRM_Utils_Array::value('street_address', $location); $locality = CRM_Utils_Array::value('city', $location); $region = NULL; diff --git a/CRM/Contact/Selector.php b/CRM/Contact/Selector.php index 0bad88963bc3..a560a9adf32b 100644 --- a/CRM/Contact/Selector.php +++ b/CRM/Contact/Selector.php @@ -1,9 +1,9 @@ _options = &$this->_query->_options; } + /** + * This method set cache key, later used in test environment + * + * @param string $key + */ + public function setKey($key) { + $this->_key = $key; + } + /** * This method returns the links that are given for each search row. * currently the links added for each row are @@ -252,34 +263,34 @@ public static function &links() { $searchContext = ($context) ? "&context=$context" : NULL; if (!(self::$_links)) { - self::$_links = array( - CRM_Core_Action::VIEW => array( + self::$_links = [ + CRM_Core_Action::VIEW => [ 'name' => ts('View'), 'url' => 'civicrm/contact/view', 'class' => 'no-popup', 'qs' => "reset=1&cid=%%id%%{$searchContext}{$extraParams}", 'title' => ts('View Contact Details'), 'ref' => 'view-contact', - ), - CRM_Core_Action::UPDATE => array( + ], + CRM_Core_Action::UPDATE => [ 'name' => ts('Edit'), 'url' => 'civicrm/contact/add', 'class' => 'no-popup', 'qs' => "reset=1&action=update&cid=%%id%%{$searchContext}{$extraParams}", 'title' => ts('Edit Contact Details'), 'ref' => 'edit-contact', - ), - ); + ], + ]; $config = CRM_Core_Config::singleton(); //CRM-16552: mapAPIKey is not mandatory as google no longer requires an API Key if ($config->mapProvider && ($config->mapAPIKey || $config->mapProvider == 'Google')) { - self::$_links[CRM_Core_Action::MAP] = array( + self::$_links[CRM_Core_Action::MAP] = [ 'name' => ts('Map'), 'url' => 'civicrm/contact/map', 'qs' => "reset=1&cid=%%id%%{$searchContext}{$extraParams}", 'title' => ts('Map Contact'), - ); + ]; } // Adding Context Menu Links in more action @@ -300,14 +311,14 @@ public static function &links() { $qs = "atype=3&action=add&reset=1&cid=%%id%%{$extraParams}"; } - self::$_links[$counter++] = array( + self::$_links[$counter++] = [ 'name' => $value['title'], 'url' => $url, 'qs' => $qs, 'title' => $value['title'], 'ref' => $value['ref'], 'class' => CRM_Utils_Array::value('class', $value), - ); + ]; } } } @@ -337,7 +348,7 @@ public function getPagerParams($action, &$params) { */ public function &getColHeads($action = NULL, $output = NULL) { $colHeads = self::_getColumnHeaders(); - $colHeads[] = array('desc' => ts('Actions'), 'name' => ts('Action')); + $colHeads[] = ['desc' => ts('Actions'), 'name' => ts('Action')]; return $colHeads; } @@ -358,18 +369,18 @@ public function &getColumnHeaders($action = NULL, $output = NULL) { // unset return property elements that we don't care if (!empty($this->_returnProperties)) { - $doNotCareElements = array( + $doNotCareElements = [ 'contact_type', 'contact_sub_type', 'sort_name', - ); + ]; foreach ($doNotCareElements as $value) { unset($this->_returnProperties[$value]); } } if ($output == CRM_Core_Selector_Controller::EXPORT) { - $csvHeaders = array(ts('Contact ID'), ts('Contact Type')); + $csvHeaders = [ts('Contact ID'), ts('Contact Type')]; foreach ($this->getColHeads($action, $output) as $column) { if (array_key_exists('name', $column)) { $csvHeaders[] = $column['name']; @@ -378,7 +389,7 @@ public function &getColumnHeaders($action = NULL, $output = NULL) { $headers = $csvHeaders; } elseif ($output == CRM_Core_Selector_Controller::SCREEN) { - $csvHeaders = array(ts('Name')); + $csvHeaders = [ts('Name')]; foreach ($this->getColHeads($action, $output) as $key => $column) { if (array_key_exists('name', $column) && $column['name'] && @@ -392,20 +403,20 @@ public function &getColumnHeaders($action = NULL, $output = NULL) { elseif ($this->_ufGroupID) { // we dont use the cached value of column headers // since it potentially changed because of the profile selected - static $skipFields = array('group', 'tag'); + static $skipFields = ['group', 'tag']; $direction = CRM_Utils_Sort::ASCENDING; $empty = TRUE; if (!self::$_columnHeaders) { - self::$_columnHeaders = array( - array('name' => ''), - array( + self::$_columnHeaders = [ + ['name' => ''], + [ 'name' => ts('Name'), 'sort' => 'sort_name', 'direction' => CRM_Utils_Sort::ASCENDING, - ), - ); + ], + ]; - $locationTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id'); + $locationTypes = CRM_Core_DAO_Address::buildOptions('location_type_id', 'validate'); foreach ($this->_fields as $name => $field) { if (!empty($field['in_selector']) && @@ -421,11 +432,11 @@ public function &getColumnHeaders($action = NULL, $output = NULL) { $locationTypeName = $locationTypes[$lType]; } - if (in_array($fieldName, array( + if (in_array($fieldName, [ 'phone', 'im', 'email', - ))) { + ])) { if ($type) { $name = "`$locationTypeName-$fieldName-$type`"; } @@ -442,11 +453,11 @@ public function &getColumnHeaders($action = NULL, $output = NULL) { $name = 'contact_id'; } - self::$_columnHeaders[] = array( + self::$_columnHeaders[] = [ 'name' => $field['title'], 'sort' => $name, 'direction' => $direction, - ); + ]; $direction = CRM_Utils_Sort::DONTCARE; $empty = FALSE; } @@ -455,23 +466,23 @@ public function &getColumnHeaders($action = NULL, $output = NULL) { // if we dont have any valid columns, dont add the implicit ones // this allows the template to check on emptiness of column headers if ($empty) { - self::$_columnHeaders = array(); + self::$_columnHeaders = []; } else { - self::$_columnHeaders[] = array('desc' => ts('Actions'), 'name' => ts('Action')); + self::$_columnHeaders[] = ['desc' => ts('Actions'), 'name' => ts('Action')]; } } $headers = self::$_columnHeaders; } elseif (!empty($this->_returnProperties)) { - self::$_columnHeaders = array( - array('name' => ''), - array( + self::$_columnHeaders = [ + ['name' => ''], + [ 'name' => ts('Name'), 'sort' => 'sort_name', 'direction' => CRM_Utils_Sort::ASCENDING, - ), - ); + ], + ]; $properties = self::makeProperties($this->_returnProperties); foreach ($properties as $prop) { @@ -481,18 +492,29 @@ public function &getColumnHeaders($action = NULL, $output = NULL) { if (trim($phoneType) && !is_numeric($phoneType) && strtolower($phoneType) != $fld) { $title .= "-{$phoneType}"; } - $title .= " ($loc)"; + // fetch Location type label from name as $loc, which will be later used in column header + $title .= sprintf(" (%s)", + CRM_Core_PseudoConstant::getLabel( + 'CRM_Core_DAO_Address', + 'location_type_id', + CRM_Core_PseudoConstant::getKey('CRM_Core_DAO_Address', 'location_type_id', $loc) + ) + ); + } elseif (isset($this->_query->_fields[$prop]) && isset($this->_query->_fields[$prop]['title'])) { $title = $this->_query->_fields[$prop]['title']; } + elseif (isset($this->_query->_pseudoConstantsSelect[$prop]) && isset($this->_query->_pseudoConstantsSelect[$prop]['pseudoconstant']['optionGroupName'])) { + $title = CRM_Core_BAO_OptionGroup::getTitleByName($this->_query->_pseudoConstantsSelect[$prop]['pseudoconstant']['optionGroupName']); + } else { $title = ''; } - self::$_columnHeaders[] = array('name' => $title, 'sort' => $prop); + self::$_columnHeaders[] = ['name' => $title, 'sort' => $prop]; } - self::$_columnHeaders[] = array('name' => ts('Actions')); + self::$_columnHeaders[] = ['name' => ts('Actions')]; $headers = self::$_columnHeaders; } else { @@ -513,11 +535,11 @@ public function &getColumnHeaders($action = NULL, $output = NULL) { public function getTotalCount($action) { // Use count from cache during paging/sorting if (!empty($_GET['crmPID']) || !empty($_GET['crmSID'])) { - $count = CRM_Core_BAO_Cache::getItem('Search Results Count', $this->_key); + $count = Civi::cache('long')->get("Search Results Count $this->_key"); } if (empty($count)) { $count = $this->_query->searchQuery(0, 0, NULL, TRUE); - CRM_Core_BAO_Cache::setItem($count, 'Search Results Count', $this->_key); + Civi::cache('long')->set("Search Results Count $this->_key", $count); } return $count; } @@ -540,8 +562,6 @@ public function getTotalCount($action) { * the total number of rows for this action */ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) { - $config = CRM_Core_Config::singleton(); - if (($output == CRM_Core_Selector_Controller::EXPORT || $output == CRM_Core_Selector_Controller::SCREEN ) && @@ -557,16 +577,19 @@ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) { // and contain the search criteria (parameters) // note that the default action is basic if ($rowCount) { + /** @var CRM_Core_PrevNextCache_Interface $prevNext */ + $prevNext = Civi::service('prevnext'); $cacheKey = $this->buildPrevNextCache($sort); - $result = $this->_query->getCachedContacts($cacheKey, $offset, $rowCount, $includeContactIds); + $cids = $prevNext->fetch($cacheKey, $offset, $rowCount); + $resultSet = empty($cids) ? [] : $this->_query->getCachedContacts($cids, $includeContactIds)->fetchGenerator(); } else { - $result = $this->_query->searchQuery($offset, $rowCount, $sort, FALSE, $includeContactIds); + $resultSet = $this->_query->searchQuery($offset, $rowCount, $sort, FALSE, $includeContactIds)->fetchGenerator(); } // process the result of the query - $rows = array(); - $permissions = array(CRM_Core_Permission::getPermission()); + $rows = []; + $permissions = [CRM_Core_Permission::getPermission()]; if (CRM_Core_Permission::check('delete contacts')) { $permissions[] = CRM_Core_Permission::DELETE; } @@ -582,8 +605,8 @@ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) { if ($this->_ufGroupID) { $locationTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id'); - $names = array(); - static $skipFields = array('group', 'tag'); + $names = []; + static $skipFields = ['group', 'tag']; foreach ($this->_fields as $key => $field) { if (!empty($field['in_selector']) && !in_array($key, $skipFields) @@ -605,11 +628,11 @@ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) { } $locationTypeName = str_replace(' ', '_', $locationTypeName); - if (in_array($fieldName, array( + if (in_array($fieldName, [ 'phone', 'im', 'email', - ))) { + ])) { if ($type) { $names[] = "{$locationTypeName}-{$fieldName}-{$type}"; } @@ -636,27 +659,23 @@ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) { $names = self::$_properties; } - $multipleSelectFields = array('preferred_communication_method' => 1); - $links = self::links($this->_context, $this->_contextMenu, $this->_key); //check explicitly added contact to a Smart Group. $groupID = CRM_Utils_Array::value('group', $this->_formValues); - $pseudoconstants = array(); + $pseudoconstants = []; // for CRM-3157 purposes if (in_array('world_region', $names)) { - $pseudoconstants['world_region'] = array( + $pseudoconstants['world_region'] = [ 'dbName' => 'worldregion_id', 'values' => CRM_Core_PseudoConstant::worldRegion(), - ); + ]; } - $seenIDs = array(); - while ($result->fetch()) { - $row = array(); + foreach ($resultSet as $result) { + $row = []; $this->_query->convertToPseudoNames($result); - // the columns we are interested in foreach ($names as $property) { if ($property == 'status') { @@ -669,17 +688,6 @@ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) { $result->contact_id ); } - elseif ( - $multipleSelectFields && - array_key_exists($property, $multipleSelectFields) - ) { - $key = $property; - $paramsNew = array($key => $result->$property); - $name = array($key => array('newName' => $key, 'groupName' => $key)); - - CRM_Core_OptionGroup::lookupValues($paramsNew, $name, FALSE); - $row[$key] = $paramsNew[$key]; - } elseif (strpos($property, '-im')) { $row[$property] = $result->$property; if (!empty($result->$property)) { @@ -689,11 +697,11 @@ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) { $row[$property] = $result->$property . " ({$providerName})"; } } - elseif (in_array($property, array( + elseif (in_array($property, [ 'addressee', 'email_greeting', 'postal_greeting', - ))) { + ])) { $greeting = $property . '_display'; $row[$property] = $result->$greeting; } @@ -733,12 +741,12 @@ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) { ) { $contactID = $result->contact_id; if ($contactID) { - $gcParams = array( + $gcParams = [ 'contact_id' => $contactID, 'group_id' => $groupID, - ); + ]; - $gcDefaults = array(); + $gcDefaults = []; CRM_Core_DAO::commonRetrieve('CRM_Contact_DAO_GroupContact', $gcParams, $gcDefaults); if (empty($gcDefaults)) { @@ -762,33 +770,33 @@ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) { if (!empty($this->_formValues['deleted_contacts']) && CRM_Core_Permission::check('access deleted contacts') ) { - $links = array( - array( + $links = [ + [ 'name' => ts('View'), 'url' => 'civicrm/contact/view', 'qs' => 'reset=1&cid=%%id%%', 'class' => 'no-popup', 'title' => ts('View Contact Details'), - ), - array( + ], + [ 'name' => ts('Restore'), 'url' => 'civicrm/contact/view/delete', 'qs' => 'reset=1&cid=%%id%%&restore=1', 'title' => ts('Restore Contact'), - ), - ); + ], + ]; if (CRM_Core_Permission::check('delete contacts')) { - $links[] = array( + $links[] = [ 'name' => ts('Delete Permanently'), 'url' => 'civicrm/contact/view/delete', 'qs' => 'reset=1&cid=%%id%%&skip_undelete=1', 'title' => ts('Permanently Delete Contact'), - ); + ]; } $row['action'] = CRM_Core_Action::formLink( $links, NULL, - array('id' => $result->contact_id), + ['id' => $result->contact_id], ts('more'), FALSE, 'contact.selector.row', @@ -804,7 +812,7 @@ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) { $row['action'] = CRM_Core_Action::formLink( $links, $mask, - array('id' => $result->contact_id), + ['id' => $result->contact_id], ts('more'), FALSE, 'contact.selector.row', @@ -816,7 +824,7 @@ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) { $row['action'] = CRM_Core_Action::formLink( $links, $mapMask, - array('id' => $result->contact_id), + ['id' => $result->contact_id], ts('more'), FALSE, 'contact.selector.row', @@ -837,16 +845,13 @@ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) { $row['contact_sub_type'] = $result->contact_sub_type ? CRM_Contact_BAO_ContactType::contactTypePairs(FALSE, $result->contact_sub_type, ', ') : $result->contact_sub_type; $row['contact_id'] = $result->contact_id; $row['sort_name'] = $result->sort_name; + // Surely this if should be if NOT - otherwise it's just wierd. if (array_key_exists('id', $row)) { $row['id'] = $result->contact_id; } } - // Dedupe contacts - if (in_array($row['contact_id'], $seenIDs) === FALSE) { - $seenIDs[] = $row['contact_id']; - $rows[] = $row; - } + $rows[$row['contact_id']] = $row; } return $rows; @@ -865,10 +870,10 @@ public function buildPrevNextCache($sort) { // 2. if records are sorted // get current page requested - $pageNum = CRM_Utils_Request::retrieve('crmPID', 'Integer', CRM_Core_DAO::$_nullObject); + $pageNum = CRM_Utils_Request::retrieve('crmPID', 'Integer'); // get the current sort order - $currentSortID = CRM_Utils_Request::retrieve('crmSID', 'String', CRM_Core_DAO::$_nullObject); + $currentSortID = CRM_Utils_Request::retrieve('crmSID', 'String'); $session = CRM_Core_Session::singleton(); @@ -878,7 +883,7 @@ public function buildPrevNextCache($sort) { // check for current != previous to ensure cache is not reset if paging is done without changing // sort criteria if (!$pageNum || (!empty($currentSortID) && $currentSortID != $previousSortID)) { - CRM_Core_BAO_PrevNextCache::deleteItem(NULL, $cacheKey, 'civicrm_contact'); + Civi::service('prevnext')->deleteItem(NULL, $cacheKey, 'civicrm_contact'); // this means it's fresh search, so set pageNum=1 if (!$pageNum) { $pageNum = 1; @@ -894,17 +899,16 @@ public function buildPrevNextCache($sort) { $firstRecord = ($pageNum - 1) * $pageSize; //for alphabetic pagination selection save - $sortByCharacter = CRM_Utils_Request::retrieve('sortByCharacter', 'String', CRM_Core_DAO::$_nullObject); + $sortByCharacter = CRM_Utils_Request::retrieve('sortByCharacter', 'String'); //for text field pagination selection save - $countRow = CRM_Core_BAO_PrevNextCache::getCount($cacheKey, NULL, "entity_table = 'civicrm_contact'"); + $countRow = Civi::service('prevnext')->getCount($cacheKey); // $sortByCharacter triggers a refresh in the prevNext cache if ($sortByCharacter && $sortByCharacter != 'all') { - $cacheKey .= "_alphabet"; - $this->fillupPrevNextCache($sort, $cacheKey); + $this->fillupPrevNextCache($sort, $cacheKey, 0, max(self::CACHE_SIZE, $pageSize)); } - elseif ($firstRecord >= $countRow) { - $this->fillupPrevNextCache($sort, $cacheKey, $countRow, 500 + $firstRecord - $countRow); + elseif (($firstRecord + $pageSize) >= $countRow) { + $this->fillupPrevNextCache($sort, $cacheKey, $countRow, max(self::CACHE_SIZE, $pageSize) + $firstRecord - $countRow); } return $cacheKey; } @@ -913,67 +917,61 @@ public function buildPrevNextCache($sort) { * @param $rows */ public function addActions(&$rows) { - $config = CRM_Core_Config::singleton(); - $permissions = array(CRM_Core_Permission::getPermission()); - if (CRM_Core_Permission::check('delete contacts')) { - $permissions[] = CRM_Core_Permission::DELETE; - } - $mask = CRM_Core_Action::mask($permissions); - // mask value to hide map link if there are not lat/long - $mapMask = $mask & 4095; + $basicPermissions = CRM_Core_Permission::check('delete contacts') ? [CRM_Core_Permission::DELETE] : []; - // mask value to hide map link if there are not lat/long - $mapMask = $mask & 4095; + // get permissions on an individual level (CRM-12645) + // @todo look at storing this to the session as this is called twice during search results render. + $can_edit_list = CRM_Contact_BAO_Contact_Permission::allowList(array_keys($rows), CRM_Core_Permission::EDIT); - $links = self::links($this->_context, $this->_contextMenu, $this->_key); + $links_template = self::links($this->_context, $this->_contextMenu, $this->_key); foreach ($rows as $id => & $row) { + $links = $links_template; + if (in_array($id, $can_edit_list)) { + $mask = CRM_Core_Action::mask(array_merge([CRM_Core_Permission::EDIT], $basicPermissions)); + } + else { + $mask = CRM_Core_Action::mask(array_merge([CRM_Core_Permission::VIEW], $basicPermissions)); + } + + if ((!is_numeric(CRM_Utils_Array::value('geo_code_1', $row))) && + (empty($row['city']) || + !CRM_Utils_Array::value('state_province', $row) + ) + ) { + $mask = $mask & 4095; + } + if (!empty($this->_formValues['deleted_contacts']) && CRM_Core_Permission::check('access deleted contacts') ) { - $links = array( - array( + $links = [ + [ 'name' => ts('View'), 'url' => 'civicrm/contact/view', 'qs' => 'reset=1&cid=%%id%%', 'class' => 'no-popup', 'title' => ts('View Contact Details'), - ), - array( + ], + [ 'name' => ts('Restore'), 'url' => 'civicrm/contact/view/delete', 'qs' => 'reset=1&cid=%%id%%&restore=1', 'title' => ts('Restore Contact'), - ), - ); + ], + ]; if (CRM_Core_Permission::check('delete contacts')) { - $links[] = array( + $links[] = [ 'name' => ts('Delete Permanently'), 'url' => 'civicrm/contact/view/delete', 'qs' => 'reset=1&cid=%%id%%&skip_undelete=1', 'title' => ts('Permanently Delete Contact'), - ); + ]; } $row['action'] = CRM_Core_Action::formLink( $links, NULL, - array('id' => $row['contact_id']), - ts('more'), - FALSE, - 'contact.selector.actions', - 'Contact', - $row['contact_id'] - ); - } - elseif ((is_numeric(CRM_Utils_Array::value('geo_code_1', $row))) || - (!empty($row['city']) && - CRM_Utils_Array::value('state_province', $row) - ) - ) { - $row['action'] = CRM_Core_Action::formLink( - $links, - $mask, - array('id' => $row['contact_id']), + ['id' => $row['contact_id']], ts('more'), FALSE, 'contact.selector.actions', @@ -984,8 +982,8 @@ public function addActions(&$rows) { else { $row['action'] = CRM_Core_Action::formLink( $links, - $mapMask, - array('id' => $row['contact_id']), + $mask, + ['id' => $row['contact_id']], ts('more'), FALSE, 'contact.selector.actions', @@ -1020,19 +1018,17 @@ public function removeActions(&$rows) { * @param int $start * @param int $end */ - public function fillupPrevNextCache($sort, $cacheKey, $start = 0, $end = 500) { + public function fillupPrevNextCache($sort, $cacheKey, $start = 0, $end = self::CACHE_SIZE) { $coreSearch = TRUE; // For custom searches, use the contactIDs method if (is_a($this, 'CRM_Contact_Selector_Custom')) { $sql = $this->_search->contactIDs($start, $end, $sort, TRUE); - $replaceSQL = "SELECT contact_a.id as contact_id"; $coreSearch = FALSE; } // For core searches use the searchQuery method else { - $sql = $this->_query->searchQuery($start, $end, $sort, FALSE, $this->_query->_includeContactIds, - FALSE, TRUE, TRUE); - $replaceSQL = "SELECT contact_a.id as id"; + $sql = $this->_query->getSearchSQL($start, $end, $sort, FALSE, $this->_query->_includeContactIds, + FALSE, TRUE); } // CRM-9096 @@ -1044,32 +1040,31 @@ public function fillupPrevNextCache($sort, $cacheKey, $start = 0, $end = 500) { // the other alternative of running the FULL query will just be incredibly inefficient // and slow things down way too much on large data sets / complex queries - $insertSQL = " -INSERT INTO civicrm_prevnext_cache ( entity_table, entity_id1, entity_id2, cacheKey, data ) -SELECT DISTINCT 'civicrm_contact', contact_a.id, contact_a.id, '$cacheKey', contact_a.display_name -"; - - $sql = str_replace($replaceSQL, $insertSQL, $sql); + $selectSQL = "SELECT DISTINCT %1, contact_a.id, contact_a.sort_name"; - $errorScope = CRM_Core_TemporaryErrorScope::ignoreException(); - $result = CRM_Core_DAO::executeQuery($sql); - unset($errorScope); - - if (is_a($result, 'DB_Error')) { - // check if we get error during core search + $sql = str_ireplace(["SELECT contact_a.id as contact_id", "SELECT contact_a.id as id"], $selectSQL, $sql); + try { + Civi::service('prevnext')->fillWithSql($cacheKey, $sql, [1 => [$cacheKey, 'String']]); + } + catch (CRM_Core_Exception $e) { if ($coreSearch) { // in the case of error, try rebuilding cache using full sql which is used for search selector display // this fixes the bugs reported in CRM-13996 & CRM-14438 $this->rebuildPreNextCache($start, $end, $sort, $cacheKey); } else { - // return if above query fails + // This will always show for CiviRules :-( as a) it orders by 'rule_label' + // which is not available in the query & b) it uses contact not contact_a + // as an alias. + // CRM_Core_Session::setStatus(ts('Query Failed')); return; } } - // also record an entry in the cache key table, so we can delete it periodically - CRM_Core_BAO_Cache::setItem($cacheKey, 'CiviCRM Search PrevNextCache', $cacheKey); + if (Civi::service('prevnext') instanceof CRM_Core_PrevNextCache_Sql) { + // SQL-backed prevnext cache uses an extra record for pruning the cache. + CRM_Core_BAO_Cache::setItem($cacheKey, 'CiviCRM Search PrevNextCache', $cacheKey); + } } /** @@ -1090,19 +1085,18 @@ public function rebuildPreNextCache($start, $end, $sort, $cacheKey) { $dao = CRM_Core_DAO::executeQuery($sql); - // build insert query, note that currently we build cache for 500 contact records at a time, hence below approach - $insertValues = array(); + // build insert query, note that currently we build cache for 500 (self::CACHE_SIZE) contact records at a time, hence below approach + $rows = []; while ($dao->fetch()) { - $insertValues[] = "('civicrm_contact', {$dao->contact_id}, {$dao->contact_id}, '{$cacheKey}', '" . CRM_Core_DAO::escapeString($dao->sort_name) . "')"; + $rows[] = [ + 'entity_table' => 'civicrm_contact', + 'entity_id1' => $dao->contact_id, + 'entity_id2' => $dao->contact_id, + 'data' => $dao->sort_name, + ]; } - //update pre/next cache using single insert query - if (!empty($insertValues)) { - $sql = 'INSERT INTO civicrm_prevnext_cache ( entity_table, entity_id1, entity_id2, cacheKey, data ) VALUES -' . implode(',', $insertValues); - - $result = CRM_Core_DAO::executeQuery($sql); - } + Civi::service('prevnext')->fillWithArray($cacheKey, $rows); } /** @@ -1136,38 +1130,38 @@ private static function &_getColumnHeaders() { 'address_options', TRUE, NULL, TRUE ); - self::$_columnHeaders = array( - 'contact_type' => array('desc' => ts('Contact Type')), - 'sort_name' => array( + self::$_columnHeaders = [ + 'contact_type' => ['desc' => ts('Contact Type')], + 'sort_name' => [ 'name' => ts('Name'), 'sort' => 'sort_name', 'direction' => CRM_Utils_Sort::ASCENDING, - ), - ); + ], + ]; - $defaultAddress = array( - 'street_address' => array('name' => ts('Address')), - 'city' => array( + $defaultAddress = [ + 'street_address' => ['name' => ts('Address')], + 'city' => [ 'name' => ts('City'), 'sort' => 'city', 'direction' => CRM_Utils_Sort::DONTCARE, - ), - 'state_province' => array( + ], + 'state_province' => [ 'name' => ts('State'), 'sort' => 'state_province', 'direction' => CRM_Utils_Sort::DONTCARE, - ), - 'postal_code' => array( + ], + 'postal_code' => [ 'name' => ts('Postal'), 'sort' => 'postal_code', 'direction' => CRM_Utils_Sort::DONTCARE, - ), - 'country' => array( + ], + 'country' => [ 'name' => ts('Country'), 'sort' => 'country', 'direction' => CRM_Utils_Sort::DONTCARE, - ), - ); + ], + ]; foreach ($defaultAddress as $columnName => $column) { if (!empty($addressOptions[$columnName])) { @@ -1175,13 +1169,13 @@ private static function &_getColumnHeaders() { } } - self::$_columnHeaders['email'] = array( + self::$_columnHeaders['email'] = [ 'name' => ts('Email'), 'sort' => 'email', 'direction' => CRM_Utils_Sort::DONTCARE, - ); + ]; - self::$_columnHeaders['phone'] = array('name' => ts('Phone')); + self::$_columnHeaders['phone'] = ['name' => ts('Phone')]; } return self::$_columnHeaders; } @@ -1197,19 +1191,18 @@ public function getQuery() { * @return CRM_Contact_DAO_Contact */ public function alphabetQuery() { - return $this->_query->searchQuery(NULL, NULL, NULL, FALSE, FALSE, TRUE); + return $this->_query->alphabetQuery(); } /** * @param array $params - * @param $action * @param int $sortID * @param null $displayRelationshipType * @param string $queryOperator * * @return CRM_Contact_DAO_Contact */ - public function contactIDQuery($params, $action, $sortID, $displayRelationshipType = NULL, $queryOperator = 'AND') { + public function contactIDQuery($params, $sortID, $displayRelationshipType = NULL, $queryOperator = 'AND') { $sortOrder = &$this->getSortOrder($this->_action); $sort = new CRM_Utils_Sort($sortOrder, $sortID); @@ -1235,11 +1228,7 @@ public function contactIDQuery($params, $action, $sortID, $displayRelationshipTy $queryOperator ); } - $value = $query->searchQuery(0, 0, $sort, - FALSE, FALSE, FALSE, - FALSE, FALSE - ); - return $value; + return $query->searchQuery(0, 0, $sort); } /** @@ -1248,16 +1237,16 @@ public function contactIDQuery($params, $action, $sortID, $displayRelationshipTy * @return array */ public function &makeProperties(&$returnProperties) { - $properties = array(); + $properties = []; foreach ($returnProperties as $name => $value) { if ($name != 'location') { // special handling for group and tag - if (in_array($name, array('group', 'tag'))) { + if (in_array($name, ['group', 'tag'])) { $name = "{$name}s"; } // special handling for notes - if (in_array($name, array('note', 'note_subject', 'note_body'))) { + if (in_array($name, ['note', 'note_subject', 'note_body'])) { $name = "notes"; } @@ -1268,6 +1257,7 @@ public function &makeProperties(&$returnProperties) { foreach ($value as $n => $v) { foreach ($v as $n1 => $v1) { if (!strpos('_id', $n1) && $n1 != 'location_type') { + $n = str_replace(' ', '_', $n); $properties[] = "{$n}-{$n1}"; } } diff --git a/CRM/Contact/Selector/Controller.php b/CRM/Contact/Selector/Controller.php index e1dab181dd96..27dc70f75327 100644 --- a/CRM/Contact/Selector/Controller.php +++ b/CRM/Contact/Selector/Controller.php @@ -1,9 +1,9 @@ array( + self::$_links = [ + CRM_Core_Action::VIEW => [ 'name' => ts('View'), 'url' => 'civicrm/contact/view', 'qs' => "reset=1&cid=%%id%%{$extraParams}{$searchContext}", 'class' => 'no-popup', 'title' => ts('View Contact Details'), - ), - CRM_Core_Action::UPDATE => array( + ], + CRM_Core_Action::UPDATE => [ 'name' => ts('Edit'), 'url' => 'civicrm/contact/add', 'qs' => 'reset=1&action=update&cid=%%id%%', 'class' => 'no-popup', 'title' => ts('Edit Contact Details'), - ), - ); + ], + ]; $config = CRM_Core_Config::singleton(); //CRM-16552: mapAPIKey is not mandatory as google no longer requires an API Key if ($config->mapProvider && ($config->mapAPIKey || $config->mapProvider == 'Google')) { - self::$_links[CRM_Core_Action::MAP] = array( + self::$_links[CRM_Core_Action::MAP] = [ 'name' => ts('Map'), 'url' => 'civicrm/contact/map', 'qs' => 'reset=1&cid=%%id%%&searchType=custom', 'class' => 'no-popup', 'title' => ts('Map Contact'), - ); + ]; } } return self::$_links; @@ -227,21 +228,24 @@ public function getPagerParams($action, &$params) { */ public function &getColumnHeaders($action = NULL, $output = NULL) { $columns = $this->_search->columns(); - if ($output == CRM_Core_Selector_Controller::EXPORT) { - return array_keys($columns); + $headers = []; + if ($output == CRM_Core_Selector_Controller::EXPORT || $output == CRM_Core_Selector_Controller::SCREEN) { + foreach ($columns as $name => $key) { + $headers[$key] = $name; + } + return $headers; } else { - $headers = array(); foreach ($columns as $name => $key) { if (!empty($name)) { - $headers[] = array( + $headers[] = [ 'name' => $name, 'sort' => $key, 'direction' => CRM_Utils_Sort::ASCENDING, - ); + ]; } else { - $headers[] = array(); + $headers[] = []; } } return $headers; @@ -297,13 +301,13 @@ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) { $contactQueryObj = $this->_search->getQueryObj(); } - $dao = CRM_Core_DAO::executeQuery($sql, CRM_Core_DAO::$_nullArray); + $dao = CRM_Core_DAO::executeQuery($sql); $columns = $this->_search->columns(); $columnNames = array_values($columns); $links = self::links($this->_key); - $permissions = array(CRM_Core_Permission::getPermission()); + $permissions = [CRM_Core_Permission::getPermission()]; if (CRM_Core_Permission::check('delete contacts')) { $permissions[] = CRM_Core_Permission::DELETE; } @@ -320,9 +324,9 @@ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) { $image = TRUE; } // process the result of the query - $rows = array(); + $rows = []; while ($dao->fetch()) { - $row = array(); + $row = []; $empty = TRUE; // if contact query object present @@ -346,7 +350,7 @@ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) { $row['checkbox'] = CRM_Core_Form::CB_PREFIX . $contactID; $row['action'] = CRM_Core_Action::formLink($links, $mask, - array('id' => $contactID), + ['id' => $contactID], ts('more'), FALSE, 'contact.custom.actions', @@ -424,12 +428,12 @@ public function alphabetQuery() { * * @return Object */ - public function contactIDQuery($params, $action, $sortID, $displayRelationshipType = NULL, $queryOperator = 'AND') { + public function contactIDQuery($params, $sortID, $displayRelationshipType = NULL, $queryOperator = 'AND') { // $action, $displayRelationshipType and $queryOperator are unused. I have // no idea why they are there. // I wonder whether there is some helper function for this: - $matches = array(); + $matches = []; if (preg_match('/([0-9]*)(_(u|d))?/', $sortID, $matches)) { $columns = array_values($this->_search->columns()); $sort = $columns[$matches[1] - 1]; @@ -453,7 +457,7 @@ public function contactIDQuery($params, $action, $sortID, $displayRelationshipTy public function addActions(&$rows) { $links = self::links($this->_key); - $permissions = array(CRM_Core_Permission::getPermission()); + $permissions = [CRM_Core_Permission::getPermission()]; if (CRM_Core_Permission::check('delete contacts')) { $permissions[] = CRM_Core_Permission::DELETE; } @@ -462,7 +466,7 @@ public function addActions(&$rows) { foreach ($rows as $id => & $row) { $row['action'] = CRM_Core_Action::formLink($links, $mask, - array('id' => $row['contact_id']), + ['id' => $row['contact_id']], ts('more'), FALSE, 'contact.custom.actions', diff --git a/CRM/Contact/StateMachine/Search.php b/CRM/Contact/StateMachine/Search.php index 296087b65320..97d8b375cd20 100644 --- a/CRM/Contact/StateMachine/Search.php +++ b/CRM/Contact/StateMachine/Search.php @@ -1,9 +1,9 @@ _pages = array(); + $this->_pages = []; if ($action == CRM_Core_Action::ADVANCED) { $this->_pages['CRM_Contact_Form_Search_Advanced'] = NULL; list($task, $result) = $this->taskName($controller, 'Advanced'); @@ -92,7 +92,7 @@ public function __construct($controller, $action = CRM_Core_Action::NONE) { * * @param string $formName * - * @return string + * @return array * the name of the form that will handle the task */ public function taskName($controller, $formName = 'Search') { @@ -103,15 +103,10 @@ public function taskName($controller, $formName = 'Search') { } $this->_controller->set('task', $value); - if ($value) { - $componentMode = $this->_controller->get('component_mode'); - $modeValue = CRM_Contact_Form_Search::getModeValue($componentMode); - $taskClassName = $modeValue['taskClassName']; - return $taskClassName::getTask($value); - } - else { - return CRM_Contact_Task::getTask($value); - } + $componentMode = $this->_controller->get('component_mode'); + $modeValue = CRM_Contact_Form_Search::getModeValue($componentMode); + $taskClassName = $modeValue['taskClassName']; + return $taskClassName::getTask($value); } /** diff --git a/CRM/Contact/Task.php b/CRM/Contact/Task.php index c690fb03b5ae..97c62e6b17bc 100644 --- a/CRM/Contact/Task.php +++ b/CRM/Contact/Task.php @@ -1,9 +1,9 @@ array( + self::GROUP_ADD => array( 'title' => ts('Group - add contacts'), 'class' => 'CRM_Contact_Form_Task_AddToGroup', + 'url' => 'civicrm/task/add-to-group', ), - self::REMOVE_CONTACTS => array( + self::GROUP_REMOVE => array( 'title' => ts('Group - remove contacts'), 'class' => 'CRM_Contact_Form_Task_RemoveFromGroup', + 'url' => 'civicrm/task/remove-from-group', ), - self::TAG_CONTACTS => array( + self::TAG_ADD => array( 'title' => ts('Tag - add to contacts'), 'class' => 'CRM_Contact_Form_Task_AddToTag', + 'url' => 'civicrm/task/add-to-tag', ), - self::REMOVE_TAGS => array( + self::TAG_REMOVE => array( 'title' => ts('Tag - remove from contacts'), 'class' => 'CRM_Contact_Form_Task_RemoveFromTag', + 'url' => 'civicrm/task/remove-from-tag', ), - self::EXPORT_CONTACTS => array( + self::TASK_EXPORT => array( 'title' => ts('Export contacts'), 'class' => array( 'CRM_Export_Form_Select', @@ -103,15 +88,20 @@ public static function initTasks() { ), 'result' => FALSE, ), - self::EMAIL_CONTACTS => array( - 'title' => ts('Email - send now (to 50 or less)'), + self::TASK_EMAIL => array( + 'title' => ts('Email - send now (to %1 or less)', array( + 1 => Civi::settings() + ->get('simple_mail_limit'), + )), 'class' => 'CRM_Contact_Form_Task_Email', 'result' => TRUE, + 'url' => 'civicrm/task/send-email', ), - self::DELETE_CONTACTS => array( + self::TASK_DELETE => array( 'title' => ts('Delete contacts'), 'class' => 'CRM_Contact_Form_Task_Delete', 'result' => FALSE, + 'url' => 'civicrm/task/delete-contact', ), self::RECORD_CONTACTS => array( 'title' => ts('Add activity'), @@ -127,7 +117,7 @@ public static function initTasks() { 'class' => 'CRM_Contact_Form_Task_SaveSearch_Update', 'result' => TRUE, ), - self::PRINT_CONTACTS => array( + self::TASK_PRINT => array( 'title' => ts('Print selected rows'), 'class' => 'CRM_Contact_Form_Task_Print', 'result' => FALSE, @@ -136,6 +126,7 @@ public static function initTasks() { 'title' => ts('Mailing labels - print'), 'class' => 'CRM_Contact_Form_Task_Label', 'result' => TRUE, + 'url' => 'civicrm/task/make-mailing-label', ), self::BATCH_UPDATE => array( 'title' => ts('Update multiple contacts'), @@ -144,19 +135,23 @@ public static function initTasks() { 'CRM_Contact_Form_Task_Batch', ), 'result' => TRUE, + 'url' => 'civicrm/task/pick-profile', ), - self::PRINT_FOR_CONTACTS => array( + self::PDF_LETTER => array( 'title' => ts('Print/merge document'), 'class' => 'CRM_Contact_Form_Task_PDF', 'result' => TRUE, + 'url' => 'civicrm/task/print-document', ), self::EMAIL_UNHOLD => array( 'title' => ts('Email - unhold addresses'), 'class' => 'CRM_Contact_Form_Task_Unhold', + 'url' => 'civicrm/task/unhold-email', ), self::COMMUNICATION_PREFS => array( 'title' => ts('Communication preferences - alter'), 'class' => 'CRM_Contact_Form_Task_AlterPreferences', + 'url' => 'civicrm/task/alter-contact-preference', ), self::RESTORE => array( 'title' => ts('Restore contacts from trash'), @@ -172,14 +167,24 @@ public static function initTasks() { //CRM-16329, if SMS provider is configured show sms action. $providersCount = CRM_SMS_BAO_Provider::activeProviderCount(); - if ($providersCount) { - self::$_tasks[self::SMS_CONTACTS] = array( + if ($providersCount && CRM_Core_Permission::check('send SMS')) { + self::$_tasks[self::TASK_SMS] = array( 'title' => ts('SMS - schedule/send'), 'class' => 'CRM_Contact_Form_Task_SMS', 'result' => TRUE, ); } + if (CRM_Contact_BAO_ContactType::isActive('Individual')) { + $label = CRM_Contact_BAO_ContactType::getLabel('individual'); + self::$_tasks[self::INDIVIDUAL_CONTACTS] = array( + 'title' => ts('Add relationship - to %1', + array(1 => $label) + ), + 'class' => 'CRM_Contact_Form_Task_AddToIndividual', + ); + } + if (CRM_Contact_BAO_ContactType::isActive('Household')) { $label = CRM_Contact_BAO_ContactType::getLabel('household'); self::$_tasks[self::HOUSEHOLD_CONTACTS] = array( @@ -210,7 +215,7 @@ public static function initTasks() { //CRM-4418, check for delete if (!CRM_Core_Permission::check('delete contacts')) { - unset(self::$_tasks[self::DELETE_CONTACTS]); + unset(self::$_tasks[self::TASK_DELETE]); } //show map action only if map provider and geoprovider are set (Google doesn't need geoprovider) @@ -248,43 +253,18 @@ public static function initTasks() { ); } - self::$_tasks += CRM_Core_Component::taskList(); - - CRM_Utils_Hook::searchTasks('contact', self::$_tasks); - - } - } - - /** - * These tasks are the core set of tasks that the user can perform - * on a contact / group of contacts - * - * @return array - * the set of tasks for a group of contacts - */ - public static function &taskTitles() { - self::initTasks(); - - $titles = array(); - foreach (self::$_tasks as $id => $value) { - $titles[$id] = $value['title']; - } - - // hack unset update saved search - unset($titles[self::SAVE_SEARCH_UPDATE]); + if (CRM_Core_Permission::access('CiviCase')) { + self::$_tasks[self::ADD_TO_CASE] = array( + 'title' => 'Add to case as role', + 'class' => 'CRM_Case_Form_AddToCaseAsRole', + 'result' => FALSE, + ); + } - if (!CRM_Utils_Mail::validOutBoundMail()) { - unset($titles[self::EMAIL_CONTACTS]); - unset($titles[self::CREATE_MAILING]); + parent::tasks(); } - // CRM-6806 - if (!CRM_Core_Permission::check('access deleted contacts') || - !CRM_Core_Permission::check('delete contacts') - ) { - unset($titles[self::DELETE_PERMANENTLY]); - } - return $titles; + return self::$_tasks; } /** @@ -292,16 +272,19 @@ public static function &taskTitles() { * of the user * * @param int $permission - * @param bool $deletedContacts - * Are these tasks for operating on deleted contacts?. + * @param array $params + * bool deletedContacts: Are these tasks for operating on deleted contacts?. * * @return array * set of tasks that are valid for the user */ - public static function &permissionedTaskTitles($permission, $deletedContacts = FALSE) { - self::initTasks(); + public static function permissionedTaskTitles($permission, $params = array()) { + if (!isset($params['deletedContacts'])) { + $params['deletedContacts'] = FALSE; + } + self::tasks(); $tasks = array(); - if ($deletedContacts) { + if ($params['deletedContacts']) { if (CRM_Core_Permission::check('access deleted contacts')) { $tasks[self::RESTORE] = self::$_tasks[self::RESTORE]['title']; if (CRM_Core_Permission::check('delete contacts')) { @@ -314,36 +297,25 @@ public static function &permissionedTaskTitles($permission, $deletedContacts = F } else { $tasks = array( - self::EXPORT_CONTACTS => self::$_tasks[self::EXPORT_CONTACTS]['title'], - self::EMAIL_CONTACTS => self::$_tasks[self::EMAIL_CONTACTS]['title'], + self::TASK_EXPORT => self::$_tasks[self::TASK_EXPORT]['title'], + self::TASK_EMAIL => self::$_tasks[self::TASK_EMAIL]['title'], self::LABEL_CONTACTS => self::$_tasks[self::LABEL_CONTACTS]['title'], ); - if (isset(self::$_tasks[self::MAP_CONTACTS]) && - !empty(self::$_tasks[self::MAP_CONTACTS]['title']) - ) { - $tasks[self::MAP_CONTACTS] = self::$_tasks[self::MAP_CONTACTS]['title']; - } - - if (isset(self::$_tasks[self::CREATE_MAILING]) && - !empty(self::$_tasks[self::CREATE_MAILING]['title']) - ) { - $tasks[self::CREATE_MAILING] = self::$_tasks[self::CREATE_MAILING]['title']; + foreach ([ + self::MAP_CONTACTS, + self::CREATE_MAILING, + self::TASK_SMS, + ] as $task) { + if (isset(self::$_tasks[$task]) && + !empty(self::$_tasks[$task]['title']) + ) { + $tasks[$task] = self::$_tasks[$task]['title']; + } } } - return $tasks; - } - /** - * These tasks get added based on the context the user is in. - * - * @return array - * the set of optional tasks for a group of contacts - */ - public static function &optionalTaskTitle() { - $tasks = array( - self::SAVE_SEARCH_UPDATE => self::$_tasks[self::SAVE_SEARCH_UPDATE]['title'], - ); + $tasks = parent::corePermissionedTaskTitles($tasks, $permission, $params); return $tasks; } @@ -353,16 +325,13 @@ public static function &optionalTaskTitle() { * @return array */ public static function getTask($value) { - self::initTasks(); + self::tasks(); if (!CRM_Utils_Array::value($value, self::$_tasks)) { // make it the print task by default - $value = self::PRINT_CONTACTS; + $value = self::TASK_PRINT; } - return array( - CRM_Utils_Array::value('class', self::$_tasks[$value]), - CRM_Utils_Array::value('result', self::$_tasks[$value]), - ); + return parent::getTask($value); } } diff --git a/CRM/Contribute/ActionMapping/ByPage.php b/CRM/Contribute/ActionMapping/ByPage.php index 175c41275736..5e1d62448bcf 100644 --- a/CRM/Contribute/ActionMapping/ByPage.php +++ b/CRM/Contribute/ActionMapping/ByPage.php @@ -1,9 +1,9 @@ string $fieldLabel). */ public function getDateFields() { - return array( + return [ 'receive_date' => ts('Receive Date'), 'cancel_date' => ts('Cancel Date'), 'receipt_date' => ts('Receipt Date'), 'thankyou_date' => ts('Thank You Date'), - ); + ]; } /** @@ -146,7 +145,7 @@ public function getDateFields() { * Ex: array('assignee' => 'Activity Assignee'). */ public function getRecipientTypes() { - return array(); + return []; } /** @@ -163,7 +162,7 @@ public function getRecipientTypes() { * @see getRecipientTypes */ public function getRecipientListing($recipientType) { - return array(); + return []; } /** @@ -176,7 +175,7 @@ public function getRecipientListing($recipientType) { * List of error messages. */ public function validateSchedule($schedule) { - return array(); + return []; } /** diff --git a/CRM/Contribute/ActionMapping/ByType.php b/CRM/Contribute/ActionMapping/ByType.php index a52506c322d9..3d4a85c8aeb3 100644 --- a/CRM/Contribute/ActionMapping/ByType.php +++ b/CRM/Contribute/ActionMapping/ByType.php @@ -1,9 +1,9 @@ string $fieldLabel). */ public function getDateFields() { - return array( + return [ 'receive_date' => ts('Receive Date'), 'cancel_date' => ts('Cancel Date'), 'receipt_date' => ts('Receipt Date'), 'thankyou_date' => ts('Thank You Date'), - ); + ]; } /** @@ -146,9 +145,9 @@ public function getDateFields() { * Ex: array('assignee' => 'Activity Assignee'). */ public function getRecipientTypes() { - return array( + return [ 'soft_credit_type' => ts('Soft Credit Role'), - ); + ]; } /** @@ -170,7 +169,7 @@ public function getRecipientListing($recipientType) { return \CRM_Core_OptionGroup::values('soft_credit_type', FALSE, FALSE, FALSE, NULL, 'label', TRUE, FALSE, 'name'); default: - return array(); + return []; } } @@ -184,7 +183,7 @@ public function getRecipientListing($recipientType) { * List of error messages. */ public function validateSchedule($schedule) { - return array(); + return []; } /** diff --git a/CRM/Contribute/BAO/Contribution.php b/CRM/Contribute/BAO/Contribution.php index 2da2df36f21d..48e16a4dbf27 100644 --- a/CRM/Contribute/BAO/Contribution.php +++ b/CRM/Contribute/BAO/Contribution.php @@ -1,9 +1,9 @@ push(CRM_Core_Error::DUPLICATE_CONTRIBUTION, - 'Fatal', - array($d), - "Duplicate error - existing contribution record(s) have a matching Transaction ID or Invoice ID. Contribution record ID(s) are: $d" - ); - return $error; + $message = ts("Duplicate error - existing contribution record(s) have a matching Transaction ID or Invoice ID. Contribution record ID(s) are: " . implode(', ', $duplicates)); + throw new CRM_Core_Exception($message); } // first clean up all the money fields @@ -120,7 +122,12 @@ public static function add(&$params, $ids = array()) { //if priceset is used, no need to cleanup money if (!empty($params['skipCleanMoney'])) { - unset($moneyFields[0]); + $moneyFields = []; + } + else { + // @todo put a deprecated here - this should be done in the form layer. + $params['skipCleanMoney'] = FALSE; + Civi::log()->warning('Deprecated code path. Money should always be clean before it hits the BAO.', array('civi.tag' => 'deprecated')); } foreach ($moneyFields as $field) { @@ -132,6 +139,11 @@ public static function add(&$params, $ids = array()) { //set defaults in create mode if (!$contributionID) { CRM_Core_DAO::setCreateDefaults($params, self::getDefaults()); + + if (empty($params['invoice_number'])) { + $nextContributionID = CRM_Core_DAO::singleValueQuery("SELECT COALESCE(MAX(id) + 1, 1) FROM civicrm_contribution"); + $params['invoice_number'] = self::getInvoiceNumber($nextContributionID); + } } $contributionStatus = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name'); @@ -172,12 +184,12 @@ public static function add(&$params, $ids = array()) { $setPrevContribution = TRUE; // CRM-13964 partial payment - if (!empty($params['partial_payment_total']) && !empty($params['partial_amount_pay'])) { + if (!empty($params['partial_payment_total']) && !empty($params['partial_amount_to_pay'])) { $partialAmtTotal = $params['partial_payment_total']; - $partialAmtPay = $params['partial_amount_pay']; + $partialAmtPay = $params['partial_amount_to_pay']; $params['total_amount'] = $partialAmtTotal; if ($partialAmtPay < $partialAmtTotal) { - $params['contribution_status_id'] = CRM_Core_OptionGroup::getValue('contribution_status', 'Partially paid', 'name'); + $params['contribution_status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Partially paid'); $params['is_pay_later'] = 0; $setPrevContribution = FALSE; } @@ -186,8 +198,12 @@ public static function add(&$params, $ids = array()) { $params['prevContribution'] = self::getOriginalContribution($contributionID); } - // CRM-16189 - CRM_Financial_BAO_FinancialAccount::checkFinancialTypeHasDeferred($params, $contributionID); + if ($contributionID && !empty($params['revenue_recognition_date']) && !empty($params['prevContribution']) + && !($contributionStatus[$params['prevContribution']->contribution_status_id] == 'Pending') + && !self::allowUpdateRevenueRecognitionDate($contributionID) + ) { + unset($params['revenue_recognition_date']); + } if (!isset($params['tax_amount']) && $setPrevContribution && (isset($params['total_amount']) || isset($params['financial_type_id']))) { @@ -225,7 +241,8 @@ public static function add(&$params, $ids = array()) { if (self::isUpdateToRecurringContribution($params)) { CRM_Contribute_BAO_ContributionRecur::updateOnNewPayment( (!empty($params['contribution_recur_id']) ? $params['contribution_recur_id'] : $params['prevContribution']->contribution_recur_id), - $contributionStatus[$params['contribution_status_id']] + $contributionStatus[$params['contribution_status_id']], + CRM_Utils_Array::value('receive_date', $params) ); } @@ -277,7 +294,7 @@ public static function getDefaults() { 'payment_instrument_id' => key(CRM_Core_OptionGroup::values('payment_instrument', FALSE, FALSE, FALSE, 'AND is_default = 1') ), - 'contribution_status_id' => CRM_Core_OptionGroup::getValue('contribution_status', 'Completed', 'name'), + 'contribution_status_id' => CRM_Core_Pseudoconstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'), ); } @@ -309,7 +326,8 @@ public static function getValues($params, &$values, &$ids) { return $contribution; } - $null = NULL; // return by reference + // return by reference + $null = NULL; return $null; } @@ -371,7 +389,7 @@ public static function calculateMissingAmountParams(&$params, $contributionID) { // net_amount may need adjusting. $contribution = civicrm_api3('Contribution', 'getsingle', array( 'id' => $contributionID, - 'return' => array('total_amount', 'net_amount'), + 'return' => array('total_amount', 'net_amount', 'fee_amount'), )); $totalAmount = isset($params['total_amount']) ? $params['total_amount'] : CRM_Utils_Array::value('total_amount', $contribution); $feeAmount = isset($params['fee_amount']) ? $params['fee_amount'] : CRM_Utils_Array::value('fee_amount', $contribution); @@ -478,26 +496,14 @@ public static function create(&$params, $ids = array()) { } } - //if contribution is created with cancelled or refunded status, add credit note id - if (!empty($params['contribution_status_id'])) { - $contributionStatus = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name'); - - if (($params['contribution_status_id'] == array_search('Refunded', $contributionStatus) - || $params['contribution_status_id'] == array_search('Cancelled', $contributionStatus)) - ) { - if (empty($params['creditnote_id']) || $params['creditnote_id'] == "null") { - $params['creditnote_id'] = self::createCreditNoteId(); - } - } - } - $transaction = new CRM_Core_Transaction(); - $contribution = self::add($params, $ids); - - if (is_a($contribution, 'CRM_Core_Error')) { + try { + $contribution = self::add($params, $ids); + } + catch (CRM_Core_Exception $e) { $transaction->rollback(); - return $contribution; + throw $e; } $params['contribution_id'] = $contribution->id; @@ -549,25 +555,26 @@ public static function create(&$params, $ids = array()) { $transaction->commit(); - // check if activity record exist for this contribution, if - // not add activity - $activity = new CRM_Activity_DAO_Activity(); - $activity->source_record_id = $contribution->id; - $activity->activity_type_id = CRM_Core_OptionGroup::getValue('activity_type', - 'Contribution', - 'name' - ); + $activity = civicrm_api3('Activity', 'get', array( + 'source_record_id' => $contribution->id, + 'options' => array('limit' => 1), + 'sequential' => 1, + 'activity_type_id' => 'Contribution', + 'return' => array('id', 'campaign'), + )); //CRM-18406: Update activity when edit contribution. - if ($activity->find(TRUE)) { + if ($activity['count']) { // CRM-13237 : if activity record found, update it with campaign id of contribution - CRM_Core_DAO::setFieldValue('CRM_Activity_BAO_Activity', $activity->id, 'campaign_id', $contribution->campaign_id); - $contribution->activity_id = $activity->id; + // @todo compare campaign ids first. + CRM_Core_DAO::setFieldValue('CRM_Activity_BAO_Activity', $activity['id'], 'campaign_id', $contribution->campaign_id); + $contribution->activity_id = $activity['id']; } + if (empty($contribution->contact_id)) { $contribution->find(TRUE); } - CRM_Activity_BAO_Activity::addActivity($contribution, 'Offline'); + CRM_Activity_BAO_Activity::addActivity($contribution, 'Contribution'); // do not add to recent items for import, CRM-4399 if (empty($params['skipRecentView'])) { @@ -591,8 +598,8 @@ public static function create(&$params, $ids = array()) { if ($retrieveRequired == 1) { $contribution->find(TRUE); } - $contributionTypes = CRM_Contribute_PseudoConstant::financialType(); - $title = CRM_Contact_BAO_Contact::displayName($contribution->contact_id) . ' - (' . CRM_Utils_Money::format($contribution->total_amount, $contribution->currency) . ' ' . ' - ' . $contributionTypes[$contribution->financial_type_id] . ')'; + $financialType = CRM_Contribute_PseudoConstant::financialType($contribution->financial_type_id); + $title = CRM_Contact_BAO_Contact::displayName($contribution->contact_id) . ' - (' . CRM_Utils_Money::format($contribution->total_amount, $contribution->currency) . ' ' . ' - ' . $financialType . ')'; $recentOther = array(); if (CRM_Core_Permission::checkActionPermission('CiviContribute', CRM_Core_Action::UPDATE)) { @@ -781,19 +788,25 @@ public static function &exportableFields($checkPermission = TRUE) { self::$_exportableFields = array(); } - $impFields = CRM_Contribute_DAO_Contribution::export(); - $expFieldProduct = CRM_Contribute_DAO_Product::export(); - $expFieldsContrib = CRM_Contribute_DAO_ContributionProduct::export(); - $typeField = CRM_Financial_DAO_FinancialType::export(); + $fields = CRM_Contribute_DAO_Contribution::export(); + if (CRM_Contribute_BAO_Query::isSiteHasProducts()) { + $fields = array_merge( + $fields, + CRM_Contribute_DAO_Product::export(), + CRM_Contribute_DAO_ContributionProduct::export(), + // CRM-16713 - contribution search by Premiums on 'Find Contribution' form. + [ + 'contribution_product_id' => [ + 'title' => ts('Premium'), + 'name' => 'contribution_product_id', + 'where' => 'civicrm_product.id', + 'data_type' => CRM_Utils_Type::T_INT, + ], + ] + ); + } + $financialAccount = CRM_Financial_DAO_FinancialAccount::export(); - $optionField = CRM_Core_OptionValue::getFields($mode = 'contribute'); - $contributionStatus = array( - 'contribution_status' => array( - 'title' => ts('Contribution Status'), - 'name' => 'contribution_status', - 'data_type' => CRM_Utils_Type::T_STRING, - ), - ); $contributionPage = array( 'contribution_page' => array( @@ -812,15 +825,6 @@ public static function &exportableFields($checkPermission = TRUE) { ), ); - $contributionRecurId = array( - 'contribution_recur_id' => array( - 'title' => ts('Recurring Contributions ID'), - 'name' => 'contribution_recur_id', - 'where' => 'civicrm_contribution.contribution_recur_id', - 'data_type' => CRM_Utils_Type::T_INT, - ), - ); - $extraFields = array( 'contribution_batch' => array( 'title' => ts('Batch Name'), @@ -839,48 +843,38 @@ public static function &exportableFields($checkPermission = TRUE) { $softCreditFields = array( 'contribution_soft_credit_name' => array( 'name' => 'contribution_soft_credit_name', - 'title' => 'Soft Credit For', + 'title' => ts('Soft Credit For'), 'where' => 'civicrm_contact_d.display_name', 'data_type' => CRM_Utils_Type::T_STRING, ), 'contribution_soft_credit_amount' => array( 'name' => 'contribution_soft_credit_amount', - 'title' => 'Soft Credit Amount', + 'title' => ts('Soft Credit Amount'), 'where' => 'civicrm_contribution_soft.amount', 'data_type' => CRM_Utils_Type::T_MONEY, ), 'contribution_soft_credit_type' => array( 'name' => 'contribution_soft_credit_type', - 'title' => 'Soft Credit Type', + 'title' => ts('Soft Credit Type'), 'where' => 'contribution_softcredit_type.label', 'data_type' => CRM_Utils_Type::T_STRING, ), 'contribution_soft_credit_contribution_id' => array( 'name' => 'contribution_soft_credit_contribution_id', - 'title' => 'Soft Credit For Contribution ID', + 'title' => ts('Soft Credit For Contribution ID'), 'where' => 'civicrm_contribution_soft.contribution_id', 'data_type' => CRM_Utils_Type::T_INT, ), 'contribution_soft_credit_contact_id' => array( 'name' => 'contribution_soft_credit_contact_id', - 'title' => 'Soft Credit For Contact ID', + 'title' => ts('Soft Credit For Contact ID'), 'where' => 'civicrm_contact_d.id', 'data_type' => CRM_Utils_Type::T_INT, ), ); - // CRM-16713 - contribution search by Premiums on 'Find Contribution' form. - $premiums = array( - 'contribution_product_id' => array( - 'title' => ts('Premium'), - 'name' => 'contribution_product_id', - 'where' => 'civicrm_product.id', - 'data_type' => CRM_Utils_Type::T_INT, - ), - ); - - $fields = array_merge($impFields, $typeField, $contributionStatus, $contributionPage, $optionField, $expFieldProduct, - $expFieldsContrib, $contributionNote, $contributionRecurId, $extraFields, $softCreditFields, $financialAccount, $premiums, $campaignTitle, + $fields = array_merge($fields, $contributionPage, + $contributionNote, $extraFields, $softCreditFields, $financialAccount, $campaignTitle, CRM_Core_BAO_CustomField::getFieldsForImport('Contribution', FALSE, FALSE, FALSE, $checkPermission) ); @@ -890,6 +884,119 @@ public static function &exportableFields($checkPermission = TRUE) { return self::$_exportableFields; } + /** + * @param $contributionId + * @param $participantId + * @param array $financialTrxn + * + * @param $financialTrxn + */ + protected static function recordPaymentActivity($contributionId, $participantId, $financialTrxn) { + $activityType = ($financialTrxn->total_amount < 0) ? 'Refund' : 'Payment'; + if ($participantId) { + $inputParams['id'] = $participantId; + $values = []; + $ids = []; + $component = 'event'; + $entityObj = CRM_Event_BAO_Participant::getValues($inputParams, $values, $ids); + $entityObj = $entityObj[$participantId]; + } + else { + $entityObj = new CRM_Contribute_BAO_Contribution(); + $entityObj->id = $contributionId; + $entityObj->find(TRUE); + $component = 'contribution'; + } + + self::addActivityForPayment($entityObj, $financialTrxn, $activityType, $component, $contributionId); + } + + /** + * Get the value for the To Financial Account. + * + * @param $contribution + * @param $params + * + * @return int + */ + protected static function getToFinancialAccount($contribution, $params) { + $contributionStatuses = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name'); + CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name'); + $pendingStatus = [ + array_search('Pending', $contributionStatuses), + array_search('In Progress', $contributionStatuses), + ]; + if (in_array(CRM_Utils_Array::value('contribution_status_id', $contribution), $pendingStatus)) { + return CRM_Contribute_PseudoConstant::getRelationalFinancialAccount($contribution['financial_type_id'], 'Accounts Receivable Account is'); + } + elseif (!empty($params['payment_processor'])) { + return CRM_Contribute_PseudoConstant::getRelationalFinancialAccount($contribution['payment_processor'], NULL, 'civicrm_payment_processor'); + } + elseif (!empty($params['payment_instrument_id'])) { + return CRM_Financial_BAO_FinancialTypeAccount::getInstrumentFinancialAccount($contribution['payment_instrument_id']); + } + else { + $relationTypeId = key(CRM_Core_PseudoConstant::accountOptionValues('financial_account_type', NULL, " AND v.name LIKE 'Asset' ")); + $queryParams = [1 => [$relationTypeId, 'Integer']]; + return CRM_Core_DAO::singleValueQuery("SELECT id FROM civicrm_financial_account WHERE is_default = 1 AND financial_account_type_id = %1", $queryParams); + } + } + + /** + * Get memberships realted to the contribution. + * + * @param int $contributionID + * + * @return array + */ + protected static function getRelatedMemberships($contributionID) { + $membershipPayments = civicrm_api3('MembershipPayment', 'get', [ + 'return' => 'membership_id', + 'contribution_id' => (int) $contributionID, + ])['values']; + $membershipIDs = []; + foreach ($membershipPayments as $membershipPayment) { + $membershipIDs[] = $membershipPayment['membership_id']; + } + if (empty($membershipIDs)) { + return []; + } + // We could combine this with the MembershipPayment.get - we'd + // need to re-wrangle the params (here or in the calling function) + // as they would then me membership.contact_id, membership.is_test etc + return civicrm_api3('Membership', 'get', [ + 'id' => ['IN' => $membershipIDs], + 'return' => ['id', 'contact_id', 'membership_type_id', 'is_test'], + ])['values']; + } + + /** + * @inheritDoc + */ + public function addSelectWhereClause() { + $whereClauses = parent::addSelectWhereClause(); + if ($whereClauses !== []) { + // In this case permisssions have been applied & we assume the + // financialaclreport is applying these + // https://github.com/JMAConsulting/biz.jmaconsulting.financialaclreport/blob/master/financialaclreport.php#L107 + return $whereClauses; + } + + if (!CRM_Financial_BAO_FinancialType::isACLFinancialTypeStatus()) { + return $whereClauses; + } + $types = CRM_Financial_BAO_FinancialType::getAllEnabledAvailableFinancialTypes(); + if (empty($types)) { + $whereClauses['financial_type_id'] = 'IN (0)'; + } + else { + $whereClauses['financial_type_id'] = [ + 'IN (' . implode(',', array_keys($types)) . ')', + ]; + } + return $whereClauses; + } + /** * @param null $status * @param null $startDate @@ -915,13 +1022,18 @@ public static function getTotalAmountAndCount($status = NULL, $startDate = NULL, if ($endDate) { $where[] = "receive_date <= '" . CRM_Utils_Type::escape($endDate, 'Timestamp') . "'"; } - CRM_Financial_BAO_FinancialType::getAvailableFinancialTypes($financialTypes); - if ($financialTypes) { - $where[] = "c.financial_type_id IN (" . implode(',', array_keys($financialTypes)) . ")"; - $where[] = "i.financial_type_id IN (" . implode(',', array_keys($financialTypes)) . ")"; - } - else { - $where[] = "c.financial_type_id IN (0)"; + $financialTypeACLJoin = ''; + if (CRM_Financial_BAO_FinancialType::isACLFinancialTypeStatus()) { + $financialTypeACLJoin = " LEFT JOIN civicrm_line_item i ON (i.contribution_id = c.id AND i.entity_table = 'civicrm_contribution') "; + $financialTypes = CRM_Contribute_PseudoConstant::financialType(); + CRM_Financial_BAO_FinancialType::getAvailableFinancialTypes($financialTypes); + if ($financialTypes) { + $where[] = "c.financial_type_id IN (" . implode(',', array_keys($financialTypes)) . ")"; + $where[] = "i.financial_type_id IN (" . implode(',', array_keys($financialTypes)) . ")"; + } + else { + $where[] = "c.financial_type_id IN (0)"; + } } $whereCond = implode(' AND ', $where); @@ -932,7 +1044,7 @@ public static function getTotalAmountAndCount($status = NULL, $startDate = NULL, currency FROM civicrm_contribution c INNER JOIN civicrm_contact contact ON ( contact.id = c.contact_id ) -LEFT JOIN civicrm_line_item i ON ( i.contribution_id = c.id AND i.entity_table = 'civicrm_contribution' ) + $financialTypeACLJoin WHERE $whereCond AND ( is_test = 0 OR is_test IS NULL ) AND contact.is_deleted = 0 @@ -1046,7 +1158,13 @@ public static function failPayment($contributionID, $contactID, $message) { 'subject' => ts('Payment failed at payment processor'), 'source_record_id' => $contributionID, 'source_contact_id' => CRM_Core_Session::getLoggedInContactID() ? CRM_Core_Session::getLoggedInContactID() : - $contactID, + $contactID, + )); + + // CRM-20336 Make sure that the contribution status is Failed, not Pending. + civicrm_api3('contribution', 'create', array( + 'id' => $contributionID, + 'contribution_status_id' => 'Failed', )); } @@ -1133,6 +1251,8 @@ public static function addPremium(&$params) { */ public static function getContributionFields($addExtraFields = TRUE) { $contributionFields = CRM_Contribute_DAO_Contribution::export(); + // @todo remove this - this line was added because payment_instrument_id was not + // set to exportable - but now it is. $contributionFields = array_merge($contributionFields, CRM_Core_OptionValue::getFields($mode = 'contribute')); if ($addExtraFields) { @@ -1162,31 +1282,31 @@ public static function getSpecialContributionFields() { $extraFields = array( 'contribution_soft_credit_name' => array( 'name' => 'contribution_soft_credit_name', - 'title' => 'Soft Credit Name', + 'title' => ts('Soft Credit Name'), 'headerPattern' => '/^soft_credit_name$/i', 'where' => 'civicrm_contact_d.display_name', ), 'contribution_soft_credit_email' => array( 'name' => 'contribution_soft_credit_email', - 'title' => 'Soft Credit Email', + 'title' => ts('Soft Credit Email'), 'headerPattern' => '/^soft_credit_email$/i', 'where' => 'soft_email.email', ), 'contribution_soft_credit_phone' => array( 'name' => 'contribution_soft_credit_phone', - 'title' => 'Soft Credit Phone', + 'title' => ts('Soft Credit Phone'), 'headerPattern' => '/^soft_credit_phone$/i', 'where' => 'soft_phone.phone', ), 'contribution_soft_credit_contact_id' => array( 'name' => 'contribution_soft_credit_contact_id', - 'title' => 'Soft Credit Contact ID', + 'title' => ts('Soft Credit Contact ID'), 'headerPattern' => '/^soft_credit_contact_id$/i', 'where' => 'civicrm_contribution_soft.contact_id', ), 'contribution_pcp_title' => array( 'name' => 'contribution_pcp_title', - 'title' => 'Personal Campaign Page Title', + 'title' => ts('Personal Campaign Page Title'), 'headerPattern' => '/^contribution_pcp_title$/i', 'where' => 'contribution_pcp.title', ), @@ -1287,73 +1407,20 @@ public static function sortName($id) { } /** - * @param int $contactID + * Generate summary of amount received in the current fiscal year to date from the contact or contacts. + * + * @param int|array $contactIDs * * @return array */ - public static function annual($contactID) { - if (is_array($contactID)) { - $contactIDs = implode(',', $contactID); - } - else { - $contactIDs = $contactID; - } - - $config = CRM_Core_Config::singleton(); - $startDate = $endDate = NULL; - - $currentMonth = date('m'); - $currentDay = date('d'); - if ((int ) $config->fiscalYearStart['M'] > $currentMonth || - ((int ) $config->fiscalYearStart['M'] == $currentMonth && - (int ) $config->fiscalYearStart['d'] > $currentDay - ) - ) { - $year = date('Y') - 1; + public static function annual($contactIDs) { + if (!is_array($contactIDs)) { + // In practice I can't fine any evidence that this function is ever called with + // anything other than a single contact id, but left like this due to .... fear. + $contactIDs = explode(',', $contactIDs); } - else { - $year = date('Y'); - } - $nextYear = $year + 1; - if ($config->fiscalYearStart) { - $newFiscalYearStart = $config->fiscalYearStart; - if ($newFiscalYearStart['M'] < 10) { - $newFiscalYearStart['M'] = '0' . $newFiscalYearStart['M']; - } - if ($newFiscalYearStart['d'] < 10) { - $newFiscalYearStart['d'] = '0' . $newFiscalYearStart['d']; - } - $config->fiscalYearStart = $newFiscalYearStart; - $monthDay = $config->fiscalYearStart['M'] . $config->fiscalYearStart['d']; - } - else { - $monthDay = '0101'; - } - $startDate = "$year$monthDay"; - $endDate = "$nextYear$monthDay"; - CRM_Financial_BAO_FinancialType::getAvailableFinancialTypes($financialTypes); - $additionalWhere = " AND b.financial_type_id IN (0)"; - $liWhere = " AND i.financial_type_id IN (0)"; - if (!empty($financialTypes)) { - $additionalWhere = " AND b.financial_type_id IN (" . implode(',', array_keys($financialTypes)) . ") AND i.id IS NULL"; - $liWhere = " AND i.financial_type_id NOT IN (" . implode(',', array_keys($financialTypes)) . ")"; - } - $query = " - SELECT count(*) as count, - sum(total_amount) as amount, - avg(total_amount) as average, - currency - FROM civicrm_contribution b - LEFT JOIN civicrm_line_item i ON i.contribution_id = b.id AND i.entity_table = 'civicrm_contribution' $liWhere - WHERE b.contact_id IN ( $contactIDs ) - AND b.contribution_status_id = 1 - AND b.is_test = 0 - AND b.receive_date >= $startDate - AND b.receive_date < $endDate - $additionalWhere - GROUP BY currency - "; + $query = self::getAnnualQuery($contactIDs); $dao = CRM_Core_DAO::executeQuery($query); $count = 0; $amount = $average = array(); @@ -1411,7 +1478,7 @@ public static function checkDuplicateIds($params) { * * @param int $exportMode * Export mode. - * @param string $componentIds + * @param array $componentIds * Component ids. * * @return array @@ -1590,7 +1657,6 @@ public static function checkOnlinePendingContribution($componentId, $componentNa strpos($dao->source, $source) !== FALSE ) { $contributionId = $dao->contribution_id; - $dao->free(); } } @@ -1714,9 +1780,39 @@ public static function transitionComponents($params, $processContributionObject if ($contributionStatusId == array_search('Cancelled', $contributionStatuses)) { if (is_array($memberships)) { foreach ($memberships as $membership) { - if ($membership) { - $membership->status_id = array_search('Cancelled', $membershipStatuses); + $update = TRUE; + //Update Membership status if there is no other completed contribution associated with the membership. + $relatedContributions = CRM_Member_BAO_Membership::getMembershipContributionId($membership->id, TRUE); + foreach ($relatedContributions as $contriId) { + if ($contriId == $contributionId) { + continue; + } + $statusId = CRM_Core_DAO::getFieldValue('CRM_Contribute_BAO_Contribution', $contriId, 'contribution_status_id'); + if (CRM_Core_PseudoConstant::getName('CRM_Contribute_BAO_Contribution', 'contribution_status_id', $statusId) === 'Completed') { + $update = FALSE; + } + } + if ($membership && $update) { + $newStatus = array_search('Cancelled', $membershipStatuses); + + // Create activity + $allStatus = CRM_Member_BAO_Membership::buildOptions('status_id', 'get'); + $activityParam = array( + 'subject' => "Status changed from {$allStatus[$membership->status_id]} to {$allStatus[$newStatus]}", + 'source_contact_id' => CRM_Core_Session::singleton()->get('userID'), + 'target_contact_id' => $membership->contact_id, + 'source_record_id' => $membership->id, + 'activity_type_id' => 'Change Membership Status', + 'status_id' => 'Completed', + 'priority_id' => 'Normal', + 'activity_date_time' => 'now', + ); + + $membership->status_id = $newStatus; + $membership->is_override = TRUE; + $membership->status_override_end_date = 'null'; $membership->save(); + civicrm_api3('activity', 'create', $activityParam); $updateResult['updatedComponents']['CiviMember'] = $membership->status_id; if ($processContributionObject) { @@ -1748,8 +1844,22 @@ public static function transitionComponents($params, $processContributionObject elseif ($contributionStatusId == array_search('Failed', $contributionStatuses)) { if (is_array($memberships)) { foreach ($memberships as $membership) { - if ($membership) { + $update = TRUE; + //Update Membership status if there is no other completed contribution associated with the membership. + $relatedContributions = CRM_Member_BAO_Membership::getMembershipContributionId($membership->id, TRUE); + foreach ($relatedContributions as $contriId) { + if ($contriId == $contributionId) { + continue; + } + $statusId = CRM_Core_DAO::getFieldValue('CRM_Contribute_BAO_Contribution', $contriId, 'contribution_status_id'); + if (CRM_Core_PseudoConstant::getName('CRM_Contribute_BAO_Contribution', 'contribution_status_id', $statusId) === 'Completed') { + $update = FALSE; + } + } + if ($membership && $update) { $membership->status_id = array_search('Expired', $membershipStatuses); + $membership->is_override = TRUE; + $membership->status_override_end_date = 'null'; $membership->save(); $updateResult['updatedComponents']['CiviMember'] = $membership->status_id; @@ -1782,13 +1892,13 @@ public static function transitionComponents($params, $processContributionObject // only pending contribution related object processed. if ($previousContriStatusId && - ($previousContriStatusId != array_search('Pending', $contributionStatuses)) + !in_array($contributionStatuses[$previousContriStatusId], array('Pending', 'Partially paid')) ) { // this is case when we already processed contribution object. return $updateResult; } elseif (!$previousContriStatusId && - $contribution->contribution_status_id != array_search('Pending', $contributionStatuses) + !in_array($contributionStatuses[$contribution->contribution_status_id], array('Pending', 'Partially paid')) ) { // this is case when we are going to process contribution object later. return $updateResult; @@ -1822,11 +1932,9 @@ public static function transitionComponents($params, $processContributionObject } } // else fall back to using current membership type - $dao->free(); - // Figure out number of terms $numterms = 1; - $lineitems = CRM_Price_BAO_LineItem::getLineItems($contributionId, 'contribution'); + $lineitems = CRM_Price_BAO_LineItem::getLineItemsByContributionID($contributionId); foreach ($lineitems as $lineitem) { if ($membership->membership_type_id == CRM_Utils_Array::value('membership_type_id', $lineitem)) { $numterms = CRM_Utils_Array::value('membership_num_terms', $lineitem); @@ -1839,6 +1947,7 @@ public static function transitionComponents($params, $processContributionObject // CRM-15735-to update the membership status as per the contribution receive date $joinDate = NULL; + $oldStatus = $membership->status_id; if (!empty($params['receive_date'])) { $joinDate = $params['receive_date']; $status = CRM_Member_BAO_MembershipStatus::getMembershipStatusByDate($membership->start_date, @@ -1898,14 +2007,47 @@ public static function transitionComponents($params, $processContributionObject $membershipLog['modified_date'] = date('Ymd'); $membershipLog['membership_type_id'] = $membership->membership_type_id; - CRM_Member_BAO_MembershipLog::add($membershipLog, CRM_Core_DAO::$_nullArray); + CRM_Member_BAO_MembershipLog::add($membershipLog); //update related Memberships. CRM_Member_BAO_Membership::updateRelatedMemberships($membership->id, $formattedParams); - $updateResult['membership_end_date'] = CRM_Utils_Date::customFormat($dates['end_date'], - '%B %E%f, %Y' - ); + foreach (array('Membership Signup', 'Membership Renewal') as $activityType) { + $scheduledActivityID = CRM_Utils_Array::value('id', + civicrm_api3('Activity', 'Get', + array( + 'source_record_id' => $membership->id, + 'activity_type_id' => $activityType, + 'status_id' => 'Scheduled', + 'options' => array( + 'limit' => 1, + 'sort' => 'id DESC', + ), + ) + ) + ); + // 1. Update Schedule Membership Signup/Renewal activity to completed on successful payment of pending membership + // 2. OR Create renewal activity scheduled if its membership renewal will be paid later + if ($scheduledActivityID) { + CRM_Activity_BAO_Activity::addActivity($membership, $activityType, $membership->contact_id, array('id' => $scheduledActivityID)); + break; + } + } + + // track membership status change if any + if (!empty($oldStatus) && $membership->status_id != $oldStatus) { + $allStatus = CRM_Member_BAO_Membership::buildOptions('status_id', 'get'); + CRM_Activity_BAO_Activity::addActivity($membership, + 'Change Membership Status', + NULL, + array( + 'subject' => "Status changed from {$allStatus[$oldStatus]} to {$allStatus[$membership->status_id]}", + 'source_contact_id' => $membershipLog['modified_id'], + 'priority_id' => 'Normal', + ) + ); + } + $updateResult['updatedComponents']['CiviMember'] = $membership->status_id; if ($processContributionObject) { $processContribution = TRUE; @@ -2042,7 +2184,7 @@ public static function contributionCount($contactId, $includeSoftCredit = TRUE) if (!$contactId) { return 0; } - CRM_Financial_BAO_FinancialType::getAvailableFinancialTypes($financialTypes); + $financialTypes = CRM_Financial_BAO_FinancialType::getAllAvailableFinancialTypes(); $additionalWhere = " AND contribution.financial_type_id IN (0)"; $liWhere = " AND i.financial_type_id IN (0)"; if (!empty($financialTypes)) { @@ -2089,7 +2231,7 @@ public static function contributionCount($contactId, $includeSoftCredit = TRUE) * @param array $contributionParams * @param int $paymentProcessorID * - * @return array + * @return bool * @throws CiviCRM_API3_Exception */ protected static function repeatTransaction(&$contribution, &$input, $contributionParams, $paymentProcessorID) { @@ -2135,7 +2277,8 @@ protected static function repeatTransaction(&$contribution, &$input, $contributi //CRM-18805 -- Contribution page not recorded on recurring transactions, Recurring contribution payments //do not create CC or BCC emails or profile notifications. //The if is just to be safe. Not sure if we can ever arrive with this unset - if (isset($contribution->contribution_page_id)) { + // but per CRM-19478 it seems it can be 'null' + if (isset($contribution->contribution_page_id) && is_numeric($contribution->contribution_page_id)) { $contributionParams['contribution_page_id'] = $contribution->contribution_page_id; } @@ -2185,7 +2328,7 @@ public static function getOnbehalfIds($contributionId, $contributorId = NULL) { AND civicrm_activity_contact.record_type_id = %3 "; - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); + $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts); $params = array( @@ -2273,6 +2416,14 @@ public static function getContributionDates() { * @throws Exception */ public function loadRelatedObjects(&$input, &$ids, $loadAll = FALSE) { + // @todo deprecate this function - the steps should be + // 1) add additional functions like 'getRelatedMemberships' + // 2) switch all calls that refer to ->_relatedObjects to + // using the helper functions + // 3) make ->_relatedObjects noisy in some way (deprecation won't work for properties - hmm + // 4) make ->_relatedObjects protected + // 5) hone up the individual functions to not use rely on this having been called + // 6) deprecate like mad if ($loadAll) { $ids = array_merge($this->getComponentDetails($this->id), $ids); if (empty($ids['contact']) && isset($this->contact_id)) { @@ -2299,7 +2450,7 @@ public function loadRelatedObjects(&$input, &$ids, $loadAll = FALSE) { $ids )); - if (!$paymentProcessorID && $this->contribution_page_id) { + if (!isset($input['payment_processor_id']) && !$paymentProcessorID && $this->contribution_page_id) { $paymentProcessorID = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_ContributionPage', $this->contribution_page_id, 'payment_processor' @@ -2311,23 +2462,12 @@ public function loadRelatedObjects(&$input, &$ids, $loadAll = FALSE) { $ids['contributionType'] = $this->financial_type_id; $ids['financialType'] = $this->financial_type_id; - - $entities = array( - 'contact' => 'CRM_Contact_BAO_Contact', - 'contributionRecur' => 'CRM_Contribute_BAO_ContributionRecur', - 'contributionType' => 'CRM_Financial_BAO_FinancialType', - 'financialType' => 'CRM_Financial_BAO_FinancialType', - ); - foreach ($entities as $entity => $bao) { - if (!empty($ids[$entity])) { - $this->_relatedObjects[$entity] = new $bao(); - $this->_relatedObjects[$entity]->id = $ids[$entity]; - if (!$this->_relatedObjects[$entity]->find(TRUE)) { - throw new CRM_Core_Exception($entity . ' could not be loaded'); - } - } + if ($this->contribution_page_id) { + $ids['contributionPage'] = $this->contribution_page_id; } + $this->loadRelatedEntitiesByID($ids); + if (!empty($ids['contributionRecur']) && !$paymentProcessorID) { $paymentProcessorID = $this->_relatedObjects['contributionRecur']->payment_processor_id; } @@ -2346,7 +2486,9 @@ public function loadRelatedObjects(&$input, &$ids, $loadAll = FALSE) { } } - $this->loadRelatedMembershipObjects($ids); + // These are probably no longer accessed from anywhere + // @todo remove this line, after ensuring not used. + $ids = $this->loadRelatedMembershipObjects($ids); if ($this->_component != 'contribute') { // we are in event mode @@ -2389,6 +2531,9 @@ public function loadRelatedObjects(&$input, &$ids, $loadAll = FALSE) { $ids['paymentProcessor'] = $paymentProcessorID; $this->_relatedObjects['paymentProcessor'] = $paymentProcessor; } + + // Add contribution id to $ids. CRM-20401 + $ids['contribution'] = $this->id; return TRUE; } @@ -2405,7 +2550,6 @@ public function loadRelatedObjects(&$input, &$ids, $loadAll = FALSE) { * @param array $values * Any values that may have already been compiled by calling process. * This is augmented by values 'gathered' by gatherMessageValues - * @param bool $recur * @param bool $returnMessageText * Distinguishes between whether to send message or return. * message text. We are working towards this function ALWAYS returning message text & calling @@ -2415,7 +2559,7 @@ public function loadRelatedObjects(&$input, &$ids, $loadAll = FALSE) { * messages * @throws Exception */ - public function composeMessageArray(&$input, &$ids, &$values, $recur = FALSE, $returnMessageText = TRUE) { + public function composeMessageArray(&$input, &$ids, &$values, $returnMessageText = TRUE) { $this->loadRelatedObjects($input, $ids); if (empty($this->_component)) { @@ -2424,15 +2568,15 @@ public function composeMessageArray(&$input, &$ids, &$values, $recur = FALSE, $r //not really sure what params might be passed in but lets merge em into values $values = array_merge($this->_gatherMessageValues($input, $values, $ids), $values); + $values['is_email_receipt'] = $this->isEmailReceipt($input, $values); if (!empty($input['receipt_date'])) { $values['receipt_date'] = $input['receipt_date']; } - $template = CRM_Core_Smarty::singleton(); - $this->_assignMessageVariablesToTemplate($values, $input, $template, $recur, $returnMessageText); + $template = $this->_assignMessageVariablesToTemplate($values, $input, $returnMessageText); //what does recur 'mean here - to do with payment processor return functionality but // what is the importance - if ($recur && !empty($this->_relatedObjects['paymentProcessor'])) { + if (!empty($this->contribution_recur_id) && !empty($this->_relatedObjects['paymentProcessor'])) { $paymentObject = Civi\Payment\System::singleton()->getByProcessor($this->_relatedObjects['paymentProcessor']); $entityID = $entity = NULL; @@ -2489,7 +2633,7 @@ public function composeMessageArray(&$input, &$ids, &$values, $recur = FALSE, $r if (!empty($input['amount'])) { $values['totalAmount'] = $input['amount']; } - + // @todo set this in is_email_receipt, based on $this->_relatedObjects. if ($values['event']['is_email_confirm']) { $values['is_email_receipt'] = 1; } @@ -2525,6 +2669,7 @@ public function composeMessageArray(&$input, &$ids, &$values, $recur = FALSE, $r if (!empty($this->_relatedObjects['membership'])) { foreach ($this->_relatedObjects['membership'] as $membership) { if ($membership->id) { + $values['membership_id'] = $membership->id; $values['isMembership'] = TRUE; $values['membership_assign'] = TRUE; @@ -2540,13 +2685,13 @@ public function composeMessageArray(&$input, &$ids, &$values, $recur = FALSE, $r if ($membership_status == 'Pending' && $membership->is_pay_later == 1) { $values['is_pay_later'] = 1; } + // Pass amount to floatval as string '0.00' is considered a + // valid amount and includes Fee section in the mail. + if (isset($values['amount'])) { + $values['amount'] = floatval($values['amount']); + } - // if separate payment there are two contributions recorded and the - // admin will need to send a receipt for each of them separately. - // we dont link the two in the db (but can potentially infer it if needed) - $template->assign('is_separate_payment', 0); - - if ($recur && $paymentObject) { + if (!empty($this->contribution_recur_id) && $paymentObject) { $url = $paymentObject->subscriptionURL($membership->id, 'membership', 'cancel'); $template->assign('cancelSubscriptionUrl', $url); $url = $paymentObject->subscriptionURL($membership->id, 'membership', 'billing'); @@ -2607,8 +2752,14 @@ public function _gatherMessageValues($input, &$values, $ids = array()) { $values['softContributions'] = $softContributions['soft_credit']; } if (isset($this->contribution_page_id)) { + // This is a call we want to use less, in favour of loading related objects. $values = $this->addContributionPageValuesToValuesHeavyHandedly($values); if ($this->contribution_page_id) { + // This is precautionary as there are some legacy flows, but it should really be + // loaded by now. + if (!isset($this->_relatedObjects['contributionPage'])) { + $this->loadRelatedEntitiesByID(array('contributionPage' => $this->contribution_page_id)); + } // CRM-8254 - override default currency if applicable $config = CRM_Core_Config::singleton(); $config->defaultCurrency = CRM_Utils_Array::value( @@ -2621,15 +2772,19 @@ public function _gatherMessageValues($input, &$values, $ids = array()) { // no contribution page -probably back office else { // Handle re-print receipt for offline contributions (call from PDF.php - no contribution_page_id) - $values['is_email_receipt'] = 1; $values['title'] = 'Contribution'; } // set lineItem for contribution if ($this->id) { - $lineItem = CRM_Price_BAO_LineItem::getLineItems($this->id, 'contribution', 1); - if (!empty($lineItem)) { - $itemId = key($lineItem); - foreach ($lineItem as &$eachItem) { + $lineItems = CRM_Price_BAO_LineItem::getLineItemsByContributionID($this->id); + if (!empty($lineItems)) { + $firstLineItem = reset($lineItems); + $priceSet = array(); + if (CRM_Utils_Array::value('price_set_id', $firstLineItem)) { + $priceSet = civicrm_api3('PriceSet', 'getsingle', array('id' => $firstLineItem['price_set_id'], 'return' => 'is_quick_config, id')); + $values['priceSetID'] = $priceSet['id']; + } + foreach ($lineItems as &$eachItem) { if (isset($this->_relatedObjects['membership']) && is_array($this->_relatedObjects['membership']) && array_key_exists($eachItem['membership_type_id'], $this->_relatedObjects['membership'])) { @@ -2637,9 +2792,11 @@ public function _gatherMessageValues($input, &$values, $ids = array()) { $eachItem['start_date'] = CRM_Utils_Date::customFormat($this->_relatedObjects['membership'][$eachItem['membership_type_id']]->start_date); $eachItem['end_date'] = CRM_Utils_Date::customFormat($this->_relatedObjects['membership'][$eachItem['membership_type_id']]->end_date); } + // This is actually used in conjunction with is_quick_config in the template & we should deprecate it. + // However, that does create upgrade pain so would be better to be phased in. + $values['useForMember'] = empty($priceSet['is_quick_config']); } - $values['lineItem'][0] = $lineItem; - $values['priceSetID'] = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceField', $lineItem[$itemId]['price_field_id'], 'price_set_id'); + $values['lineItem'][0] = $lineItems; } } @@ -2661,7 +2818,7 @@ public function _gatherMessageValues($input, &$values, $ids = array()) { CRM_Event_BAO_Event::retrieve($eventParams, $values['event']); // add custom fields for event - $eventGroupTree = CRM_Core_BAO_CustomGroup::getTree('Event', $this->_relatedObjects['event'], $this->_relatedObjects['event']->id); + $eventGroupTree = CRM_Core_BAO_CustomGroup::getTree('Event', NULL, $this->_relatedObjects['event']->id); $eventCustomGroup = array(); foreach ($eventGroupTree as $key => $group) { @@ -2689,7 +2846,7 @@ public function _gatherMessageValues($input, &$values, $ids = array()) { CRM_Event_BAO_Participant::getValues($participantParams, $values['participant'], $participantIds); // add custom fields for event - $participantGroupTree = CRM_Core_BAO_CustomGroup::getTree('Participant', $this->_relatedObjects['participant'], $this->_relatedObjects['participant']->id); + $participantGroupTree = CRM_Core_BAO_CustomGroup::getTree('Participant', NULL, $this->_relatedObjects['participant']->id); $participantCustomGroup = array(); foreach ($participantGroupTree as $key => $group) { if ($key === 'info') { @@ -2741,7 +2898,7 @@ public function _gatherMessageValues($input, &$values, $ids = array()) { } } - $groupTree = CRM_Core_BAO_CustomGroup::getTree('Contribution', $this, $this->id); + $groupTree = CRM_Core_BAO_CustomGroup::getTree('Contribution', NULL, $this->id); $customGroup = array(); foreach ($groupTree as $key => $group) { @@ -2760,6 +2917,8 @@ public function _gatherMessageValues($input, &$values, $ids = array()) { } $values['customGroup'] = $customGroup; + $values['is_pay_later'] = $this->is_pay_later; + return $values; } @@ -2778,22 +2937,28 @@ public function _gatherMessageValues($input, &$values, $ids = array()) { * * @param $values * @param $input - * @param CRM_Core_SMARTY $template - * @param bool $recur * @param bool $returnMessageText * * @return mixed */ - public function _assignMessageVariablesToTemplate(&$values, $input, &$template, $recur = FALSE, $returnMessageText = TRUE) { + public function _assignMessageVariablesToTemplate(&$values, $input, $returnMessageText = TRUE) { + // @todo - this should have a better separation of concerns - ie. + // gatherMessageValues should build an array of values to be assigned to the template + // and this function should assign them (assigning null if not set). + // the way the pcpParams & honor Params section works is a baby-step towards this. + $template = CRM_Core_Smarty::singleton(); $template->assign('first_name', $this->_relatedObjects['contact']->first_name); $template->assign('last_name', $this->_relatedObjects['contact']->last_name); $template->assign('displayName', $this->_relatedObjects['contact']->display_name); - if (!empty($values['lineItem']) && !empty($this->_relatedObjects['membership'])) { - $values['useForMember'] = TRUE; - } + + // For some unit tests contribution cannot contain paymentProcessor information + $billingMode = empty($this->_relatedObjects['paymentProcessor']) ? CRM_Core_Payment::BILLING_MODE_NOTIFY : $this->_relatedObjects['paymentProcessor']['billing_mode']; + $template->assign('contributeMode', CRM_Utils_Array::value($billingMode, CRM_Core_SelectValues::contributeMode())); + //assign honor information to receipt message $softRecord = CRM_Contribute_BAO_ContributionSoft::getSoftContribution($this->id); + $honorParams = ['soft_credit_type' => NULL, 'honor_block_is_active' => NULL]; if (isset($softRecord['soft_credit'])) { //if id of contribution page is present if (!empty($values['id'])) { @@ -2802,10 +2967,9 @@ public function _assignMessageVariablesToTemplate(&$values, $input, &$template, 'honor_profile_id' => CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFJoin', $values['id'], 'uf_group_id', 'entity_id'), 'honor_id' => $softRecord['soft_credit'][1]['contact_id'], ); - $softCreditTypes = CRM_Core_OptionGroup::values('soft_credit_type'); - $template->assign('soft_credit_type', $softRecord['soft_credit'][1]['soft_credit_type_label']); - $template->assign('honor_block_is_active', CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFJoin', $values['id'], 'is_active', 'entity_id')); + $honorParams['soft_credit_type'] = $softRecord['soft_credit'][1]['soft_credit_type_label']; + $honorParams['honor_block_is_active'] = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFJoin', $values['id'], 'is_active', 'entity_id'); } else { //offline contribution @@ -2842,25 +3006,29 @@ public function _assignMessageVariablesToTemplate(&$values, $input, &$template, $values['amount'] = $this->total_amount; } - // add the new contribution values + $pcpParams = ['pcpBlock' => NULL, 'pcp_display_in_roll' => NULL, 'pcp_roll_nickname' => NULL, 'pcp_personal_note' => NULL, 'title' => NULL]; + if (strtolower($this->_component) == 'contribute') { //PCP Info $softDAO = new CRM_Contribute_DAO_ContributionSoft(); $softDAO->contribution_id = $this->id; if ($softDAO->find(TRUE)) { - $template->assign('pcpBlock', TRUE); - $template->assign('pcp_display_in_roll', $softDAO->pcp_display_in_roll); - $template->assign('pcp_roll_nickname', $softDAO->pcp_roll_nickname); - $template->assign('pcp_personal_note', $softDAO->pcp_personal_note); + $pcpParams['pcpBlock'] = TRUE; + $pcpParams['pcp_display_in_roll'] = $softDAO->pcp_display_in_roll; + $pcpParams['pcp_roll_nickname'] = $softDAO->pcp_roll_nickname; + $pcpParams['pcp_personal_note'] = $softDAO->pcp_personal_note; //assign the pcp page title for email subject $pcpDAO = new CRM_PCP_DAO_PCP(); $pcpDAO->id = $softDAO->pcp_id; if ($pcpDAO->find(TRUE)) { - $template->assign('title', $pcpDAO->title); + $pcpParams['title'] = $pcpDAO->title; } } } + foreach (array_merge($honorParams, $pcpParams) as $templateKey => $templateValue) { + $template->assign($templateKey, $templateValue); + } if ($this->financial_type_id) { $values['financial_type_id'] = $this->financial_type_id; @@ -2871,7 +3039,6 @@ public function _assignMessageVariablesToTemplate(&$values, $input, &$template, CRM_Utils_Date::processDate($this->receive_date) ); $values['receipt_date'] = (empty($this->receipt_date) ? NULL : $this->receipt_date); - $template->assign('contributeMode', 'notify'); $template->assign('action', $this->is_test ? 1024 : 1); $template->assign('receipt_text', CRM_Utils_Array::value('receipt_text', @@ -2879,7 +3046,7 @@ public function _assignMessageVariablesToTemplate(&$values, $input, &$template, ) ); $template->assign('is_monetary', 1); - $template->assign('is_recur', (bool) $recur); + $template->assign('is_recur', !empty($this->contribution_recur_id)); $template->assign('currency', $this->currency); $template->assign('address', CRM_Utils_Address::format($input)); if (!empty($values['customGroup'])) { @@ -2944,7 +3111,6 @@ public function _assignMessageVariablesToTemplate(&$values, $input, &$template, 'amount' => $additional->fee_amount, ); $additional->save(); - $additional->free(); $template->assign('amount', $amount); CRM_Event_BAO_Event::sendMail($cId, $values, $pId, $isTest, $returnMessageText); } @@ -3039,7 +3205,7 @@ public static function isSubscriptionCancelled($contributionId) { * * @param array $financialTrxnValues * - * @return null|object + * @return null|\CRM_Core_BAO_FinancialTrxn */ public static function recordFinancialAccounts(&$params, $financialTrxnValues = NULL) { $skipRecords = $update = $return = $isRelatedId = FALSE; @@ -3083,21 +3249,19 @@ public static function recordFinancialAccounts(&$params, $financialTrxnValues = $statusId = $params['contribution']->contribution_status_id; // CRM-13964 partial payment if ($contributionStatus == 'Partially paid' - && !empty($params['partial_payment_total']) && !empty($params['partial_amount_pay']) + && !empty($params['partial_payment_total']) && !empty($params['partial_amount_to_pay']) ) { - $partialAmtPay = $params['partial_amount_pay']; - $partialAmtTotal = $params['partial_payment_total']; + $partialAmtPay = CRM_Utils_Rule::cleanMoney($params['partial_amount_to_pay']); + $partialAmtTotal = CRM_Utils_Rule::cleanMoney($params['partial_payment_total']); - $relationTypeId = key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Accounts Receivable Account is' ")); - $fromFinancialAccountId = CRM_Contribute_PseudoConstant::financialAccountType($params['financial_type_id'], $relationTypeId); - $statusId = CRM_Core_OptionGroup::getValue('contribution_status', 'Completed', 'name'); + $fromFinancialAccountId = CRM_Financial_BAO_FinancialAccount::getFinancialAccountForFinancialTypeByRelationship($params['financial_type_id'], 'Accounts Receivable Account is'); + $statusId = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'); $params['total_amount'] = $partialAmtPay; $balanceTrxnInfo = CRM_Core_BAO_FinancialTrxn::getBalanceTrxnAmt($params['contribution']->id, $params['financial_type_id']); if (empty($balanceTrxnInfo['trxn_id'])) { // create new balance transaction record - $relationTypeId = key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Accounts Receivable Account is' ")); - $toFinancialAccount = CRM_Contribute_PseudoConstant::financialAccountType($params['financial_type_id'], $relationTypeId); + $toFinancialAccount = CRM_Financial_BAO_FinancialAccount::getFinancialAccountForFinancialTypeByRelationship($params['financial_type_id'], 'Accounts Receivable Account is'); $balanceTrxnParams['total_amount'] = $partialAmtTotal; $balanceTrxnParams['to_financial_account_id'] = $toFinancialAccount; @@ -3110,6 +3274,8 @@ public static function recordFinancialAccounts(&$params, $financialTrxnValues = $balanceTrxnParams['status_id'] = $statusId; $balanceTrxnParams['payment_instrument_id'] = $params['contribution']->payment_instrument_id; $balanceTrxnParams['check_number'] = CRM_Utils_Array::value('check_number', $params); + $balanceTrxnParams['pan_truncation'] = CRM_Utils_Array::value('pan_truncation', $params); + $balanceTrxnParams['card_type_id'] = CRM_Utils_Array::value('card_type_id', $params); if (!empty($balanceTrxnParams['from_financial_account_id']) && ($statusId == array_search('Completed', $contributionStatuses) || $statusId == array_search('Partially paid', $contributionStatuses)) ) { @@ -3142,7 +3308,7 @@ public static function recordFinancialAccounts(&$params, $financialTrxnValues = ); } elseif (!empty($params['payment_processor'])) { - $params['to_financial_account_id'] = CRM_Financial_BAO_FinancialTypeAccount::getFinancialAccount($params['payment_processor'], 'civicrm_payment_processor', 'financial_account_id'); + $params['to_financial_account_id'] = CRM_Contribute_PseudoConstant::getRelationalFinancialAccount($params['payment_processor'], NULL, 'civicrm_payment_processor'); $params['payment_instrument_id'] = civicrm_api3('PaymentProcessor', 'getvalue', array( 'id' => $params['payment_processor'], 'return' => 'payment_instrument_id', @@ -3161,7 +3327,6 @@ public static function recordFinancialAccounts(&$params, $financialTrxnValues = if (!isset($totalAmount) && !empty($params['prevContribution'])) { $totalAmount = $params['total_amount'] = $params['prevContribution']->total_amount; } - //build financial transaction params $trxnParams = array( 'contribution_id' => $params['contribution']->id, @@ -3175,8 +3340,10 @@ public static function recordFinancialAccounts(&$params, $financialTrxnValues = 'status_id' => $statusId, 'payment_instrument_id' => CRM_Utils_Array::value('payment_instrument_id', $params, $params['contribution']->payment_instrument_id), 'check_number' => CRM_Utils_Array::value('check_number', $params), + 'pan_truncation' => CRM_Utils_Array::value('pan_truncation', $params), + 'card_type_id' => CRM_Utils_Array::value('card_type_id', $params), ); - if ($contributionStatus == 'Refunded' || $contributionStatus == 'Chargeback') { + if ($contributionStatus == 'Refunded' || $contributionStatus == 'Chargeback' || $contributionStatus == 'Cancelled') { $trxnParams['trxn_date'] = !empty($params['contribution']->cancel_date) ? $params['contribution']->cancel_date : date('YmdHis'); if (isset($params['refund_trxn_id'])) { // CRM-17751 allow a separate trxn_id for the refund to be passed in via api & form. @@ -3199,6 +3366,9 @@ public static function recordFinancialAccounts(&$params, $financialTrxnValues = if (!empty($financialTrxnValues)) { $trxnParams = array_merge($trxnParams, $financialTrxnValues); } + if (empty($trxnParams['payment_processor_id'])) { + unset($trxnParams['payment_processor_id']); + } $params['trxnParams'] = $trxnParams; @@ -3232,14 +3402,13 @@ public static function recordFinancialAccounts(&$params, $financialTrxnValues = if (!empty($params['revenue_recognition_date']) || $params['prevContribution']->revenue_recognition_date) { $accountRelationship = 'Deferred Revenue Account is'; } - $relationTypeId = key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE '$accountRelationship' ")); - $oldFinancialAccount = CRM_Contribute_PseudoConstant::financialAccountType($params['prevContribution']->financial_type_id, $relationTypeId); - $newFinancialAccount = CRM_Contribute_PseudoConstant::financialAccountType($params['financial_type_id'], $relationTypeId); + $oldFinancialAccount = CRM_Financial_BAO_FinancialAccount::getFinancialAccountForFinancialTypeByRelationship($params['prevContribution']->financial_type_id, $accountRelationship); + $newFinancialAccount = CRM_Financial_BAO_FinancialAccount::getFinancialAccountForFinancialTypeByRelationship($params['financial_type_id'], $accountRelationship); if ($oldFinancialAccount != $newFinancialAccount) { $params['total_amount'] = 0; if (in_array($params['contribution']->contribution_status_id, $pendingStatus)) { - $params['trxnParams']['to_financial_account_id'] = CRM_Contribute_PseudoConstant::financialAccountType( - $params['prevContribution']->financial_type_id, $relationTypeId); + $params['trxnParams']['to_financial_account_id'] = CRM_Financial_BAO_FinancialAccount::getFinancialAccountForFinancialTypeByRelationship( + $params['prevContribution']->financial_type_id, $accountRelationship); } else { $lastFinancialTrxnId = CRM_Core_BAO_FinancialTrxn::getFinancialTrxnId($params['prevContribution']->id, 'DESC'); @@ -3277,48 +3446,14 @@ public static function recordFinancialAccounts(&$params, $financialTrxnValues = // change Payment Instrument for a Completed contribution // first handle special case when contribution is changed from Pending to Completed status when initial payment // instrument is null and now new payment instrument is added along with the payment + if (!$params['contribution']->payment_instrument_id) { + $params['contribution']->find(TRUE); + } $params['trxnParams']['payment_instrument_id'] = $params['contribution']->payment_instrument_id; $params['trxnParams']['check_number'] = CRM_Utils_Array::value('check_number', $params); - if (array_key_exists('payment_instrument_id', $params)) { - $params['trxnParams']['total_amount'] = -$trxnParams['total_amount']; - if (CRM_Utils_System::isNull($params['prevContribution']->payment_instrument_id) && - !CRM_Utils_System::isNull($params['contribution']->payment_instrument_id) - ) { - //check if status is changed from Pending to Completed - // do not update payment instrument changes for Pending to Completed - if (!($params['contribution']->contribution_status_id == array_search('Completed', $contributionStatuses) && - in_array($params['prevContribution']->contribution_status_id, $pendingStatus)) - ) { - // for all other statuses create new financial records - self::updateFinancialAccounts($params, 'changePaymentInstrument'); - $params['total_amount'] = $params['trxnParams']['total_amount'] = $trxnParams['total_amount']; - self::updateFinancialAccounts($params, 'changePaymentInstrument'); - $updated = TRUE; - } - } - elseif ((!CRM_Utils_System::isNull($params['contribution']->payment_instrument_id) || - !CRM_Utils_System::isNull($params['prevContribution']->payment_instrument_id)) && - $params['contribution']->payment_instrument_id != $params['prevContribution']->payment_instrument_id - ) { - // for any other payment instrument changes create new financial records - self::updateFinancialAccounts($params, 'changePaymentInstrument'); - $params['total_amount'] = $params['trxnParams']['total_amount'] = $trxnParams['total_amount']; - self::updateFinancialAccounts($params, 'changePaymentInstrument'); - $updated = TRUE; - } - elseif (!CRM_Utils_System::isNull($params['contribution']->check_number) && - $params['contribution']->check_number != $params['prevContribution']->check_number - ) { - // another special case when check number is changed, create new financial records - // create financial trxn with negative amount - $params['trxnParams']['check_number'] = $params['prevContribution']->check_number; - self::updateFinancialAccounts($params, 'changePaymentInstrument'); - // create financial trxn with positive amount - $params['trxnParams']['check_number'] = $params['contribution']->check_number; - $params['total_amount'] = $params['trxnParams']['total_amount'] = $trxnParams['total_amount']; - self::updateFinancialAccounts($params, 'changePaymentInstrument'); - $updated = TRUE; - } + + if (self::isPaymentInstrumentChange($params, $pendingStatus)) { + $updated = CRM_Core_BAO_FinancialTrxn::updateFinancialAccountsOnPaymentInstrumentChange($params); } //if Change contribution amount @@ -3348,14 +3483,23 @@ public static function recordFinancialAccounts(&$params, $financialTrxnValues = civicrm_api3('FinancialTrxn', 'create', array('id' => $refundIDs['financialTrxnId'], 'trxn_id' => $params['refund_trxn_id'])); } } + $cardType = CRM_Utils_Array::value('card_type_id', $params); + $panTruncation = CRM_Utils_Array::value('pan_truncation', $params); + CRM_Core_BAO_FinancialTrxn::updateCreditCardDetails($params['contribution']->id, $panTruncation, $cardType); } } if (!$update) { // records finanical trxn and entity financial trxn // also make it available as return value + self::recordAlwaysAccountsReceivable($trxnParams, $params); + $trxnParams['pan_truncation'] = CRM_Utils_Array::value('pan_truncation', $params); + $trxnParams['card_type_id'] = CRM_Utils_Array::value('card_type_id', $params); $return = $financialTxn = CRM_Core_BAO_FinancialTrxn::create($trxnParams); $params['entity_id'] = $financialTxn->id; + if (empty($params['partial_payment_total']) && empty($params['partial_amount_to_pay'])) { + self::$_trxnIDs[] = $financialTxn->id; + } } } // record line items and financial items @@ -3371,7 +3515,7 @@ public static function recordFinancialAccounts(&$params, $financialTrxnValues = 'entity_table' => 'civicrm_financial_trxn', 'entity_id' => $financialTxn->id, ); - CRM_Batch_BAO_Batch::addBatchEntity($entityParams); + CRM_Batch_BAO_EntityBatch::create($entityParams); } // when a fee is charged @@ -3387,7 +3531,7 @@ public static function recordFinancialAccounts(&$params, $financialTrxnValues = CRM_Event_BAO_Participant::createDiscountTrxn($eventID, $params, $feeLevel); } unset($params['line_item']); - + self::$_trxnIDs = NULL; return $return; } @@ -3397,44 +3541,46 @@ public static function recordFinancialAccounts(&$params, $financialTrxnValues = * @param array $params * Contribution object, line item array and params for trxn. * + * @todo stop passing $params by reference. It is unclear the purpose of doing this & + * adds unpredictability. + * * @param string $context * Update scenarios. * - * @param null $skipTrxn - * */ - public static function updateFinancialAccounts(&$params, $context = NULL, $skipTrxn = NULL) { - $itemAmount = $trxnID = NULL; - //get all the statuses - $contributionStatus = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name'); + public static function updateFinancialAccounts(&$params, $context = NULL) { + $trxnID = NULL; + $inputParams = $params; + $isARefund = FALSE; + $currentContributionStatus = CRM_Core_PseudoConstant::getLabel('CRM_Contribute_BAO_Contribution', 'contribution_status_id', $params['contribution']->contribution_status_id); $previousContributionStatus = CRM_Contribute_PseudoConstant::contributionStatus($params['prevContribution']->contribution_status_id, 'name'); + if (($previousContributionStatus == 'Pending' || $previousContributionStatus == 'In Progress') - && $params['contribution']->contribution_status_id == array_search('Completed', $contributionStatus) + && $currentContributionStatus == 'Completed' && $context == 'changePaymentInstrument' ) { return; } if ((($previousContributionStatus == 'Partially paid' - && $params['contribution']->contribution_status_id == array_search('Completed', $contributionStatus)) + && $currentContributionStatus == 'Completed') || ($previousContributionStatus == 'Pending' && $params['prevContribution']->is_pay_later == TRUE - && $params['contribution']->contribution_status_id == array_search('Partially paid', $contributionStatus))) + && $currentContributionStatus == 'Partially paid')) && $context == 'changedStatus' ) { return; } if ($context == 'changedAmount' || $context == 'changeFinancialType') { - $itemAmount = $params['trxnParams']['total_amount'] = $params['trxnParams']['net_amount'] = $params['total_amount'] - $params['prevContribution']->total_amount; + // @todo we should stop passing $params by reference - splitting this out would be a step towards that. + $params['trxnParams']['total_amount'] = $params['trxnParams']['net_amount'] = ($params['total_amount'] - $params['prevContribution']->total_amount); } if ($context == 'changedStatus') { - //get all the statuses - $contributionStatus = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name'); - $cancelledTaxAmount = 0; if ($previousContributionStatus == 'Completed' && (self::isContributionStatusNegative($params['contribution']->contribution_status_id)) ) { + $isARefund = TRUE; + // @todo we should stop passing $params by reference - splitting this out would be a step towards that. $params['trxnParams']['total_amount'] = -$params['total_amount']; - $cancelledTaxAmount = CRM_Utils_Array::value('tax_amount', $params, '0.00'); if (empty($params['contribution']->creditnote_id) || $params['contribution']->creditnote_id == "null") { $creditNoteId = self::createCreditNoteId(); CRM_Core_DAO::setFieldValue('CRM_Contribute_DAO_Contribution', $params['contribution']->id, 'creditnote_id', $creditNoteId); @@ -3444,10 +3590,10 @@ public static function updateFinancialAccounts(&$params, $context = NULL, $skipT && $params['prevContribution']->is_pay_later) || $previousContributionStatus == 'In Progress' ) { $financialTypeID = CRM_Utils_Array::value('financial_type_id', $params) ? $params['financial_type_id'] : $params['prevContribution']->financial_type_id; - $relationTypeId = key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Accounts Receivable Account is' ")); - $arAccountId = CRM_Contribute_PseudoConstant::financialAccountType($financialTypeID, $relationTypeId); + $arAccountId = CRM_Contribute_PseudoConstant::getRelationalFinancialAccount($financialTypeID, 'Accounts Receivable Account is'); - if ($params['contribution']->contribution_status_id == array_search('Cancelled', $contributionStatus)) { + if ($currentContributionStatus == 'Cancelled') { + // @todo we should stop passing $params by reference - splitting this out would be a step towards that. $params['trxnParams']['to_financial_account_id'] = $arAccountId; $params['trxnParams']['total_amount'] = -$params['total_amount']; if (is_null($params['contribution']->creditnote_id) || $params['contribution']->creditnote_id == "null") { @@ -3456,47 +3602,16 @@ public static function updateFinancialAccounts(&$params, $context = NULL, $skipT } } else { + // @todo we should stop passing $params by reference - splitting this out would be a step towards that. $params['trxnParams']['from_financial_account_id'] = $arAccountId; } } - $itemAmount = $params['trxnParams']['total_amount'] + $cancelledTaxAmount; - } - elseif ($context == 'changePaymentInstrument') { - $params['trxnParams']['net_amount'] = $params['trxnParams']['total_amount']; - $deferredFinancialAccount = CRM_Utils_Array::value('deferred_financial_account_id', $params); - if (empty($deferredFinancialAccount)) { - $relationTypeId = key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Deferred Revenue Account is' ")); - $deferredFinancialAccount = CRM_Contribute_PseudoConstant::financialAccountType($params['prevContribution']->financial_type_id, $relationTypeId); - } - $lastFinancialTrxnId = CRM_Core_BAO_FinancialTrxn::getFinancialTrxnId($params['prevContribution']->id, 'DESC', FALSE, NULL, $deferredFinancialAccount); - if ($params['trxnParams']['total_amount'] < 0) { - if (!empty($lastFinancialTrxnId['financialTrxnId'])) { - if ($params['total_amount'] > 0) { - $params['trxnParams']['to_financial_account_id'] = CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_FinancialTrxn', $lastFinancialTrxnId['financialTrxnId'], 'to_financial_account_id'); - $params['trxnParams']['payment_instrument_id'] = $params['prevContribution']->payment_instrument_id; - } - else { - $params['trxnParams']['to_financial_account_id'] = $params['to_financial_account_id']; - $params['trxnParams']['payment_instrument_id'] = $params['contribution']->payment_instrument_id; - } - } - } - else { - if ($params['total_amount'] < 0) { - $params['trxnParams']['payment_instrument_id'] = $params['prevContribution']->payment_instrument_id; - $params['trxnParams']['to_financial_account_id'] = CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_FinancialTrxn', $lastFinancialTrxnId['financialTrxnId'], 'to_financial_account_id'); - } - else { - $params['trxnParams']['to_financial_account_id'] = $params['to_financial_account_id']; - $params['trxnParams']['payment_instrument_id'] = $params['contribution']->payment_instrument_id; - } - } } if ($context == 'changedStatus') { if (($previousContributionStatus == 'Pending' || $previousContributionStatus == 'In Progress') - && ($params['contribution']->contribution_status_id == array_search('Completed', $contributionStatus)) + && ($currentContributionStatus == 'Completed') ) { if (empty($params['line_item'])) { //CRM-15296 @@ -3505,91 +3620,102 @@ public static function updateFinancialAccounts(&$params, $context = NULL, $skipT // & this can be removed return; } + // @todo we should stop passing $params by reference - splitting this out would be a step towards that. + // This is an update so original currency if none passed in. + $params['trxnParams']['currency'] = CRM_Utils_Array::value('currency', $params, $params['prevContribution']->currency); + + self::recordAlwaysAccountsReceivable($params['trxnParams'], $params); $trxn = CRM_Core_BAO_FinancialTrxn::create($params['trxnParams']); - $params['entity_id'] = $trxn->id; + // @todo we should stop passing $params by reference - splitting this out would be a step towards that. + $params['entity_id'] = self::$_trxnIDs[] = $trxn->id; $query = "UPDATE civicrm_financial_item SET status_id = %1 WHERE entity_id = %2 and entity_table = 'civicrm_line_item'"; $sql = "SELECT id, amount FROM civicrm_financial_item WHERE entity_id = %1 and entity_table = 'civicrm_line_item'"; $entityParams = array( 'entity_table' => 'civicrm_financial_item', - 'financial_trxn_id' => $trxn->id, ); foreach ($params['line_item'] as $fieldId => $fields) { - foreach ($fields as $fieldValueId => $fieldValues) { + foreach ($fields as $fieldValueId => $lineItemDetails) { $fparams = array( - 1 => array(CRM_Core_OptionGroup::getValue('financial_item_status', 'Paid', 'name'), 'Integer'), - 2 => array($fieldValues['id'], 'Integer'), + 1 => array(CRM_Core_PseudoConstant::getKey('CRM_Financial_BAO_FinancialItem', 'status_id', 'Paid'), 'Integer'), + 2 => array($lineItemDetails['id'], 'Integer'), ); CRM_Core_DAO::executeQuery($query, $fparams); $fparams = array( - 1 => array($fieldValues['id'], 'Integer'), + 1 => array($lineItemDetails['id'], 'Integer'), ); $financialItem = CRM_Core_DAO::executeQuery($sql, $fparams); while ($financialItem->fetch()) { $entityParams['entity_id'] = $financialItem->id; $entityParams['amount'] = $financialItem->amount; - CRM_Financial_BAO_FinancialItem::createEntityTrxn($entityParams); + foreach (self::$_trxnIDs as $tID) { + $entityParams['financial_trxn_id'] = $tID; + CRM_Financial_BAO_FinancialItem::createEntityTrxn($entityParams); + } } } } return; } } + $trxn = CRM_Core_BAO_FinancialTrxn::create($params['trxnParams']); + // @todo we should stop passing $params by reference - splitting this out would be a step towards that. $params['entity_id'] = $trxn->id; if ($context != 'changePaymentInstrument') { $itemParams['entity_table'] = 'civicrm_line_item'; $trxnIds['id'] = $params['entity_id']; + $previousLineItems = CRM_Price_BAO_LineItem::getLineItemsByContributionID($params['contribution']->id); foreach ($params['line_item'] as $fieldId => $fields) { - foreach ($fields as $fieldValueId => $fieldValues) { - $prevFinancialItem = CRM_Financial_BAO_FinancialItem::getPreviousFinancialItem($fieldValues['id']); + foreach ($fields as $fieldValueId => $lineItemDetails) { + $prevFinancialItem = CRM_Financial_BAO_FinancialItem::getPreviousFinancialItem($lineItemDetails['id']); $receiveDate = CRM_Utils_Date::isoToMysql($params['prevContribution']->receive_date); if ($params['contribution']->receive_date) { $receiveDate = CRM_Utils_Date::isoToMysql($params['contribution']->receive_date); } - $financialAccount = self::getFinancialAccountForStatusChangeTrxn($params, $prevFinancialItem); + $financialAccount = self::getFinancialAccountForStatusChangeTrxn($params, CRM_Utils_Array::value('financial_account_id', $prevFinancialItem)); $currency = $params['prevContribution']->currency; + // @todo we should stop passing $params by reference - splitting this out would be a step towards that. if ($params['contribution']->currency) { $currency = $params['contribution']->currency; } - $diff = 1; - if ($context == 'changeFinancialType' || self::isContributionStatusNegative($params['contribution']->contribution_status_id)) { - $diff = -1; - } - if (!empty($params['is_quick_config'])) { - $amount = $itemAmount; - if (!$amount) { - $amount = $params['total_amount']; - } - } - else { - $amount = $diff * $fieldValues['line_total']; - } - + $previousLineItemTotal = CRM_Utils_Array::value('line_total', CRM_Utils_Array::value($fieldValueId, $previousLineItems), 0); $itemParams = array( 'transaction_date' => $receiveDate, 'contact_id' => $params['prevContribution']->contact_id, 'currency' => $currency, - 'amount' => $amount, - 'description' => $prevFinancialItem->description, - 'status_id' => $prevFinancialItem->status_id, + 'amount' => self::getFinancialItemAmountFromParams($inputParams, $context, $lineItemDetails, $isARefund, $previousLineItemTotal), + 'description' => CRM_Utils_Array::value('description', $prevFinancialItem), + 'status_id' => $prevFinancialItem['status_id'], 'financial_account_id' => $financialAccount, 'entity_table' => 'civicrm_line_item', - 'entity_id' => $fieldValues['id'], + 'entity_id' => $lineItemDetails['id'], ); $financialItem = CRM_Financial_BAO_FinancialItem::create($itemParams, NULL, $trxnIds); - $params['line_item'][$fieldId][$fieldValueId]['deferred_line_total'] = $amount; + // @todo we should stop passing $params by reference - splitting this out would be a step towards that. + $params['line_item'][$fieldId][$fieldValueId]['deferred_line_total'] = $itemParams['amount']; $params['line_item'][$fieldId][$fieldValueId]['financial_item_id'] = $financialItem->id; - if ($fieldValues['tax_amount']) { + if (($lineItemDetails['tax_amount'] && $lineItemDetails['tax_amount'] !== 'null') || ($context == 'changeFinancialType')) { $invoiceSettings = Civi::settings()->get('contribution_invoice_settings'); $taxTerm = CRM_Utils_Array::value('tax_term', $invoiceSettings); - $itemParams['amount'] = $diff * $fieldValues['tax_amount']; + $taxAmount = (float) $lineItemDetails['tax_amount']; + if ($context == 'changeFinancialType' && $lineItemDetails['tax_amount'] === 'null') { + // reverse the Sale Tax amount if there is no tax rate associated with new Financial Type + $taxAmount = CRM_Utils_Array::value('tax_amount', CRM_Utils_Array::value($fieldValueId, $previousLineItems), 0); + } + elseif ($previousLineItemTotal != $lineItemDetails['line_total']) { + $taxAmount -= CRM_Utils_Array::value('tax_amount', CRM_Utils_Array::value($fieldValueId, $previousLineItems), 0); + } + $itemParams['amount'] = self::getMultiplier($params['contribution']->contribution_status_id, $context) * $taxAmount; $itemParams['description'] = $taxTerm; - if ($fieldValues['financial_type_id']) { - $itemParams['financial_account_id'] = self::getFinancialAccountId($fieldValues['financial_type_id']); + if ($lineItemDetails['financial_type_id']) { + $itemParams['financial_account_id'] = CRM_Contribute_PseudoConstant::getRelationalFinancialAccount( + $lineItemDetails['financial_type_id'], + 'Sales Tax Account is' + ); } CRM_Financial_BAO_FinancialItem::create($itemParams, NULL, $trxnIds); } @@ -3597,6 +3723,7 @@ public static function updateFinancialAccounts(&$params, $context = NULL, $skipT } } if ($context == 'changeFinancialType') { + // @todo we should stop passing $params by reference - splitting this out would be a step towards that. $params['skipLineItem'] = FALSE; foreach ($params['line_item'] as &$lineItems) { foreach ($lineItems as &$line) { @@ -3604,28 +3731,7 @@ public static function updateFinancialAccounts(&$params, $context = NULL, $skipT } } } - if ($context == 'changePaymentInstrument') { - foreach ($params['line_item'] as $lineitems) { - foreach ($lineitems as $fieldValueId => $fieldValues) { - $prevFinancialItem = CRM_Financial_BAO_FinancialItem::getPreviousFinancialItem($fieldValues['id']); - if (!CRM_Utils_Rule::currencyCode($trxn->currency)) { - $trxn->currency = CRM_Core_Config::singleton()->defaultCurrency; - } - // save to entity_financial_trxn table - $entityFinancialTrxnParams = array( - 'entity_table' => "civicrm_financial_item", - 'entity_id' => $prevFinancialItem->id, - 'financial_trxn_id' => $trxn->id, - 'amount' => $trxn->total_amount, - 'currency' => $trxn->currency, - ); - $entityTrxn = new CRM_Financial_DAO_EntityFinancialTrxn(); - $entityTrxn->copyValues($entityFinancialTrxnParams); - $entityTrxn->save(); - } - } - } CRM_Core_BAO_FinancialTrxn::createDeferredTrxn(CRM_Utils_Array::value('line_item', $params), $params['contribution'], TRUE, $context); } @@ -3715,6 +3821,9 @@ public static function deleteContactContribution($contactId) { public static function buildOptions($fieldName, $context = NULL, $props = array()) { $className = __CLASS__; $params = array(); + if (isset($props['orderColumn'])) { + $params['orderColumn'] = $props['orderColumn']; + } switch ($fieldName) { // This field is not part of this object but the api supports it case 'payment_processor': @@ -3753,8 +3862,7 @@ public static function buildOptions($fieldName, $context = NULL, $props = array( * @return array|bool */ public static function validateFinancialType($financialTypeId, $relationName = 'Expense Account is') { - $expenseTypeId = key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE '{$relationName}' ")); - $financialAccount = CRM_Contribute_PseudoConstant::financialAccountType($financialTypeId, $expenseTypeId); + $financialAccount = CRM_Contribute_PseudoConstant::getRelationalFinancialAccount($financialTypeId, $relationName); if (!$financialAccount) { return CRM_Contribute_PseudoConstant::financialType($financialTypeId); @@ -3762,7 +3870,6 @@ public static function validateFinancialType($financialTypeId, $relationName = ' return FALSE; } - /** * Function to record additional payment for partial and refund contributions. * @@ -3773,149 +3880,20 @@ public static function validateFinancialType($financialTypeId, $relationName = ' * @param string $paymentType * 'owed' for purpose of recording partial payments, 'refund' for purpose of recording refund payments. * @param int $participantId + * @param bool $updateStatus * * @return null|object */ public static function recordAdditionalPayment($contributionId, $trxnsData, $paymentType = 'owed', $participantId = NULL, $updateStatus = TRUE) { - $statusId = CRM_Core_OptionGroup::getValue('contribution_status', 'Completed', 'name'); - $getInfoOf['id'] = $contributionId; - $defaults = array(); - $contributionDAO = CRM_Contribute_BAO_Contribution::retrieve($getInfoOf, $defaults, CRM_Core_DAO::$_nullArray); - if (!$participantId) { - $participantId = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_ParticipantPayment', $contributionId, 'participant_id'); - } if ($paymentType == 'owed') { - // build params for recording financial trxn entry - $params['contribution'] = $contributionDAO; - $params = array_merge($defaults, $params); - $params['skipLineItem'] = TRUE; - $params['partial_payment_total'] = $contributionDAO->total_amount; - $params['partial_amount_pay'] = $trxnsData['total_amount']; - $trxnsData['trxn_date'] = !empty($trxnsData['trxn_date']) ? $trxnsData['trxn_date'] : date('YmdHis'); - $trxnsData['net_amount'] = !empty($trxnsData['net_amount']) ? $trxnsData['net_amount'] : $trxnsData['total_amount']; - - // record the entry - $financialTrxn = CRM_Contribute_BAO_Contribution::recordFinancialAccounts($params, $trxnsData); - $relationTypeId = key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Accounts Receivable Account is' ")); - $toFinancialAccount = CRM_Contribute_PseudoConstant::financialAccountType($contributionDAO->financial_type_id, $relationTypeId); - - $trxnId = CRM_Core_BAO_FinancialTrxn::getBalanceTrxnAmt($contributionId, $contributionDAO->financial_type_id); - if (!empty($trxnId)) { - $trxnId = $trxnId['trxn_id']; - } - elseif (!empty($contributionDAO->payment_instrument_id)) { - $trxnId = CRM_Financial_BAO_FinancialTypeAccount::getInstrumentFinancialAccount($contributionDAO->payment_instrument_id); - } - else { - $relationTypeId = key(CRM_Core_PseudoConstant::accountOptionValues('financial_account_type', NULL, " AND v.name LIKE 'Asset' ")); - $queryParams = array(1 => array($relationTypeId, 'Integer')); - $trxnId = CRM_Core_DAO::singleValueQuery("SELECT id FROM civicrm_financial_account WHERE is_default = 1 AND financial_account_type_id = %1", $queryParams); - } - - // update statuses - // criteria for updates contribution total_amount == financial_trxns of partial_payments - $sql = "SELECT SUM(ft.total_amount) as sum_of_payments, SUM(ft.net_amount) as net_amount_total -FROM civicrm_financial_trxn ft -LEFT JOIN civicrm_entity_financial_trxn eft - ON (ft.id = eft.financial_trxn_id) -WHERE eft.entity_table = 'civicrm_contribution' - AND eft.entity_id = {$contributionId} - AND ft.to_financial_account_id != {$toFinancialAccount} - AND ft.status_id = {$statusId} -"; - $query = CRM_Core_DAO::executeQuery($sql); - $query->fetch(); - $sumOfPayments = $query->sum_of_payments; - - // update statuses - if ($contributionDAO->total_amount == $sumOfPayments) { - // update contribution status and - // clean cancel info (if any) if prev. contribution was updated in case of 'Refunded' => 'Completed' - $contributionDAO->contribution_status_id = $statusId; - $contributionDAO->cancel_date = 'null'; - $contributionDAO->cancel_reason = NULL; - $netAmount = !empty($trxnsData['net_amount']) ? NULL : $trxnsData['total_amount']; - $contributionDAO->net_amount = $query->net_amount_total + $netAmount; - $contributionDAO->fee_amount = $contributionDAO->total_amount - $contributionDAO->net_amount; - $contributionDAO->save(); - - //Change status of financial record too - $financialTrxn->status_id = $statusId; - $financialTrxn->save(); - - // note : not using the self::add method, - // the reason because it performs 'status change' related code execution for financial records - // which in 'Partial Paid' => 'Completed' is not useful, instead specific financial record updates - // are coded below i.e. just updating financial_item status to 'Paid' - - if ($participantId) { - // update participant status - $participantStatuses = CRM_Event_PseudoConstant::participantStatus(); - $ids = CRM_Event_BAO_Participant::getParticipantIds($contributionId); - foreach ($ids as $val) { - $participantUpdate['id'] = $val; - $participantUpdate['status_id'] = array_search('Registered', $participantStatuses); - CRM_Event_BAO_Participant::add($participantUpdate); - } - } - - // update financial item statuses - $financialItemStatus = CRM_Core_PseudoConstant::get('CRM_Financial_DAO_FinancialItem', 'status_id'); - $paidStatus = array_search('Paid', $financialItemStatus); - - $baseTrxnId = CRM_Core_BAO_FinancialTrxn::getFinancialTrxnId($contributionId); - $sqlFinancialItemUpdate = " -UPDATE civicrm_financial_item fi - LEFT JOIN civicrm_entity_financial_trxn eft - ON (eft.entity_id = fi.id AND eft.entity_table = 'civicrm_financial_item') -SET status_id = {$paidStatus} -WHERE eft.financial_trxn_id IN ({$trxnId}, {$baseTrxnId['financialTrxnId']}) -"; - CRM_Core_DAO::executeQuery($sqlFinancialItemUpdate); - } + $financialTrxn = CRM_Financial_BAO_Payment::recordPayment($contributionId, $trxnsData, $participantId); } elseif ($paymentType == 'refund') { - // build params for recording financial trxn entry - $params['contribution'] = $contributionDAO; - $params = array_merge($defaults, $params); - $params['skipLineItem'] = TRUE; - $trxnsData['trxn_date'] = !empty($trxnsData['trxn_date']) ? $trxnsData['trxn_date'] : date('YmdHis'); - $trxnsData['total_amount'] = -$trxnsData['total_amount']; - - $relationTypeId = key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Accounts Receivable Account is' ")); - $trxnsData['from_financial_account_id'] = CRM_Contribute_PseudoConstant::financialAccountType($contributionDAO->financial_type_id, $relationTypeId); - $trxnsData['status_id'] = CRM_Core_OptionGroup::getValue('contribution_status', 'Refunded', 'name'); - // record the entry - $financialTrxn = CRM_Contribute_BAO_Contribution::recordFinancialAccounts($params, $trxnsData); - - // note : not using the self::add method, - // the reason because it performs 'status change' related code execution for financial records - // which in 'Pending Refund' => 'Completed' is not useful, instead specific financial record updates - // are coded below i.e. just updating financial_item status to 'Paid' - if ($updateStatus) { - $contributionDetails = CRM_Core_DAO::setFieldValue('CRM_Contribute_BAO_Contribution', $contributionId, 'contribution_status_id', $statusId); - } - // add financial item entry - $financialItemStatus = CRM_Core_PseudoConstant::get('CRM_Financial_DAO_FinancialItem', 'status_id'); - $lineItems = CRM_Price_BAO_LineItem::getLineItemsByContributionID($contributionDAO->id); - if (!empty($lineItems)) { - foreach ($lineItems as $lineItemId => $lineItemValue) { - $paid = $lineItemValue['line_total'] * ($financialTrxn->total_amount / $contributionDAO->total_amount); - $addFinancialEntry = array( - 'transaction_date' => $financialTrxn->trxn_date, - 'contact_id' => $contributionDAO->contact_id, - 'amount' => round($paid, 2), - 'status_id' => array_search('Paid', $financialItemStatus), - 'entity_id' => $lineItemId, - 'entity_table' => 'civicrm_line_item', - ); - $trxnIds['id'] = $financialTrxn->id; - CRM_Financial_BAO_FinancialItem::create($addFinancialEntry, NULL, $trxnIds); - } - } + $financialTrxn = CRM_Financial_BAO_Payment::recordRefundPayment($contributionId, $trxnsData, $updateStatus); if ($participantId) { // update participant status + // @todo this doesn't make sense... $participantStatuses = CRM_Event_PseudoConstant::participantStatus(); $ids = CRM_Event_BAO_Participant::getParticipantIds($contributionId); foreach ($ids as $val) { @@ -3926,21 +3904,11 @@ public static function recordAdditionalPayment($contributionId, $trxnsData, $pay } } - // activity creation if (!empty($financialTrxn)) { - if ($participantId) { - $inputParams['id'] = $participantId; - $values = array(); - $ids = array(); - $component = 'event'; - $entityObj = CRM_Event_BAO_Participant::getValues($inputParams, $values, $ids); - $entityObj = $entityObj[$participantId]; - } - $activityType = ($paymentType == 'refund') ? 'Refund' : 'Payment'; - - self::addActivityForPayment($entityObj, $financialTrxn, $activityType, $component, $contributionId); + self::recordPaymentActivity($contributionId, $participantId, $financialTrxn); + return $financialTrxn; } - return $financialTrxn; + } /** @@ -3954,29 +3922,26 @@ public static function recordAdditionalPayment($contributionId, $trxnsData, $pay */ public static function addActivityForPayment($entityObj, $trxnObj, $activityType, $component, $contributionId) { if ($component == 'event') { - $date = CRM_Utils_Date::isoToMysql($trxnObj->trxn_date); - $paymentAmount = CRM_Utils_Money::format($trxnObj->total_amount, $trxnObj->currency); - $eventTitle = CRM_Core_DAO::getFieldValue('CRM_Event_BAO_Event', $entityObj->event_id, 'title'); - $subject = "{$paymentAmount} - Offline {$activityType} for {$eventTitle}"; - $targetCid = $entityObj->contact_id; - // source record id would be the contribution id - $srcRecId = $contributionId; + $title = CRM_Core_DAO::getFieldValue('CRM_Event_BAO_Event', $entityObj->event_id, 'title'); } + else { + $title = ts('Contribution'); + } + $paymentAmount = CRM_Utils_Money::format($trxnObj->total_amount, $trxnObj->currency); + $subject = "{$paymentAmount} - Offline {$activityType} for {$title}"; + $date = CRM_Utils_Date::isoToMysql($trxnObj->trxn_date); + $targetCid = $entityObj->contact_id; + // source record id would be the contribution id + $srcRecId = $contributionId; // activity params $activityParams = array( 'source_contact_id' => $targetCid, 'source_record_id' => $srcRecId, - 'activity_type_id' => CRM_Core_OptionGroup::getValue('activity_type', - $activityType, - 'name' - ), + 'activity_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', $activityType), 'subject' => $subject, 'activity_date_time' => $date, - 'status_id' => CRM_Core_OptionGroup::getValue('activity_status', - 'Completed', - 'name' - ), + 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', 'Completed'), 'skipRecentView' => TRUE, ); @@ -3987,6 +3952,7 @@ public static function addActivityForPayment($entityObj, $trxnObj, $activityType $activityParams['source_contact_id'] = $id; $activityParams['target_contact_id'][] = $targetCid; } + // @todo use api. CRM_Activity_BAO_Activity::create($activityParams); } @@ -4000,10 +3966,9 @@ public static function addActivityForPayment($entityObj, $trxnObj, $activityType * * @return mixed */ - public static function getPaymentInfo($id, $component, $getTrxnInfo = FALSE, $usingLineTotal = FALSE) { + public static function getPaymentInfo($id, $component = 'contribution', $getTrxnInfo = FALSE, $usingLineTotal = FALSE) { + // @todo deprecate passing in component - always call with contribution. if ($component == 'event') { - $entity = 'participant'; - $entityTable = 'civicrm_participant'; $contributionId = CRM_Core_DAO::getFieldValue('CRM_Event_BAO_ParticipantPayment', $id, 'contribution_id', 'participant_id'); if (!$contributionId) { @@ -4016,10 +3981,11 @@ public static function getPaymentInfo($id, $component, $getTrxnInfo = FALSE, $us } } } + elseif ($component == 'membership') { + $contributionId = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipPayment', $id, 'contribution_id', 'membership_id'); + } else { $contributionId = $id; - $entity = 'contribution'; - $entityTable = 'civicrm_contribution'; } $total = CRM_Core_BAO_FinancialTrxn::getBalanceTrxnAmt($contributionId); @@ -4029,31 +3995,28 @@ public static function getPaymentInfo($id, $component, $getTrxnInfo = FALSE, $us $baseTrxnId = $baseTrxnId['financialTrxnId']; } if (!CRM_Utils_Array::value('total_amount', $total) || $usingLineTotal) { - // for additional participants - if ($entityTable == 'civicrm_participant') { - $ids = CRM_Event_BAO_Participant::getParticipantIds($contributionId); - $total = 0; - foreach ($ids as $val) { - $total += CRM_Price_BAO_LineItem::getLineTotal($val, $entityTable); - } - } - else { - $total = CRM_Price_BAO_LineItem::getLineTotal($id, $entityTable); - } + $total = CRM_Price_BAO_LineItem::getLineTotal($contributionId); } else { $baseTrxnId = $total['trxn_id']; $total = $total['total_amount']; } - $paymentBalance = CRM_Core_BAO_FinancialTrxn::getPartialPaymentWithType($id, $entity, FALSE, $total); - $contributionIsPayLater = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_Contribution', $contributionId, 'is_pay_later'); + $paymentBalance = CRM_Contribute_BAO_Contribution::getContributionBalance($contributionId, $total); + + $contribution = civicrm_api3('Contribution', 'getsingle', array('id' => $contributionId, 'return' => array('currency', 'is_pay_later', 'contribution_status_id', 'financial_type_id'))); + + $info['payLater'] = $contribution['is_pay_later']; + $info['contribution_status'] = $contribution['contribution_status']; + $info['currency'] = $contribution['currency']; - $feeRelationTypeId = key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Expense Account is' ")); - $financialTypeId = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_Contribution', $contributionId, 'financial_type_id'); - $feeFinancialAccount = CRM_Contribute_PseudoConstant::financialAccountType($financialTypeId, $feeRelationTypeId); + $financialTypeId = $contribution['financial_type_id']; + $feeFinancialAccount = CRM_Contribute_PseudoConstant::getRelationalFinancialAccount($financialTypeId, 'Expense Account is'); - if ($paymentBalance == 0 && $contributionIsPayLater) { + if ($paymentBalance == 0 && $info['payLater']) { + // @todo - review - this looks very unlikely to be correct. + // the balance should be correct based on payment transactions not + // assumptions. $paymentBalance = $total; } @@ -4062,33 +4025,28 @@ public static function getPaymentInfo($id, $component, $getTrxnInfo = FALSE, $us $info['balance'] = $paymentBalance; $info['id'] = $id; $info['component'] = $component; - $info['payLater'] = $contributionIsPayLater; $rows = array(); if ($getTrxnInfo && $baseTrxnId) { - $arRelationTypeId = key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Accounts Receivable Account is' ")); - $arAccount = CRM_Contribute_PseudoConstant::financialAccountType($financialTypeId, $arRelationTypeId); - // Need to exclude fee trxn rows so filter out rows where TO FINANCIAL ACCOUNT is expense account $sql = " SELECT GROUP_CONCAT(fa.`name`) as financial_account, ft.total_amount, ft.payment_instrument_id, - ft.trxn_date, ft.trxn_id, ft.status_id, ft.check_number, con.currency + ft.trxn_date, ft.trxn_id, ft.status_id, ft.check_number, ft.currency, ft.pan_truncation, ft.card_type_id, ft.id FROM civicrm_contribution con LEFT JOIN civicrm_entity_financial_trxn eft ON (eft.entity_id = con.id AND eft.entity_table = 'civicrm_contribution') INNER JOIN civicrm_financial_trxn ft ON ft.id = eft.financial_trxn_id AND ft.to_financial_account_id != %2 - INNER JOIN civicrm_entity_financial_trxn ef ON (ef.financial_trxn_id = ft.id AND ef.entity_table = 'civicrm_financial_item') + LEFT JOIN civicrm_entity_financial_trxn ef ON (ef.financial_trxn_id = ft.id AND ef.entity_table = 'civicrm_financial_item') LEFT JOIN civicrm_financial_item fi ON fi.id = ef.entity_id - INNER JOIN civicrm_financial_account fa ON fa.id = fi.financial_account_id + LEFT JOIN civicrm_financial_account fa ON fa.id = fi.financial_account_id - WHERE con.id = %1 AND ft.to_financial_account_id <> %3 + WHERE con.id = %1 AND ft.is_payment = 1 GROUP BY ft.id"; $queryParams = array( 1 => array($contributionId, 'Integer'), 2 => array($feeFinancialAccount, 'Integer'), - 3 => array($arAccount, 'Integer'), ); $resultDAO = CRM_Core_DAO::executeQuery($sql, $queryParams); $statuses = CRM_Contribute_PseudoConstant::contributionStatus(); @@ -4096,7 +4054,39 @@ public static function getPaymentInfo($id, $component, $getTrxnInfo = FALSE, $us while ($resultDAO->fetch()) { $paidByLabel = CRM_Core_PseudoConstant::getLabel('CRM_Core_BAO_FinancialTrxn', 'payment_instrument_id', $resultDAO->payment_instrument_id); $paidByName = CRM_Core_PseudoConstant::getName('CRM_Core_BAO_FinancialTrxn', 'payment_instrument_id', $resultDAO->payment_instrument_id); + if ($resultDAO->card_type_id) { + $creditCardType = CRM_Core_PseudoConstant::getLabel('CRM_Core_BAO_FinancialTrxn', 'card_type_id', $resultDAO->card_type_id); + $pantruncation = ''; + if ($resultDAO->pan_truncation) { + $pantruncation = ": {$resultDAO->pan_truncation}"; + } + $paidByLabel .= " ({$creditCardType}{$pantruncation})"; + } + + // show payment edit link only for payments done via backoffice form + $paymentEditLink = ''; + if (empty($resultDAO->payment_processor_id) && CRM_Core_Permission::check('edit contributions')) { + $links = array( + CRM_Core_Action::UPDATE => array( + 'name' => "", + 'url' => 'civicrm/payment/edit', + 'class' => 'medium-popup', + 'qs' => "reset=1&id=%%id%%&contribution_id=%%contribution_id%%", + 'title' => ts('Edit Payment'), + ), + ); + $paymentEditLink = CRM_Core_Action::formLink( + $links, + CRM_Core_Action::mask(array(CRM_Core_Permission::EDIT)), + array( + 'id' => $resultDAO->id, + 'contribution_id' => $contributionId, + ) + ); + } + $val = array( + 'id' => $resultDAO->id, 'total_amount' => $resultDAO->total_amount, 'financial_type' => $resultDAO->financial_account, 'payment_instrument' => $paidByLabel, @@ -4104,6 +4094,7 @@ public static function getPaymentInfo($id, $component, $getTrxnInfo = FALSE, $us 'trxn_id' => $resultDAO->trxn_id, 'status' => $statuses[$resultDAO->status_id], 'currency' => $resultDAO->currency, + 'action' => $paymentEditLink, ); if ($paidByName == 'Check') { $val['check_number'] = $resultDAO->check_number; @@ -4112,28 +4103,30 @@ public static function getPaymentInfo($id, $component, $getTrxnInfo = FALSE, $us } $info['transaction'] = $rows; } + + $info['payment_links'] = self::getContributionPaymentLinks($id, $paymentBalance, $info['contribution_status']); return $info; } /** - * Get financial account id has 'Sales Tax Account is' account relationship with financial type. + * Get the outstanding balance on a contribution. * - * @param int $financialTypeId + * @param int $contributionId + * @param float $contributionTotal + * Optional amount to override the saved amount paid (e.g if calculating what it WILL be). * - * @return int - * Financial Account Id + * @return float */ - public static function getFinancialAccountId($financialTypeId) { - $accountRel = key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Sales Tax Account is' ")); - $searchParams = array( - 'entity_table' => 'civicrm_financial_type', - 'entity_id' => $financialTypeId, - 'account_relationship' => $accountRel, - ); - $result = array(); - CRM_Financial_BAO_FinancialTypeAccount::retrieve($searchParams, $result); + public static function getContributionBalance($contributionId, $contributionTotal = NULL) { + if ($contributionTotal === NULL) { + $contributionTotal = CRM_Price_BAO_LineItem::getLineTotal($contributionId); + } - return CRM_Utils_Array::value('financial_account_id', $result); + return CRM_Utils_Money::subtractCurrencies( + $contributionTotal, + CRM_Core_BAO_FinancialTrxn::getTotalPayments($contributionId, TRUE) ?: 0, + CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_Contribution', $contributionId, 'currency') + ); } /** @@ -4147,6 +4140,11 @@ public static function getFinancialAccountId($financialTypeId) { public static function checkTaxAmount($params, $isLineItem = FALSE) { $taxRates = CRM_Core_PseudoConstant::getTaxRates(); + // This function should be only called after standardisation (removal of + // thousand separator & using a decimal point for cents separator. + // However, we don't know if that is always true :-( + // There is a deprecation notice tho :-) + $unknownIfMoneyIsClean = empty($params['skipCleanMoney']) && !$isLineItem; // Update contribution. if (!empty($params['id'])) { // CRM-19126 and CRM-19152 If neither total or financial_type_id are set on an update @@ -4193,7 +4191,7 @@ public static function checkTaxAmount($params, $isLineItem = FALSE) { empty($params['skipLineItem']) && !$isLineItem ) { $taxRateParams = $taxRates[$params['financial_type_id']]; - $taxAmount = CRM_Contribute_BAO_Contribution_Utils::calculateTaxAmount(CRM_Utils_Array::value('total_amount', $params), $taxRateParams); + $taxAmount = CRM_Contribute_BAO_Contribution_Utils::calculateTaxAmount(CRM_Utils_Array::value('total_amount', $params), $taxRateParams, $unknownIfMoneyIsClean); $params['tax_amount'] = round($taxAmount['tax_amount'], 2); // Get Line Item on update of contribution @@ -4225,9 +4223,9 @@ public static function checkTaxAmount($params, $isLineItem = FALSE) { } else { // update line item of contrbution - if (isset($params['financial_type_id']) && array_key_exists($params['financial_type_id'], $taxRates) && $isLineItem) { + 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); + $taxAmount = CRM_Contribute_BAO_Contribution_Utils::calculateTaxAmount($params['line_total'], $taxRate, $unknownIfMoneyIsClean); $params['tax_amount'] = round($taxAmount['tax_amount'], 2); } } @@ -4246,13 +4244,13 @@ public static function checkTaxAmount($params, $isLineItem = FALSE) { * @param array $errors * List of errors. * - * @return bool + * @return void */ public static function checkFinancialTypeChange($financialTypeId, $contributionId, &$errors) { if (!empty($financialTypeId)) { $oldFinancialTypeId = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_Contribution', $contributionId, 'financial_type_id'); if ($oldFinancialTypeId == $financialTypeId) { - return FALSE; + return; } } $sql = 'SELECT financial_type_id FROM civicrm_line_item WHERE contribution_id = %1 GROUP BY financial_type_id;'; @@ -4341,63 +4339,17 @@ public static function updateRelatedPledge( /** * Compute the stats values * - * @param $stat either 'mode' or 'median' - * @param $sql - * @param $alias of civicrm_contribution + * @deprecated + * + * @param string $stat either 'mode' or 'median' + * @param string $sql + * @param string $alias of civicrm_contribution + * + * @return array|null */ public static function computeStats($stat, $sql, $alias = NULL) { - $mode = $median = array(); - switch ($stat) { - case 'mode': - $modeDAO = CRM_Core_DAO::executeQuery($sql); - while ($modeDAO->fetch()) { - if ($modeDAO->civicrm_contribution_total_amount_count > 1) { - $mode[] = CRM_Utils_Money::format($modeDAO->amount, $modeDAO->currency); - } - else { - $mode[] = 'N/A'; - } - } - return $mode; - - case 'median': - $currencies = CRM_Core_OptionGroup::values('currencies_enabled'); - foreach ($currencies as $currency => $val) { - $midValue = 0; - $where = "AND {$alias}.currency = '{$currency}'"; - $rowCount = CRM_Core_DAO::singleValueQuery("SELECT count(*) as count {$sql} {$where}"); - - $even = FALSE; - $offset = 1; - $medianRow = floor($rowCount / 2); - if ($rowCount % 2 == 0 && !empty($medianRow)) { - $even = TRUE; - $offset++; - $medianRow--; - } - - $medianValue = "SELECT {$alias}.total_amount as median - {$sql} {$where} - ORDER BY median LIMIT {$medianRow},{$offset}"; - $medianValDAO = CRM_Core_DAO::executeQuery($medianValue); - while ($medianValDAO->fetch()) { - if ($even) { - $midValue = $midValue + $medianValDAO->median; - } - else { - $median[] = CRM_Utils_Money::format($medianValDAO->median, $currency); - } - } - if ($even) { - $midValue = $midValue / 2; - $median[] = CRM_Utils_Money::format($midValue, $currency); - } - } - return $median; - - default: - return; - } + CRM_Core_Error::deprecatedFunctionWarning('computeStats is now deprecated'); + return []; } /** @@ -4429,12 +4381,10 @@ public static function isSingleLineItem($id) { * @param CRM_Core_Transaction $transaction * @param int $recur * @param CRM_Contribute_BAO_Contribution $contribution - * @param bool $isRecurring - * Duplication of param needs review. Only used by AuthorizeNetIPN - * @param int $isFirstOrLastRecurringPayment - * Deprecated param only used by AuthorizeNetIPN. + * + * @return array */ - public static function completeOrder(&$input, &$ids, $objects, $transaction, $recur, $contribution, $isRecurring, $isFirstOrLastRecurringPayment) { + public static function completeOrder(&$input, &$ids, $objects, $transaction, $recur, $contribution) { $primaryContributionID = isset($contribution->id) ? $contribution->id : $objects['first_contribution']->id; // The previous details are used when calculating line items so keep it before any code that 'does something' if (!empty($contribution->id)) { @@ -4452,13 +4402,14 @@ public static function completeOrder(&$input, &$ids, $objects, $transaction, $re 'receive_date', 'receipt_date', 'contribution_status_id', + 'card_type_id', + 'pan_truncation', ); if (self::isSingleLineItem($primaryContributionID)) { $inputContributionWhiteList[] = 'financial_type_id'; } $participant = CRM_Utils_Array::value('participant', $objects); - $memberships = CRM_Utils_Array::value('membership', $objects); $recurContrib = CRM_Utils_Array::value('contributionRecur', $objects); $recurringContributionID = (empty($recurContrib->id)) ? NULL : $recurContrib->id; $event = CRM_Utils_Array::value('event', $objects); @@ -4480,8 +4431,20 @@ public static function completeOrder(&$input, &$ids, $objects, $transaction, $re 'source' => self::getRecurringContributionDescription($contribution, $event), ), array_intersect_key($input, array_fill_keys($inputContributionWhiteList, 1) )); + + // CRM-20678 Ensure that the currency is correct in subseqent transcations. + if (empty($contributionParams['currency']) && isset($objects['first_contribution']->currency)) { + $contributionParams['currency'] = $objects['first_contribution']->currency; + } + $contributionParams['payment_processor'] = $input['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. + if (empty($contributionParams['payment_instrument_id']) && isset($contribution->_relatedObjects['paymentProcessor']['payment_instrument_id'])) { + $contributionParams['payment_instrument_id'] = $contribution->_relatedObjects['paymentProcessor']['payment_instrument_id']; + } + if ($recurringContributionID) { $contributionParams['contribution_recur_id'] = $recurringContributionID; } @@ -4494,10 +4457,6 @@ public static function completeOrder(&$input, &$ids, $objects, $transaction, $re self::repeatTransaction($contribution, $input, $contributionParams, $paymentProcessorId); $contributionParams['financial_type_id'] = $contribution->financial_type_id; - if (is_numeric($memberships)) { - $memberships = array($objects['membership']); - } - $values = array(); if (isset($input['is_email_receipt'])) { $values['is_email_receipt'] = $input['is_email_receipt']; @@ -4506,6 +4465,7 @@ public static function completeOrder(&$input, &$ids, $objects, $transaction, $re if ($input['component'] == 'contribute') { if ($contribution->contribution_page_id) { // Figure out what we gain from this. + // Note that we may have overwritten the is_email_receipt input, fix that below. CRM_Contribute_BAO_ContributionPage::setValues($contribution->contribution_page_id, $values); } elseif ($recurContrib && $recurringContributionID) { @@ -4514,86 +4474,22 @@ public static function completeOrder(&$input, &$ids, $objects, $transaction, $re $values['title'] = $source = ts('Offline Recurring Contribution'); } - if ($recurContrib && $recurringContributionID && !isset($input['is_email_receipt'])) { + if (isset($input['is_email_receipt'])) { + // CRM-19601 - we may have overwritten this above. + $values['is_email_receipt'] = $input['is_email_receipt']; + } + elseif ($recurContrib && $recurringContributionID) { //CRM-13273 - is_email_receipt setting on recurring contribution should take precedence over contribution page setting // but CRM-16124 if $input['is_email_receipt'] is set then that should not be overridden. $values['is_email_receipt'] = $recurContrib->is_email_receipt; } - if (!empty($memberships)) { - foreach ($memberships as $membershipTypeIdKey => $membership) { - if ($membership) { - $membershipParams = array( - 'id' => $membership->id, - 'contact_id' => $membership->contact_id, - 'is_test' => $membership->is_test, - 'membership_type_id' => $membership->membership_type_id, - ); - - $currentMembership = CRM_Member_BAO_Membership::getContactMembership($membershipParams['contact_id'], - $membershipParams['membership_type_id'], - $membershipParams['is_test'], - $membershipParams['id'] - ); - - // CRM-8141 update the membership type with the value recorded in log when membership created/renewed - // this picks up membership type changes during renewals - $sql = " -SELECT membership_type_id -FROM civicrm_membership_log -WHERE membership_id={$membershipParams['id']} -ORDER BY id DESC -LIMIT 1;"; - $dao = CRM_Core_DAO::executeQuery($sql); - if ($dao->fetch()) { - if (!empty($dao->membership_type_id)) { - $membershipParams['membership_type_id'] = $dao->membership_type_id; - } - } - $dao->free(); - - $membershipParams['num_terms'] = $contribution->getNumTermsByContributionAndMembershipType( - $membershipParams['membership_type_id'], - $primaryContributionID - ); - $dates = array_fill_keys(array('join_date', 'start_date', 'end_date'), NULL); - if ($currentMembership) { - /* - * Fixed FOR CRM-4433 - * In BAO/Membership.php(renewMembership function), we skip the extend membership date and status - * when Contribution mode is notify and membership is for renewal ) - */ - CRM_Member_BAO_Membership::fixMembershipStatusBeforeRenew($currentMembership, $changeDate); - - // @todo - we should pass membership_type_id instead of null here but not - // adding as not sure of testing - $dates = CRM_Member_BAO_MembershipType::getRenewalDatesForMembershipType($membershipParams['id'], - $changeDate, NULL, $membershipParams['num_terms'] - ); - - $dates['join_date'] = $currentMembership['join_date']; - } - - //get the status for membership. - $calcStatus = CRM_Member_BAO_MembershipStatus::getMembershipStatusByDate($dates['start_date'], - $dates['end_date'], - $dates['join_date'], - 'today', - TRUE, - $membershipParams['membership_type_id'], - $membershipParams - ); - - $membershipParams['status_id'] = CRM_Utils_Array::value('id', $calcStatus, 'New'); - //we might be renewing membership, - //so make status override false. - $membershipParams['is_override'] = FALSE; - //CRM-17723 - reset static $relatedContactIds array() - $var = TRUE; - CRM_Member_BAO_Membership::createRelatedMemberships($var, $var, TRUE); - civicrm_api3('Membership', 'create', $membershipParams); - } - } + if ($contributionParams['contribution_status_id'] === $completedContributionStatusID) { + self::updateMembershipBasedOnCompletionOfContribution( + $contribution, + $primaryContributionID, + $changeDate + ); } } else { @@ -4633,6 +4529,7 @@ public static function completeOrder(&$input, &$ids, $objects, $transaction, $re $input['participant_id'] = $contribution->_relatedObjects['participant']->id; } elseif (!empty($contribution->_relatedObjects['membership'])) { + // @todo - use getRelatedMemberships instead $input['contribution_mode'] = 'membership'; $contribution->contribution_status_id = $contributionParams['contribution_status_id']; $contribution->trxn_id = CRM_Utils_Array::value('trxn_id', $input); @@ -4654,10 +4551,6 @@ public static function completeOrder(&$input, &$ids, $objects, $transaction, $re $contribution->contact_id = $ids['related_contact']; } CRM_Activity_BAO_Activity::addActivity($contribution, NULL, $targetContactID); - // event - } - else { - CRM_Activity_BAO_Activity::addActivity($participant); } // CRM-9132 legacy behaviour was that receipts were sent out in all instances. Still sending @@ -4673,10 +4566,6 @@ public static function completeOrder(&$input, &$ids, $objects, $transaction, $re } CRM_Core_Error::debug_log_message("Success: Database updated"); - if ($isRecurring) { - CRM_Contribute_BAO_ContributionRecur::sendRecurringStartOrEndNotification($ids, $recur, - $isFirstOrLastRecurringPayment); - } return $contributionResult; } @@ -4695,8 +4584,6 @@ public static function completeOrder(&$input, &$ids, $objects, $transaction, $re * @param int $contributionID * @param array $values * Values related to objects that have already been loaded. - * @param bool $recur - * Is it part of a recurring contribution. * @param bool $returnMessageText * Should text be returned instead of sent. This. * is because the function is also used to generate pdfs @@ -4705,9 +4592,8 @@ public static function completeOrder(&$input, &$ids, $objects, $transaction, $re * @throws \CRM_Core_Exception * @throws \CiviCRM_API3_Exception */ - public static function sendMail(&$input, &$ids, $contributionID, &$values, $recur = FALSE, + public static function sendMail(&$input, &$ids, $contributionID, &$values, $returnMessageText = FALSE) { - $input['is_recur'] = $recur; $contribution = new CRM_Contribute_BAO_Contribution(); $contribution->id = $contributionID; @@ -4717,23 +4603,40 @@ public static function sendMail(&$input, &$ids, $contributionID, &$values, $recu $contribution->loadRelatedObjects($input, $ids, TRUE); // set receipt from e-mail and name in value if (!$returnMessageText) { - $session = CRM_Core_Session::singleton(); - $userID = $session->get('userID'); - if (!empty($userID)) { - list($userName, $userEmail) = CRM_Contact_BAO_Contact_Location::getEmailDetails($userID); - $values['receipt_from_email'] = CRM_Utils_Array::value('receipt_from_email', $input, $userEmail); - $values['receipt_from_name'] = CRM_Utils_Array::value('receipt_from_name', $input, $userName); - } + list($values['receipt_from_name'], $values['receipt_from_email']) = self::generateFromEmailAndName($input, $contribution); } - - $return = $contribution->composeMessageArray($input, $ids, $values, $recur, $returnMessageText); - // Contribution ID should really always be set. But ? - if (!$returnMessageText && (!isset($input['receipt_update']) || $input['receipt_update']) && empty($contribution->receipt_date)) { + $values['contribution_status'] = CRM_Core_PseudoConstant::getLabel('CRM_Contribute_BAO_Contribution', 'contribution_status_id', $contribution->contribution_status_id); + $return = $contribution->composeMessageArray($input, $ids, $values, $returnMessageText); + if ((!isset($input['receipt_update']) || $input['receipt_update']) && empty($contribution->receipt_date)) { civicrm_api3('Contribution', 'create', array('receipt_date' => 'now', 'id' => $contribution->id)); } return $return; } + /** + * Generate From email and from name in an array values + * @param array $input + * @param \CRM_Contribute_BAO_Contribution $contribution + * @return array + */ + public static function generateFromEmailAndName($input, $contribution) { + // Use input value if supplied. + if (!empty($input['receipt_from_email'])) { + return array(CRM_Utils_array::value('receipt_from_name', $input, ''), $input['receipt_from_email']); + } + // if we are still empty see if we can use anything from a contribution page. + $pageValues = array(); + if (!empty($contribution->contribution_page_id)) { + $pageValues = civicrm_api3('ContributionPage', 'getsingle', array('id' => $contribution->contribution_page_id)); + } + // if we are still empty see if we can use anything from a contribution page. + if (!empty($pageValues['receipt_from_email'])) { + return array($pageValues['receipt_from_name'], $pageValues['receipt_from_email']); + } + // If we are still empty fall back to the domain or logged in user information. + return CRM_Core_BAO_Domain::getDefaultReceiptFrom(); + } + /** * Generate credit note id with next avaible number * @@ -4743,7 +4646,7 @@ public static function sendMail(&$input, &$ids, $contributionID, &$values, $recu public static function createCreditNoteId() { $prefixValue = Civi::settings()->get('contribution_invoice_settings'); - $creditNoteNum = CRM_Core_DAO::singleValueQuery("SELECT count(creditnote_id) as creditnote_number FROM civicrm_contribution"); + $creditNoteNum = CRM_Core_DAO::singleValueQuery("SELECT count(creditnote_id) as creditnote_number FROM civicrm_contribution WHERE creditnote_id IS NOT NULL"); $creditNoteId = NULL; do { @@ -4761,6 +4664,8 @@ public static function createCreditNoteId() { /** * Load related memberships. * + * @deprecated + * * Note that in theory it should be possible to retrieve these from the line_item table * with the membership_payment table being deprecated. Attempting to do this here causes tests to fail * as it seems the api is not correctly linking the line items when the contribution is created in the flow @@ -4771,22 +4676,22 @@ public static function createCreditNoteId() { * * @param array $ids * + * @return array $ids + * * @throws Exception */ - public function loadRelatedMembershipObjects(&$ids) { + public function loadRelatedMembershipObjects($ids = []) { $query = " SELECT membership_id FROM civicrm_membership_payment WHERE contribution_id = %1 "; $params = array(1 => array($this->id, 'Integer')); + $ids['membership'] = (array) CRM_Utils_Array::value('membership', $ids, array()); $dao = CRM_Core_DAO::executeQuery($query, $params); while ($dao->fetch()) { - if ($dao->membership_id) { - if (!is_array($ids['membership'])) { - $ids['membership'] = array(); - } - $ids['membership'][] = $dao->membership_id; + if ($dao->membership_id && !in_array($dao->membership_id, $ids['membership'])) { + $ids['membership'][$dao->membership_id] = $dao->membership_id; } } @@ -4802,10 +4707,10 @@ public function loadRelatedMembershipObjects(&$ids) { $membership->start_date = CRM_Utils_Date::isoToMysql($membership->start_date); $membership->end_date = CRM_Utils_Date::isoToMysql($membership->end_date); $this->_relatedObjects['membership'][$membership->membership_type_id] = $membership; - $membership->free(); } } } + return $ids; } /** @@ -4815,49 +4720,28 @@ public function loadRelatedMembershipObjects(&$ids) { * * @param array $params * - * @return object + * @return CRM_Financial_DAO_FinancialTrxn */ public static function recordPartialPayment($contribution, $params) { - $contributionStatuses = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name'); - $pendingStatus = array( - array_search('Pending', $contributionStatuses), - array_search('In Progress', $contributionStatuses), - ); - $statusId = array_search('Completed', $contributionStatuses); - if (in_array(CRM_Utils_Array::value('contribution_status_id', $contribution), $pendingStatus)) { - $relationTypeId = key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Accounts Receivable Account is' ")); - $balanceTrxnParams['to_financial_account_id'] = CRM_Contribute_PseudoConstant::financialAccountType($contribution['financial_type_id'], $relationTypeId); - } - elseif (!empty($params['payment_processor'])) { - $balanceTrxnParams['to_financial_account_id'] = CRM_Financial_BAO_FinancialTypeAccount::getFinancialAccount($contribution['payment_processor'], 'civicrm_payment_processor', 'financial_account_id'); - } - elseif (!empty($params['payment_instrument_id'])) { - $balanceTrxnParams['to_financial_account_id'] = CRM_Financial_BAO_FinancialTypeAccount::getInstrumentFinancialAccount($contribution['payment_instrument_id']); - } - else { - $relationTypeId = key(CRM_Core_PseudoConstant::accountOptionValues('financial_account_type', NULL, " AND v.name LIKE 'Asset' ")); - $queryParams = array(1 => array($relationTypeId, 'Integer')); - $balanceTrxnParams['to_financial_account_id'] = CRM_Core_DAO::singleValueQuery("SELECT id FROM civicrm_financial_account WHERE is_default = 1 AND financial_account_type_id = %1", $queryParams); - } - $relationTypeId = key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Accounts Receivable Account is' ")); - $fromFinancialAccountId = CRM_Contribute_PseudoConstant::financialAccountType($contribution['financial_type_id'], $relationTypeId); - $balanceTrxnParams['from_financial_account_id'] = $fromFinancialAccountId; + + $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'] = !empty($params['contribution_receive_date']) ? $params['contribution_receive_date'] : date('YmdHis'); + $balanceTrxnParams['trxn_date'] = CRM_Utils_Array::value('trxn_date', $params, CRM_Utils_Array::value('contribution_receive_date', $params, date('YmdHis'))); $balanceTrxnParams['fee_amount'] = CRM_Utils_Array::value('fee_amount', $params); $balanceTrxnParams['net_amount'] = CRM_Utils_Array::value('total_amount', $params); $balanceTrxnParams['currency'] = $contribution['currency']; $balanceTrxnParams['trxn_id'] = CRM_Utils_Array::value('contribution_trxn_id', $params, NULL); - $balanceTrxnParams['status_id'] = $statusId; + $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'] = CRM_Utils_Array::value('check_number', $params); - if ($fromFinancialAccountId != NULL && - ($statusId == array_search('Completed', $contributionStatuses) || $statusId == array_search('Partially paid', $contributionStatuses)) - ) { - $balanceTrxnParams['is_payment'] = 1; - } + $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); @@ -4869,14 +4753,14 @@ public static function recordPartialPayment($contribution, $params) { * @param CRM_Contribute_BAO_Contribution $contribution * @param CRM_Event_DAO_Event|null $event * - * @return array + * @return string * @throws \CiviCRM_API3_Exception */ protected static function getRecurringContributionDescription($contribution, $event) { if (!empty($contribution->source)) { return $contribution->source; } - elseif (!empty($contribution->contribution_page_id)) { + elseif (!empty($contribution->contribution_page_id) && is_numeric($contribution->contribution_page_id)) { $contributionPageTitle = civicrm_api3('ContributionPage', 'getvalue', array( 'id' => $contribution->contribution_page_id, 'return' => 'title', @@ -4896,25 +4780,20 @@ protected static function getRecurringContributionDescription($contribution, $ev * Function to add payments for contribution * for Partially Paid status * - * @param array $lineItems * @param array $contributions - * @param array $contributionStatusId + * @param string $contributionStatusId * */ - public static function addPayments($lineItems, $contributions, $contributionStatusId = NULL) { + public static function addPayments($contributions, $contributionStatusId = NULL) { // get financial trxn which is a payment $ftSql = "SELECT ft.id, ft.total_amount FROM civicrm_financial_trxn ft INNER JOIN civicrm_entity_financial_trxn eft ON eft.financial_trxn_id = ft.id AND eft.entity_table = 'civicrm_contribution' WHERE eft.entity_id = %1 AND ft.is_payment = 1 ORDER BY ft.id DESC LIMIT 1"; - $sql = "SELECT fi.id, li.price_field_value_id - FROM civicrm_financial_item fi - INNER JOIN civicrm_line_item li ON li.id = fi.entity_id - WHERE li.contribution_id = %1"; $contributionStatus = CRM_Core_PseudoConstant::get('CRM_Contribute_DAO_Contribution', 'contribution_status_id', array( 'labelColumn' => 'name', )); - foreach ($contributions as $k => $contribution) { + foreach ($contributions as $contribution) { if (!($contributionStatus[$contribution->contribution_status_id] == 'Partially paid' || CRM_Utils_Array::value($contributionStatusId, $contributionStatus) == 'Partially paid') ) { @@ -4922,30 +4801,13 @@ public static function addPayments($lineItems, $contributions, $contributionStat } $ftDao = CRM_Core_DAO::executeQuery($ftSql, array(1 => array($contribution->id, 'Integer'))); $ftDao->fetch(); - $trxnAmount = $ftDao->total_amount; - $ftId = $ftDao->id; - - // get financial item - $dao = CRM_Core_DAO::executeQuery($sql, array(1 => array($contribution->id, 'Integer'))); - while ($dao->fetch()) { - $ftIds[$dao->price_field_value_id] = $dao->id; - } - $params = array( - 'entity_table' => 'civicrm_financial_item', - 'financial_trxn_id' => $ftId, + // store financial item Proportionaly. + $trxnParams = array( + 'total_amount' => $ftDao->total_amount, + 'contribution_id' => $contribution->id, ); - foreach ($lineItems as $key => $value) { - if ($value['qty'] == 0) { - continue; - } - $paid = $value['line_total'] * ($trxnAmount / $contribution->total_amount); - // Record Entity Financial Trxn - $params['amount'] = round($paid, 2); - $params['entity_id'] = $ftIds[$value['price_field_value_id']]; - - civicrm_api3('EntityFinancialTrxn', 'create', $params); - } + self::assignProportionalLineItems($trxnParams, $ftDao->id, $contribution->total_amount); } } @@ -4953,63 +4815,62 @@ public static function addPayments($lineItems, $contributions, $contributionStat * Function use to store line item proportionaly in * in entity financial trxn table * - * @param array $params - * array of contribution params. - * @param object $trxn - * CRM_Financial_DAO_FinancialTrxn object - * @param array $contribution + * @param array $trxnParams + * + * @param Integer $trxnId + * + * @param float $contributionTotalAmount * */ - public static function assignProportionalLineItems($params, $trxn, $contribution) { - $lineItems = CRM_Price_BAO_LineItem::getLineItemsByContributionID($params['contribution_id']); + public static function assignProportionalLineItems($trxnParams, $trxnId, $contributionTotalAmount) { + $lineItems = CRM_Price_BAO_LineItem::getLineItemsByContributionID($trxnParams['contribution_id']); if (!empty($lineItems)) { // get financial item - $sql = "SELECT fi.id, li.price_field_value_id - FROM civicrm_financial_item fi - INNER JOIN civicrm_line_item li ON li.id = fi.entity_id and fi.entity_table = 'civicrm_line_item' - WHERE li.contribution_id = %1"; - $dao = CRM_Core_DAO::executeQuery($sql, array(1 => array($params['contribution_id'], 'Integer'))); - while ($dao->fetch()) { - $ftIds[$dao->price_field_value_id] = $dao->id; - } - $eftParams = array( - 'entity_table' => 'civicrm_financial_item', - 'financial_trxn_id' => $trxn->id, + list($ftIds, $taxItems) = self::getLastFinancialItemIds($trxnParams['contribution_id']); + $entityParams = array( + 'contribution_total_amount' => $contributionTotalAmount, + 'trxn_total_amount' => $trxnParams['total_amount'], + 'trxn_id' => $trxnId, ); - foreach ($lineItems as $key => $value) { - $paid = $value['line_total'] * ($params['total_amount'] / $contribution['total_amount']); - // Record Entity Financial Trxn - $eftParams['amount'] = round($paid, 2); - $eftParams['entity_id'] = $ftIds[$value['price_field_value_id']]; - - civicrm_api3('EntityFinancialTrxn', 'create', $eftParams); - } + self::createProportionalFinancialEntries($entityParams, $lineItems, $ftIds, $taxItems); } } /** - * Function to check line items + * Checks if line items total amounts + * match the contribution total amount. * * @param array $params * array of order params. * + * @throws \API_Exception */ public static function checkLineItems(&$params) { $totalAmount = CRM_Utils_Array::value('total_amount', $params); $lineItemAmount = 0; + foreach ($params['line_items'] as &$lineItems) { foreach ($lineItems['line_item'] as &$item) { if (empty($item['financial_type_id'])) { $item['financial_type_id'] = $params['financial_type_id']; } - $lineItemAmount += $item['line_total']; + $lineItemAmount += $item['line_total'] + CRM_Utils_Array::value('tax_amount', $item, 0.00); } } + if (!isset($totalAmount)) { $params['total_amount'] = $lineItemAmount; } - elseif ($totalAmount != $lineItemAmount) { - throw new API_Exception("Line item total doesn't match with total amount."); + else { + $currency = CRM_Utils_Array::value('currency', $params, ''); + + if (empty($currency)) { + $currency = CRM_Core_Config::singleton()->defaultCurrency; + } + + if (!CRM_Utils_Money::equals($totalAmount, $lineItemAmount, $currency)) { + throw new CRM_Contribute_Exception_CheckLineItemsException(); + } } } @@ -5017,20 +4878,22 @@ public static function checkLineItems(&$params) { * Get the financial account for the item associated with the new transaction. * * @param array $params - * @param CRM_Financial_BAO_FinancialItem $prevFinancialItem + * @param int $default * * @return int */ - public static function getFinancialAccountForStatusChangeTrxn($params, $prevFinancialItem) { + public static function getFinancialAccountForStatusChangeTrxn($params, $default) { if (!empty($params['financial_account_id'])) { return $params['financial_account_id']; } + $contributionStatus = CRM_Contribute_PseudoConstant::contributionStatus($params['contribution_status_id'], 'name'); $preferredAccountsRelationships = array( 'Refunded' => 'Credit/Contra Revenue Account is', 'Chargeback' => 'Chargeback Account is', ); + if (in_array($contributionStatus, array_keys($preferredAccountsRelationships))) { $financialTypeID = !empty($params['financial_type_id']) ? $params['financial_type_id'] : $params['prevContribution']->financial_type_id; return CRM_Financial_BAO_FinancialAccount::getFinancialAccountForFinancialTypeByRelationship( @@ -5038,7 +4901,8 @@ public static function getFinancialAccountForStatusChangeTrxn($params, $prevFina $preferredAccountsRelationships[$contributionStatus] ); } - return $prevFinancialItem->financial_account_id; + + return $default; } /** @@ -5066,7 +4930,6 @@ protected function addContributionPageValuesToValuesHeavyHandedly(&$values) { // These are the values that I believe to be useful. 'id', 'title', - 'is_email_receipt', 'pay_later_receipt', 'pay_later_text', 'receipt_from_email', @@ -5134,12 +4997,17 @@ protected function addContributionPageValuesToValuesHeavyHandedly(&$values) { * * * @param string $name + * @param bool $checkInvoicing * @return string * */ - public static function checkContributeSettings($name = NULL) { + public static function checkContributeSettings($name = NULL, $checkInvoicing = FALSE) { $contributeSettings = Civi::settings()->get('contribution_invoice_settings'); + if ($checkInvoicing && !CRM_Utils_Array::value('invoicing', $contributeSettings)) { + return NULL; + } + if ($name) { return CRM_Utils_Array::value($name, $contributeSettings); } @@ -5195,23 +5063,16 @@ public static function transitionComponentWithReturnMessage($contributionId, $st $updatedStatusName = CRM_Utils_Array::value($updatedStatusId, CRM_Member_PseudoConstant::membershipStatus() ); - if ($updatedStatusName == 'Cancelled') { - $statusMsg .= "
    " . ts("Membership for %1 has been Cancelled.", array(1 => $userDisplayName)); - } - elseif ($updatedStatusName == 'Expired') { - $statusMsg .= "
    " . ts("Membership for %1 has been Expired.", array(1 => $userDisplayName)); - } - else { - $endDate = CRM_Utils_Array::value('membership_end_date', $updateResult); - if ($endDate) { - $statusMsg .= "
    " . ts("Membership for %1 has been updated. The membership End Date is %2.", - array( - 1 => $userDisplayName, - 2 => $endDate, - ) - ); - } + + $statusNameMsgPart = 'updated'; + switch ($updatedStatusName) { + case 'Cancelled': + case 'Expired': + $statusNameMsgPart = $updatedStatusName; + break; } + + $statusMsg .= "
    " . ts("Membership for %1 has been %2.", array(1 => $userDisplayName, 2 => $statusNameMsgPart)); } if ($componentName == 'CiviEvent') { @@ -5250,12 +5111,366 @@ public static function transitionComponentWithReturnMessage($contributionId, $st * * @param int $contributionID * - * @return array + * @return \CRM_Contribute_BAO_Contribution|null */ private static function getOriginalContribution($contributionID) { return self::getValues(array('id' => $contributionID), CRM_Core_DAO::$_nullArray, CRM_Core_DAO::$_nullArray); } + /** + * Get the amount for the financial item row. + * + * Helper function to start to break down recordFinancialTransactions for readability. + * + * The logic is more historical than .. logical. Paths other than the deprecated one are tested. + * + * Codewise, several somewhat disimmilar things have been squished into recordFinancialAccounts + * for historical reasons. Going forwards we can hope to add tests & improve readibility + * of that function + * + * @todo move recordFinancialAccounts & helper functions to their own class? + * + * @param array $params + * Params as passed to contribution.create + * + * @param string $context + * changeFinancialType| changedAmount + * @param array $lineItemDetails + * Line items. + * @param bool $isARefund + * Is this a refund / negative transaction. + * @param int $previousLineItemTotal + * + * @return float + */ + protected static function getFinancialItemAmountFromParams($params, $context, $lineItemDetails, $isARefund, $previousLineItemTotal) { + if ($context == 'changedAmount') { + $lineTotal = $lineItemDetails['line_total']; + if ($lineTotal != $previousLineItemTotal) { + $lineTotal -= $previousLineItemTotal; + } + return $lineTotal; + } + elseif ($context == 'changeFinancialType') { + return -$lineItemDetails['line_total']; + } + elseif ($context == 'changedStatus') { + $cancelledTaxAmount = 0; + if ($isARefund) { + $cancelledTaxAmount = CRM_Utils_Array::value('tax_amount', $lineItemDetails, '0.00'); + } + return self::getMultiplier($params['contribution']->contribution_status_id, $context) * ((float) $lineItemDetails['line_total'] + (float) $cancelledTaxAmount); + } + elseif ($context === NULL) { + // erm, yes because? but, hey, it's tested. + return $lineItemDetails['line_total']; + } + elseif (empty($lineItemDetails['line_total'])) { + // follow legacy code path + Civi::log() + ->warning('Deprecated bit of code, please log a ticket explaining how you got here!', array('civi.tag' => 'deprecated')); + return $params['total_amount']; + } + else { + return self::getMultiplier($params['contribution']->contribution_status_id, $context) * ((float) $lineItemDetails['line_total']); + } + } + + /** + * Get the multiplier for adjusting rows. + * + * If we are dealing with a refund or cancellation then it will be a negative + * amount to reflect the negative transaction. + * + * If we are changing Financial Type it will be a negative amount to + * adjust down the old type. + * + * @param int $contribution_status_id + * @param string $context + * + * @return int + */ + protected static function getMultiplier($contribution_status_id, $context) { + if ($context == 'changeFinancialType' || self::isContributionStatusNegative($contribution_status_id)) { + return -1; + } + return 1; + } + + /** + * Does this transaction reflect a payment instrument change. + * + * @param array $params + * @param array $pendingStatuses + * + * @return bool + */ + protected static function isPaymentInstrumentChange(&$params, $pendingStatuses) { + $contributionStatus = CRM_Core_PseudoConstant::getName('CRM_Contribute_BAO_Contribution', 'contribution_status_id', $params['contribution']->contribution_status_id); + + if (array_key_exists('payment_instrument_id', $params)) { + if (CRM_Utils_System::isNull($params['prevContribution']->payment_instrument_id) && + !CRM_Utils_System::isNull($params['payment_instrument_id']) + ) { + //check if status is changed from Pending to Completed + // do not update payment instrument changes for Pending to Completed + if (!($contributionStatus == 'Completed' && + in_array($params['prevContribution']->contribution_status_id, $pendingStatuses)) + ) { + return TRUE; + } + } + elseif ((!CRM_Utils_System::isNull($params['payment_instrument_id']) && + !CRM_Utils_System::isNull($params['prevContribution']->payment_instrument_id)) && + $params['payment_instrument_id'] != $params['prevContribution']->payment_instrument_id + ) { + return TRUE; + } + elseif (!CRM_Utils_System::isNull($params['contribution']->check_number) && + $params['contribution']->check_number != $params['prevContribution']->check_number + ) { + // another special case when check number is changed, create new financial records + // create financial trxn with negative amount + return TRUE; + } + } + return FALSE; + } + + /** + * Update the memberships associated with a contribution if it has been completed. + * + * Note that the way in which $memberships are loaded as objects is pretty messy & I think we could just + * load them in this function. Code clean up would compensate for any minor performance implication. + * + * @param \CRM_Contribute_BAO_Contribution $contribution + * @param int $primaryContributionID + * @param string $changeDate + * + * @throws \CRM_Core_Exception + * @throws \CiviCRM_API3_Exception + */ + public static function updateMembershipBasedOnCompletionOfContribution($contribution, $primaryContributionID, $changeDate) { + $memberships = self::getRelatedMemberships($contribution->id); + foreach ($memberships as $membership) { + $membershipParams = array( + 'id' => $membership['id'], + 'contact_id' => $membership['contact_id'], + 'is_test' => $membership['is_test'], + 'membership_type_id' => $membership['membership_type_id'], + 'membership_activity_status' => 'Completed', + ); + + $currentMembership = CRM_Member_BAO_Membership::getContactMembership($membershipParams['contact_id'], + $membershipParams['membership_type_id'], + $membershipParams['is_test'], + $membershipParams['id'] + ); + + // CRM-8141 update the membership type with the value recorded in log when membership created/renewed + // this picks up membership type changes during renewals + // @todo this is almost certainly an obsolete sql call, the pre-change + // membership is accessible via $this->_relatedObjects + $sql = " +SELECT membership_type_id +FROM civicrm_membership_log +WHERE membership_id={$membershipParams['id']} +ORDER BY id DESC +LIMIT 1;"; + $dao = CRM_Core_DAO::executeQuery($sql); + if ($dao->fetch()) { + if (!empty($dao->membership_type_id)) { + $membershipParams['membership_type_id'] = $dao->membership_type_id; + } + } + + $membershipParams['num_terms'] = $contribution->getNumTermsByContributionAndMembershipType( + $membershipParams['membership_type_id'], + $primaryContributionID + ); + // @todo remove all this stuff in favour of letting the api call further down handle in + // (it is a duplication of what the api does). + $dates = array_fill_keys(array( + 'join_date', + 'start_date', + 'end_date', + ), NULL); + if ($currentMembership) { + /* + * Fixed FOR CRM-4433 + * In BAO/Membership.php(renewMembership function), we skip the extend membership date and status + * when Contribution mode is notify and membership is for renewal ) + */ + CRM_Member_BAO_Membership::fixMembershipStatusBeforeRenew($currentMembership, $changeDate); + + // @todo - we should pass membership_type_id instead of null here but not + // adding as not sure of testing + $dates = CRM_Member_BAO_MembershipType::getRenewalDatesForMembershipType($membershipParams['id'], + $changeDate, NULL, $membershipParams['num_terms'] + ); + $dates['join_date'] = $currentMembership['join_date']; + } + + //get the status for membership. + $calcStatus = CRM_Member_BAO_MembershipStatus::getMembershipStatusByDate($dates['start_date'], + $dates['end_date'], + $dates['join_date'], + 'today', + TRUE, + $membershipParams['membership_type_id'], + $membershipParams + ); + + unset($dates['end_date']); + $membershipParams['status_id'] = CRM_Utils_Array::value('id', $calcStatus, 'New'); + //we might be renewing membership, + //so make status override false. + $membershipParams['is_override'] = FALSE; + $membershipParams['status_override_end_date'] = 'null'; + + //CRM-17723 - reset static $relatedContactIds array() + // @todo move it to Civi Statics. + $var = TRUE; + CRM_Member_BAO_Membership::createRelatedMemberships($var, $var, TRUE); + civicrm_api3('Membership', 'create', $membershipParams); + } + } + + /** + * Get payment links as they relate to a contribution. + * + * If a payment can be made then include a payment link & if a refund is appropriate + * then a refund link. + * + * @param int $id + * @param float $balance + * @param string $contributionStatus + * + * @return array + * $actionLinks Links array containing: + * -url + * -title + */ + protected static function getContributionPaymentLinks($id, $balance, $contributionStatus) { + if ($contributionStatus === 'Failed' || !CRM_Core_Permission::check('edit contributions')) { + // In general the balance is the best way to determine if a payment can be added or not, + // but not for Failed contributions, where we don't accept additional payments at the moment. + // (in some cases the contribution is 'Pending' and only the payment is failed. In those we + // do accept more payments agains them. + return array(); + } + $actionLinks = array(); + if ((int) $balance > 0) { + if (CRM_Core_Config::isEnabledBackOfficeCreditCardPayments()) { + $actionLinks[] = array( + 'url' => CRM_Utils_System::url('civicrm/payment', array( + 'action' => 'add', + 'reset' => 1, + 'id' => $id, + 'mode' => 'live', + )), + 'title' => ts('Submit Credit Card payment'), + ); + } + $actionLinks[] = array( + 'url' => CRM_Utils_System::url('civicrm/payment', array( + 'action' => 'add', + 'reset' => 1, + 'id' => $id, + )), + 'title' => ts('Record Payment'), + ); + } + elseif ((int) $balance < 0) { + $actionLinks[] = array( + 'url' => CRM_Utils_System::url('civicrm/payment', array( + 'action' => 'add', + 'reset' => 1, + 'id' => $id, + )), + 'title' => ts('Record Refund'), + ); + } + return $actionLinks; + } + + /** + * Get a query to determine the amount donated by the contact/s in the current financial year. + * + * @param array $contactIDs + * + * @return string + */ + public static function getAnnualQuery($contactIDs) { + $contactIDs = implode(',', $contactIDs); + $config = CRM_Core_Config::singleton(); + $currentMonth = date('m'); + $currentDay = date('d'); + if ( + (int) $config->fiscalYearStart['M'] > $currentMonth || + ( + (int) $config->fiscalYearStart['M'] == $currentMonth && + (int) $config->fiscalYearStart['d'] > $currentDay + ) + ) { + $year = date('Y') - 1; + } + else { + $year = date('Y'); + } + $nextYear = $year + 1; + + if ($config->fiscalYearStart) { + $newFiscalYearStart = $config->fiscalYearStart; + if ($newFiscalYearStart['M'] < 10) { + // This is just a clumsy way of adding padding. + // @todo next round look for a nicer way. + $newFiscalYearStart['M'] = '0' . $newFiscalYearStart['M']; + } + if ($newFiscalYearStart['d'] < 10) { + // This is just a clumsy way of adding padding. + // @todo next round look for a nicer way. + $newFiscalYearStart['d'] = '0' . $newFiscalYearStart['d']; + } + $config->fiscalYearStart = $newFiscalYearStart; + $monthDay = $config->fiscalYearStart['M'] . $config->fiscalYearStart['d']; + } + else { + // First of January. + $monthDay = '0101'; + } + $startDate = "$year$monthDay"; + $endDate = "$nextYear$monthDay"; + + $whereClauses = [ + 'contact_id' => 'IN (' . $contactIDs . ')', + 'is_test' => ' = 0', + 'receive_date' => ['>=' . $startDate, '< ' . $endDate], + ]; + $havingClause = 'contribution_status_id = ' . (int) CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'); + CRM_Financial_BAO_FinancialType::addACLClausesToWhereClauses($whereClauses); + + $clauses = []; + foreach ($whereClauses as $key => $clause) { + $clauses[] = 'b.' . $key . " " . implode(' AND b.' . $key, (array) $clause); + } + $whereClauseString = implode(' AND ', $clauses); + + // See https://github.com/civicrm/civicrm-core/pull/13512 for discussion of how + // this group by + having on contribution_status_id improves performance + $query = " + SELECT COUNT(*) as count, + SUM(total_amount) as amount, + AVG(total_amount) as average, + currency + FROM civicrm_contribution b + WHERE " . $whereClauseString . " + GROUP BY currency, contribution_status_id + HAVING $havingClause + "; + return $query; + } + /** * Assign Test Value. * @@ -5291,7 +5506,7 @@ protected function assignTestValue($fieldName, &$fieldDef, $counter) { */ public static function allowUpdateRevenueRecognitionDate($contributionId) { // get line item for contribution - $lineItems = CRM_Price_BAO_LineItem::getLineItems($contributionId, 'contribution', NULL, TRUE, TRUE); + $lineItems = CRM_Price_BAO_LineItem::getLineItemsByContributionID($contributionId); // check if line item is for membership or participant foreach ($lineItems as $items) { if ($items['entity_table'] == 'civicrm_participant') { @@ -5309,4 +5524,273 @@ public static function allowUpdateRevenueRecognitionDate($contributionId) { return $flag; } + /** + * Create Accounts Receivable financial trxn entry for Completed Contribution. + * + * @param array $trxnParams + * Financial trxn params + * @param array $contributionParams + * Contribution Params + * + * @return null + */ + public static function recordAlwaysAccountsReceivable(&$trxnParams, $contributionParams) { + if (!self::checkContributeSettings('always_post_to_accounts_receivable')) { + return NULL; + } + $statusId = $contributionParams['contribution']->contribution_status_id; + $contributionStatuses = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name'); + $contributionStatus = empty($statusId) ? NULL : $contributionStatuses[$statusId]; + $previousContributionStatus = empty($contributionParams['prevContribution']) ? NULL : $contributionStatuses[$contributionParams['prevContribution']->contribution_status_id]; + // Return if contribution status is not completed. + if (!($contributionStatus == 'Completed' && (empty($previousContributionStatus) + || (!empty($previousContributionStatus) && $previousContributionStatus == 'Pending' + && $contributionParams['prevContribution']->is_pay_later == 0 + ))) + ) { + return NULL; + } + + $params = $trxnParams; + $financialTypeID = CRM_Utils_Array::value('financial_type_id', $contributionParams) ? $contributionParams['financial_type_id'] : $contributionParams['prevContribution']->financial_type_id; + $arAccountId = CRM_Contribute_PseudoConstant::getRelationalFinancialAccount($financialTypeID, 'Accounts Receivable Account is'); + $params['to_financial_account_id'] = $arAccountId; + $params['status_id'] = array_search('Pending', $contributionStatuses); + $params['is_payment'] = FALSE; + $trxn = CRM_Core_BAO_FinancialTrxn::create($params); + self::$_trxnIDs[] = $trxn->id; + $trxnParams['from_financial_account_id'] = $params['to_financial_account_id']; + } + + /** + * Calculate financial item amount when contribution is updated. + * + * @param array $params + * contribution params + * @param array $amountParams + * + * @param string $context + * + * @return float + */ + public static function calculateFinancialItemAmount($params, $amountParams, $context) { + if (!empty($params['is_quick_config'])) { + $amount = $amountParams['item_amount']; + if (!$amount) { + $amount = $params['total_amount']; + if ($context === NULL) { + $amount -= CRM_Utils_Array::value('tax_amount', $params, 0); + } + } + } + else { + $amount = $amountParams['line_total']; + if ($context == 'changedAmount') { + $amount -= $amountParams['previous_line_total']; + } + $amount *= $amountParams['diff']; + } + return $amount; + } + + /** + * Retrieve Sales Tax Financial Accounts. + * + * + * @return array + * + */ + public static function getSalesTaxFinancialAccounts() { + $query = "SELECT cfa.id FROM civicrm_entity_financial_account ce + INNER JOIN civicrm_financial_account cfa ON ce.financial_account_id = cfa.id + WHERE `entity_table` = 'civicrm_financial_type' AND cfa.is_tax = 1 AND ce.account_relationship = %1 GROUP BY cfa.id"; + $accountRel = key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Sales Tax Account is' ")); + $queryParams = array(1 => array($accountRel, 'Integer')); + $dao = CRM_Core_DAO::executeQuery($query, $queryParams); + $financialAccount = array(); + while ($dao->fetch()) { + $financialAccount[$dao->id] = $dao->id; + } + return $financialAccount; + } + + /** + * Create tax entry in civicrm_entity_financial_trxn table. + * + * @param array $entityParams + * + * @param array $eftParams + * + */ + public static function createProportionalEntry($entityParams, $eftParams) { + $paid = 0; + if ($entityParams['contribution_total_amount'] != 0) { + $paid = $entityParams['line_item_amount'] * ($entityParams['trxn_total_amount'] / $entityParams['contribution_total_amount']); + } + // Record Entity Financial Trxn; CRM-20145 + $eftParams['amount'] = CRM_Contribute_BAO_Contribution_Utils::formatAmount($paid); + civicrm_api3('EntityFinancialTrxn', 'create', $eftParams); + } + + /** + * Create array of last financial item id's. + * + * @param int $contributionId + * + * @return array + */ + public static function getLastFinancialItemIds($contributionId) { + $sql = "SELECT fi.id, li.price_field_value_id, li.tax_amount, fi.financial_account_id + FROM civicrm_financial_item fi + INNER JOIN civicrm_line_item li ON li.id = fi.entity_id and fi.entity_table = 'civicrm_line_item' + WHERE li.contribution_id = %1"; + $dao = CRM_Core_DAO::executeQuery($sql, array(1 => array($contributionId, 'Integer'))); + $ftIds = $taxItems = array(); + $salesTaxFinancialAccount = self::getSalesTaxFinancialAccounts(); + while ($dao->fetch()) { + /* if sales tax item*/ + if (in_array($dao->financial_account_id, $salesTaxFinancialAccount)) { + $taxItems[$dao->price_field_value_id] = array( + 'financial_item_id' => $dao->id, + 'amount' => $dao->tax_amount, + ); + } + else { + $ftIds[$dao->price_field_value_id] = $dao->id; + } + } + return array($ftIds, $taxItems); + } + + /** + * Create proportional entries in civicrm_entity_financial_trxn. + * + * @param array $entityParams + * + * @param array $lineItems + * + * @param array $ftIds + * + * @param array $taxItems + * + */ + public static function createProportionalFinancialEntries($entityParams, $lineItems, $ftIds, $taxItems) { + $eftParams = array( + 'entity_table' => 'civicrm_financial_item', + 'financial_trxn_id' => $entityParams['trxn_id'], + ); + foreach ($lineItems as $key => $value) { + if ($value['qty'] == 0) { + continue; + } + $eftParams['entity_id'] = $ftIds[$value['price_field_value_id']]; + $entityParams['line_item_amount'] = $value['line_total']; + self::createProportionalEntry($entityParams, $eftParams); + if (array_key_exists($value['price_field_value_id'], $taxItems)) { + $entityParams['line_item_amount'] = $taxItems[$value['price_field_value_id']]['amount']; + $eftParams['entity_id'] = $taxItems[$value['price_field_value_id']]['financial_item_id']; + self::createProportionalEntry($entityParams, $eftParams); + } + } + } + + /** + * Load entities related to the contribution into $this->_relatedObjects. + * + * @param array $ids + * + * @throws \CRM_Core_Exception + */ + protected function loadRelatedEntitiesByID($ids) { + $entities = array( + 'contact' => 'CRM_Contact_BAO_Contact', + 'contributionRecur' => 'CRM_Contribute_BAO_ContributionRecur', + 'contributionType' => 'CRM_Financial_BAO_FinancialType', + 'financialType' => 'CRM_Financial_BAO_FinancialType', + 'contributionPage' => 'CRM_Contribute_BAO_ContributionPage', + ); + foreach ($entities as $entity => $bao) { + if (!empty($ids[$entity])) { + $this->_relatedObjects[$entity] = new $bao(); + $this->_relatedObjects[$entity]->id = $ids[$entity]; + if (!$this->_relatedObjects[$entity]->find(TRUE)) { + throw new CRM_Core_Exception($entity . ' could not be loaded'); + } + } + } + } + + /** + * Should an email receipt be sent for this contribution when complete. + * + * @param array $input + * + * @return mixed + */ + protected function isEmailReceipt($input) { + if (isset($input['is_email_receipt'])) { + return $input['is_email_receipt']; + } + if (!empty($this->_relatedObjects['contribution_page_id'])) { + return $this->_relatedObjects['contribution_page_id']->is_email_receipt; + } + return TRUE; + } + + /** + * Function to replace contribution tokens. + * + * @param array $contributionIds + * + * @param string $subject + * + * @param array $subjectToken + * + * @param string $text + * + * @param string $html + * + * @param array $messageToken + * + * @param bool $escapeSmarty + * + * @return array + */ + public static function replaceContributionTokens( + $contributionIds, + $subject, + $subjectToken, + $text, + $html, + $messageToken, + $escapeSmarty + ) { + if (empty($contributionIds)) { + return array(); + } + $contributionDetails = array(); + foreach ($contributionIds as $id) { + $result = civicrm_api3('Contribution', 'get', array('id' => $id)); + $contributionDetails[$result['values'][$result['id']]['contact_id']]['subject'] = CRM_Utils_Token::replaceContributionTokens($subject, $result, FALSE, $subjectToken, FALSE, $escapeSmarty); + $contributionDetails[$result['values'][$result['id']]['contact_id']]['text'] = CRM_Utils_Token::replaceContributionTokens($text, $result, FALSE, $messageToken, FALSE, $escapeSmarty); + $contributionDetails[$result['values'][$result['id']]['contact_id']]['html'] = CRM_Utils_Token::replaceContributionTokens($html, $result, FALSE, $messageToken, FALSE, $escapeSmarty); + } + return $contributionDetails; + } + + /** + * Get invoice_number for contribution. + * + * @param int $contributionID + * + * @return string + */ + public static function getInvoiceNumber($contributionID) { + if ($invoicePrefix = self::checkContributeSettings('invoice_prefix', TRUE)) { + return $invoicePrefix . $contributionID; + } + + return NULL; + } + } diff --git a/CRM/Contribute/BAO/Contribution/Utils.php b/CRM/Contribute/BAO/Contribution/Utils.php index ab6453648d4b..08c84098c384 100644 --- a/CRM/Contribute/BAO/Contribution/Utils.php +++ b/CRM/Contribute/BAO/Contribution/Utils.php @@ -1,9 +1,9 @@ _bltID, $form->_params, $paymentParams, TRUE); - $lineItems = $form->_lineItem; $isPaymentTransaction = self::isPaymentTransaction($form); $financialType = new CRM_Financial_DAO_FinancialType(); - $financialType->id = $contributionTypeId; + $financialType->id = $financialTypeID; $financialType->find(TRUE); if ($financialType->is_deductible) { $form->assign('is_deductible', TRUE); @@ -77,36 +74,68 @@ public static function processConfirm( // add some financial type details to the params list // if folks need to use it + //CRM-15297 deprecate contributionTypeID + $paymentParams['financial_type_id'] = $paymentParams['financialTypeID'] = $paymentParams['contributionTypeID'] = $financialType->id; //CRM-15297 - contributionType is obsolete - pass financial type as well so people can deprecate it $paymentParams['financialType_name'] = $paymentParams['contributionType_name'] = $form->_params['contributionType_name'] = $financialType->name; //CRM-11456 - $paymentParams['financialType_accounting_code'] = $paymentParams['contributionType_accounting_code'] = $form->_params['contributionType_accounting_code'] = CRM_Financial_BAO_FinancialAccount::getAccountingCode($contributionTypeId); + $paymentParams['financialType_accounting_code'] = $paymentParams['contributionType_accounting_code'] = $form->_params['contributionType_accounting_code'] = CRM_Financial_BAO_FinancialAccount::getAccountingCode($financialTypeID); $paymentParams['contributionPageID'] = $form->_params['contributionPageID'] = $form->_values['id']; $paymentParams['contactID'] = $form->_params['contactID'] = $contactID; //fix for CRM-16317 - $form->_params['receive_date'] = date('YmdHis'); + if (empty($form->_params['receive_date'])) { + $form->_params['receive_date'] = date('YmdHis'); + } + if (!empty($form->_params['start_date'])) { + $form->_params['start_date'] = date('YmdHis'); + } $form->assign('receive_date', CRM_Utils_Date::mysqlToIso($form->_params['receive_date']) ); + if (empty($form->_values['amount'])) { + // If the amount is not in _values[], set it + $form->_values['amount'] = $form->_params['amount']; + } + if ($isPaymentTransaction) { - $contributionParams = array( + $contributionParams = [ 'id' => CRM_Utils_Array::value('contribution_id', $paymentParams), 'contact_id' => $contactID, - 'line_item' => $lineItems, 'is_test' => $isTest, - 'campaign_id' => CRM_Utils_Array::value('campaign_id', $paymentParams, CRM_Utils_Array::value('campaign_id', $form->_values)), - 'contribution_page_id' => $form->_id, 'source' => CRM_Utils_Array::value('source', $paymentParams, CRM_Utils_Array::value('description', $paymentParams)), - ); - $isMonetary = !empty($form->_values['is_monetary']); - if ($isMonetary) { - if (empty($paymentParams['is_pay_later'])) { - // @todo look up payment_instrument_id on payment processor table. - $contributionParams['payment_instrument_id'] = 1; - } + ]; + + // CRM-21200: Don't overwrite contribution details during 'Pay now' payment + if (empty($form->_params['contribution_id'])) { + $contributionParams['contribution_page_id'] = $form->_id; + $contributionParams['campaign_id'] = CRM_Utils_Array::value('campaign_id', $paymentParams, CRM_Utils_Array::value('campaign_id', $form->_values)); + } + // In case of 'Pay now' payment, append the contribution source with new text 'Paid later via page ID: N.' + else { + // contribution.source only allows 255 characters so we are using ellipsify(...) to ensure it. + $contributionParams['source'] = CRM_Utils_String::ellipsify( + ts('Paid later via page ID: %1. %2', [ + 1 => $form->_id, + 2 => $contributionParams['source'], + ]), + // eventually activity.description append price information to source text so keep it 220 to ensure string length doesn't exceed 255 characters. + 220 + ); + } + + if (isset($paymentParams['line_item'])) { + // @todo make sure this is consisently set at this point. + $contributionParams['line_item'] = $paymentParams['line_item']; + } + if (!empty($form->_paymentProcessor)) { + $contributionParams['payment_instrument_id'] = $paymentParams['payment_instrument_id'] = $form->_paymentProcessor['payment_instrument_id']; } + + // @todo this is the wrong place for this - it should be done as close to form submission + // as possible + $paymentParams['amount'] = CRM_Utils_Rule::cleanMoney($paymentParams['amount']); $contribution = CRM_Contribute_Form_Contribution_Confirm::processFormContribution( $form, $paymentParams, @@ -118,23 +147,22 @@ public static function processConfirm( $isRecur ); - $paymentParams['contributionTypeID'] = $contributionTypeId; $paymentParams['item_name'] = $form->_params['description']; - $paymentParams['qfKey'] = $form->controller->_key; - if ($component == 'membership') { - return array('contribution' => $contribution); + $paymentParams['qfKey'] = empty($paymentParams['qfKey']) ? $form->controller->_key : $paymentParams['qfKey']; + if ($paymentParams['skipLineItem']) { + // We are not processing the line item here because we are processing a membership. + // Do not continue with contribution processing in this function. + return ['contribution' => $contribution]; } $paymentParams['contributionID'] = $contribution->id; - //CRM-15297 deprecate contributionTypeID - $paymentParams['financialTypeID'] = $paymentParams['contributionTypeID'] = $contribution->financial_type_id; $paymentParams['contributionPageID'] = $contribution->contribution_page_id; if (isset($paymentParams['contribution_source'])) { $paymentParams['source'] = $paymentParams['contribution_source']; } - if ($form->_values['is_recur'] && $contribution->contribution_recur_id) { + if (CRM_Utils_Array::value('is_recur', $form->_params) && $contribution->contribution_recur_id) { $paymentParams['contributionRecurID'] = $contribution->contribution_recur_id; } if (isset($paymentParams['contribution_source'])) { @@ -202,16 +230,13 @@ public static function processConfirm( // This is kind of a back-up for pay-later $0 transactions. // In other flows they pick up the manual processor & get dealt with above (I // think that might be better...). - return array( + return [ 'payment_status_id' => 1, 'contribution' => $contribution, 'payment_processor_id' => 0, - ); - } - elseif (empty($form->_values['amount'])) { - // If the amount is not in _values[], set it - $form->_values['amount'] = $form->_params['amount']; + ]; } + CRM_Contribute_BAO_ContributionPage::sendMail($contactID, $form->_values, $contribution->is_test @@ -220,13 +245,14 @@ public static function processConfirm( /** * Is a payment being made. + * * Note that setting is_monetary on the form is somewhat legacy and the behaviour around this setting is confusing. It would be preferable * to look for the amount only (assuming this cannot refer to payment in goats or other non-monetary currency * @param CRM_Core_Form $form * * @return bool */ - static protected function isPaymentTransaction($form) { + protected static function isPaymentTransaction($form) { return ($form->_amount >= 0.0) ? TRUE : FALSE; } @@ -241,11 +267,11 @@ static protected function isPaymentTransaction($form) { */ public static function contributionChartMonthly($param) { if ($param) { - $param = array(1 => array($param, 'Integer')); + $param = [1 => [$param, 'Integer']]; } else { $param = date("Y"); - $param = array(1 => array($param, 'Integer')); + $param = [1 => [$param, 'Integer']]; } $query = " @@ -360,12 +386,12 @@ public static function _fillCommonParams(&$params, $type = 'paypal') { $params['address'][1]['location_type_id'] = $billingLocTypeId; } if (!CRM_Utils_System::isNull($params['email'])) { - $params['email'] = array( - 1 => array( + $params['email'] = [ + 1 => [ 'email' => $params['email'], 'location_type_id' => $billingLocTypeId, - ), - ); + ], + ]; } if (isset($transaction['trxn_id'])) { @@ -395,7 +421,7 @@ public static function _fillCommonParams(&$params, $type = 'paypal') { } $source = ts('ContributionProcessor: %1 API', - array(1 => ucfirst($type)) + [1 => ucfirst($type)] ); if (isset($transaction['source'])) { $transaction['source'] = $source . ':: ' . $transaction['source']; @@ -416,7 +442,7 @@ public static function getFirstLastDetails($contactID) { static $_cache; if (!$_cache) { - $_cache = array(); + $_cache = []; } if (!isset($_cache[$contactID])) { @@ -427,28 +453,28 @@ public static function getFirstLastDetails($contactID) { ORDER BY receive_date ASC LIMIT 1 "; - $params = array(1 => array($contactID, 'Integer')); + $params = [1 => [$contactID, 'Integer']]; $dao = CRM_Core_DAO::executeQuery($sql, $params); - $details = array( + $details = [ 'first' => NULL, 'last' => NULL, - ); + ]; if ($dao->fetch()) { - $details['first'] = array( + $details['first'] = [ 'total_amount' => $dao->total_amount, 'receive_date' => $dao->receive_date, - ); + ]; } // flip asc and desc to get the last query $sql = str_replace('ASC', 'DESC', $sql); $dao = CRM_Core_DAO::executeQuery($sql, $params); if ($dao->fetch()) { - $details['last'] = array( + $details['last'] = [ 'total_amount' => $dao->total_amount, 'receive_date' => $dao->receive_date, - ); + ]; } $_cache[$contactID] = $details; @@ -463,16 +489,155 @@ public static function getFirstLastDetails($contactID) { * Amount of field. * @param float $taxRate * Tax rate of selected financial account for field. + * @param bool $ugWeDoNotKnowIfItNeedsCleaning_Help + * This should ALWAYS BE FALSE and then be removed. A 'clean' money string uses a standardised format + * such as '1000.99' for one thousand $/Euro/CUR and ninety nine cents/units. + * However, we are in the habit of not necessarily doing that so need to grandfather in + * the new expectation. * * @return array * array of tax amount * */ - public static function calculateTaxAmount($amount, $taxRate) { - $taxAmount = array(); - $taxAmount['tax_amount'] = round(($taxRate / 100) * CRM_Utils_Rule::cleanMoney($amount), 2); + public static function calculateTaxAmount($amount, $taxRate, $ugWeDoNotKnowIfItNeedsCleaning_Help = FALSE) { + $taxAmount = []; + if ($ugWeDoNotKnowIfItNeedsCleaning_Help) { + Civi::log()->warning('Deprecated function, make sure money is in usable format before calling this.', ['civi.tag' => 'deprecated']); + $amount = CRM_Utils_Rule::cleanMoney($amount); + } + // There can not be any rounding at this stage - as this is prior to quantity multiplication + $taxAmount['tax_amount'] = ($taxRate / 100) * $amount; return $taxAmount; } + /** + * Format monetary amount: round and return to desired decimal place + * CRM-20145 + * + * @param float $amount + * Monetary amount + * @param int $decimals + * How many decimal places to round to and return + * + * @return float + * Amount rounded and returned with the desired decimal places + */ + public static function formatAmount($amount, $decimals = 2) { + return number_format((float) round($amount, (int) $decimals), (int) $decimals, '.', ''); + } + + /** + * Get contribution statuses by entity e.g. contribution, membership or 'participant' + * + * @param string $usedFor + * @param int $id + * Contribution ID + * + * @return array + * Array of contribution statuses in array('status id' => 'label') format + */ + public static function getContributionStatuses($usedFor = 'contribution', $id = NULL) { + if ($usedFor == 'pledge') { + $statusNames = CRM_Pledge_BAO_Pledge::buildOptions('status_id', 'validate'); + } + else { + $statusNames = CRM_Contribute_BAO_Contribution::buildOptions('contribution_status_id', 'validate'); + } + + $statusNamesToUnset = []; + // on create fetch statuses on basis of component + if (!$id) { + $statusNamesToUnset = [ + 'Refunded', + 'Chargeback', + 'Pending refund', + ]; + + // Event registration and New Membership backoffice form support partially paid payment, + // so exclude this status only for 'New Contribution' form + if ($usedFor == 'contribution') { + $statusNamesToUnset = array_merge($statusNamesToUnset, [ + 'In Progress', + 'Overdue', + 'Partially paid', + ]); + } + elseif ($usedFor == 'participant') { + $statusNamesToUnset = array_merge($statusNamesToUnset, [ + 'Cancelled', + 'Failed', + ]); + } + elseif ($usedFor == 'membership') { + $statusNamesToUnset = array_merge($statusNamesToUnset, [ + 'In Progress', + 'Overdue', + ]); + } + } + else { + $contributionStatus = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_Contribution', $id, 'contribution_status_id'); + $name = CRM_Utils_Array::value($contributionStatus, $statusNames); + switch ($name) { + case 'Completed': + // [CRM-17498] Removing unsupported status change options. + $statusNamesToUnset = array_merge($statusNamesToUnset, [ + 'Pending', + 'Failed', + 'Partially paid', + 'Pending refund', + ]); + break; + + case 'Cancelled': + case 'Chargeback': + case 'Refunded': + $statusNamesToUnset = array_merge($statusNamesToUnset, [ + 'Pending', + 'Failed', + ]); + break; + + case 'Pending': + case 'In Progress': + $statusNamesToUnset = array_merge($statusNamesToUnset, [ + 'Refunded', + 'Chargeback', + ]); + break; + + case 'Failed': + $statusNamesToUnset = array_merge($statusNamesToUnset, [ + 'Pending', + 'Refunded', + 'Chargeback', + 'Completed', + 'In Progress', + 'Cancelled', + ]); + break; + } + } + + foreach ($statusNamesToUnset as $name) { + unset($statusNames[CRM_Utils_Array::key($name, $statusNames)]); + } + + // based on filtered statuse names fetch the final list of statuses in array('id' => 'label') format + if ($usedFor == 'pledge') { + $statuses = CRM_Pledge_BAO_Pledge::buildOptions('status_id'); + } + else { + $statuses = CRM_Contribute_BAO_Contribution::buildOptions('contribution_status_id'); + } + foreach ($statuses as $statusID => $label) { + if (!array_key_exists($statusID, $statusNames)) { + unset($statuses[$statusID]); + } + } + + return $statuses; + } + } diff --git a/CRM/Contribute/BAO/ContributionPage.php b/CRM/Contribute/BAO/ContributionPage.php index 15902f3c60b8..492afdb67c0b 100644 --- a/CRM/Contribute/BAO/ContributionPage.php +++ b/CRM/Contribute/BAO/ContributionPage.php @@ -1,9 +1,9 @@ $id); + $params = ['id' => $id]; CRM_Core_DAO::commonRetrieve('CRM_Contribute_DAO_ContributionPage', $params, $values); // get the profile ids - $ufJoinParams = array( + $ufJoinParams = [ 'entity_table' => 'civicrm_contribution_page', 'entity_id' => $id, - ); + ]; // retrieve profile id as also unserialize module_data corresponding to each $module foreach ($modules as $module) { @@ -150,15 +150,15 @@ public static function setValues($id, &$values) { * @param array $fieldTypes */ public static function sendMail($contactID, $values, $isTest = FALSE, $returnMessageText = FALSE, $fieldTypes = NULL) { - $gIds = array(); - $params = array('custom_pre_id' => array(), 'custom_post_id' => array()); + $gIds = []; + $params = ['custom_pre_id' => [], 'custom_post_id' => []]; $email = NULL; // We are trying to fight the good fight against leaky variables (CRM-17519) so let's get really explicit // about ensuring the variables we want for the template are defined. // @todo add to this until all tpl params are explicit in this function and not waltzing around the codebase. // Next stage is to remove this & ensure there are no e-notices - ie. all are set before they hit this fn. - $valuesRequiredForTemplate = array( + $valuesRequiredForTemplate = [ 'customPre', 'customPost', 'customPre_grouptitle', @@ -168,7 +168,7 @@ public static function sendMail($contactID, $values, $isTest = FALSE, $returnMes 'amount', 'receipt_date', 'is_pay_later', - ); + ]; foreach ($valuesRequiredForTemplate as $valueRequiredForTemplate) { if (!isset($values[$valueRequiredForTemplate])) { @@ -179,26 +179,26 @@ public static function sendMail($contactID, $values, $isTest = FALSE, $returnMes if (isset($values['custom_pre_id'])) { $preProfileType = CRM_Core_BAO_UFField::getProfileType($values['custom_pre_id']); if ($preProfileType == 'Membership' && !empty($values['membership_id'])) { - $params['custom_pre_id'] = array( - array( + $params['custom_pre_id'] = [ + [ 'member_id', '=', $values['membership_id'], 0, 0, - ), - ); + ], + ]; } elseif ($preProfileType == 'Contribution' && !empty($values['contribution_id'])) { - $params['custom_pre_id'] = array( - array( + $params['custom_pre_id'] = [ + [ 'contribution_id', '=', $values['contribution_id'], 0, 0, - ), - ); + ], + ]; } $gIds['custom_pre_id'] = $values['custom_pre_id']; @@ -207,26 +207,26 @@ public static function sendMail($contactID, $values, $isTest = FALSE, $returnMes if (isset($values['custom_post_id'])) { $postProfileType = CRM_Core_BAO_UFField::getProfileType($values['custom_post_id']); if ($postProfileType == 'Membership' && !empty($values['membership_id'])) { - $params['custom_post_id'] = array( - array( + $params['custom_post_id'] = [ + [ 'member_id', '=', $values['membership_id'], 0, 0, - ), - ); + ], + ]; } elseif ($postProfileType == 'Contribution' && !empty($values['contribution_id'])) { - $params['custom_post_id'] = array( - array( + $params['custom_post_id'] = [ + [ 'contribution_id', '=', $values['contribution_id'], 0, 0, - ), - ); + ], + ]; } $gIds['custom_post_id'] = $values['custom_post_id']; @@ -234,48 +234,48 @@ public static function sendMail($contactID, $values, $isTest = FALSE, $returnMes if (!empty($values['is_for_organization'])) { if (!empty($values['membership_id'])) { - $params['onbehalf_profile'] = array( - array( + $params['onbehalf_profile'] = [ + [ 'member_id', '=', $values['membership_id'], 0, 0, - ), - ); + ], + ]; } elseif (!empty($values['contribution_id'])) { - $params['onbehalf_profile'] = array( - array( + $params['onbehalf_profile'] = [ + [ 'contribution_id', '=', $values['contribution_id'], 0, 0, - ), - ); + ], + ]; } } //check whether it is a test drive if ($isTest && !empty($params['custom_pre_id'])) { - $params['custom_pre_id'][] = array( + $params['custom_pre_id'][] = [ 'contribution_test', '=', 1, 0, 0, - ); + ]; } if ($isTest && !empty($params['custom_post_id'])) { - $params['custom_post_id'][] = array( + $params['custom_post_id'][] = [ 'contribution_test', '=', 1, 0, 0, - ); + ]; } if (!$returnMessageText && !empty($gIds)) { @@ -349,7 +349,7 @@ public static function sendMail($contactID, $values, $isTest = FALSE, $returnMes } if (isset($values['honor'])) { $honorValues = $values['honor']; - $template->_values = array('honoree_profile_id' => $values['honoree_profile_id']); + $template->_values = ['honoree_profile_id' => $values['honoree_profile_id']]; CRM_Contribute_BAO_ContributionSoft::formatHonoreeProfileFields( $template, $honorValues['honor_profile_values'], @@ -361,7 +361,7 @@ public static function sendMail($contactID, $values, $isTest = FALSE, $returnMes // Set email variables explicitly to avoid leaky smarty variables. // All of these will be assigned to the template, replacing any that might be assigned elsewhere. - $tplParams = array( + $tplParams = [ 'email' => $email, 'receiptFromEmail' => CRM_Utils_Array::value('receipt_from_email', $values), 'contactID' => $contactID, @@ -385,7 +385,9 @@ public static function sendMail($contactID, $values, $isTest = FALSE, $returnMes 'is_pay_later' => $values['is_pay_later'], 'receipt_date' => !$values['receipt_date'] ? NULL : date('YmdHis', strtotime($values['receipt_date'])), 'pay_later_receipt' => CRM_Utils_Array::value('pay_later_receipt', $values), - ); + 'honor_block_is_active' => CRM_Utils_Array::value('honor_block_is_active', $values), + 'contributionStatus' => CRM_Utils_Array::value('contribution_status', $values), + ]; if ($contributionTypeId = CRM_Utils_Array::value('financial_type_id', $values)) { $tplParams['financialTypeId'] = $contributionTypeId; @@ -430,23 +432,23 @@ public static function sendMail($contactID, $values, $isTest = FALSE, $returnMes } // use either the contribution or membership receipt, based on whether it’s a membership-related contrib or not - $sendTemplateParams = array( + $sendTemplateParams = [ 'groupName' => !empty($values['isMembership']) ? 'msg_tpl_workflow_membership' : 'msg_tpl_workflow_contribution', 'valueName' => !empty($values['isMembership']) ? 'membership_online_receipt' : 'contribution_online_receipt', 'contactId' => $contactID, 'tplParams' => $tplParams, 'isTest' => $isTest, 'PDFFilename' => 'receipt.pdf', - ); + ]; if ($returnMessageText) { list($sent, $subject, $message, $html) = CRM_Core_BAO_MessageTemplate::sendTemplate($sendTemplateParams); - return array( + return [ 'subject' => $subject, 'body' => $message, 'to' => $displayName, 'html' => $html, - ); + ]; } if (empty($values['receipt_from_name']) && empty($values['receipt_from_name'])) { @@ -499,9 +501,9 @@ public static function sendMail($contactID, $values, $isTest = FALSE, $returnMes * * @return array */ - protected static function getProfileNameAndFields($gid, $cid, &$params, $fieldTypes = array()) { + protected static function getProfileNameAndFields($gid, $cid, &$params, $fieldTypes = []) { $groupTitle = NULL; - $values = array(); + $values = []; if ($gid) { if (CRM_Core_BAO_UFGroup::filterUFGroups($gid, $cid)) { $fields = CRM_Core_BAO_UFGroup::getFields($gid, FALSE, CRM_Core_Action::VIEW, NULL, NULL, FALSE, NULL, FALSE, NULL, CRM_Core_Permission::CREATE, NULL); @@ -526,7 +528,7 @@ protected static function getProfileNameAndFields($gid, $cid, &$params, $fieldTy CRM_Core_BAO_UFGroup::getValues($cid, $fields, $values, FALSE, $params); } } - return array($groupTitle, $values); + return [$groupTitle, $values]; } /** @@ -543,17 +545,17 @@ protected static function getProfileNameAndFields($gid, $cid, &$params, $fieldTy * @param bool|object $autoRenewMembership is it a auto renew membership. */ public static function recurringNotify($type, $contactID, $pageID, $recur, $autoRenewMembership = FALSE) { - $value = array(); + $value = []; $isEmailReceipt = FALSE; if ($pageID) { - CRM_Core_DAO::commonRetrieveAll('CRM_Contribute_DAO_ContributionPage', 'id', $pageID, $value, array( + CRM_Core_DAO::commonRetrieveAll('CRM_Contribute_DAO_ContributionPage', 'id', $pageID, $value, [ 'title', 'is_email_receipt', 'receipt_from_name', 'receipt_from_email', 'cc_receipt', 'bcc_receipt', - )); + ]); $isEmailReceipt = CRM_Utils_Array::value('is_email_receipt', $value[$pageID]); } elseif ($recur->id) { @@ -577,11 +579,11 @@ public static function recurringNotify($type, $contactID, $pageID, $recur, $auto } list($displayName, $email) = CRM_Contact_BAO_Contact_Location::getEmailDetails($contactID, FALSE); - $templatesParams = array( + $templatesParams = [ 'groupName' => 'msg_tpl_workflow_contribution', 'valueName' => 'contribution_recurring_notify', 'contactId' => $contactID, - 'tplParams' => array( + 'tplParams' => [ 'recur_frequency_interval' => $recur->frequency_interval, 'recur_frequency_unit' => $recur->frequency_unit, 'recur_installments' => $recur->installments, @@ -593,11 +595,11 @@ public static function recurringNotify($type, $contactID, $pageID, $recur, $auto 'receipt_from_name' => $receiptFromName, 'receipt_from_email' => $receiptFromEmail, 'auto_renew_membership' => $autoRenewMembership, - ), + ], 'from' => $receiptFrom, 'toName' => $displayName, 'toEmail' => $email, - ); + ]; //CRM-13811 if ($pageID) { $templatesParams['cc'] = CRM_Utils_Array::value('cc_receipt', $value[$pageID]); @@ -665,67 +667,67 @@ public static function buildCustomDisplay($gid, $name, $cid, &$template, &$param * @return CRM_Contribute_DAO_ContributionPage */ public static function copy($id) { - $fieldsFix = array( - 'prefix' => array( + $fieldsFix = [ + 'prefix' => [ 'title' => ts('Copy of') . ' ', - ), - ); - $copy = &CRM_Core_DAO::copyGeneric('CRM_Contribute_DAO_ContributionPage', array( + ], + ]; + $copy = &CRM_Core_DAO::copyGeneric('CRM_Contribute_DAO_ContributionPage', [ 'id' => $id, - ), NULL, $fieldsFix); + ], NULL, $fieldsFix); //copying all the blocks pertaining to the contribution page - $copyPledgeBlock = &CRM_Core_DAO::copyGeneric('CRM_Pledge_DAO_PledgeBlock', array( + $copyPledgeBlock = &CRM_Core_DAO::copyGeneric('CRM_Pledge_DAO_PledgeBlock', [ 'entity_id' => $id, 'entity_table' => 'civicrm_contribution_page', - ), array( + ], [ 'entity_id' => $copy->id, - )); + ]); - $copyMembershipBlock = &CRM_Core_DAO::copyGeneric('CRM_Member_DAO_MembershipBlock', array( + $copyMembershipBlock = &CRM_Core_DAO::copyGeneric('CRM_Member_DAO_MembershipBlock', [ 'entity_id' => $id, 'entity_table' => 'civicrm_contribution_page', - ), array( + ], [ 'entity_id' => $copy->id, - )); + ]); - $copyUFJoin = &CRM_Core_DAO::copyGeneric('CRM_Core_DAO_UFJoin', array( + $copyUFJoin = &CRM_Core_DAO::copyGeneric('CRM_Core_DAO_UFJoin', [ 'entity_id' => $id, 'entity_table' => 'civicrm_contribution_page', - ), array( + ], [ 'entity_id' => $copy->id, - )); + ]); - $copyWidget = &CRM_Core_DAO::copyGeneric('CRM_Contribute_DAO_Widget', array( + $copyWidget = &CRM_Core_DAO::copyGeneric('CRM_Contribute_DAO_Widget', [ 'contribution_page_id' => $id, - ), array( + ], [ 'contribution_page_id' => $copy->id, - )); + ]); //copy price sets CRM_Price_BAO_PriceSet::copyPriceSet('civicrm_contribution_page', $id, $copy->id); - $copyTellFriend = &CRM_Core_DAO::copyGeneric('CRM_Friend_DAO_Friend', array( + $copyTellFriend = &CRM_Core_DAO::copyGeneric('CRM_Friend_DAO_Friend', [ 'entity_id' => $id, 'entity_table' => 'civicrm_contribution_page', - ), array( + ], [ 'entity_id' => $copy->id, - )); + ]); - $copyPersonalCampaignPages = &CRM_Core_DAO::copyGeneric('CRM_PCP_DAO_PCPBlock', array( + $copyPersonalCampaignPages = &CRM_Core_DAO::copyGeneric('CRM_PCP_DAO_PCPBlock', [ 'entity_id' => $id, 'entity_table' => 'civicrm_contribution_page', - ), array( + ], [ 'entity_id' => $copy->id, 'target_entity_id' => $copy->id, - )); + ]); - $copyPremium = &CRM_Core_DAO::copyGeneric('CRM_Contribute_DAO_Premium', array( + $copyPremium = &CRM_Core_DAO::copyGeneric('CRM_Contribute_DAO_Premium', [ 'entity_id' => $id, 'entity_table' => 'civicrm_contribution_page', - ), array( + ], [ 'entity_id' => $copy->id, - )); + ]); $premiumQuery = " SELECT id FROM civicrm_premiums @@ -735,11 +737,11 @@ public static function copy($id) { $premiumDao = CRM_Core_DAO::executeQuery($premiumQuery, CRM_Core_DAO::$_nullArray); while ($premiumDao->fetch()) { if ($premiumDao->id) { - CRM_Core_DAO::copyGeneric('CRM_Contribute_DAO_PremiumsProduct', array( + CRM_Core_DAO::copyGeneric('CRM_Contribute_DAO_PremiumsProduct', [ 'premiums_id' => $premiumDao->id, - ), array( + ], [ 'premiums_id' => $copyPremium->id, - )); + ]); } } @@ -757,14 +759,14 @@ public static function copy($id) { * @return array * info regarding all sections. */ - public static function getSectionInfo($contribPageIds = array()) { - $info = array(); + public static function getSectionInfo($contribPageIds = []) { + $info = []; $whereClause = NULL; if (is_array($contribPageIds) && !empty($contribPageIds)) { $whereClause = 'WHERE civicrm_contribution_page.id IN ( ' . implode(', ', $contribPageIds) . ' )'; } - $sections = array( + $sections = [ 'settings', 'amount', 'membership', @@ -774,7 +776,7 @@ public static function getSectionInfo($contribPageIds = array()) { 'pcp', 'widget', 'premium', - ); + ]; $query = " SELECT civicrm_contribution_page.id as id, civicrm_contribution_page.financial_type_id as settings, @@ -832,8 +834,8 @@ public static function getSectionInfo($contribPageIds = array()) { * * @return array|bool */ - public static function buildOptions($fieldName, $context = NULL, $props = array()) { - $params = array(); + public static function buildOptions($fieldName, $context = NULL, $props = []) { + $params = []; // Special logic for fields whose options depend on context or properties switch ($fieldName) { case 'financial_type_id': @@ -861,21 +863,21 @@ public static function formatModuleData($params, $setDefault = FALSE, $module) { $domain = new CRM_Core_DAO_Domain(); $domain->find(TRUE); - $moduleDataFormat = array( - 'soft_credit' => array( + $moduleDataFormat = [ + 'soft_credit' => [ 1 => 'soft_credit_types', - 'multilingual' => array( + 'multilingual' => [ 'honor_block_title', 'honor_block_text', - ), - ), - 'on_behalf' => array( + ], + ], + 'on_behalf' => [ 1 => 'is_for_organization', - 'multilingual' => array( + 'multilingual' => [ 'for_organization', - ), - ), - ); + ], + ], + ]; //When we are fetching the honor params respecting both multi and mono lingual state //and setting it to default param of Contribution Page's Main and Setting form @@ -900,10 +902,10 @@ public static function formatModuleData($params, $setDefault = FALSE, $module) { //check and handle multilingual honoree params if (!$domain->locales) { //if in singlelingual state simply return the array format - $json = array($module => NULL); + $json = [$module => NULL]; foreach ($moduleDataFormat[$module] as $key => $attribute) { if ($key === 'multilingual') { - $json[$module]['default'] = array(); + $json[$module]['default'] = []; foreach ($attribute as $attr) { $json[$module]['default'][$attr] = $params[$attr]; } @@ -917,10 +919,10 @@ public static function formatModuleData($params, $setDefault = FALSE, $module) { else { //if in multilingual state then retrieve the module_data against this contribution and //merge with earlier module_data json data to current so not to lose earlier multilingual module_data information - $json = array($module => NULL); + $json = [$module => NULL]; foreach ($moduleDataFormat[$module] as $key => $attribute) { if ($key === 'multilingual') { - $json[$module][$config->lcMessages] = array(); + $json[$module][$config->lcMessages] = []; foreach ($attribute as $attr) { $json[$module][$config->lcMessages][$attr] = $params[$attr]; } @@ -952,14 +954,13 @@ public static function formatModuleData($params, $setDefault = FALSE, $module) { * @return array */ public static function addInvoicePdfToEmail($contributionId, $userID) { - $contributionID = array($contributionId); - $contactId = array($userID); - $pdfParams = array( + $contributionID = [$contributionId]; + $contactId = [$userID]; + $pdfParams = [ 'output' => 'pdf_invoice', 'forPage' => 'confirmpage', - ); - $pdfHtml = CRM_Contribute_Form_Task_Invoice::printPDF($contributionID, - $pdfParams, $contactId, CRM_Core_DAO::$_nullObject); + ]; + $pdfHtml = CRM_Contribute_Form_Task_Invoice::printPDF($contributionID, $pdfParams, $contactId); return $pdfHtml; } @@ -972,11 +973,11 @@ public static function addInvoicePdfToEmail($contributionId, $userID) { * isSeparateMembershipPayment */ public static function getIsMembershipPayment($id) { - $membershipBlocks = civicrm_api3('membership_block', 'get', array( - 'entity_table' => 'civicrm_contribution_page', - 'entity_id' => $id, - 'sequential' => TRUE, - )); + $membershipBlocks = civicrm_api3('membership_block', 'get', [ + 'entity_table' => 'civicrm_contribution_page', + 'entity_id' => $id, + 'sequential' => TRUE, + ]); if (!$membershipBlocks['count']) { return FALSE; } diff --git a/CRM/Contribute/BAO/ContributionRecur.php b/CRM/Contribute/BAO/ContributionRecur.php index ea40fa3d7423..c72b03236836 100644 --- a/CRM/Contribute/BAO/ContributionRecur.php +++ b/CRM/Contribute/BAO/ContributionRecur.php @@ -1,9 +1,9 @@ push(CRM_Core_Error::DUPLICATE_CONTRIBUTION, 'Fatal', - array($d), + [$d], "Found matching recurring contribution(s): $d" ); return $error; @@ -89,7 +96,7 @@ public static function add(&$params) { $config = CRM_Core_Config::singleton(); $recurring->currency = $config->defaultCurrency; } - $result = $recurring->save(); + $recurring->save(); if (!empty($params['id'])) { CRM_Utils_Hook::post('edit', 'ContributionRecur', $recurring->id, $recurring); @@ -104,7 +111,7 @@ public static function add(&$params) { CRM_Core_BAO_CustomValueTable::store($params['custom'], 'civicrm_contribution_recur', $recurring->id); } - return $result; + return $recurring; } /** @@ -123,17 +130,17 @@ public static function checkDuplicate($params, &$duplicates) { $trxn_id = CRM_Utils_Array::value('trxn_id', $params); $invoice_id = CRM_Utils_Array::value('invoice_id', $params); - $clause = array(); - $params = array(); + $clause = []; + $params = []; if ($trxn_id) { $clause[] = "trxn_id = %1"; - $params[1] = array($trxn_id, 'String'); + $params[1] = [$trxn_id, 'String']; } if ($invoice_id) { $clause[] = "invoice_id = %2"; - $params[2] = array($invoice_id, 'String'); + $params[2] = [$invoice_id, 'String']; } if (empty($clause)) { @@ -143,7 +150,7 @@ public static function checkDuplicate($params, &$duplicates) { $clause = implode(' OR ', $clause); if ($id) { $clause = "( $clause ) AND id != %3"; - $params[3] = array($id, 'Integer'); + $params[3] = [$id, 'Integer']; } $query = "SELECT id FROM civicrm_contribution_recur WHERE $clause"; @@ -160,25 +167,44 @@ public static function checkDuplicate($params, &$duplicates) { * Get the payment processor (array) for a recurring processor. * * @param int $id - * @param string $mode - * - Test or NULL - all other variants are ignored. * * @return array|null */ - public static function getPaymentProcessor($id, $mode = NULL) { - $sql = " -SELECT r.payment_processor_id - FROM civicrm_contribution_recur r - WHERE r.id = %1"; - $params = array(1 => array($id, 'Integer')); - $paymentProcessorID = CRM_Core_DAO::singleValueQuery($sql, - $params - ); - if (!$paymentProcessorID) { - return NULL; - } + public static function getPaymentProcessor($id) { + $paymentProcessorID = self::getPaymentProcessorID($id); + return CRM_Financial_BAO_PaymentProcessor::getPayment($paymentProcessorID); + } - return CRM_Financial_BAO_PaymentProcessor::getPayment($paymentProcessorID, $mode); + /** + * Get the processor object for the recurring contribution record. + * + * @param int $id + * + * @return CRM_Core_Payment|NULL + * Returns a processor object or NULL if the processor is disabled. + * Note this returns the 'Manual' processor object if no processor is attached + * (since it still makes sense to update / cancel + */ + public static function getPaymentProcessorObject($id) { + $processor = self::getPaymentProcessor($id); + return is_array($processor) ? $processor['object'] : NULL; + } + + /** + * Get the payment processor for the given recurring contribution. + * + * @param int $recurID + * + * @return int + * Payment processor id. If none found return 0 which represents the + * pseudo processor used for pay-later. + */ + public static function getPaymentProcessorID($recurID) { + $recur = civicrm_api3('ContributionRecur', 'getsingle', [ + 'id' => $recurID, + 'return' => ['payment_processor_id'], + ]); + return (int) CRM_Utils_Array::value('payment_processor_id', $recur, 0); } /** @@ -192,7 +218,7 @@ public static function getPaymentProcessor($id, $mode = NULL) { */ public static function getCount(&$ids) { $recurID = implode(',', $ids); - $totalCount = array(); + $totalCount = []; $query = " SELECT contribution_recur_id, count( contribution_recur_id ) as commpleted @@ -231,20 +257,24 @@ public static function deleteRecurContribution($recurId) { /** * Cancel Recurring contribution. * - * @param int $recurId - * Recur contribution id. - * @param array $objects - * An array of objects that is to be cancelled like. - * contribution, membership, event. At least contribution object is a must. - * - * @param array $activityParams + * @param array $params + * Recur contribution params * * @return bool */ - public static function cancelRecurContribution($recurId, $objects, $activityParams = array()) { + public static function cancelRecurContribution($params) { + if (is_numeric($params)) { + CRM_Core_Error::deprecatedFunctionWarning('You are using a BAO function whose signature has changed. Please use the ContributionRecur.cancel api'); + $params = ['id' => $params]; + } + $recurId = $params['id']; if (!$recurId) { return FALSE; } + $activityParams = [ + 'subject' => !empty($params['membership_id']) ? ts('Auto-renewal membership cancelled') : ts('Recurring contribution cancelled'), + 'details' => CRM_Utils_Array::value('processor_message', $params), + ]; $contributionStatus = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name'); $canceledId = array_search('Cancelled', $contributionStatus); @@ -258,6 +288,7 @@ public static function cancelRecurContribution($recurId, $objects, $activityPara $recur->start_date = CRM_Utils_Date::isoToMysql($recur->start_date); $recur->create_date = CRM_Utils_Date::isoToMysql($recur->create_date); $recur->modified_date = CRM_Utils_Date::isoToMysql($recur->modified_date); + $recur->cancel_reason = CRM_Utils_Array::value('cancel_reason', $params); $recur->cancel_date = date('YmdHis'); $recur->save(); @@ -270,49 +301,34 @@ public static function cancelRecurContribution($recurId, $objects, $activityPara $membershipType = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_Membership', $dao->membership_id, 'membership_type_id'); $membershipType = CRM_Utils_Array::value($membershipType, $membershipTypes); $details .= ' -
    ' . ts('Automatic renewal of %1 membership cancelled.', array(1 => $membershipType)); +
    ' . ts('Automatic renewal of %1 membership cancelled.', [1 => $membershipType]); } else { - $details .= ' -
    ' . ts('The recurring contribution of %1, every %2 %3 has been cancelled.', array( - 1 => $dao->amount, - 2 => $dao->frequency_interval, - 3 => $dao->frequency_unit, - )); + $details .= '
    ' . ts('The recurring contribution of %1, every %2 %3 has been cancelled.', [ + 1 => $dao->amount, + 2 => $dao->frequency_interval, + 3 => $dao->frequency_unit, + ]); } - $activityParams = array( + $activityParams = [ 'source_contact_id' => $dao->contact_id, - 'source_record_id' => CRM_Utils_Array::value('source_record_id', $activityParams), - 'activity_type_id' => CRM_Core_OptionGroup::getValue('activity_type', - 'Cancel Recurring Contribution', - 'name' - ), + 'source_record_id' => $dao->recur_id, + 'activity_type_id' => 'Cancel Recurring Contribution', 'subject' => CRM_Utils_Array::value('subject', $activityParams, ts('Recurring contribution cancelled')), 'details' => $details, - 'activity_date_time' => date('YmdHis'), - 'status_id' => CRM_Core_OptionGroup::getValue('activity_status', - 'Completed', - 'name' - ), - ); - $session = CRM_Core_Session::singleton(); - $cid = $session->get('userID'); + 'status_id' => 'Completed', + ]; + + $cid = CRM_Core_Session::singleton()->get('userID'); if ($cid) { $activityParams['target_contact_id'][] = $activityParams['source_contact_id']; $activityParams['source_contact_id'] = $cid; } - CRM_Activity_BAO_Activity::create($activityParams); + civicrm_api3('Activity', 'create', $activityParams); } - // if there are associated objects, cancel them as well - if ($objects == CRM_Core_DAO::$_nullObject) { - $transaction->commit(); - return TRUE; - } - else { - $baseIPN = new CRM_Core_Payment_BaseIPN(); - return $baseIPN->cancelled($objects, $transaction); - } + $transaction->commit(); + return TRUE; } else { // if already cancelled, return true @@ -327,7 +343,7 @@ public static function cancelRecurContribution($recurId, $objects, $activityPara } /** - * Get list of recurring contribution of contact Ids. + * @deprecated Get list of recurring contribution of contact Ids. * * @param int $contactId * Contact ID. @@ -337,7 +353,8 @@ public static function cancelRecurContribution($recurId, $objects, $activityPara * */ public static function getRecurContributions($contactId) { - $params = array(); + CRM_Core_Error::deprecatedFunctionWarning('ContributionRecur.get API instead'); + $params = []; $recurDAO = new CRM_Contribute_DAO_ContributionRecur(); $recurDAO->contact_id = $contactId; $recurDAO->find(); @@ -395,8 +412,7 @@ public static function getSubscriptionDetails($entityID, $entity = 'recur') { FROM civicrm_contribution_recur rec LEFT JOIN civicrm_contribution con ON ( con.contribution_recur_id = rec.id ) LEFT JOIN civicrm_membership_payment mp ON ( mp.contribution_id = con.id ) - WHERE rec.id = %1 - GROUP BY rec.id"; + WHERE rec.id = %1"; } elseif ($entity == 'contribution') { $sql .= " @@ -414,7 +430,7 @@ public static function getSubscriptionDetails($entityID, $entity = 'recur') { WHERE mp.membership_id = %1"; } - $dao = CRM_Core_DAO::executeQuery($sql, array(1 => array($entityID, 'Integer'))); + $dao = CRM_Core_DAO::executeQuery($sql, [1 => [$entityID, 'Integer']]); if ($dao->fetch()) { return $dao; } @@ -456,19 +472,19 @@ public static function supportsFinancialTypeChange($id) { * @return array * @throws \CiviCRM_API3_Exception */ - public static function getTemplateContribution($id, $overrides = array()) { - $templateContribution = civicrm_api3('Contribution', 'get', array( + public static function getTemplateContribution($id, $overrides = []) { + $templateContribution = civicrm_api3('Contribution', 'get', [ 'contribution_recur_id' => $id, - 'options' => array('limit' => 1, 'sort' => array('id DESC')), + 'options' => ['limit' => 1, 'sort' => ['id DESC']], 'sequential' => 1, 'contribution_test' => '', - )); + ]); if ($templateContribution['count']) { $result = array_merge($templateContribution['values'][0], $overrides); $result['line_item'] = CRM_Contribute_BAO_ContributionRecur::calculateRecurLineItems($id, $result['total_amount'], $result['financial_type_id']); return $result; } - return array(); + return []; } public static function setSubscriptionContext() { @@ -479,7 +495,7 @@ public static function setSubscriptionContext() { $cid = CRM_Utils_Request::retrieve('cid', 'Integer'); $mid = CRM_Utils_Request::retrieve('mid', 'Integer'); $qfkey = CRM_Utils_Request::retrieve('key', 'String'); - $context = CRM_Utils_Request::retrieve('context', 'String'); + $context = CRM_Utils_Request::retrieve('context', 'Alphanumeric'); if ($cid) { switch ($context) { case 'contribution': @@ -569,7 +585,7 @@ public static function sendRecurringStartOrEndNotification($ids, $recur, $isFirs * @param int $recurId * @param int $targetContributionId */ - static public function copyCustomValues($recurId, $targetContributionId) { + public static function copyCustomValues($recurId, $targetContributionId) { if ($recurId && $targetContributionId) { // get the initial contribution id of recur id $sourceContributionId = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_Contribution', $recurId, 'id', 'contribution_recur_id'); @@ -585,11 +601,11 @@ static public function copyCustomValues($recurId, $targetContributionId) { } // copy custom data - $extends = array('Contribution'); + $extends = ['Contribution']; $groupTree = CRM_Core_BAO_CustomGroup::getGroupDetail(NULL, NULL, $extends); if ($groupTree) { foreach ($groupTree as $groupID => $group) { - $table[$groupTree[$groupID]['table_name']] = array('entity_id'); + $table[$groupTree[$groupID]['table_name']] = ['entity_id']; foreach ($group['fields'] as $fieldID => $field) { $table[$groupTree[$groupID]['table_name']][] = $groupTree[$groupID]['fields'][$fieldID]['column_name']; } @@ -633,7 +649,7 @@ public static function addrecurSoftCredit($recurId, $targetContributionId) { * Add line items for recurring contribution. * * @param int $recurId - * @param $contribution + * @param \CRM_Contribute_BAO_Contribution $contribution * * @return array */ @@ -648,10 +664,11 @@ public static function addRecurLineItems($recurId, $contribution) { try { // @todo this should be done by virtue of editing the line item as this link // is deprecated. This may be the case but needs testing. - civicrm_api3('membership_payment', 'create', array( + civicrm_api3('membership_payment', 'create', [ 'membership_id' => $value['entity_id'], 'contribution_id' => $contribution->id, - )); + 'is_transactional' => FALSE, + ]); } catch (CiviCRM_API3_Exception $e) { // we are catching & ignoring errors as an extra precaution since lost IPNs may be more serious that lost membership_payment data @@ -689,8 +706,8 @@ public static function addRecurLineItems($recurId, $contribution) { * @throws \CiviCRM_API3_Exception */ public static function updateRecurLinkedPledge($contributionID, $contributionRecurID, $contributionStatusID, $contributionAmount) { - $returnProperties = array('id', 'pledge_id'); - $paymentDetails = $paymentIDs = array(); + $returnProperties = ['id', 'pledge_id']; + $paymentDetails = $paymentIDs = []; if (CRM_Core_DAO::commonRetrieveAll('CRM_Pledge_DAO_PledgePayment', 'contribution_id', $contributionID, $paymentDetails, $returnProperties @@ -750,7 +767,7 @@ public static function updateRecurLinkedPledge($contributionID, $contributionRec } /** - * @param $form + * @param CRM_Core_Form $form */ public static function recurringContribution(&$form) { // Recurring contribution fields @@ -769,32 +786,40 @@ public static function recurringContribution(&$form) { } } + // If values have been supplied for recurring contribution fields, open the recurring contributions pane. + foreach (['contribution_status_id', 'payment_processor_id', 'processor_id', 'trxn_id'] as $fieldName) { + if (!empty($form->_formValues['contribution_recur_' . $fieldName])) { + $form->assign('contribution_recur_pane_open', TRUE); + break; + } + } + // Add field to check if payment is made for recurring contribution - $recurringPaymentOptions = array( + $recurringPaymentOptions = [ 1 => ts('All recurring contributions'), 2 => ts('Recurring contributions with at least one payment'), - ); - $form->addRadio('contribution_recur_payment_made', NULL, $recurringPaymentOptions, array('allowClear' => TRUE)); + ]; + $form->addRadio('contribution_recur_payment_made', NULL, $recurringPaymentOptions, ['allowClear' => TRUE]); CRM_Core_Form_Date::buildDateRange($form, 'contribution_recur_start_date', 1, '_low', '_high', ts('From'), FALSE, FALSE, 'birth'); CRM_Core_Form_Date::buildDateRange($form, 'contribution_recur_end_date', 1, '_low', '_high', ts('From'), FALSE, FALSE, 'birth'); CRM_Core_Form_Date::buildDateRange($form, 'contribution_recur_modified_date', 1, '_low', '_high', ts('From'), FALSE, FALSE, 'birth'); CRM_Core_Form_Date::buildDateRange($form, 'contribution_recur_next_sched_contribution_date', 1, '_low', '_high', ts('From'), FALSE, FALSE, 'birth'); CRM_Core_Form_Date::buildDateRange($form, 'contribution_recur_failure_retry_date', 1, '_low', '_high', ts('From'), FALSE, FALSE, 'birth'); CRM_Core_Form_Date::buildDateRange($form, 'contribution_recur_cancel_date', 1, '_low', '_high', ts('From'), FALSE, FALSE, 'birth'); + + // Add field for contribution status + $form->addSelect('contribution_recur_contribution_status_id', + ['entity' => 'contribution', 'multiple' => 'multiple', 'context' => 'search', 'options' => CRM_Contribute_PseudoConstant::contributionStatus()] + ); + $form->addElement('text', 'contribution_recur_processor_id', ts('Processor ID'), CRM_Core_DAO::getAttribute('CRM_Contribute_DAO_ContributionRecur', 'processor_id')); $form->addElement('text', 'contribution_recur_trxn_id', ts('Transaction ID'), CRM_Core_DAO::getAttribute('CRM_Contribute_DAO_ContributionRecur', 'trxn_id')); - $contributionRecur = array('ContributionRecur'); - $groupDetails = CRM_Core_BAO_CustomGroup::getGroupDetail(NULL, TRUE, $contributionRecur); - if ($groupDetails) { - $form->assign('contributeRecurGroupTree', $groupDetails); - foreach ($groupDetails as $group) { - foreach ($group['fields'] as $field) { - $fieldId = $field['id']; - $elementName = 'custom_' . $fieldId; - CRM_Core_BAO_CustomField::addQuickFormElement($form, $elementName, $fieldId, FALSE, TRUE); - } - } - } + + $paymentProcessorOpts = CRM_Contribute_BAO_ContributionRecur::buildOptions('payment_processor_id', 'get'); + $form->add('select', 'contribution_recur_payment_processor_id', ts('Payment Processor ID'), $paymentProcessorOpts, FALSE, ['class' => 'crm-select2', 'multiple' => 'multiple']); + + CRM_Core_BAO_Query::addCustomFormFields($form, ['ContributionRecur']); + } /** @@ -803,7 +828,7 @@ public static function recurringContribution(&$form) { * @return array */ public static function getRecurringFields() { - return array( + return [ 'contribution_recur_payment_made' => ts(''), 'contribution_recur_start_date' => ts('Recurring Contribution Start Date'), 'contribution_recur_next_sched_contribution_date' => ts('Next Scheduled Recurring Contribution'), @@ -812,7 +837,7 @@ public static function getRecurringFields() { 'contribution_recur_create_date' => ('Recurring Contribution Create Date'), 'contribution_recur_modified_date' => ('Recurring Contribution Modified Date'), 'contribution_recur_failure_retry_date' => ts('Failed Recurring Contribution Retry Date'), - ); + ]; } /** @@ -825,24 +850,27 @@ public static function getRecurringFields() { * Payment status - this correlates to the machine name of the contribution status ID ie * - Completed * - Failed + * @param string $effectiveDate * * @throws \CiviCRM_API3_Exception */ - public static function updateOnNewPayment($recurringContributionID, $paymentStatus) { - if (!in_array($paymentStatus, array('Completed', 'Failed'))) { + public static function updateOnNewPayment($recurringContributionID, $paymentStatus, $effectiveDate) { + + $effectiveDate = $effectiveDate ? date('Y-m-d', strtotime($effectiveDate)) : date('Y-m-d'); + if (!in_array($paymentStatus, ['Completed', 'Failed'])) { return; } - $params = array( + $params = [ 'id' => $recurringContributionID, - 'return' => array( + 'return' => [ 'contribution_status_id', 'next_sched_contribution_date', 'frequency_unit', 'frequency_interval', 'installments', 'failure_count', - ), - ); + ], + ]; $existing = civicrm_api3('ContributionRecur', 'getsingle', $params); @@ -862,8 +890,8 @@ public static function updateOnNewPayment($recurringContributionID, $paymentStat // Only update next sched date if it's empty or 'just now' because payment processors may be managing // the scheduled date themselves as core did not previously provide any help. if (empty($existing['next_sched_contribution_date']) || strtotime($existing['next_sched_contribution_date']) == - strtotime(date('Y-m-d'))) { - $params['next_sched_contribution_date'] = date('Y-m-d', strtotime('+' . $existing['frequency_interval'] . ' ' . $existing['frequency_unit'])); + strtotime($effectiveDate)) { + $params['next_sched_contribution_date'] = date('Y-m-d', strtotime('+' . $existing['frequency_interval'] . ' ' . $existing['frequency_unit'], strtotime($effectiveDate))); } } civicrm_api3('ContributionRecur', 'create', $params); @@ -881,8 +909,10 @@ public static function updateOnNewPayment($recurringContributionID, $paymentStat */ protected static function isComplete($recurringContributionID, $installments) { $paidInstallments = CRM_Core_DAO::singleValueQuery( - 'SELECT count(*) FROM civicrm_contribution WHERE id = %1', - array(1 => array($recurringContributionID, 'Integer')) + 'SELECT count(*) FROM civicrm_contribution + WHERE contribution_recur_id = %1 + AND contribution_status_id = ' . CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'), + [1 => [$recurringContributionID, 'Integer']] ); if ($paidInstallments >= $installments) { return TRUE; @@ -900,11 +930,20 @@ protected static function isComplete($recurringContributionID, $installments) { * @return array */ public static function calculateRecurLineItems($recurId, $total_amount, $financial_type_id) { - $originalContributionID = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_Contribution', $recurId, 'id', 'contribution_recur_id'); - $lineItems = CRM_Price_BAO_LineItem::getLineItemsByContributionID($originalContributionID); - $lineSets = array(); + $originalContribution = civicrm_api3('Contribution', 'getsingle', [ + 'contribution_recur_id' => $recurId, + 'contribution_test' => '', + 'options' => ['limit' => 1], + 'return' => ['id', 'financial_type_id'], + ]); + $lineItems = CRM_Price_BAO_LineItem::getLineItemsByContributionID($originalContribution['id']); + $lineSets = []; if (count($lineItems) == 1) { foreach ($lineItems as $index => $lineItem) { + if ($lineItem['financial_type_id'] != $originalContribution['financial_type_id']) { + // CRM-20685, Repeattransaction produces incorrect Financial Type ID (in specific circumstance) - if number of lineItems = 1, So this conditional will set the financial_type_id as the original if line_item and contribution comes with different data. + $financial_type_id = $lineItem['financial_type_id']; + } if ($financial_type_id) { // CRM-17718 allow for possibility of changed financial type ID having been set prior to calling this. $lineItem['financial_type_id'] = $financial_type_id; @@ -918,17 +957,71 @@ public static function calculateRecurLineItems($recurId, $total_amount, $financi $priceField = new CRM_Price_DAO_PriceField(); $priceField->id = $lineItem['price_field_id']; $priceField->find(TRUE); - $lineSets[$priceField->price_set_id][] = $lineItem; + $lineSets[$priceField->price_set_id][$lineItem['price_field_id']] = $lineItem; } } // CRM-19309 if more than one then just pass them through: elseif (count($lineItems) > 1) { foreach ($lineItems as $index => $lineItem) { - $lineSets[$index][] = $lineItem; + $lineSets[$index][$lineItem['price_field_id']] = $lineItem; } } return $lineSets; } + /** + * Returns array with statuses that are considered to make a recurring + * contribution inactive. + * + * @return array + */ + public static function getInactiveStatuses() { + return self::$inactiveStatuses; + } + + /** + * Get options for the called BAO object's field. + * + * This function can be overridden by each BAO to add more logic related to context. + * The overriding function will generally call the lower-level CRM_Core_PseudoConstant::get + * + * @param string $fieldName + * @param string $context + * @see CRM_Core_DAO::buildOptionsContext + * @param array $props + * whatever is known about this bao object. + * + * @return array|bool + */ + public static function buildOptions($fieldName, $context = NULL, $props = []) { + + switch ($fieldName) { + case 'payment_processor_id': + if (isset(\Civi::$statics[__CLASS__]['buildoptions_payment_processor_id'])) { + return \Civi::$statics[__CLASS__]['buildoptions_payment_processor_id']; + } + $baoName = 'CRM_Contribute_BAO_ContributionRecur'; + $props['condition']['test'] = "is_test = 0"; + $liveProcessors = CRM_Core_PseudoConstant::get($baoName, $fieldName, $props, $context); + $props['condition']['test'] = "is_test != 0"; + $testProcessors = CRM_Core_PseudoConstant::get($baoName, $fieldName, $props, $context); + foreach ($testProcessors as $key => $value) { + if ($context === 'validate') { + // @fixme: Ideally the names would be different in the civicrm_payment_processor table but they are not. + // So we append '_test' to the test one so that we can select the correct processor by name using the ContributionRecur.create API. + $testProcessors[$key] = $value . '_test'; + } + else { + $testProcessors[$key] = CRM_Core_TestEntity::appendTestText($value); + } + } + $allProcessors = $liveProcessors + $testProcessors; + ksort($allProcessors); + \Civi::$statics[__CLASS__]['buildoptions_payment_processor_id'] = $allProcessors; + return $allProcessors; + } + return parent::buildOptions($fieldName, $context, $props); + } + } diff --git a/CRM/Contribute/BAO/ContributionSoft.php b/CRM/Contribute/BAO/ContributionSoft.php index 756c22a37c03..8b7e1ec8c9d6 100644 --- a/CRM/Contribute/BAO/ContributionSoft.php +++ b/CRM/Contribute/BAO/ContributionSoft.php @@ -1,9 +1,9 @@ pcp_id && empty($pcpId)) { @@ -94,8 +94,7 @@ public static function processSoftContribution($params, $contribution) { } //Delete PCP against this contribution and create new on submitted PCP information elseif (array_key_exists('pcp', $params) && $pcpId) { - $deleteParams = array('id' => $pcpId); - CRM_Contribute_BAO_ContributionSoft::del($deleteParams); + civicrm_api3('ContributionSoft', 'delete', array('id' => $pcpId)); } if (isset($params['soft_credit'])) { $softParams = $params['soft_credit']; @@ -117,8 +116,7 @@ public static function processSoftContribution($params, $contribution) { // delete any extra soft-credit while updating back-office contribution foreach ((array) $softIDs as $softID) { if (!in_array($softID, $params['soft_credit_ids'])) { - $deleteParams = array('id' => $softID); - CRM_Contribute_BAO_ContributionSoft::del($deleteParams); + civicrm_api3('ContributionSoft', 'delete', array('id' => $softID)); } } } @@ -150,12 +148,22 @@ public static function formatSoftCreditParams(&$params, &$form) { if (!empty($form->_values['honoree_profile_id']) && !empty($params['soft_credit_type_id'])) { $honorId = NULL; - $contributionSoftParams['soft_credit_type_id'] = CRM_Core_OptionGroup::getValue('soft_credit_type', 'pcp', 'name'); + // @todo fix use of deprecated function. + $contributionSoftParams['soft_credit_type_id'] = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_ContributionSoft', 'soft_credit_type_id', 'pcp'); //check if there is any duplicate contact - $profileContactType = CRM_Core_BAO_UFGroup::getContactType($form->_values['honoree_profile_id']); - $dedupeParams = CRM_Dedupe_Finder::formatParams($params['honor'], $profileContactType); - $dedupeParams['check_permission'] = FALSE; - $ids = CRM_Dedupe_Finder::dupesByParams($dedupeParams, $profileContactType); + // honoree should never be the donor + $exceptKeys = array( + 'contactID' => 0, + 'onbehalf_contact_id' => 0, + ); + $except = array_values(array_intersect_key($params, $exceptKeys)); + $ids = CRM_Contact_BAO_Contact::getDuplicateContacts( + $params['honor'], + CRM_Core_BAO_UFGroup::getContactType($form->_values['honoree_profile_id']), + 'Unsupervised', + $except, + FALSE + ); if (count($ids)) { $honorId = CRM_Utils_Array::value(0, $ids); } @@ -221,21 +229,6 @@ public static function retrieve(&$params, &$defaults) { return NULL; } - /** - * Delete soft credits. - * - * @param array $params - * - */ - public static function del($params) { - //delete from contribution soft table - $contributionSoft = new CRM_Contribute_DAO_ContributionSoft(); - foreach ($params as $column => $value) { - $contributionSoft->$column = $value; - } - $contributionSoft->delete(); - } - /** * @param int $contact_id * @param int $isTest @@ -260,15 +253,14 @@ public static function getSoftContributionTotals($contact_id, $isTest = 0) { $cs = CRM_Core_DAO::executeQuery($query, $params); - $count = 0; + $count = $countCancelled = 0; $amount = $average = $cancelAmount = array(); while ($cs->fetch()) { if ($cs->amount > 0) { $count++; - $amount[] = $cs->amount; - $average[] = $cs->average; - $currency[] = $cs->currency; + $amount[] = CRM_Utils_Money::format($cs->amount, $cs->currency); + $average[] = CRM_Utils_Money::format($cs->average, $cs->currency); } } @@ -278,16 +270,17 @@ public static function getSoftContributionTotals($contact_id, $isTest = 0) { $cancelAmountSQL = CRM_Core_DAO::executeQuery($query, $params); while ($cancelAmountSQL->fetch()) { if ($cancelAmountSQL->amount > 0) { - $count++; - $cancelAmount[] = $cancelAmountSQL->amount; + $countCancelled++; + $cancelAmount[] = CRM_Utils_Money::format($cancelAmountSQL->amount, $cancelAmountSQL->currency); } } - if ($count > 0) { + if ($count > 0 || $countCancelled > 0) { return array( + $count, + $countCancelled, implode(', ', $amount), implode(', ', $average), - implode(', ', $currency), implode(', ', $cancelAmount), ); } @@ -527,7 +520,7 @@ public static function getSoftContributionList($contact_id, $filter = NULL, $isT $result[$cs->id]['links'] = CRM_Core_Action::formLink($links, NULL, $replace); if ($isTest) { - $result[$cs->id]['contribution_status'] = $result[$cs->id]['contribution_status'] . '
    (test)'; + $result[$cs->id]['contribution_status'] = CRM_Core_TestEntity::appendTestText($result[$cs->id]['contribution_status']); } } return $result; diff --git a/CRM/Contribute/BAO/ManagePremiums.php b/CRM/Contribute/BAO/ManagePremiums.php index 4c7e3b8c4510..97d1f632e93d 100644 --- a/CRM/Contribute/BAO/ManagePremiums.php +++ b/CRM/Contribute/BAO/ManagePremiums.php @@ -1,9 +1,9 @@ copyValues($params); - if ($premium->find(TRUE)) { - $premium->product_name = $premium->name; - CRM_Core_DAO::storeValues($premium, $defaults); - return $premium; - } - return NULL; + CRM_Core_Error::deprecatedFunctionWarning('CRM_Contribute_BAO_Product::retrieve'); + return parent::retrieve($params, $defaults); } /** * Update the is_active flag in the db. * + * @deprecated * @param int $id * Id of the database record. * @param bool $is_active * Value we want to set the is_active field. * - * @return Object - * DAO object on success, null otherwise + * @return bool */ public static function setIsActive($id, $is_active) { - if (!$is_active) { - $dao = new CRM_Contribute_DAO_PremiumsProduct(); - $dao->product_id = $id; - $dao->delete(); - } - return CRM_Core_DAO::setFieldValue('CRM_Contribute_DAO_Product', $id, 'is_active', $is_active); + CRM_Core_Error::deprecatedFunctionWarning('CRM_Contribute_BAO_Product::setIsActive'); + return parent::setIsActive($id, $is_active); } /** - * add the financial types. + * Add a premium product to the database, and return it. * + * @deprecated * @param array $params * Reference array contains the values submitted by the form. - * @param array $ids + * @param array $ids (deprecated) * Reference array contains the id. * - * - * @return object + * @return CRM_Contribute_DAO_Product */ - public static function add(&$params, &$ids) { - // CRM-14283 - strip protocol and domain from image URLs - $image_type = array('image', 'thumbnail'); - foreach ($image_type as $key) { - if (isset($params[$key]) && $params[$key]) { - $parsedURL = explode('/', $params[$key]); - $pathComponents = array_slice($parsedURL, 3); - $params[$key] = '/' . implode('/', $pathComponents); - } + public static function add(&$params, $ids) { + CRM_Core_Error::deprecatedFunctionWarning('CRM_Contribute_BAO_Product::create'); + $id = CRM_Utils_Array::value('id', $params, CRM_Utils_Array::value('premium', $ids)); + if ($id) { + $params['id'] = $id; } - - $params['is_active'] = CRM_Utils_Array::value('is_active', $params, FALSE); - $params['is_deductible'] = CRM_Utils_Array::value('is_deductible', $params, FALSE); - - // action is taken depending upon the mode - $premium = new CRM_Contribute_DAO_Product(); - $premium->copyValues($params); - - $premium->id = CRM_Utils_Array::value('premium', $ids); - - // set currency for CRM-1496 - if (!isset($premium->currency)) { - $config = CRM_Core_Config::singleton(); - $premium->currency = $config->defaultCurrency; - } - - $premium->save(); - return $premium; + return parent::create($params); } /** * Delete premium Types. * + * @deprecated * @param int $productID + * + * @throws \CRM_Core_Exception */ public static function del($productID) { - //check dependencies - $premiumsProduct = new CRM_Contribute_DAO_PremiumsProduct(); - $premiumsProduct->product_id = $productID; - if ($premiumsProduct->find(TRUE)) { - $session = CRM_Core_Session::singleton(); - $message .= ts('This Premium is being linked to Online Contribution page. Please remove it in order to delete this Premium.', array(1 => CRM_Utils_System::url('civicrm/admin/contribute', 'reset=1')), ts('Deletion Error'), 'error'); - CRM_Core_Session::setStatus($message); - return CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/admin/contribute/managePremiums', 'reset=1&action=browse')); - } - - //delete from financial Type table - $premium = new CRM_Contribute_DAO_Product(); - $premium->id = $productID; - $premium->delete(); + CRM_Core_Error::deprecatedFunctionWarning('CRM_Contribute_BAO_Product::del'); + return parent::del($productID); } } diff --git a/CRM/Contribute/BAO/Premium.php b/CRM/Contribute/BAO/Premium.php index a92aced3cb96..f3fca06da77a 100644 --- a/CRM/Contribute/BAO/Premium.php +++ b/CRM/Contribute/BAO/Premium.php @@ -1,9 +1,9 @@ add('hidden', "selectProduct", $selectedProductID, array('id' => 'selectProduct')); + $form->add('hidden', "selectProduct", $selectedProductID, ['id' => 'selectProduct']); - $dao = new CRM_Contribute_DAO_Premium(); - $dao->entity_table = 'civicrm_contribution_page'; - $dao->entity_id = $pageID; - $dao->premiums_active = 1; - CRM_Financial_BAO_FinancialType::getAvailableFinancialTypes($financialTypes, CRM_Core_Action::ADD); - $addWhere = "financial_type_id IN (0)"; - if (!empty($financialTypes)) { - $addWhere = "financial_type_id IN (" . implode(',', array_keys($financialTypes)) . ")"; - } + $premiumDao = new CRM_Contribute_DAO_Premium(); + $premiumDao->entity_table = 'civicrm_contribution_page'; + $premiumDao->entity_id = $pageID; + $premiumDao->premiums_active = 1; - if ($dao->find(TRUE)) { - $premiumID = $dao->id; - $premiumBlock = array(); - CRM_Core_DAO::storeValues($dao, $premiumBlock); + if ($premiumDao->find(TRUE)) { + $premiumID = $premiumDao->id; + $premiumBlock = []; + CRM_Core_DAO::storeValues($premiumDao, $premiumBlock); - $dao = new CRM_Contribute_DAO_PremiumsProduct(); - $dao->premiums_id = $premiumID; - $dao->whereAdd($addWhere); - $dao->orderBy('weight'); - $dao->find(); + CRM_Financial_BAO_FinancialType::getAvailableFinancialTypes($financialTypes, CRM_Core_Action::ADD); + $addWhere = "financial_type_id IN (0)"; + if (!empty($financialTypes)) { + $addWhere = "financial_type_id IN (" . implode(',', array_keys($financialTypes)) . ")"; + } + $addWhere = "{$addWhere} OR financial_type_id IS NULL"; - $products = array(); - $radio = array(); - while ($dao->fetch()) { + $premiumsProductDao = new CRM_Contribute_DAO_PremiumsProduct(); + $premiumsProductDao->premiums_id = $premiumID; + $premiumsProductDao->whereAdd($addWhere); + $premiumsProductDao->orderBy('weight'); + $premiumsProductDao->find(); + + $products = []; + while ($premiumsProductDao->fetch()) { $productDAO = new CRM_Contribute_DAO_Product(); - $productDAO->id = $dao->product_id; + $productDAO->id = $premiumsProductDao->product_id; $productDAO->is_active = 1; if ($productDAO->find(TRUE)) { if ($selectedProductID != NULL) { @@ -147,7 +148,7 @@ public static function buildPremiumBlock(&$form, $pageID, $formItems = FALSE, $s CRM_Core_DAO::storeValues($productDAO, $products[$productDAO->id]); } } - $options = $temp = array(); + $options = $temp = []; $temp = explode(',', $productDAO->options); foreach ($temp as $value) { $options[trim($value)] = trim($value); @@ -187,7 +188,7 @@ public function buildPremiumPreviewBlock($form, $productID, $premiumProductID = } $radio[$productDAO->id] = $form->createElement('radio', NULL, NULL, NULL, $productDAO->id, NULL); - $options = $temp = array(); + $options = $temp = []; $temp = explode(',', $productDAO->options); foreach ($temp as $value) { $options[$value] = $value; @@ -217,10 +218,10 @@ public static function deletePremium($contributionPageID) { //need to delete entries from civicrm_premiums //as well as from civicrm_premiums_product, CRM-4586 - $params = array( + $params = [ 'entity_id' => $contributionPageID, 'entity_table' => 'civicrm_contribution_page', - ); + ]; $premium = new CRM_Contribute_DAO_Premium(); $premium->copyValues($params); @@ -244,7 +245,7 @@ public static function deletePremium($contributionPageID) { */ public static function getPremiumProductInfo() { if (!self::$productInfo) { - $products = $options = array(); + $products = $options = []; $dao = new CRM_Contribute_DAO_Product(); $dao->is_active = 1; @@ -261,7 +262,7 @@ public static function getPremiumProductInfo() { } } - self::$productInfo = array($products, $options); + self::$productInfo = [$products, $options]; } return self::$productInfo; } diff --git a/CRM/Contribute/BAO/Product.php b/CRM/Contribute/BAO/Product.php new file mode 100644 index 000000000000..f3505f972cfc --- /dev/null +++ b/CRM/Contribute/BAO/Product.php @@ -0,0 +1,146 @@ +copyValues($params); + if ($premium->find(TRUE)) { + $premium->product_name = $premium->name; + CRM_Core_DAO::storeValues($premium, $defaults); + return $premium; + } + return NULL; + } + + /** + * Update the is_active flag in the db. + * + * @param int $id + * Id of the database record. + * @param bool $is_active + * Value we want to set the is_active field. + * + * @return bool + */ + public static function setIsActive($id, $is_active) { + if (!$is_active) { + $dao = new CRM_Contribute_DAO_PremiumsProduct(); + $dao->product_id = $id; + $dao->delete(); + } + return CRM_Core_DAO::setFieldValue('CRM_Contribute_DAO_Product', $id, 'is_active', $is_active); + } + + /** + * Add a premium product to the database, and return it. + * + * @param array $params + * Update parameters. + * + * @return CRM_Contribute_DAO_Product + */ + public static function create($params) { + $id = CRM_Utils_Array::value('id', $params); + if (empty($id)) { + $defaultParams = [ + 'id' => $id, + 'image' => '', + 'thumbnail' => '', + 'is_active' => 0, + 'is_deductible' => FALSE, + 'currency' => CRM_Core_Config::singleton()->defaultCurrency, + ]; + $params = array_merge($defaultParams, $params); + } + + // Modify the submitted values for 'image' and 'thumbnail' so that we use + // local URLs for these images when possible. + if (isset($params['image'])) { + $params['image'] = CRM_Utils_String::simplifyURL($params['image'], TRUE); + } + if (isset($params['thumbnail'])) { + $params['thumbnail'] = CRM_Utils_String::simplifyURL($params['thumbnail'], TRUE); + } + + // Save and return + $premium = new CRM_Contribute_DAO_Product(); + $premium->copyValues($params); + $premium->save(); + return $premium; + } + + /** + * Delete premium Types. + * + * @param int $productID + * + * @throws \CRM_Core_Exception + */ + public static function del($productID) { + //check dependencies + $premiumsProduct = new CRM_Contribute_DAO_PremiumsProduct(); + $premiumsProduct->product_id = $productID; + if ($premiumsProduct->find(TRUE)) { + throw new CRM_Core_Exception('Cannot delete a Premium that is linked to a Contribution page'); + } + // delete product + $premium = new CRM_Contribute_DAO_Product(); + $premium->id = $productID; + $premium->delete(); + } + +} diff --git a/CRM/Contribute/BAO/Query.php b/CRM/Contribute/BAO/Query.php index 60769cedc870..3c51f78c2357 100644 --- a/CRM/Contribute/BAO/Query.php +++ b/CRM/Contribute/BAO/Query.php @@ -1,9 +1,9 @@ _tables['civicrm_contribution'] = $query->_whereTables['civicrm_contribution'] = 1; } - // get financial_type - if (!empty($query->_returnProperties['financial_type'])) { - $query->_select['financial_type'] = "civicrm_financial_type.name as financial_type"; - $query->_element['financial_type'] = 1; - $query->_tables['civicrm_contribution'] = 1; - $query->_tables['civicrm_financial_type'] = 1; - } - // get accounting code if (!empty($query->_returnProperties['accounting_code'])) { $query->_select['accounting_code'] = "civicrm_financial_account.accounting_code as accounting_code"; @@ -103,154 +87,16 @@ public static function select(&$query) { if (!empty($query->_returnProperties['contribution_batch'])) { $query->_select['contribution_batch'] = "civicrm_batch.title as contribution_batch"; $query->_element['contribution_batch'] = 1; + $query->_tables['civicrm_financial_trxn'] = 1; $query->_tables['contribution_batch'] = 1; } - if (!empty($query->_returnProperties['contribution_source'])) { - $query->_select['contribution_source'] = "civicrm_contribution.source as contribution_source"; - $query->_element['contribution_source'] = 1; - $query->_tables['civicrm_contribution'] = 1; - } - - // get contribution_status - if (!empty($query->_returnProperties['contribution_status_id'])) { - $query->_select['contribution_status_id'] = "contribution_status.value as contribution_status_id"; - $query->_element['contribution_status_id'] = 1; - $query->_tables['civicrm_contribution'] = 1; - $query->_tables['contribution_status'] = 1; - } - - // get contribution_status label - if (!empty($query->_returnProperties['contribution_status'])) { - $query->_select['contribution_status'] = "contribution_status.label as contribution_status"; - $query->_element['contribution_status'] = 1; - $query->_tables['civicrm_contribution'] = 1; - $query->_tables['contribution_status'] = 1; - } - - // get payment instrument - if (!empty($query->_returnProperties['payment_instrument'])) { - $query->_select['payment_instrument'] = "contribution_payment_instrument.label as payment_instrument"; - $query->_element['payment_instrument'] = 1; - $query->_tables['civicrm_contribution'] = 1; - $query->_tables['contribution_payment_instrument'] = 1; - } - - // get payment instrument id - if (!empty($query->_returnProperties['payment_instrument_id'])) { - $query->_select['instrument_id'] = "contribution_payment_instrument.value as instrument_id"; - $query->_select['payment_instrument_id'] = "contribution_payment_instrument.value as payment_instrument_id"; - $query->_element['instrument_id'] = $query->_element['payment_instrument_id'] = 1; - $query->_tables['civicrm_contribution'] = 1; - $query->_tables['contribution_payment_instrument'] = 1; - } - - if (!empty($query->_returnProperties['check_number'])) { - $query->_select['contribution_check_number'] = "civicrm_contribution.check_number as contribution_check_number"; - $query->_element['contribution_check_number'] = 1; - $query->_tables['civicrm_contribution'] = 1; - } - - if (!empty($query->_returnProperties['contribution_campaign_id'])) { - $query->_select['contribution_campaign_id'] = 'civicrm_contribution.campaign_id as contribution_campaign_id'; - $query->_element['contribution_campaign_id'] = 1; - $query->_tables['civicrm_contribution'] = 1; - } - - // LCD 716 - $includeSoftCredits = self::isSoftCreditOptionEnabled($query->_params); - if (!empty($query->_returnProperties['contribution_soft_credit_name'])) { - if ($includeSoftCredits) { - $query->_select['contribution_soft_credit_name'] = "civicrm_contact_d.sort_name as contribution_soft_credit_name"; - // also include contact id. Will help build hyperlinks - $query->_select['contribution_soft_credit_contact_id'] = "civicrm_contact_d.id as contribution_soft_credit_contact_id"; - } - $query->_element['contribution_soft_credit_name'] = 1; - $query->_element['contribution_soft_credit_contact_id'] = 1; - - $query->_tables['civicrm_contribution'] = 1; - $query->_tables['civicrm_contribution_soft'] = 1; - $query->_tables['civicrm_contribution_soft_contact'] = 1; - } - - if (!empty($query->_returnProperties['contribution_soft_credit_contact_id'])) { - $query->_tables['civicrm_contribution'] = 1; - $query->_tables['civicrm_contribution_soft'] = 1; - $query->_tables['civicrm_contribution_soft_contact'] = 1; - } - - if (!empty($query->_returnProperties['contribution_soft_credit_amount'])) { - if ($includeSoftCredits) { - $query->_select['contribution_soft_credit_amount'] = "civicrm_contribution_soft.amount as contribution_soft_credit_amount"; - } - $query->_element['contribution_soft_credit_amount'] = 1; - $query->_tables['civicrm_contribution'] = 1; - $query->_tables['civicrm_contribution_soft'] = 1; - } - - if (!empty($query->_returnProperties['contribution_soft_credit_type'])) { - if ($includeSoftCredits) { - $query->_select['contribution_soft_credit_type'] = "contribution_softcredit_type.label as contribution_soft_credit_type"; - } - $query->_element['contribution_soft_credit_type'] = 1; - $query->_tables['civicrm_contribution'] = 1; - $query->_tables['contribution_softcredit_type'] = 1; - } - - if (!empty($query->_returnProperties['contribution_soft_credit_contribution_id'])) { - if ($includeSoftCredits) { - $query->_select['contribution_soft_credit_contribution_id'] = "civicrm_contribution_soft.contribution_id as contribution_soft_credit_contribution_id"; - } - $query->_element['contribution_soft_credit_contribution_id'] = 1; - $query->_tables['civicrm_contribution'] = 1; - $query->_tables['civicrm_contribution_soft'] = 1; - } - - if (!empty($query->_returnProperties['contribution_soft_credit_email'])) { - $query->_select['contribution_soft_credit_email'] = "soft_email.email as contribution_soft_credit_email"; - $query->_element['contribution_soft_credit_email'] = 1; - $query->_tables['civicrm_contribution'] = 1; - $query->_tables['civicrm_contribution_soft'] = 1; - $query->_tables['civicrm_contribution_soft_contact'] = 1; - $query->_tables['civicrm_contribution_soft_email'] = 1; - } - - if (!empty($query->_returnProperties['contribution_soft_credit_phone'])) { - $query->_select['contribution_soft_credit_email'] = "soft_phone.phone as contribution_soft_credit_phone"; - $query->_element['contribution_soft_credit_phone'] = 1; - $query->_tables['civicrm_contribution'] = 1; - $query->_tables['civicrm_contribution_soft'] = 1; - $query->_tables['civicrm_contribution_soft_contact'] = 1; - $query->_tables['civicrm_contribution_soft_phone'] = 1; - } - - if (!empty($query->_returnProperties['contribution_soft_credit_pcp_id'])) { - $query->_select['contribution_soft_credit_pcp_id'] = "civicrm_contribution_soft.pcp_id as contribution_soft_credit_pcp_id"; - $query->_element['contribution_soft_credit_pcp_id'] = 1; - $query->_tables['civicrm_contribution'] = 1; - $query->_tables['civicrm_contribution_soft'] = 1; - } - if (!empty($query->_returnProperties['contribution_campaign_title'])) { $query->_select['contribution_campaign_title'] = "civicrm_campaign.title as contribution_campaign_title"; $query->_element['contribution_campaign_title'] = $query->_tables['civicrm_campaign'] = 1; } - // Adding address_id in a way that is more easily extendable since the above is a bit ... wordy. - $supportedBasicReturnValues = array('address_id'); - foreach ($supportedBasicReturnValues as $fieldName) { - if (!empty($query->_returnProperties[$fieldName]) && empty($query->_select[$fieldName])) { - $query->_select[$fieldName] = "civicrm_contribution.{$fieldName} as $fieldName"; - $query->_element[$fieldName] = $query->_tables['civicrm_contribution'] = 1; - } - } - - //CRM-16116: get financial_type_id - if (!empty($query->_returnProperties['financial_type_id'])) { - $query->_select['financial_type_id'] = "civicrm_contribution.financial_type_id as financial_type_id"; - $query->_element['financial_type_id'] = $query->_tables['civicrm_contribution'] = 1; - } - // LCD 716 END + self::addSoftCreditFields($query); } /** @@ -259,7 +105,6 @@ public static function select(&$query) { * @param CRM_Contact_BAO_Query $query */ public static function where(&$query) { - $grouping = NULL; self::initializeAnySoftCreditClause($query); foreach (array_keys($query->_params) as $id) { if (empty($query->_params[$id][0])) { @@ -282,7 +127,6 @@ public static function where(&$query) { continue; } - $grouping = $query->_params[$id][3]; self::whereClauseSingle($query->_params[$id], $query); } } @@ -298,14 +142,12 @@ public static function whereClauseSingle(&$values, &$query) { list($name, $op, $value, $grouping, $wildcard) = $values; $quoteValue = NULL; - $fields = array_merge(CRM_Contribute_BAO_Contribution::fields(), self::getFields()); + $fields = self::getFields(); if (!empty($value) && !is_array($value)) { $quoteValue = "\"$value\""; } - $strtolower = function_exists('mb_strtolower') ? 'mb_strtolower' : 'strtolower'; - $recurrringFields = CRM_Contribute_BAO_ContributionRecur::getRecurringFields(); unset($recurrringFields['contribution_recur_payment_made']); foreach ($recurrringFields as $dateField => $dateFieldTitle) { @@ -313,6 +155,24 @@ public static function whereClauseSingle(&$values, &$query) { return; } } + // These are legacy names. + // @todo enotices when these are hit so we can start to elimnate them. + $fieldAliases = [ + 'financial_type' => 'financial_type_id', + 'contribution_page' => 'contribution_page_id', + 'payment_instrument' => 'payment_instrument_id', + // or payment_instrument_id? + 'contribution_payment_instrument' => 'contribution_payment_instrument_id', + 'contribution_status' => 'contribution_status_id', + ]; + + $name = isset($fieldAliases[$name]) ? $fieldAliases[$name] : $name; + $qillName = $name; + if (in_array($name, $fieldAliases)) { + $qillName = array_search($name, $fieldAliases); + } + $pseudoExtraParam = []; + switch ($name) { case 'contribution_date': case 'contribution_date_low': @@ -321,7 +181,7 @@ public static function whereClauseSingle(&$values, &$query) { case 'contribution_date_high_time': // process to / from date $query->dateQueryBuilder($values, - 'civicrm_contribution', 'contribution_date', 'receive_date', 'Contribution Date' + 'civicrm_contribution', 'contribution_date', 'receive_date', ts('Contribution Date') ); return; @@ -362,16 +222,20 @@ public static function whereClauseSingle(&$values, &$query) { $query->_tables['civicrm_contribution'] = $query->_whereTables['civicrm_contribution'] = 1; return; - case 'financial_type': - case 'contribution_page': - case 'payment_instrument': - case 'contribution_payment_instrument': - case 'contribution_status': - $name .= '_id'; + case 'contribution_cancel_date': + case 'contribution_cancel_date_low': + case 'contribution_cancel_date_low_time': + case 'contribution_cancel_date_high': + case 'contribution_cancel_date_high_time': + // process to / from date + $query->dateQueryBuilder($values, + 'civicrm_contribution', 'contribution_cancel_date', 'cancel_date', ts('Cancelled / Refunded Date') + ); + return; + case 'financial_type_id': - CRM_Financial_BAO_FinancialType::getAvailableFinancialTypes($financialTypes); - $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause("civicrm_contribution.$name", 'IN', array_keys($financialTypes), 'String'); case 'invoice_id': + case 'invoice_number': case 'payment_instrument_id': case 'contribution_payment_instrument_id': case 'contribution_page_id': @@ -386,37 +250,32 @@ public static function whereClauseSingle(&$values, &$query) { case (strpos($name, '_amount') !== FALSE): case (strpos($name, '_date') !== FALSE && $name != 'contribution_fulfilled_date'): case 'contribution_campaign_id': - $qillName = $name; - $pseudoExtraParam = array(); - // @todo including names using a switch statement & then using an 'if' to filter them out is ... odd! - if ((strpos($name, '_amount') !== FALSE) || (strpos($name, '_date') !== FALSE) || in_array($name, - array( - 'contribution_id', - 'contribution_currency', - 'contribution_source', - 'contribution_trxn_id', - 'contribution_check_number', - 'contribution_payment_instrument_id', - 'contribution_contact_id', - 'contribution_campaign_id', - ) - ) + + $fieldNamesNotToStripContributionFrom = [ + 'contribution_currency_type', + 'contribution_status_id', + 'contribution_page_id', + ]; + // @todo these are mostly legacy params. Find a better way to deal with them. + if (!in_array($name, $fieldNamesNotToStripContributionFrom) ) { - $name = str_replace('contribution_', '', $name); - if (!in_array($name, array('source', 'id', 'contact_id', 'campaign_id'))) { + if (!isset($fields[$name])) { $qillName = str_replace('contribution_', '', $qillName); } + $name = str_replace('contribution_', '', $name); } - if (in_array($name, array('contribution_currency', 'contribution_currency_type'))) { + if (in_array($name, ['contribution_currency', 'contribution_currency_type'])) { $qillName = $name = 'currency'; - $pseudoExtraParam = array('labelColumn' => 'name'); + $pseudoExtraParam = ['labelColumn' => 'name']; } $dataType = !empty($fields[$qillName]['type']) ? CRM_Utils_Type::typeToString($fields[$qillName]['type']) : 'String'; $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause("civicrm_contribution.$name", $op, $value, $dataType); list($op, $value) = CRM_Contact_BAO_Query::buildQillForFieldValue('CRM_Contribute_DAO_Contribution', $name, $value, $op, $pseudoExtraParam); - $query->_qill[$grouping][] = ts('%1 %2 %3', array(1 => $fields[$qillName]['title'], 2 => $op, 3 => $value)); + if (!($name == 'id' && $value == 0)) { + $query->_qill[$grouping][] = ts('%1 %2 %3', [1 => $fields[$qillName]['title'], 2 => $op, 3 => $value]); + } $query->_tables['civicrm_contribution'] = $query->_whereTables['civicrm_contribution'] = 1; return; @@ -425,7 +284,7 @@ public static function whereClauseSingle(&$values, &$query) { $qillName = $name; if ($name == 'contribution_pcp_made_through_id') { $qillName = $name = 'pcp_id'; - $fields[$name] = array('title' => ts('Personal Campaign Page'), 'type' => 2); + $fields[$name] = ['title' => ts('Personal Campaign Page'), 'type' => 2]; } if ($name == 'contribution_soft_credit_type_id') { $qillName = str_replace('_id', '', $qillName); @@ -436,7 +295,7 @@ public static function whereClauseSingle(&$values, &$query) { $op, $value, CRM_Utils_Type::typeToString($fields[$qillName]['type']) ); list($op, $value) = CRM_Contact_BAO_Query::buildQillForFieldValue('CRM_Contribute_DAO_ContributionSoft', $name, $value, $op); - $query->_qill[$grouping][] = ts('%1 %2 %3', array(1 => $fields[$qillName]['title'], 2 => $op, 3 => $value)); + $query->_qill[$grouping][] = ts('%1 %2 %3', [1 => $fields[$qillName]['title'], 2 => $op, 3 => $value]); $query->_tables['civicrm_contribution_soft'] = $query->_whereTables['civicrm_contribution_soft'] = 1; return; @@ -510,12 +369,25 @@ public static function whereClauseSingle(&$values, &$query) { $query->_tables['civicrm_contribution'] = $query->_whereTables['civicrm_contribution'] = 1; return; + case 'contribution_recur_payment_processor_id': + $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause("civicrm_contribution_recur.payment_processor_id", $op, $value, "String"); + $paymentProcessors = civicrm_api3('PaymentProcessor', 'get', []); + $paymentProcessorNames = []; + foreach ($value as $paymentProcessorId) { + $paymentProcessorNames[] = $paymentProcessors['values'][$paymentProcessorId]['name']; + } + $query->_qill[$grouping][] = ts("Recurring Contribution Payment Processor %1 %2", [1 => $op, 2 => implode(', ', $paymentProcessorNames)]); + $query->_tables['civicrm_contribution_recur'] = $query->_whereTables['civicrm_contribution_recur'] = 1; + return; + case 'contribution_recur_processor_id': case 'contribution_recur_trxn_id': $fieldName = str_replace('contribution_recur_', '', $name); $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause("civicrm_contribution_recur.{$fieldName}", $op, $value, "String" ); + $recurFields = CRM_Contribute_DAO_ContributionRecur::fields(); + $query->_qill[$grouping][] = ts("Recurring Contribution %1 %2 '%3'", [1 => $recurFields[$fieldName]['title'], 2 => $op, 3 => $value]); $query->_tables['civicrm_contribution_recur'] = $query->_whereTables['civicrm_contribution_recur'] = 1; return; @@ -532,15 +404,21 @@ public static function whereClauseSingle(&$values, &$query) { $query->_tables['civicrm_contribution_recur'] = $query->_whereTables['civicrm_contribution_recur'] = 1; return; + case 'contribution_recur_contribution_status_id': + $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause("civicrm_contribution_recur.contribution_status_id", $op, $value, 'String'); + list($op, $value) = CRM_Contact_BAO_Query::buildQillForFieldValue('CRM_Contribute_DAO_ContributionRecur', 'contribution_status_id', $value, $op, $pseudoExtraParam); + $query->_qill[$grouping][] = ts("Recurring Contribution Status %1 '%2'", [1 => $op, 2 => $value]); + $query->_tables['civicrm_contribution_recur'] = $query->_whereTables['civicrm_contribution_recur'] = 1; + return; + case 'contribution_note': - $value = $strtolower(CRM_Core_DAO::escapeString($value)); + $value = CRM_Core_DAO::escapeString($value); if ($wildcard) { $value = "%$value%"; $op = 'LIKE'; } - $wc = ($op != 'LIKE') ? "LOWER(civicrm_note.note)" : "civicrm_note.note"; - $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause($wc, $op, $value, "String"); - $query->_qill[$grouping][] = ts('Contribution Note %1 %2', array(1 => $op, 2 => $quoteValue)); + $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause('civicrm_note.note', $op, $value, "String"); + $query->_qill[$grouping][] = ts('Contribution Note %1 %2', [1 => $op, 2 => $quoteValue]); $query->_tables['civicrm_contribution'] = $query->_whereTables['civicrm_contribution'] = $query->_whereTables['contribution_note'] = 1; return; @@ -567,10 +445,11 @@ public static function whereClauseSingle(&$values, &$query) { return; case 'contribution_batch_id': - $batches = CRM_Contribute_PseudoConstant::batch(); - $query->_where[$grouping][] = " civicrm_entity_batch.batch_id $op $value"; - $query->_qill[$grouping][] = ts('Batch Name %1 %2', array(1 => $op, 2 => $batches[$value])); + list($qillOp, $qillValue) = CRM_Contact_BAO_Query::buildQillForFieldValue('CRM_Batch_BAO_EntityBatch', 'batch_id', $value, $op); + $query->_qill[$grouping][] = ts('Batch Name %1 %2', [1 => $qillOp, 2 => $qillValue]); + $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause('civicrm_entity_batch.batch_id', $op, $value); $query->_tables['civicrm_contribution'] = $query->_whereTables['civicrm_contribution'] = 1; + $query->_tables['civicrm_financial_trxn'] = $query->_whereTables['civicrm_financial_trxn'] = 1; $query->_tables['contribution_batch'] = $query->_whereTables['contribution_batch'] = 1; return; @@ -578,14 +457,30 @@ public static function whereClauseSingle(&$values, &$query) { // CRM-16713 - contribution search by premiums on 'Find Contribution' form. $qillName = $name; list($operator, $productValue) = CRM_Contact_BAO_Query::buildQillForFieldValue('CRM_Contribute_DAO_Product', $name, $value, $op); - $query->_qill[$grouping][] = ts('%1 %2 %3', array(1 => $fields[$qillName]['title'], 2 => $operator, 3 => $productValue)); + $query->_qill[$grouping][] = ts('%1 %2 %3', [1 => $fields[$qillName]['title'], 2 => $operator, 3 => $productValue]); $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause("civicrm_product.id", $op, $value); $query->_tables['civicrm_product'] = $query->_whereTables['civicrm_product'] = 1; return; case 'contribution_is_payment': $query->_where[$grouping][] = " civicrm_financial_trxn.is_payment $op $value"; - $query->_tables['contribution_financial_trxn'] = $query->_whereTables['contribution_financial_trxn'] = 1; + $query->_tables['civicrm_financial_trxn'] = $query->_whereTables['civicrm_financial_trxn'] = 1; + return; + + case 'financial_trxn_card_type_id': + $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause('civicrm_financial_trxn.card_type_id', $op, $value); + $query->_tables['civicrm_financial_trxn'] = $query->_whereTables['civicrm_financial_trxn'] = 1; + $query->_tables['civicrm_contribution'] = $query->_whereTables['civicrm_contribution'] = 1; + list($op, $value) = CRM_Contact_BAO_Query::buildQillForFieldValue('CRM_Financial_DAO_FinancialTrxn', 'card_type_id', $value, $op); + $query->_qill[$grouping][] = ts('Card Type %1 %2', [1 => $op, 2 => $value]); + return; + + case 'financial_trxn_pan_truncation': + $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause('civicrm_financial_trxn.pan_truncation', $op, $value); + $query->_tables['civicrm_financial_trxn'] = $query->_whereTables['civicrm_financial_trxn'] = 1; + $query->_tables['civicrm_contribution'] = $query->_whereTables['civicrm_contribution'] = 1; + list($op, $value) = CRM_Contact_BAO_Query::buildQillForFieldValue('CRM_Financial_DAO_FinancialTrxn', 'pan_truncation', $value, $op); + $query->_qill[$grouping][] = ts('Card Number %1 %2', [1 => $op, 2 => $value]); return; default: @@ -597,23 +492,24 @@ public static function whereClauseSingle(&$values, &$query) { // CRM-12597 CRM_Core_Session::setStatus(ts( 'We did not recognize the search field: %1. Please check and fix your contribution related smart groups.', - array(1 => $fldName) + [1 => $fldName] ) ); return; } $whereTable = $fields[$fldName]; - $value = trim($value); + if (!is_array($value)) { + $value = trim($value); + } $dataType = "String"; if (!empty($whereTable['type'])) { $dataType = CRM_Utils_Type::typeToString($whereTable['type']); } - $wc = ($op != 'LIKE' && $dataType != 'Date') ? "LOWER($whereTable[where])" : "$whereTable[where]"; - $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause($wc, $op, $value, $dataType); + $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause($whereTable['where'], $op, $value, $dataType); $query->_qill[$grouping][] = "$whereTable[title] $op $quoteValue"; - list($tableName, $fieldName) = explode('.', $whereTable['where'], 2); + list($tableName) = explode('.', $whereTable['where'], 2); $query->_tables[$tableName] = $query->_whereTables[$tableName] = 1; if ($tableName == 'civicrm_contribution_product') { $query->_tables['civicrm_product'] = $query->_whereTables['civicrm_product'] = 1; @@ -639,7 +535,7 @@ public static function from($name, $mode, $side) { switch ($name) { case 'civicrm_contribution': $from = " $side JOIN civicrm_contribution ON civicrm_contribution.contact_id = contact_a.id "; - if (in_array(self::$_contribOrSoftCredit, array("only_scredits", "both_related", "both"))) { + if (in_array(self::$_contribOrSoftCredit, ["only_scredits", "both_related", "both"])) { // switch the from table if its only soft credit search $from = " $side JOIN contribution_search_scredit_combined ON contribution_search_scredit_combined.contact_id = contact_a.id "; $from .= " $side JOIN civicrm_contribution ON civicrm_contribution.id = contribution_search_scredit_combined.id "; @@ -693,18 +589,6 @@ public static function from($name, $mode, $side) { $from .= " $side JOIN civicrm_product ON civicrm_contribution_product.product_id =civicrm_product.id "; break; - case 'contribution_payment_instrument': - $from = " $side JOIN civicrm_option_group option_group_payment_instrument ON ( option_group_payment_instrument.name = 'payment_instrument')"; - $from .= " $side JOIN civicrm_option_value contribution_payment_instrument ON (civicrm_contribution.payment_instrument_id = contribution_payment_instrument.value - AND option_group_payment_instrument.id = contribution_payment_instrument.option_group_id ) "; - break; - - case 'contribution_status': - $from = " $side JOIN civicrm_option_group option_group_contribution_status ON (option_group_contribution_status.name = 'contribution_status')"; - $from .= " $side JOIN civicrm_option_value contribution_status ON (civicrm_contribution.contribution_status_id = contribution_status.value - AND option_group_contribution_status.id = contribution_status.option_group_id ) "; - break; - case 'contribution_softcredit_type': $from = " $side JOIN civicrm_option_group option_group_contribution_softcredit_type ON (option_group_contribution_softcredit_type.name = 'soft_credit_type')"; @@ -736,13 +620,13 @@ public static function from($name, $mode, $side) { break; case 'civicrm_contribution_soft': - if (!in_array(self::$_contribOrSoftCredit, array("only_scredits", "both_related", "both"))) { + if (!in_array(self::$_contribOrSoftCredit, ["only_scredits", "both_related", "both"])) { $from = " $side JOIN civicrm_contribution_soft ON civicrm_contribution_soft.contribution_id = civicrm_contribution.id"; } break; case 'civicrm_contribution_soft_contact': - if (in_array(self::$_contribOrSoftCredit, array("only_scredits", "both_related", "both"))) { + if (in_array(self::$_contribOrSoftCredit, ["only_scredits", "both_related", "both"])) { $from .= " $side JOIN civicrm_contact civicrm_contact_d ON (civicrm_contribution.contact_id = civicrm_contact_d.id ) AND contribution_search_scredit_combined.scredit_id IS NOT NULL"; } @@ -760,18 +644,20 @@ public static function from($name, $mode, $side) { break; case 'contribution_batch': - $from .= " $side JOIN civicrm_entity_financial_trxn ON ( - civicrm_entity_financial_trxn.entity_table = 'civicrm_contribution' - AND civicrm_contribution.id = civicrm_entity_financial_trxn.entity_id )"; - - $from .= " $side JOIN civicrm_financial_trxn ON ( - civicrm_entity_financial_trxn.financial_trxn_id = civicrm_financial_trxn.id )"; - $from .= " $side JOIN civicrm_entity_batch ON ( civicrm_entity_batch.entity_table = 'civicrm_financial_trxn' AND civicrm_financial_trxn.id = civicrm_entity_batch.entity_id )"; $from .= " $side JOIN civicrm_batch ON civicrm_entity_batch.batch_id = civicrm_batch.id"; break; + + case 'civicrm_financial_trxn': + $from .= " $side JOIN civicrm_entity_financial_trxn ON ( + civicrm_entity_financial_trxn.entity_table = 'civicrm_contribution' + AND civicrm_contribution.id = civicrm_entity_financial_trxn.entity_id )"; + + $from .= " $side JOIN civicrm_financial_trxn ON ( + civicrm_entity_financial_trxn.financial_trxn_id = civicrm_financial_trxn.id )"; + break; } return $from; } @@ -803,7 +689,7 @@ public static function initializeAnySoftCreditClause(&$query) { * * @return bool */ - public static function isSoftCreditOptionEnabled($queryParams = array()) { + public static function isSoftCreditOptionEnabled($queryParams = []) { static $tempTableFilled = FALSE; if (!empty($queryParams)) { foreach (array_keys($queryParams) as $id) { @@ -816,7 +702,7 @@ public static function isSoftCreditOptionEnabled($queryParams = array()) { } } if (in_array(self::$_contribOrSoftCredit, - array("only_scredits", "both_related", "both"))) { + ["only_scredits", "both_related", "both"])) { if (!$tempTableFilled) { // build a temp table which is union of contributions and soft credits // note: group-by in first part ensures uniqueness in counts @@ -845,11 +731,11 @@ public static function isSoftCreditOptionEnabled($queryParams = array()) { * @return array */ public static function softCreditReturnProperties($isExportMode = FALSE) { - $properties = array( + $properties = [ 'contribution_soft_credit_name' => 1, 'contribution_soft_credit_amount' => 1, 'contribution_soft_credit_type' => 1, - ); + ]; if ($isExportMode) { $properties['contribution_soft_credit_contact_id'] = 1; $properties['contribution_soft_credit_contribution_id'] = 1; @@ -862,9 +748,12 @@ public static function softCreditReturnProperties($isExportMode = FALSE) { * * The default return properties array returns far too many fields for 'everyday use. Every field you add to this array * kills a small kitten so add carefully. + * + * @param array $queryParams + * @return array */ - public static function selectorReturnProperties() { - $properties = array( + public static function selectorReturnProperties($queryParams) { + $properties = [ 'contact_type' => 1, 'contact_sub_type' => 1, 'sort_name' => 1, @@ -880,21 +769,33 @@ public static function selectorReturnProperties() { 'contribution_status_id' => 1, // @todo return this & fix query to do pseudoconstant thing. 'contribution_status' => 1, - // @todo the product field got added because it suited someone's use case. - // ideally we would have some configurability here because I think 90% of sites would - // disagree this is the right field to show - but they wouldn't agree with each other - // on what field to show instead. - 'contribution_product_id' => 1, - 'product_name' => 1, 'currency' => 1, - ); - if (self::isSoftCreditOptionEnabled()) { + 'cancel_date' => 1, + 'contribution_recur_id' => 1, + ]; + if (self::isSiteHasProducts()) { + $properties['product_name'] = 1; + $properties['contribution_product_id'] = 1; + } + if (self::isSoftCreditOptionEnabled($queryParams)) { $properties = array_merge($properties, self::softCreditReturnProperties()); } return $properties; } + /** + * Do any products exist in this site's database. + * + * @return bool + */ + public static function isSiteHasProducts() { + if (!isset(\Civi::$statics[__CLASS__]['has_products'])) { + \Civi::$statics[__CLASS__]['has_products'] = (bool) CRM_Core_DAO::singleValueQuery('SELECT id FROM civicrm_contribution_product LIMIT 1'); + } + return \Civi::$statics[__CLASS__]['has_products']; + } + /** * Function you should avoid. * @@ -912,7 +813,7 @@ public static function selectorReturnProperties() { public static function defaultReturnProperties($mode, $includeCustomFields = TRUE) { $properties = NULL; if ($mode & CRM_Contact_BAO_Query::MODE_CONTRIBUTE) { - $properties = array( + $properties = [ // add 'contact_type' => 1, // fields @@ -940,7 +841,7 @@ public static function defaultReturnProperties($mode, $includeCustomFields = TRU // kittens 'payment_instrument_id' => 1, // argh - 'check_number' => 1, + 'contribution_check_number' => 1, // no 'non_deductible_amount' => 1, // not @@ -951,6 +852,7 @@ public static function defaultReturnProperties($mode, $includeCustomFields = TRU 'trxn_id' => 1, // join 'invoice_id' => 1, + 'invoice_number' => 1, // added 'currency' => 1, // to @@ -958,17 +860,11 @@ public static function defaultReturnProperties($mode, $includeCustomFields = TRU //every 'receipt_date' => 1, // query - 'product_name' => 1, //whether - 'sku' => 1, // or - 'product_option' => 1, // not - 'fulfilled_date' => 1, // the - 'contribution_start_date' => 1, // field - 'contribution_end_date' => 1, // is 'is_test' => 1, // actually @@ -990,9 +886,17 @@ public static function defaultReturnProperties($mode, $includeCustomFields = TRU // on 'contribution_campaign_id' => 1, // calling - 'contribution_product_id' => 1, //function - ); + ]; + if (self::isSiteHasProducts()) { + $properties['fulfilled_date'] = 1; + $properties['product_name'] = 1; + $properties['contribution_product_id'] = 1; + $properties['product_option'] = 1; + $properties['sku'] = 1; + $properties['contribution_start_date'] = 1; + $properties['contribution_end_date'] = 1; + } if (self::isSoftCreditOptionEnabled()) { $properties = array_merge($properties, self::softCreditReturnProperties()); } @@ -1023,51 +927,56 @@ public static function buildSearchForm(&$form) { // CRM-17602 // This hidden element added for displaying Date Range error correctly. Definitely a dirty hack, but... it works. $form->addElement('hidden', 'contribution_date_range_error'); - $form->addFormRule(array('CRM_Contribute_BAO_Query', 'formRule'), $form); + $form->addFormRule(['CRM_Contribute_BAO_Query', 'formRule'], $form); + + $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->add('text', 'contribution_amount_low', ts('From'), array('size' => 8, 'maxlength' => 8)); - $form->addRule('contribution_amount_low', ts('Please enter a valid money value (e.g. %1).', array(1 => CRM_Utils_Money::format('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->add('text', 'contribution_amount_high', ts('To'), array('size' => 8, 'maxlength' => 8)); - $form->addRule('contribution_amount_high', ts('Please enter a valid money value (e.g. %1).', array(1 => CRM_Utils_Money::format('99.99', ' '))), 'money'); + $form->addField('cancel_reason', ['entity' => 'Contribution']); + CRM_Core_Form_Date::buildDateRange($form, 'contribution_cancel_date', 1, '_low', '_high', ts('From:'), FALSE); + $form->addElement('hidden', 'contribution_cancel_date_range_error'); // Adding select option for curreny type -- CRM-4711 $form->add('select', 'contribution_currency_type', ts('Currency Type'), - array( + [ '' => ts('- any -'), - ) + - CRM_Core_PseudoConstant::get('CRM_Contribute_DAO_Contribution', 'currency', array('labelColumn' => 'name')), - FALSE, array('class' => 'crm-select2') + ] + + CRM_Core_PseudoConstant::get('CRM_Contribute_DAO_Contribution', 'currency', ['labelColumn' => 'name']), + FALSE, ['class' => 'crm-select2'] ); // CRM-13848 CRM_Financial_BAO_FinancialType::getAvailableFinancialTypes($financialTypes, CRM_Core_Action::VIEW); $form->addSelect('financial_type_id', - array('entity' => 'contribution', 'multiple' => 'multiple', 'context' => 'search', 'options' => $financialTypes) + ['entity' => 'contribution', 'multiple' => 'multiple', 'context' => 'search', 'options' => $financialTypes] ); $form->add('select', 'contribution_page_id', ts('Contribution Page'), CRM_Contribute_PseudoConstant::contributionPage(), - FALSE, array('class' => 'crm-select2', 'multiple' => 'multiple', 'placeholder' => ts('- any -')) + FALSE, ['class' => 'crm-select2', 'multiple' => 'multiple', 'placeholder' => ts('- any -')] ); - $form->addSelect('payment_instrument_id', - array('entity' => 'contribution', 'multiple' => 'multiple', 'label' => ts('Payment Method'), 'option_url' => NULL, 'placeholder' => ts('- any -')) + // use contribution_payment_instrument_id instead of payment_instrument_id + // Contribution Edit form (pop-up on contribution/Contact(display Result as Contribution) open on search form), + // then payment method change action not working properly because of same html ID present two time on one page + $form->addSelect('contribution_payment_instrument_id', + ['entity' => 'contribution', 'field' => 'payment_instrument_id', 'multiple' => 'multiple', 'label' => ts('Payment Method'), 'option_url' => NULL, 'placeholder' => ts('- any -')] ); $form->add('select', 'contribution_pcp_made_through_id', ts('Personal Campaign Page'), - CRM_Contribute_PseudoConstant::pcPage(), FALSE, array('class' => 'crm-select2', 'multiple' => 'multiple', 'placeholder' => ts('- any -'))); + CRM_Contribute_PseudoConstant::pcPage(), FALSE, ['class' => 'crm-select2', 'multiple' => 'multiple', 'placeholder' => ts('- any -')]); $statusValues = CRM_Core_PseudoConstant::get('CRM_Contribute_DAO_Contribution', 'contribution_status_id'); - // Remove status values that are only used for recurring contributions or pledges (In Progress, Overdue). - unset($statusValues['5'], $statusValues['6']); $form->add('select', 'contribution_status_id', ts('Contribution Status'), $statusValues, - FALSE, array('class' => 'crm-select2', 'multiple' => 'multiple') + FALSE, ['class' => 'crm-select2', 'multiple' => 'multiple'] ); // Add fields for thank you and receipt @@ -1081,51 +990,53 @@ public static function buildSearchForm(&$form) { // Add field for transaction ID search $form->addElement('text', 'contribution_trxn_id', ts("Transaction ID")); - $form->addElement('text', 'invoice_id', ts("Invoice ID")); + $form->addElement('text', 'invoice_number', ts("Invoice Number")); $form->addElement('text', 'contribution_check_number', ts('Check Number')); // Add field for pcp display in roll search $form->addYesNo('contribution_pcp_display_in_roll', ts('Personal Campaign Page Honor Roll?'), TRUE); // Soft credit related fields - $options = array( + $options = [ 'only_contribs' => ts('Contributions Only'), 'only_scredits' => ts('Soft Credits Only'), 'both_related' => ts('Soft Credits with related Hard Credit'), 'both' => ts('Both'), - ); - $form->add('select', 'contribution_or_softcredits', ts('Contributions OR Soft Credits?'), $options, FALSE, array('class' => "crm-select2")); + ]; + $form->add('select', 'contribution_or_softcredits', ts('Contributions OR Soft Credits?'), $options, FALSE, ['class' => "crm-select2"]); $form->addSelect( 'contribution_soft_credit_type_id', - array( + [ 'entity' => 'contribution_soft', 'field' => 'soft_credit_type_id', 'multiple' => TRUE, 'context' => 'search', - ) - ); - - // CRM-16713 - contribution search by premiums on 'Find Contribution' form. - $form->add('select', 'contribution_product_id', - ts('Premium'), - CRM_Contribute_PseudoConstant::products(), - FALSE, array('class' => 'crm-select2', 'multiple' => 'multiple', 'placeholder' => ts('- any -')) + ] ); - // Add all the custom searchable fields - $contribution = array('Contribution'); - $groupDetails = CRM_Core_BAO_CustomGroup::getGroupDetail(NULL, TRUE, $contribution); - if ($groupDetails) { - $form->assign('contributeGroupTree', $groupDetails); - foreach ($groupDetails as $group) { - foreach ($group['fields'] as $field) { - $fieldId = $field['id']; - $elementName = 'custom_' . $fieldId; - CRM_Core_BAO_CustomField::addQuickFormElement($form, $elementName, $fieldId, FALSE, TRUE); - } - } + $form->addField('financial_trxn_card_type_id', ['entity' => 'FinancialTrxn', 'name' => 'card_type_id', 'action' => 'get']); + + $form->add('text', 'financial_trxn_pan_truncation', ts('Card Number'), [ + 'size' => 5, + 'maxlength' => 4, + 'autocomplete' => 'off', + ]); + + if (CRM_Contribute_BAO_Query::isSiteHasProducts()) { + // CRM-16713 - contribution search by premiums on 'Find Contribution' form. + $form->add('select', 'contribution_product_id', + ts('Premium'), + CRM_Contribute_PseudoConstant::products(), + FALSE, [ + 'class' => 'crm-select2', + 'multiple' => 'multiple', + 'placeholder' => ts('- any -'), + ] + ); } + self::addCustomFormFields($form, ['Contribution']); + CRM_Campaign_BAO_Campaign::addCampaignInComponentSearch($form, 'contribution_campaign_id'); // Add batch select @@ -1134,26 +1045,21 @@ public static function buildSearchForm(&$form) { if (!empty($batches)) { $form->add('select', 'contribution_batch_id', ts('Batch Name'), - array('' => ts('- any -')) + $batches, - FALSE, array('class' => 'crm-select2') + [ + '' => ts('- any -'), + // CRM-19325 + 'IS NULL' => ts('None'), + ] + $batches, + FALSE, ['class' => 'crm-select2'] ); } $form->assign('validCiviContribute', TRUE); - $form->setDefaults(array('contribution_test' => 0)); + $form->setDefaults(['contribution_test' => 0]); CRM_Contribute_BAO_ContributionRecur::recurringContribution($form); } - /** - * Function that may not be needed. - * - * @param array $row - * @param int $id - */ - public static function searchAction(&$row, $id) { - } - /** * Get table names. * @@ -1164,7 +1070,7 @@ public static function searchAction(&$row, $id) { public static function tableNames(&$tables) { // Add contribution table if (!empty($tables['civicrm_product'])) { - $tables = array_merge(array('civicrm_contribution' => 1), $tables); + $tables = array_merge(['civicrm_contribution' => 1], $tables); } if (!empty($tables['civicrm_contribution_product']) && empty($tables['civicrm_product'])) { @@ -1212,15 +1118,98 @@ public static function buildDateWhere(&$values, $query, $name, $field, $title) { * @return bool|array */ public static function formRule($fields, $files, $form) { - $errors = array(); + $errors = []; - if (empty($fields['contribution_date_high']) || empty($fields['contribution_date_low'])) { - return TRUE; + if (!empty($fields['contribution_date_high']) && !empty($fields['contribution_date_low'])) { + CRM_Utils_Rule::validDateRange($fields, 'contribution_date', $errors, ts('Date Received')); + } + if (!empty($fields['contribution_cancel_date_high']) && !empty($fields['contribution_cancel_date_low'])) { + CRM_Utils_Rule::validDateRange($fields, 'contribution_cancel_date', $errors, ts('Cancel Date')); } - - CRM_Utils_Rule::validDateRange($fields, 'contribution_date', $errors, ts('Date Received')); return empty($errors) ? TRUE : $errors; } + /** + * Add the soft credit fields to the select fields. + * + * Extracted into separate function to improve readability of main select function. + * + * @param $query + */ + private static function addSoftCreditFields(&$query) { + $includeSoftCredits = self::isSoftCreditOptionEnabled($query->_params); + if (!empty($query->_returnProperties['contribution_soft_credit_name'])) { + if ($includeSoftCredits) { + $query->_select['contribution_soft_credit_name'] = "civicrm_contact_d.sort_name as contribution_soft_credit_name"; + // also include contact id. Will help build hyperlinks + $query->_select['contribution_soft_credit_contact_id'] = "civicrm_contact_d.id as contribution_soft_credit_contact_id"; + } + $query->_element['contribution_soft_credit_name'] = 1; + $query->_element['contribution_soft_credit_contact_id'] = 1; + + $query->_tables['civicrm_contribution'] = 1; + $query->_tables['civicrm_contribution_soft'] = 1; + $query->_tables['civicrm_contribution_soft_contact'] = 1; + } + + if (!empty($query->_returnProperties['contribution_soft_credit_contact_id'])) { + $query->_tables['civicrm_contribution'] = 1; + $query->_tables['civicrm_contribution_soft'] = 1; + $query->_tables['civicrm_contribution_soft_contact'] = 1; + } + + if (!empty($query->_returnProperties['contribution_soft_credit_amount'])) { + if ($includeSoftCredits) { + $query->_select['contribution_soft_credit_amount'] = "civicrm_contribution_soft.amount as contribution_soft_credit_amount"; + } + $query->_element['contribution_soft_credit_amount'] = 1; + $query->_tables['civicrm_contribution'] = 1; + $query->_tables['civicrm_contribution_soft'] = 1; + } + + if (!empty($query->_returnProperties['contribution_soft_credit_type'])) { + if ($includeSoftCredits) { + $query->_select['contribution_soft_credit_type'] = "contribution_softcredit_type.label as contribution_soft_credit_type"; + } + $query->_element['contribution_soft_credit_type'] = 1; + $query->_tables['civicrm_contribution'] = 1; + $query->_tables['contribution_softcredit_type'] = 1; + } + + if (!empty($query->_returnProperties['contribution_soft_credit_contribution_id'])) { + if ($includeSoftCredits) { + $query->_select['contribution_soft_credit_contribution_id'] = "civicrm_contribution_soft.contribution_id as contribution_soft_credit_contribution_id"; + } + $query->_element['contribution_soft_credit_contribution_id'] = 1; + $query->_tables['civicrm_contribution'] = 1; + $query->_tables['civicrm_contribution_soft'] = 1; + } + + if (!empty($query->_returnProperties['contribution_soft_credit_email'])) { + $query->_select['contribution_soft_credit_email'] = "soft_email.email as contribution_soft_credit_email"; + $query->_element['contribution_soft_credit_email'] = 1; + $query->_tables['civicrm_contribution'] = 1; + $query->_tables['civicrm_contribution_soft'] = 1; + $query->_tables['civicrm_contribution_soft_contact'] = 1; + $query->_tables['civicrm_contribution_soft_email'] = 1; + } + + if (!empty($query->_returnProperties['contribution_soft_credit_phone'])) { + $query->_select['contribution_soft_credit_email'] = "soft_phone.phone as contribution_soft_credit_phone"; + $query->_element['contribution_soft_credit_phone'] = 1; + $query->_tables['civicrm_contribution'] = 1; + $query->_tables['civicrm_contribution_soft'] = 1; + $query->_tables['civicrm_contribution_soft_contact'] = 1; + $query->_tables['civicrm_contribution_soft_phone'] = 1; + } + + if (!empty($query->_returnProperties['contribution_soft_credit_pcp_id'])) { + $query->_select['contribution_soft_credit_pcp_id'] = "civicrm_contribution_soft.pcp_id as contribution_soft_credit_pcp_id"; + $query->_element['contribution_soft_credit_pcp_id'] = 1; + $query->_tables['civicrm_contribution'] = 1; + $query->_tables['civicrm_contribution_soft'] = 1; + } + } + } diff --git a/CRM/Contribute/BAO/Widget.php b/CRM/Contribute/BAO/Widget.php index ce1afe2b7b6f..45ecc4116cf8 100644 --- a/CRM/Contribute/BAO/Widget.php +++ b/CRM/Contribute/BAO/Widget.php @@ -1,9 +1,9 @@ defaultCurrencySymbol; if (empty($contributionPageID) || @@ -91,7 +91,7 @@ public static function getContributionPageData($contributionPageID, $widgetID, $ WHERE is_test = 0 AND contribution_status_id IN ({$status}) AND contribution_page_id = %1"; - $params = array(1 => array($contributionPageID, 'Integer')); + $params = [1 => [$contributionPageID, 'Integer']]; $dao = CRM_Core_DAO::executeQuery($query, $params); if ($dao->fetch()) { $data['num_donors'] = (int) $dao->count; @@ -101,11 +101,13 @@ public static function getContributionPageData($contributionPageID, $widgetID, $ $data['num_donors'] = $data['money_raised'] = $data->money_raised = 0; } + $data['money_raised_amount'] = CRM_Utils_Money::format($data['money_raised']); + $query = " SELECT goal_amount, start_date, end_date, is_active FROM civicrm_contribution_page WHERE id = %1"; - $params = array(1 => array($contributionPageID, 'Integer')); + $params = [1 => [$contributionPageID, 'Integer']]; $dao = CRM_Core_DAO::executeQuery($query, $params); $data['campaign_start'] = ''; @@ -125,14 +127,11 @@ public static function getContributionPageData($contributionPageID, $widgetID, $ $now = time(); if ($dao->start_date) { $startDate = CRM_Utils_Date::unixTime($dao->start_date); - if ($startDate && - $startDate >= $now - ) { + if ($startDate && $startDate >= $now) { $data['is_active'] = FALSE; - $data['campaign_start'] = ts('Campaign starts on %1', array( - 1 => CRM_Utils_Date::customFormat($dao->start_date, $config->dateformatFull), - ) - ); + $data['campaign_start'] = ts('Campaign starts on %1', [ + 1 => CRM_Utils_Date::customFormat($dao->start_date, $config->dateformatFull), + ]); } } @@ -143,23 +142,23 @@ public static function getContributionPageData($contributionPageID, $widgetID, $ ) { $data['is_active'] = FALSE; $data['campaign_start'] = ts('Campaign ended on %1', - array( + [ 1 => CRM_Utils_Date::customFormat($dao->end_date, $config->dateformatFull), - ) + ] ); } elseif ($startDate >= $now) { $data['campaign_start'] = ts('Campaign starts on %1', - array( + [ 1 => CRM_Utils_Date::customFormat($dao->start_date, $config->dateformatFull), - ) + ] ); } else { $data['campaign_start'] = ts('Campaign ends on %1', - array( + [ 1 => CRM_Utils_Date::customFormat($dao->end_date, $config->dateformatFull), - ) + ] ); } } @@ -177,13 +176,13 @@ public static function getContributionPageData($contributionPageID, $widgetID, $ $percent = $data['money_raised'] / $data['money_target']; $data['money_raised_percentage'] = (round($percent, 2)) * 100 . "%"; $data['money_target_display'] = CRM_Utils_Money::format($data['money_target']); - $data['money_raised'] = ts('Raised %1 of %2', array( - 1 => CRM_Utils_Money::format($data['money_raised']), - 2 => $data['money_target_display'], - )); + $data['money_raised'] = ts('Raised %1 of %2', [ + 1 => CRM_Utils_Money::format($data['money_raised']), + 2 => $data['money_target_display'], + ]); } else { - $data['money_raised'] = ts('Raised %1', array(1 => CRM_Utils_Money::format($data['money_raised']))); + $data['money_raised'] = ts('Raised %1', [1 => CRM_Utils_Money::format($data['money_raised'])]); } $data['money_low'] = 0; @@ -193,7 +192,7 @@ public static function getContributionPageData($contributionPageID, $widgetID, $ // if is_active is false, show this link and hide the contribute button $data['homepage_link'] = $widget->url_homepage; - $data['colors'] = array(); + $data['colors'] = []; $data['colors']["title"] = $widget->color_title; $data['colors']["button"] = $widget->color_button; diff --git a/CRM/Contribute/Controller/Contribution.php b/CRM/Contribute/Controller/Contribution.php index 75e81eeb87b0..1dee55abcd12 100644 --- a/CRM/Contribute/Controller/Contribution.php +++ b/CRM/Contribute/Controller/Contribution.php @@ -1,9 +1,9 @@ _stateMachine = new CRM_Contribute_StateMachine_Search($this, $action); - - // create and instantiate the pages $this->addPages($this->_stateMachine, $action); - - // add all the actions - $config = CRM_Core_Config::singleton(); $this->addActions(); } diff --git a/CRM/Contribute/DAO/Contribution.php b/CRM/Contribute/DAO/Contribution.php index 1417839cf8ff..a3640ef1a4d9 100644 --- a/CRM/Contribute/DAO/Contribution.php +++ b/CRM/Contribute/DAO/Contribution.php @@ -1,737 +1,907 @@ __table = 'civicrm_contribution'; parent::__construct(); } + /** - * Returns foreign keys and entity references + * Returns foreign keys and entity references. * * @return array * [CRM_Core_Reference_Interface] */ - static function getReferenceColumns() { + public static function getReferenceColumns() { if (!isset(Civi::$statics[__CLASS__]['links'])) { - Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'contact_id', 'civicrm_contact', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'financial_type_id', 'civicrm_financial_type', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'contribution_page_id', 'civicrm_contribution_page', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'contribution_recur_id', 'civicrm_contribution_recur', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'address_id', 'civicrm_address', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'campaign_id', 'civicrm_campaign', 'id'); + Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'contact_id', 'civicrm_contact', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'financial_type_id', 'civicrm_financial_type', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'contribution_page_id', 'civicrm_contribution_page', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'contribution_recur_id', 'civicrm_contribution_recur', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'address_id', 'civicrm_address', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'campaign_id', 'civicrm_campaign', 'id'); CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']); } return Civi::$statics[__CLASS__]['links']; } + /** * Returns all the column names of this table * * @return array */ - static function &fields() { + public static function &fields() { if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = array( - 'contribution_id' => array( + Civi::$statics[__CLASS__]['fields'] = [ + 'contribution_id' => [ 'name' => 'id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Contribution ID') , - 'description' => 'Contribution ID', - 'required' => true, - 'import' => true, + 'title' => ts('Contribution ID'), + 'description' => ts('Contribution ID'), + 'required' => TRUE, + 'import' => TRUE, 'where' => 'civicrm_contribution.id', 'headerPattern' => '', 'dataPattern' => '', - 'export' => true, - ) , - 'contribution_contact_id' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contribution', + 'entity' => 'Contribution', + 'bao' => 'CRM_Contribute_BAO_Contribution', + 'localizable' => 0, + ], + 'contribution_contact_id' => [ 'name' => 'contact_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Contact ID') , - 'description' => 'FK to Contact ID', - 'required' => true, - 'import' => true, + 'title' => ts('Contact ID'), + 'description' => ts('FK to Contact ID'), + 'required' => TRUE, + 'import' => TRUE, 'where' => 'civicrm_contribution.contact_id', 'headerPattern' => '/contact(.?id)?/i', 'dataPattern' => '/^\d+$/', - 'export' => true, + 'export' => TRUE, + 'table_name' => 'civicrm_contribution', + 'entity' => 'Contribution', + 'bao' => 'CRM_Contribute_BAO_Contribution', + 'localizable' => 0, 'FKClassName' => 'CRM_Contact_DAO_Contact', - 'html' => array( + 'html' => [ 'type' => 'EntityRef', - ) , - ) , - 'financial_type_id' => array( + ], + ], + 'financial_type_id' => [ 'name' => 'financial_type_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Financial Type') , - 'description' => 'FK to Financial Type for (total_amount - non_deductible_amount).', - 'export' => false, + 'title' => ts('Financial Type'), + 'description' => ts('FK to Financial Type for (total_amount - non_deductible_amount).'), + 'export' => TRUE, 'where' => 'civicrm_contribution.financial_type_id', 'headerPattern' => '', 'dataPattern' => '', + 'table_name' => 'civicrm_contribution', + 'entity' => 'Contribution', + 'bao' => 'CRM_Contribute_BAO_Contribution', + 'localizable' => 0, 'FKClassName' => 'CRM_Financial_DAO_FinancialType', - 'html' => array( + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'table' => 'civicrm_financial_type', 'keyColumn' => 'id', 'labelColumn' => 'name', - ) - ) , - 'contribution_page_id' => array( + ] + ], + 'contribution_page_id' => [ 'name' => 'contribution_page_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Contribution Page ID') , - 'description' => 'The Contribution Page which triggered this contribution', - 'import' => true, + 'title' => ts('Contribution Page ID'), + 'description' => ts('The Contribution Page which triggered this contribution'), + 'import' => TRUE, 'where' => 'civicrm_contribution.contribution_page_id', 'headerPattern' => '', 'dataPattern' => '', - 'export' => true, + 'export' => TRUE, + 'table_name' => 'civicrm_contribution', + 'entity' => 'Contribution', + 'bao' => 'CRM_Contribute_BAO_Contribution', + 'localizable' => 0, 'FKClassName' => 'CRM_Contribute_DAO_ContributionPage', - 'html' => array( + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'table' => 'civicrm_contribution_page', 'keyColumn' => 'id', 'labelColumn' => 'title', - ) - ) , - 'payment_instrument_id' => array( + ] + ], + 'payment_instrument_id' => [ 'name' => 'payment_instrument_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Payment Method') , - 'description' => 'FK to Payment Instrument', - 'html' => array( + 'title' => ts('Payment Method ID'), + 'description' => ts('FK to Payment Instrument'), + 'export' => TRUE, + 'where' => 'civicrm_contribution.payment_instrument_id', + 'headerPattern' => '/^payment|(p(ayment\s)?instrument)$/i', + 'dataPattern' => '', + 'table_name' => 'civicrm_contribution', + 'entity' => 'Contribution', + 'bao' => 'CRM_Contribute_BAO_Contribution', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'optionGroupName' => 'payment_instrument', 'optionEditPath' => 'civicrm/admin/options/payment_instrument', - ) - ) , - 'receive_date' => array( + ] + ], + 'receive_date' => [ 'name' => 'receive_date', 'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME, - 'title' => ts('Date Received') , - 'description' => 'Date contribution was received - not necessarily the creation date of the record', - 'import' => true, + 'title' => ts('Date Received'), + 'description' => ts('Date contribution was received - not necessarily the creation date of the record'), + 'import' => TRUE, 'where' => 'civicrm_contribution.receive_date', 'headerPattern' => '/receive(.?date)?/i', 'dataPattern' => '/^\d{4}-?\d{2}-?\d{2} ?(\d{2}:?\d{2}:?(\d{2})?)?$/', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contribution', + 'entity' => 'Contribution', + 'bao' => 'CRM_Contribute_BAO_Contribution', + 'localizable' => 0, + 'html' => [ 'type' => 'Select Date', - ) , - ) , - 'non_deductible_amount' => array( + 'formatType' => 'activityDateTime', + ], + ], + 'non_deductible_amount' => [ 'name' => 'non_deductible_amount', 'type' => CRM_Utils_Type::T_MONEY, - 'title' => ts('Non-deductible Amount') , - 'description' => 'Portion of total amount which is NOT tax deductible. Equal to total_amount for non-deductible financial types.', - 'precision' => array( + 'title' => ts('Non-deductible Amount'), + 'description' => ts('Portion of total amount which is NOT tax deductible. Equal to total_amount for non-deductible financial types.'), + 'precision' => [ 20, 2 - ) , - 'import' => true, + ], + 'import' => TRUE, 'where' => 'civicrm_contribution.non_deductible_amount', 'headerPattern' => '/non?.?deduct/i', 'dataPattern' => '/^\d+(\.\d{2})?$/', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'default' => '0', + 'table_name' => 'civicrm_contribution', + 'entity' => 'Contribution', + 'bao' => 'CRM_Contribute_BAO_Contribution', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'total_amount' => array( + ], + ], + 'total_amount' => [ 'name' => 'total_amount', 'type' => CRM_Utils_Type::T_MONEY, - 'title' => ts('Total Amount') , - 'description' => 'Total amount of this contribution. Use market value for non-monetary gifts.', - 'required' => true, - 'precision' => array( + 'title' => ts('Total Amount'), + 'description' => ts('Total amount of this contribution. Use market value for non-monetary gifts.'), + 'required' => TRUE, + 'precision' => [ 20, 2 - ) , - 'import' => true, + ], + 'import' => TRUE, 'where' => 'civicrm_contribution.total_amount', 'headerPattern' => '/^total|(.?^am(ou)?nt)/i', 'dataPattern' => '/^\d+(\.\d{2})?$/', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contribution', + 'entity' => 'Contribution', + 'bao' => 'CRM_Contribute_BAO_Contribution', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'fee_amount' => array( + ], + ], + 'fee_amount' => [ 'name' => 'fee_amount', 'type' => CRM_Utils_Type::T_MONEY, - 'title' => ts('Fee Amount') , - 'description' => 'actual processor fee if known - may be 0.', - 'precision' => array( + 'title' => ts('Fee Amount'), + 'description' => ts('actual processor fee if known - may be 0.'), + 'precision' => [ 20, 2 - ) , - 'import' => true, + ], + 'import' => TRUE, 'where' => 'civicrm_contribution.fee_amount', 'headerPattern' => '/fee(.?am(ou)?nt)?/i', 'dataPattern' => '/^\d+(\.\d{2})?$/', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contribution', + 'entity' => 'Contribution', + 'bao' => 'CRM_Contribute_BAO_Contribution', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'net_amount' => array( + ], + ], + 'net_amount' => [ 'name' => 'net_amount', 'type' => CRM_Utils_Type::T_MONEY, - 'title' => ts('Net Amount') , - 'description' => 'actual funds transfer amount. total less fees. if processor does not report actual fee during transaction, this is set to total_amount.', - 'precision' => array( + 'title' => ts('Net Amount'), + 'description' => ts('actual funds transfer amount. total less fees. if processor does not report actual fee during transaction, this is set to total_amount.'), + 'precision' => [ 20, 2 - ) , - 'import' => true, + ], + 'import' => TRUE, 'where' => 'civicrm_contribution.net_amount', 'headerPattern' => '/net(.?am(ou)?nt)?/i', 'dataPattern' => '/^\d+(\.\d{2})?$/', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contribution', + 'entity' => 'Contribution', + 'bao' => 'CRM_Contribute_BAO_Contribution', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'trxn_id' => array( + ], + ], + 'trxn_id' => [ 'name' => 'trxn_id', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Transaction ID') , - 'description' => 'unique transaction id. may be processor id, bank id + trans id, or account number + check number... depending on payment_method', + 'title' => ts('Transaction ID'), + 'description' => ts('unique transaction id. may be processor id, bank id + trans id, or account number + check number... depending on payment_method'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - 'import' => true, + 'import' => TRUE, 'where' => 'civicrm_contribution.trxn_id', 'headerPattern' => '/tr(ansactio|x)n(.?id)?/i', 'dataPattern' => '', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contribution', + 'entity' => 'Contribution', + 'bao' => 'CRM_Contribute_BAO_Contribution', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'invoice_id' => array( + ], + ], + 'invoice_id' => [ 'name' => 'invoice_id', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Invoice ID') , - 'description' => 'unique invoice id, system generated or passed in', + 'title' => ts('Invoice Reference'), + 'description' => ts('unique invoice id, system generated or passed in'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - 'import' => true, + 'import' => TRUE, 'where' => 'civicrm_contribution.invoice_id', 'headerPattern' => '/invoice(.?id)?/i', 'dataPattern' => '', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contribution', + 'entity' => 'Contribution', + 'bao' => 'CRM_Contribute_BAO_Contribution', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'currency' => array( + ], + ], + 'invoice_number' => [ + 'name' => 'invoice_number', + 'type' => CRM_Utils_Type::T_STRING, + 'title' => ts('Invoice Number'), + 'description' => ts('Human readable invoice number'), + 'maxlength' => 255, + 'size' => CRM_Utils_Type::HUGE, + 'import' => TRUE, + 'where' => 'civicrm_contribution.invoice_number', + 'headerPattern' => '/invoice(.?number)?/i', + 'dataPattern' => '', + 'export' => TRUE, + 'table_name' => 'civicrm_contribution', + 'entity' => 'Contribution', + 'bao' => 'CRM_Contribute_BAO_Contribution', + 'localizable' => 0, + 'html' => [ + 'type' => 'Text', + ], + ], + 'currency' => [ 'name' => 'currency', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Currency') , - 'description' => '3 character string, value from config setting or input via user.', + 'title' => ts('Currency'), + 'description' => ts('3 character string, value from config setting or input via user.'), 'maxlength' => 3, 'size' => CRM_Utils_Type::FOUR, - 'import' => true, + 'import' => TRUE, 'where' => 'civicrm_contribution.currency', 'headerPattern' => '/cur(rency)?/i', 'dataPattern' => '/^[A-Z]{3}$/i', - 'export' => true, + 'export' => TRUE, 'default' => 'NULL', - 'html' => array( + 'table_name' => 'civicrm_contribution', + 'entity' => 'Contribution', + 'bao' => 'CRM_Contribute_BAO_Contribution', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'table' => 'civicrm_currency', 'keyColumn' => 'name', 'labelColumn' => 'full_name', 'nameColumn' => 'name', - ) - ) , - 'cancel_date' => array( + ] + ], + 'cancel_date' => [ 'name' => 'cancel_date', 'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME, - 'title' => ts('Cancel Date') , - 'description' => 'when was gift cancelled', - 'import' => true, + 'title' => ts('Cancel Date'), + 'description' => ts('when was gift cancelled'), + 'import' => TRUE, 'where' => 'civicrm_contribution.cancel_date', 'headerPattern' => '/cancel(.?date)?/i', 'dataPattern' => '/^\d{4}-?\d{2}-?\d{2} ?(\d{2}:?\d{2}:?(\d{2})?)?$/', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contribution', + 'entity' => 'Contribution', + 'bao' => 'CRM_Contribute_BAO_Contribution', + 'localizable' => 0, + 'html' => [ 'type' => 'Select Date', - ) , - ) , - 'cancel_reason' => array( + 'formatType' => 'activityDateTime', + ], + ], + 'cancel_reason' => [ 'name' => 'cancel_reason', 'type' => CRM_Utils_Type::T_TEXT, - 'title' => ts('Cancel Reason') , - 'import' => true, + 'title' => ts('Cancellation / Refund Reason'), + 'import' => TRUE, 'where' => 'civicrm_contribution.cancel_reason', 'headerPattern' => '/(cancel.?)?reason/i', 'dataPattern' => '', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contribution', + 'entity' => 'Contribution', + 'bao' => 'CRM_Contribute_BAO_Contribution', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'receipt_date' => array( + ], + ], + 'receipt_date' => [ 'name' => 'receipt_date', 'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME, - 'title' => ts('Receipt Date') , - 'description' => 'when (if) receipt was sent. populated automatically for online donations w/ automatic receipting', - 'import' => true, + 'title' => ts('Receipt Date'), + 'description' => ts('when (if) receipt was sent. populated automatically for online donations w/ automatic receipting'), + 'import' => TRUE, 'where' => 'civicrm_contribution.receipt_date', 'headerPattern' => '/receipt(.?date)?/i', 'dataPattern' => '/^\d{4}-?\d{2}-?\d{2} ?(\d{2}:?\d{2}:?(\d{2})?)?$/', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contribution', + 'entity' => 'Contribution', + 'bao' => 'CRM_Contribute_BAO_Contribution', + 'localizable' => 0, + 'html' => [ 'type' => 'Select Date', - ) , - ) , - 'thankyou_date' => array( + 'formatType' => 'activityDateTime', + ], + ], + 'thankyou_date' => [ 'name' => 'thankyou_date', 'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME, - 'title' => ts('Thank-you Date') , - 'description' => 'when (if) was donor thanked', - 'import' => true, + 'title' => ts('Thank-you Date'), + 'description' => ts('when (if) was donor thanked'), + 'import' => TRUE, 'where' => 'civicrm_contribution.thankyou_date', 'headerPattern' => '/thank(s|(.?you))?(.?date)?/i', 'dataPattern' => '/^\d{4}-?\d{2}-?\d{2} ?(\d{2}:?\d{2}:?(\d{2})?)?$/', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contribution', + 'entity' => 'Contribution', + 'bao' => 'CRM_Contribute_BAO_Contribution', + 'localizable' => 0, + 'html' => [ 'type' => 'Select Date', - ) , - ) , - 'contribution_source' => array( + 'formatType' => 'activityDateTime', + ], + ], + 'contribution_source' => [ 'name' => 'source', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Contribution Source') , - 'description' => 'Origin of this Contribution.', + 'title' => ts('Contribution Source'), + 'description' => ts('Origin of this Contribution.'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - 'import' => true, + 'import' => TRUE, 'where' => 'civicrm_contribution.source', 'headerPattern' => '/source/i', 'dataPattern' => '', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contribution', + 'entity' => 'Contribution', + 'bao' => 'CRM_Contribute_BAO_Contribution', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'amount_level' => array( + ], + ], + 'amount_level' => [ 'name' => 'amount_level', 'type' => CRM_Utils_Type::T_TEXT, - 'title' => ts('Amount Label') , - 'import' => true, + 'title' => ts('Amount Label'), + 'import' => TRUE, 'where' => 'civicrm_contribution.amount_level', 'headerPattern' => '', 'dataPattern' => '', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contribution', + 'entity' => 'Contribution', + 'bao' => 'CRM_Contribute_BAO_Contribution', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'contribution_recur_id' => array( + ], + ], + 'contribution_recur_id' => [ 'name' => 'contribution_recur_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Recurring Contribution ID') , - 'description' => 'Conditional foreign key to civicrm_contribution_recur id. Each contribution made in connection with a recurring contribution carries a foreign key to the recurring contribution record. This assumes we can track these processor initiated events.', + 'title' => ts('Recurring Contribution ID'), + 'description' => ts('Conditional foreign key to civicrm_contribution_recur id. Each contribution made in connection with a recurring contribution carries a foreign key to the recurring contribution record. This assumes we can track these processor initiated events.'), + 'export' => TRUE, + 'where' => 'civicrm_contribution.contribution_recur_id', + 'headerPattern' => '', + 'dataPattern' => '', + 'table_name' => 'civicrm_contribution', + 'entity' => 'Contribution', + 'bao' => 'CRM_Contribute_BAO_Contribution', + 'localizable' => 0, 'FKClassName' => 'CRM_Contribute_DAO_ContributionRecur', - ) , - 'is_test' => array( + ], + 'is_test' => [ 'name' => 'is_test', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Test') , - 'import' => true, + 'title' => ts('Test'), + 'import' => TRUE, 'where' => 'civicrm_contribution.is_test', 'headerPattern' => '', 'dataPattern' => '', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'default' => '0', + 'table_name' => 'civicrm_contribution', + 'entity' => 'Contribution', + 'bao' => 'CRM_Contribute_BAO_Contribution', + 'localizable' => 0, + 'html' => [ 'type' => 'CheckBox', - ) , - ) , - 'is_pay_later' => array( + ], + ], + 'is_pay_later' => [ 'name' => 'is_pay_later', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Is Pay Later') , - 'import' => true, + 'title' => ts('Is Pay Later'), + 'import' => TRUE, 'where' => 'civicrm_contribution.is_pay_later', 'headerPattern' => '', 'dataPattern' => '', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'default' => '0', + 'table_name' => 'civicrm_contribution', + 'entity' => 'Contribution', + 'bao' => 'CRM_Contribute_BAO_Contribution', + 'localizable' => 0, + 'html' => [ 'type' => 'CheckBox', - ) , - ) , - 'contribution_status_id' => array( + ], + ], + 'contribution_status_id' => [ 'name' => 'contribution_status_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Contribution Status ID') , - 'import' => true, + 'title' => ts('Contribution Status ID'), + 'import' => TRUE, 'where' => 'civicrm_contribution.contribution_status_id', 'headerPattern' => '/status/i', 'dataPattern' => '', - 'export' => true, + 'export' => TRUE, 'default' => '1', - 'html' => array( + 'table_name' => 'civicrm_contribution', + 'entity' => 'Contribution', + 'bao' => 'CRM_Contribute_BAO_Contribution', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'optionGroupName' => 'contribution_status', 'optionEditPath' => 'civicrm/admin/options/contribution_status', - ) - ) , - 'address_id' => array( + ] + ], + 'contribution_address_id' => [ 'name' => 'address_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Contribution Address') , - 'description' => 'Conditional foreign key to civicrm_address.id. We insert an address record for each contribution when we have associated billing name and address data.', + 'title' => ts('Contribution Address'), + 'description' => ts('Conditional foreign key to civicrm_address.id. We insert an address record for each contribution when we have associated billing name and address data.'), + 'export' => TRUE, + 'where' => 'civicrm_contribution.address_id', + 'headerPattern' => '', + 'dataPattern' => '', + 'table_name' => 'civicrm_contribution', + 'entity' => 'Contribution', + 'bao' => 'CRM_Contribute_BAO_Contribution', + 'localizable' => 0, 'FKClassName' => 'CRM_Core_DAO_Address', - ) , - 'check_number' => array( + ], + 'contribution_check_number' => [ 'name' => 'check_number', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Check Number') , + 'title' => ts('Check Number'), 'maxlength' => 255, 'size' => 6, - 'import' => true, + 'import' => TRUE, 'where' => 'civicrm_contribution.check_number', 'headerPattern' => '/check(.?number)?/i', 'dataPattern' => '', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contribution', + 'entity' => 'Contribution', + 'bao' => 'CRM_Contribute_BAO_Contribution', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'contribution_campaign_id' => array( + ], + ], + 'contribution_campaign_id' => [ 'name' => 'campaign_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Campaign') , - 'description' => 'The campaign for which this contribution has been triggered.', - 'import' => true, + 'title' => ts('Campaign'), + 'description' => ts('The campaign for which this contribution has been triggered.'), + 'import' => TRUE, 'where' => 'civicrm_contribution.campaign_id', 'headerPattern' => '', 'dataPattern' => '', - 'export' => true, + 'export' => TRUE, + 'table_name' => 'civicrm_contribution', + 'entity' => 'Contribution', + 'bao' => 'CRM_Contribute_BAO_Contribution', + 'localizable' => 0, 'FKClassName' => 'CRM_Campaign_DAO_Campaign', - 'html' => array( + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'table' => 'civicrm_campaign', 'keyColumn' => 'id', 'labelColumn' => 'title', - ) - ) , - 'creditnote_id' => array( + ] + ], + 'creditnote_id' => [ 'name' => 'creditnote_id', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Credit Note ID') , - 'description' => 'unique credit note id, system generated or passed in', + 'title' => ts('Credit Note ID'), + 'description' => ts('unique credit note id, system generated or passed in'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - 'import' => true, + 'import' => TRUE, 'where' => 'civicrm_contribution.creditnote_id', 'headerPattern' => '/creditnote(.?id)?/i', 'dataPattern' => '', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contribution', + 'entity' => 'Contribution', + 'bao' => 'CRM_Contribute_BAO_Contribution', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'tax_amount' => array( + ], + ], + 'tax_amount' => [ 'name' => 'tax_amount', 'type' => CRM_Utils_Type::T_MONEY, - 'title' => ts('Tax Amount') , - 'description' => 'Total tax amount of this contribution.', - 'precision' => array( + 'title' => ts('Tax Amount'), + 'description' => ts('Total tax amount of this contribution.'), + 'precision' => [ 20, 2 - ) , - 'import' => true, + ], + 'import' => TRUE, 'where' => 'civicrm_contribution.tax_amount', 'headerPattern' => '/tax(.?am(ou)?nt)?/i', 'dataPattern' => '/^\d+(\.\d{2})?$/', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contribution', + 'entity' => 'Contribution', + 'bao' => 'CRM_Contribute_BAO_Contribution', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'revenue_recognition_date' => array( + ], + ], + 'revenue_recognition_date' => [ 'name' => 'revenue_recognition_date', 'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME, - 'title' => ts('Revenue Recognition Date') , - 'description' => 'Stores the date when revenue should be recognized.', - 'import' => true, + 'title' => ts('Revenue Recognition Date'), + 'description' => ts('Stores the date when revenue should be recognized.'), + 'import' => TRUE, 'where' => 'civicrm_contribution.revenue_recognition_date', 'headerPattern' => '/revenue(.?date)?/i', 'dataPattern' => '/^\d{4}-?\d{2}-?\d{2} ?(\d{2}:?\d{2}:?(\d{2})?)?$/', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contribution', + 'entity' => 'Contribution', + 'bao' => 'CRM_Contribute_BAO_Contribution', + 'localizable' => 0, + 'html' => [ 'type' => 'Select Date', - ) , - ) , - ); + 'formatType' => 'activityDateTime', + ], + ], + ]; CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } return Civi::$statics[__CLASS__]['fields']; } + /** * Return a mapping from field-name to the corresponding key (as used in fields()). * * @return array * Array(string $name => string $uniqueName). */ - static function &fieldKeys() { + public static function &fieldKeys() { if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); } return Civi::$statics[__CLASS__]['fieldKeys']; } + /** * Returns the names of this table * * @return string */ - static function getTableName() { + public static function getTableName() { return self::$_tableName; } + /** * Returns if this table needs to be logged * - * @return boolean + * @return bool */ - function getLog() { + public function getLog() { return self::$_log; } + /** * Returns the list of fields that can be imported * @@ -739,10 +909,11 @@ function getLog() { * * @return array */ - static function &import($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'contribution', $prefix, array()); + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'contribution', $prefix, []); return $r; } + /** * Returns the list of fields that can be exported * @@ -750,8 +921,97 @@ static function &import($prefix = false) { * * @return array */ - static function &export($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'contribution', $prefix, array()); + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'contribution', $prefix, []); return $r; } + + /** + * Returns the list of indices + * + * @param bool $localize + * + * @return array + */ + public static function indices($localize = TRUE) { + $indices = [ + 'UI_contrib_payment_instrument_id' => [ + 'name' => 'UI_contrib_payment_instrument_id', + 'field' => [ + 0 => 'payment_instrument_id', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_contribution::0::payment_instrument_id', + ], + 'index_total_amount_receive_date' => [ + 'name' => 'index_total_amount_receive_date', + 'field' => [ + 0 => 'total_amount', + 1 => 'receive_date', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_contribution::0::total_amount::receive_date', + ], + 'index_source' => [ + 'name' => 'index_source', + 'field' => [ + 0 => 'source', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_contribution::0::source', + ], + 'UI_contrib_trxn_id' => [ + 'name' => 'UI_contrib_trxn_id', + 'field' => [ + 0 => 'trxn_id', + ], + 'localizable' => FALSE, + 'unique' => TRUE, + 'sig' => 'civicrm_contribution::1::trxn_id', + ], + 'UI_contrib_invoice_id' => [ + 'name' => 'UI_contrib_invoice_id', + 'field' => [ + 0 => 'invoice_id', + ], + 'localizable' => FALSE, + 'unique' => TRUE, + 'sig' => 'civicrm_contribution::1::invoice_id', + ], + 'index_contribution_status' => [ + 'name' => 'index_contribution_status', + 'field' => [ + 0 => 'contribution_status_id', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_contribution::0::contribution_status_id', + ], + 'received_date' => [ + 'name' => 'received_date', + 'field' => [ + 0 => 'receive_date', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_contribution::0::receive_date', + ], + 'check_number' => [ + 'name' => 'check_number', + 'field' => [ + 0 => 'check_number', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_contribution::0::check_number', + ], + 'index_creditnote_id' => [ + 'name' => 'index_creditnote_id', + 'field' => [ + 0 => 'creditnote_id', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_contribution::0::creditnote_id', + ], + ]; + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + } diff --git a/CRM/Contribute/DAO/ContributionPage.php b/CRM/Contribute/DAO/ContributionPage.php index 5bc1178b5d6c..116c52df9da6 100644 --- a/CRM/Contribute/DAO/ContributionPage.php +++ b/CRM/Contribute/DAO/ContributionPage.php @@ -1,759 +1,987 @@ __table = 'civicrm_contribution_page'; parent::__construct(); } + /** - * Returns foreign keys and entity references + * Returns foreign keys and entity references. * * @return array * [CRM_Core_Reference_Interface] */ - static function getReferenceColumns() { + public static function getReferenceColumns() { if (!isset(Civi::$statics[__CLASS__]['links'])) { - Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'financial_type_id', 'civicrm_financial_type', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'created_id', 'civicrm_contact', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'campaign_id', 'civicrm_campaign', 'id'); + Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'financial_type_id', 'civicrm_financial_type', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'created_id', 'civicrm_contact', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'campaign_id', 'civicrm_campaign', 'id'); CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']); } return Civi::$statics[__CLASS__]['links']; } + /** * Returns all the column names of this table * * @return array */ - static function &fields() { + public static function &fields() { if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = array( - 'id' => array( + Civi::$statics[__CLASS__]['fields'] = [ + 'id' => [ 'name' => 'id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Contribution Page ID') , - 'description' => 'Contribution Id', - 'required' => true, - ) , - 'title' => array( + 'title' => ts('Contribution Page ID'), + 'description' => ts('Contribution Id'), + 'required' => TRUE, + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, + ], + 'title' => [ 'name' => 'title', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Contribution Page Title') , - 'description' => 'Contribution Page title. For top of page display', + 'title' => ts('Contribution Page Title'), + 'description' => ts('Contribution Page title. For top of page display'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - ) , - 'intro_text' => array( + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 1, + ], + 'intro_text' => [ 'name' => 'intro_text', 'type' => CRM_Utils_Type::T_TEXT, - 'title' => ts('Contribution Page Introduction Text') , - 'description' => 'Text and html allowed. Displayed below title.', + 'title' => ts('Contribution Page Introduction Text'), + 'description' => ts('Text and html allowed. Displayed below title.'), 'rows' => 6, 'cols' => 50, - 'html' => array( + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 1, + 'html' => [ 'type' => 'RichTextEditor', - ) , - ) , - 'financial_type_id' => array( + ], + ], + 'financial_type_id' => [ 'name' => 'financial_type_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Financial Type') , - 'description' => 'default financial type assigned to contributions submitted via this page, e.g. Contribution, Campaign Contribution', + 'title' => ts('Financial Type'), + 'description' => ts('default financial type assigned to contributions submitted via this page, e.g. Contribution, Campaign Contribution'), + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, 'FKClassName' => 'CRM_Financial_DAO_FinancialType', - 'html' => array( + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'table' => 'civicrm_financial_type', 'keyColumn' => 'id', 'labelColumn' => 'name', - ) - ) , - 'payment_processor' => array( + ] + ], + 'payment_processor' => [ 'name' => 'payment_processor', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Payment Processor') , - 'description' => 'Payment Processors configured for this contribution Page', + 'title' => ts('Payment Processor'), + 'description' => ts('Payment Processors configured for this contribution Page'), 'maxlength' => 128, 'size' => CRM_Utils_Type::HUGE, - 'html' => array( + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'table' => 'civicrm_payment_processor', 'keyColumn' => 'id', 'labelColumn' => 'name', - ) - ) , - 'is_credit_card_only' => array( + ] + ], + 'is_credit_card_only' => [ 'name' => 'is_credit_card_only', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Is Credit Card Only?') , - 'description' => 'if true - processing logic must reject transaction at confirmation stage if pay method != credit card', - ) , - 'is_monetary' => array( + 'title' => ts('Is Credit Card Only?'), + 'description' => ts('if true - processing logic must reject transaction at confirmation stage if pay method != credit card'), + 'default' => '0', + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, + ], + 'is_monetary' => [ 'name' => 'is_monetary', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Is Monetary') , - 'description' => 'if true - allows real-time monetary transactions otherwise non-monetary transactions', + 'title' => ts('Is Monetary'), + 'description' => ts('if true - allows real-time monetary transactions otherwise non-monetary transactions'), 'default' => '1', - ) , - 'is_recur' => array( + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, + ], + 'is_recur' => [ 'name' => 'is_recur', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Is Recurring') , - 'description' => 'if true - allows recurring contributions, valid only for PayPal_Standard', - ) , - 'is_confirm_enabled' => array( + 'title' => ts('Is Recurring'), + 'description' => ts('if true - allows recurring contributions, valid only for PayPal_Standard'), + 'default' => '0', + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, + ], + 'is_confirm_enabled' => [ 'name' => 'is_confirm_enabled', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Confirmation Page?') , - 'description' => 'if false, the confirm page in contribution pages gets skipped', + 'title' => ts('Confirmation Page?'), + 'description' => ts('if false, the confirm page in contribution pages gets skipped'), 'default' => '1', - ) , - 'recur_frequency_unit' => array( + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, + ], + 'recur_frequency_unit' => [ 'name' => 'recur_frequency_unit', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Recurring Frequency') , - 'description' => 'Supported recurring frequency units.', + 'title' => ts('Recurring Frequency'), + 'description' => ts('Supported recurring frequency units.'), 'maxlength' => 128, 'size' => CRM_Utils_Type::HUGE, - ) , - 'is_recur_interval' => array( + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, + 'serialize' => self::SERIALIZE_SEPARATOR_TRIMMED, + 'html' => [ + 'type' => 'Select', + ], + 'pseudoconstant' => [ + 'optionGroupName' => 'recur_frequency_units', + 'keyColumn' => 'name', + 'optionEditPath' => 'civicrm/admin/options/recur_frequency_units', + ] + ], + 'is_recur_interval' => [ 'name' => 'is_recur_interval', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Support Recurring Intervals') , - 'description' => 'if true - supports recurring intervals', - ) , - 'is_recur_installments' => array( + 'title' => ts('Support Recurring Intervals'), + 'description' => ts('if true - supports recurring intervals'), + 'default' => '0', + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, + ], + 'is_recur_installments' => [ 'name' => 'is_recur_installments', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Recurring Installments?') , - 'description' => 'if true - asks user for recurring installments', - ) , - 'adjust_recur_start_date' => array( + 'title' => ts('Recurring Installments?'), + 'description' => ts('if true - asks user for recurring installments'), + 'default' => '0', + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, + ], + 'adjust_recur_start_date' => [ 'name' => 'adjust_recur_start_date', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Adjust Recurring Start Date') , - 'description' => 'if true - user is able to adjust payment start date', - ) , - 'is_pay_later' => array( + 'title' => ts('Adjust Recurring Start Date'), + 'description' => ts('if true - user is able to adjust payment start date'), + 'default' => '0', + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, + ], + 'is_pay_later' => [ 'name' => 'is_pay_later', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Pay Later') , - 'description' => 'if true - allows the user to send payment directly to the org later', - ) , - 'pay_later_text' => array( + 'title' => ts('Pay Later'), + 'description' => ts('if true - allows the user to send payment directly to the org later'), + 'default' => '0', + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, + ], + 'pay_later_text' => [ 'name' => 'pay_later_text', 'type' => CRM_Utils_Type::T_TEXT, - 'title' => ts('Pay Later Text') , - 'description' => 'The text displayed to the user in the main form', - ) , - 'pay_later_receipt' => array( + 'title' => ts('Pay Later Text'), + 'description' => ts('The text displayed to the user in the main form'), + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 1, + ], + 'pay_later_receipt' => [ 'name' => 'pay_later_receipt', 'type' => CRM_Utils_Type::T_TEXT, - 'title' => ts('Pay Later Receipt') , - 'description' => 'The receipt sent to the user instead of the normal receipt text', - ) , - 'is_partial_payment' => array( + 'title' => ts('Pay Later Receipt'), + 'description' => ts('The receipt sent to the user instead of the normal receipt text'), + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 1, + ], + 'is_partial_payment' => [ 'name' => 'is_partial_payment', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Allow Partial Payment') , - 'description' => 'is partial payment enabled for this online contribution page', - ) , - 'initial_amount_label' => array( + 'title' => ts('Allow Partial Payment'), + 'description' => ts('is partial payment enabled for this online contribution page'), + 'default' => '0', + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, + ], + 'initial_amount_label' => [ 'name' => 'initial_amount_label', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Initial Amount Label') , - 'description' => 'Initial amount label for partial payment', + 'title' => ts('Initial Amount Label'), + 'description' => ts('Initial amount label for partial payment'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - ) , - 'initial_amount_help_text' => array( + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 1, + ], + 'initial_amount_help_text' => [ 'name' => 'initial_amount_help_text', 'type' => CRM_Utils_Type::T_TEXT, - 'title' => ts('Initial Amount Help Text') , - 'description' => 'Initial amount help text for partial payment', - ) , - 'min_initial_amount' => array( + 'title' => ts('Initial Amount Help Text'), + 'description' => ts('Initial amount help text for partial payment'), + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 1, + ], + 'min_initial_amount' => [ 'name' => 'min_initial_amount', 'type' => CRM_Utils_Type::T_MONEY, - 'title' => ts('Min Initial Amount') , - 'description' => 'Minimum initial amount for partial payment', - 'precision' => array( + 'title' => ts('Min Initial Amount'), + 'description' => ts('Minimum initial amount for partial payment'), + 'precision' => [ 20, 2 - ) , - ) , - 'is_allow_other_amount' => array( + ], + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, + ], + 'is_allow_other_amount' => [ 'name' => 'is_allow_other_amount', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Allow Other Amounts') , - 'description' => 'if true, page will include an input text field where user can enter their own amount', - ) , - 'default_amount_id' => array( + 'title' => ts('Allow Other Amounts'), + 'description' => ts('if true, page will include an input text field where user can enter their own amount'), + 'default' => '0', + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, + ], + 'default_amount_id' => [ 'name' => 'default_amount_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Default Amount') , - 'description' => 'FK to civicrm_option_value.', - ) , - 'min_amount' => array( + 'title' => ts('Default Amount'), + 'description' => ts('FK to civicrm_option_value.'), + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, + ], + 'min_amount' => [ 'name' => 'min_amount', 'type' => CRM_Utils_Type::T_MONEY, - 'title' => ts('Minimum Amount') , - 'description' => 'if other amounts allowed, user can configure minimum allowed.', - 'precision' => array( + 'title' => ts('Minimum Amount'), + 'description' => ts('if other amounts allowed, user can configure minimum allowed.'), + 'precision' => [ 20, 2 - ) , - ) , - 'max_amount' => array( + ], + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, + ], + 'max_amount' => [ 'name' => 'max_amount', 'type' => CRM_Utils_Type::T_MONEY, - 'title' => ts('Maximum Amount') , - 'description' => 'if other amounts allowed, user can configure maximum allowed.', - 'precision' => array( + 'title' => ts('Maximum Amount'), + 'description' => ts('if other amounts allowed, user can configure maximum allowed.'), + 'precision' => [ 20, 2 - ) , - ) , - 'goal_amount' => array( + ], + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, + ], + 'goal_amount' => [ 'name' => 'goal_amount', 'type' => CRM_Utils_Type::T_MONEY, - 'title' => ts('Goal Amount') , - 'description' => 'The target goal for this page, allows people to build a goal meter', - 'precision' => array( + 'title' => ts('Goal Amount'), + 'description' => ts('The target goal for this page, allows people to build a goal meter'), + 'precision' => [ 20, 2 - ) , - ) , - 'thankyou_title' => array( + ], + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, + ], + 'thankyou_title' => [ 'name' => 'thankyou_title', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Thank-you Title') , - 'description' => 'Title for Thank-you page (header title tag, and display at the top of the page).', + 'title' => ts('Thank-you Title'), + 'description' => ts('Title for Thank-you page (header title tag, and display at the top of the page).'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - ) , - 'thankyou_text' => array( + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 1, + ], + 'thankyou_text' => [ 'name' => 'thankyou_text', 'type' => CRM_Utils_Type::T_TEXT, - 'title' => ts('Thank-you Text') , - 'description' => 'text and html allowed. displayed above result on success page', + 'title' => ts('Thank-you Text'), + 'description' => ts('text and html allowed. displayed above result on success page'), 'rows' => 8, 'cols' => 60, - 'html' => array( + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 1, + 'html' => [ 'type' => 'RichTextEditor', - ) , - ) , - 'thankyou_footer' => array( + ], + ], + 'thankyou_footer' => [ 'name' => 'thankyou_footer', 'type' => CRM_Utils_Type::T_TEXT, - 'title' => ts('Thank-you Footer') , - 'description' => 'Text and html allowed. displayed at the bottom of the success page. Common usage is to include link(s) to other pages such as tell-a-friend, etc.', + 'title' => ts('Thank-you Footer'), + 'description' => ts('Text and html allowed. displayed at the bottom of the success page. Common usage is to include link(s) to other pages such as tell-a-friend, etc.'), 'rows' => 8, 'cols' => 60, - 'html' => array( + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 1, + 'html' => [ 'type' => 'RichTextEditor', - ) , - ) , - 'is_email_receipt' => array( + ], + ], + 'is_email_receipt' => [ 'name' => 'is_email_receipt', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Send email Receipt') , - 'description' => 'if true, receipt is automatically emailed to contact on success', - ) , - 'receipt_from_name' => array( + 'title' => ts('Send email Receipt'), + 'description' => ts('if true, receipt is automatically emailed to contact on success'), + 'default' => '0', + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, + ], + 'receipt_from_name' => [ 'name' => 'receipt_from_name', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Receipt From') , - 'description' => 'FROM email name used for receipts generated by contributions to this contribution page.', + 'title' => ts('Receipt From'), + 'description' => ts('FROM email name used for receipts generated by contributions to this contribution page.'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - ) , - 'receipt_from_email' => array( + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 1, + ], + 'receipt_from_email' => [ 'name' => 'receipt_from_email', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Receipt From email') , - 'description' => 'FROM email address used for receipts generated by contributions to this contribution page.', + 'title' => ts('Receipt From email'), + 'description' => ts('FROM email address used for receipts generated by contributions to this contribution page.'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - ) , - 'cc_receipt' => array( + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, + ], + 'cc_receipt' => [ 'name' => 'cc_receipt', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Receipt cc') , - 'description' => 'comma-separated list of email addresses to cc each time a receipt is sent', + 'title' => ts('Receipt cc'), + 'description' => ts('comma-separated list of email addresses to cc each time a receipt is sent'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - ) , - 'bcc_receipt' => array( + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, + ], + 'bcc_receipt' => [ 'name' => 'bcc_receipt', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Receipt bcc') , - 'description' => 'comma-separated list of email addresses to bcc each time a receipt is sent', + 'title' => ts('Receipt bcc'), + 'description' => ts('comma-separated list of email addresses to bcc each time a receipt is sent'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - ) , - 'receipt_text' => array( + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, + ], + 'receipt_text' => [ 'name' => 'receipt_text', 'type' => CRM_Utils_Type::T_TEXT, - 'title' => ts('Receipt Text') , - 'description' => 'text to include above standard receipt info on receipt email. emails are text-only, so do not allow html for now', + 'title' => ts('Receipt Text'), + 'description' => ts('text to include above standard receipt info on receipt email. emails are text-only, so do not allow html for now'), 'rows' => 6, 'cols' => 50, - 'html' => array( + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 1, + 'html' => [ 'type' => 'TextArea', - ) , - ) , - 'is_active' => array( + ], + ], + 'is_active' => [ 'name' => 'is_active', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Is Page Active?') , - 'description' => 'Is this property active?', - ) , - 'footer_text' => array( + 'title' => ts('Is Page Active?'), + 'description' => ts('Is this property active?'), + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, + ], + 'footer_text' => [ 'name' => 'footer_text', 'type' => CRM_Utils_Type::T_TEXT, - 'title' => ts('Footer Text') , - 'description' => 'Text and html allowed. Displayed at the bottom of the first page of the contribution wizard.', + 'title' => ts('Footer Text'), + 'description' => ts('Text and html allowed. Displayed at the bottom of the first page of the contribution wizard.'), 'rows' => 6, 'cols' => 50, - 'html' => array( + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 1, + 'html' => [ 'type' => 'RichTextEditor', - ) , - ) , - 'amount_block_is_active' => array( + ], + ], + 'amount_block_is_active' => [ 'name' => 'amount_block_is_active', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Is Amount Block Active?') , - 'description' => 'Is this property active?', + 'title' => ts('Is Amount Block Active?'), + 'description' => ts('Is this property active?'), 'default' => '1', - ) , - 'start_date' => array( + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, + ], + 'start_date' => [ 'name' => 'start_date', 'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME, - 'title' => ts('Contribution Page Start Date') , - 'description' => 'Date and time that this page starts.', - ) , - 'end_date' => array( + 'title' => ts('Contribution Page Start Date'), + 'description' => ts('Date and time that this page starts.'), + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, + ], + 'end_date' => [ 'name' => 'end_date', 'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME, - 'title' => ts('Contribution Page End Date') , - 'description' => 'Date and time that this page ends. May be NULL if no defined end date/time', - ) , - 'created_id' => array( + 'title' => ts('Contribution Page End Date'), + 'description' => ts('Date and time that this page ends. May be NULL if no defined end date/time'), + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, + ], + 'created_id' => [ 'name' => 'created_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Contribution Page Created By') , - 'description' => 'FK to civicrm_contact, who created this contribution page', + 'title' => ts('Contribution Page Created By'), + 'description' => ts('FK to civicrm_contact, who created this contribution page'), + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, 'FKClassName' => 'CRM_Contact_DAO_Contact', - ) , - 'created_date' => array( + ], + 'created_date' => [ 'name' => 'created_date', 'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME, - 'title' => ts('Contribution Page Created Date') , - 'description' => 'Date and time that contribution page was created.', - ) , - 'currency' => array( + 'title' => ts('Contribution Page Created Date'), + 'description' => ts('Date and time that contribution page was created.'), + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, + ], + 'currency' => [ 'name' => 'currency', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Contribution Page Currency') , - 'description' => '3 character string, value from config setting or input via user.', + 'title' => ts('Contribution Page Currency'), + 'description' => ts('3 character string, value from config setting or input via user.'), 'maxlength' => 3, 'size' => CRM_Utils_Type::FOUR, 'default' => 'NULL', - 'html' => array( + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'table' => 'civicrm_currency', 'keyColumn' => 'name', 'labelColumn' => 'full_name', 'nameColumn' => 'name', - ) - ) , - 'campaign_id' => array( + ] + ], + 'campaign_id' => [ 'name' => 'campaign_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Contribution Page Campaign ID') , - 'description' => 'The campaign for which we are collecting contributions with this page.', + 'title' => ts('Contribution Page Campaign ID'), + 'description' => ts('The campaign for which we are collecting contributions with this page.'), + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, 'FKClassName' => 'CRM_Campaign_DAO_Campaign', - 'pseudoconstant' => array( + 'pseudoconstant' => [ 'table' => 'civicrm_campaign', 'keyColumn' => 'id', 'labelColumn' => 'title', - ) - ) , - 'is_share' => array( + ] + ], + 'is_share' => [ 'name' => 'is_share', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Is Contribution Page Shared?') , - 'description' => 'Can people share the contribution page through social media?', + 'title' => ts('Is Contribution Page Shared?'), + 'description' => ts('Can people share the contribution page through social media?'), 'default' => '1', - ) , - 'is_billing_required' => array( + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, + ], + 'is_billing_required' => [ 'name' => 'is_billing_required', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Is billing block required') , - 'description' => 'if true - billing block is required for online contribution page', - ) , - ); + 'title' => ts('Is billing block required'), + 'description' => ts('if true - billing block is required for online contribution page'), + 'default' => '0', + 'table_name' => 'civicrm_contribution_page', + 'entity' => 'ContributionPage', + 'bao' => 'CRM_Contribute_BAO_ContributionPage', + 'localizable' => 0, + ], + ]; CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } return Civi::$statics[__CLASS__]['fields']; } + /** * Return a mapping from field-name to the corresponding key (as used in fields()). * * @return array * Array(string $name => string $uniqueName). */ - static function &fieldKeys() { + public static function &fieldKeys() { if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); } return Civi::$statics[__CLASS__]['fieldKeys']; } + /** * Returns the names of this table * * @return string */ - static function getTableName() { + public static function getTableName() { return CRM_Core_DAO::getLocaleTableName(self::$_tableName); } + /** * Returns if this table needs to be logged * - * @return boolean + * @return bool */ - function getLog() { + public function getLog() { return self::$_log; } + /** * Returns the list of fields that can be imported * @@ -761,10 +989,11 @@ function getLog() { * * @return array */ - static function &import($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'contribution_page', $prefix, array()); + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'contribution_page', $prefix, []); return $r; } + /** * Returns the list of fields that can be exported * @@ -772,8 +1001,21 @@ static function &import($prefix = false) { * * @return array */ - static function &export($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'contribution_page', $prefix, array()); + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'contribution_page', $prefix, []); return $r; } + + /** + * Returns the list of indices + * + * @param bool $localize + * + * @return array + */ + public static function indices($localize = TRUE) { + $indices = []; + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + } diff --git a/CRM/Contribute/DAO/ContributionProduct.php b/CRM/Contribute/DAO/ContributionProduct.php index ffd4a07134d4..58dd6257ce82 100644 --- a/CRM/Contribute/DAO/ContributionProduct.php +++ b/CRM/Contribute/DAO/ContributionProduct.php @@ -1,261 +1,294 @@ __table = 'civicrm_contribution_product'; parent::__construct(); } + /** - * Returns foreign keys and entity references + * Returns foreign keys and entity references. * * @return array * [CRM_Core_Reference_Interface] */ - static function getReferenceColumns() { + public static function getReferenceColumns() { if (!isset(Civi::$statics[__CLASS__]['links'])) { - Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'contribution_id', 'civicrm_contribution', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'financial_type_id', 'civicrm_financial_type', 'id'); + Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'contribution_id', 'civicrm_contribution', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'financial_type_id', 'civicrm_financial_type', 'id'); CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']); } return Civi::$statics[__CLASS__]['links']; } + /** * Returns all the column names of this table * * @return array */ - static function &fields() { + public static function &fields() { if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = array( - 'id' => array( + Civi::$statics[__CLASS__]['fields'] = [ + 'id' => [ 'name' => 'id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Contribution Product ID') , - 'required' => true, - ) , - 'product_id' => array( + 'title' => ts('Contribution Product ID'), + 'required' => TRUE, + 'table_name' => 'civicrm_contribution_product', + 'entity' => 'ContributionProduct', + 'bao' => 'CRM_Contribute_DAO_ContributionProduct', + 'localizable' => 0, + ], + 'product_id' => [ 'name' => 'product_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Product ID') , - 'required' => true, - ) , - 'contribution_id' => array( + 'title' => ts('Product ID'), + 'required' => TRUE, + 'table_name' => 'civicrm_contribution_product', + 'entity' => 'ContributionProduct', + 'bao' => 'CRM_Contribute_DAO_ContributionProduct', + 'localizable' => 0, + ], + 'contribution_id' => [ 'name' => 'contribution_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Contribution ID') , - 'required' => true, + 'title' => ts('Contribution ID'), + 'required' => TRUE, + 'table_name' => 'civicrm_contribution_product', + 'entity' => 'ContributionProduct', + 'bao' => 'CRM_Contribute_DAO_ContributionProduct', + 'localizable' => 0, 'FKClassName' => 'CRM_Contribute_DAO_Contribution', - ) , - 'product_option' => array( + ], + 'product_option' => [ 'name' => 'product_option', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Product Option') , - 'description' => 'Option value selected if applicable - e.g. color, size etc.', + 'title' => ts('Product Option'), + 'description' => ts('Option value selected if applicable - e.g. color, size etc.'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - 'export' => true, + 'export' => TRUE, 'where' => 'civicrm_contribution_product.product_option', 'headerPattern' => '', 'dataPattern' => '', - ) , - 'quantity' => array( + 'table_name' => 'civicrm_contribution_product', + 'entity' => 'ContributionProduct', + 'bao' => 'CRM_Contribute_DAO_ContributionProduct', + 'localizable' => 0, + ], + 'quantity' => [ 'name' => 'quantity', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Quantity') , - 'export' => true, + 'title' => ts('Quantity'), + 'export' => TRUE, 'where' => 'civicrm_contribution_product.quantity', 'headerPattern' => '', 'dataPattern' => '', - ) , - 'fulfilled_date' => array( + 'table_name' => 'civicrm_contribution_product', + 'entity' => 'ContributionProduct', + 'bao' => 'CRM_Contribute_DAO_ContributionProduct', + 'localizable' => 0, + ], + 'fulfilled_date' => [ 'name' => 'fulfilled_date', 'type' => CRM_Utils_Type::T_DATE, - 'title' => ts('Fulfilled Date') , - 'description' => 'Optional. Can be used to record the date this product was fulfilled or shipped.', - 'export' => true, + 'title' => ts('Fulfilled Date'), + 'description' => ts('Optional. Can be used to record the date this product was fulfilled or shipped.'), + 'export' => TRUE, 'where' => 'civicrm_contribution_product.fulfilled_date', 'headerPattern' => '', 'dataPattern' => '', - ) , - 'contribution_start_date' => array( + 'table_name' => 'civicrm_contribution_product', + 'entity' => 'ContributionProduct', + 'bao' => 'CRM_Contribute_DAO_ContributionProduct', + 'localizable' => 0, + 'html' => [ + 'type' => 'Select Date', + 'formatType' => 'activityDate', + ], + ], + 'contribution_start_date' => [ 'name' => 'start_date', 'type' => CRM_Utils_Type::T_DATE, - 'title' => ts('Start date for premium') , - 'description' => 'Actual start date for a time-delimited premium (subscription, service or membership)', - 'export' => true, + 'title' => ts('Start date for premium'), + 'description' => ts('Actual start date for a time-delimited premium (subscription, service or membership)'), + 'export' => TRUE, 'where' => 'civicrm_contribution_product.start_date', 'headerPattern' => '', 'dataPattern' => '', - ) , - 'contribution_end_date' => array( + 'table_name' => 'civicrm_contribution_product', + 'entity' => 'ContributionProduct', + 'bao' => 'CRM_Contribute_DAO_ContributionProduct', + 'localizable' => 0, + ], + 'contribution_end_date' => [ 'name' => 'end_date', 'type' => CRM_Utils_Type::T_DATE, - 'title' => ts('End date for premium') , - 'description' => 'Actual end date for a time-delimited premium (subscription, service or membership)', - 'export' => true, + 'title' => ts('End date for premium'), + 'description' => ts('Actual end date for a time-delimited premium (subscription, service or membership)'), + 'export' => TRUE, 'where' => 'civicrm_contribution_product.end_date', 'headerPattern' => '', 'dataPattern' => '', - ) , - 'comment' => array( + 'table_name' => 'civicrm_contribution_product', + 'entity' => 'ContributionProduct', + 'bao' => 'CRM_Contribute_DAO_ContributionProduct', + 'localizable' => 0, + ], + 'comment' => [ 'name' => 'comment', 'type' => CRM_Utils_Type::T_TEXT, - 'title' => ts('Premium comment') , - ) , - 'financial_type_id' => array( + 'title' => ts('Premium comment'), + 'table_name' => 'civicrm_contribution_product', + 'entity' => 'ContributionProduct', + 'bao' => 'CRM_Contribute_DAO_ContributionProduct', + 'localizable' => 0, + ], + 'financial_type_id' => [ 'name' => 'financial_type_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Financial Type') , - 'description' => 'FK to Financial Type(for membership price sets only).', + 'title' => ts('Financial Type'), + 'description' => ts('FK to Financial Type(for membership price sets only).'), 'default' => 'NULL', + 'table_name' => 'civicrm_contribution_product', + 'entity' => 'ContributionProduct', + 'bao' => 'CRM_Contribute_DAO_ContributionProduct', + 'localizable' => 0, 'FKClassName' => 'CRM_Financial_DAO_FinancialType', - 'pseudoconstant' => array( + 'pseudoconstant' => [ 'table' => 'civicrm_financial_type', 'keyColumn' => 'id', 'labelColumn' => 'name', - ) - ) , - ); + ] + ], + ]; CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } return Civi::$statics[__CLASS__]['fields']; } + /** * Return a mapping from field-name to the corresponding key (as used in fields()). * * @return array * Array(string $name => string $uniqueName). */ - static function &fieldKeys() { + public static function &fieldKeys() { if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); } return Civi::$statics[__CLASS__]['fieldKeys']; } + /** * Returns the names of this table * * @return string */ - static function getTableName() { + public static function getTableName() { return self::$_tableName; } + /** * Returns if this table needs to be logged * - * @return boolean + * @return bool */ - function getLog() { + public function getLog() { return self::$_log; } + /** * Returns the list of fields that can be imported * @@ -263,10 +296,11 @@ function getLog() { * * @return array */ - static function &import($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'contribution_product', $prefix, array()); + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'contribution_product', $prefix, []); return $r; } + /** * Returns the list of fields that can be exported * @@ -274,8 +308,21 @@ static function &import($prefix = false) { * * @return array */ - static function &export($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'contribution_product', $prefix, array()); + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'contribution_product', $prefix, []); return $r; } + + /** + * Returns the list of indices + * + * @param bool $localize + * + * @return array + */ + public static function indices($localize = TRUE) { + $indices = []; + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + } diff --git a/CRM/Contribute/DAO/ContributionRecur.php b/CRM/Contribute/DAO/ContributionRecur.php index cf690539d78b..ba78c33c9c6e 100644 --- a/CRM/Contribute/DAO/ContributionRecur.php +++ b/CRM/Contribute/DAO/ContributionRecur.php @@ -1,587 +1,768 @@ __table = 'civicrm_contribution_recur'; parent::__construct(); } + /** - * Returns foreign keys and entity references + * Returns foreign keys and entity references. * * @return array * [CRM_Core_Reference_Interface] */ - static function getReferenceColumns() { + public static function getReferenceColumns() { if (!isset(Civi::$statics[__CLASS__]['links'])) { - Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'contact_id', 'civicrm_contact', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'payment_token_id', 'civicrm_payment_token', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'payment_processor_id', 'civicrm_payment_processor', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'financial_type_id', 'civicrm_financial_type', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'campaign_id', 'civicrm_campaign', 'id'); + Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'contact_id', 'civicrm_contact', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'payment_token_id', 'civicrm_payment_token', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'payment_processor_id', 'civicrm_payment_processor', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'financial_type_id', 'civicrm_financial_type', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'campaign_id', 'civicrm_campaign', 'id'); CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']); } return Civi::$statics[__CLASS__]['links']; } + /** * Returns all the column names of this table * * @return array */ - static function &fields() { + public static function &fields() { if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = array( - 'id' => array( + Civi::$statics[__CLASS__]['fields'] = [ + 'id' => [ 'name' => 'id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Recurring Contribution ID') , - 'description' => 'Contribution Recur ID', - 'required' => true, - ) , - 'contact_id' => array( + 'title' => ts('Recurring Contribution ID'), + 'description' => ts('Contribution Recur ID'), + 'required' => TRUE, + 'table_name' => 'civicrm_contribution_recur', + 'entity' => 'ContributionRecur', + 'bao' => 'CRM_Contribute_BAO_ContributionRecur', + 'localizable' => 0, + ], + 'contact_id' => [ 'name' => 'contact_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Contact ID') , - 'description' => 'Foreign key to civicrm_contact.id .', - 'required' => true, + 'title' => ts('Contact'), + 'description' => ts('Foreign key to civicrm_contact.id.'), + 'required' => TRUE, + 'table_name' => 'civicrm_contribution_recur', + 'entity' => 'ContributionRecur', + 'bao' => 'CRM_Contribute_BAO_ContributionRecur', + 'localizable' => 0, 'FKClassName' => 'CRM_Contact_DAO_Contact', - ) , - 'amount' => array( + 'html' => [ + 'type' => 'EntityRef', + ], + ], + 'amount' => [ 'name' => 'amount', 'type' => CRM_Utils_Type::T_MONEY, - 'title' => ts('Amount') , - 'description' => 'Amount to be contributed or charged each recurrence.', - 'required' => true, - 'precision' => array( + 'title' => ts('Amount'), + 'description' => ts('Amount to be contributed or charged each recurrence.'), + 'required' => TRUE, + 'precision' => [ 20, 2 - ) , - 'html' => array( + ], + 'table_name' => 'civicrm_contribution_recur', + 'entity' => 'ContributionRecur', + 'bao' => 'CRM_Contribute_BAO_ContributionRecur', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'currency' => array( + ], + ], + 'currency' => [ 'name' => 'currency', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Currency') , - 'description' => '3 character string, value from config setting or input via user.', + 'title' => ts('Currency'), + 'description' => ts('3 character string, value from config setting or input via user.'), 'maxlength' => 3, 'size' => CRM_Utils_Type::FOUR, 'default' => 'NULL', - 'html' => array( + 'table_name' => 'civicrm_contribution_recur', + 'entity' => 'ContributionRecur', + 'bao' => 'CRM_Contribute_BAO_ContributionRecur', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'table' => 'civicrm_currency', 'keyColumn' => 'name', 'labelColumn' => 'full_name', 'nameColumn' => 'name', - ) - ) , - 'frequency_unit' => array( + ] + ], + 'frequency_unit' => [ 'name' => 'frequency_unit', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Frequency Unit') , - 'description' => 'Time units for recurrence of payment.', + 'title' => ts('Frequency Unit'), + 'description' => ts('Time units for recurrence of payment.'), 'maxlength' => 8, 'size' => CRM_Utils_Type::EIGHT, 'default' => 'month', - 'html' => array( + 'table_name' => 'civicrm_contribution_recur', + 'entity' => 'ContributionRecur', + 'bao' => 'CRM_Contribute_BAO_ContributionRecur', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'optionGroupName' => 'recur_frequency_units', 'keyColumn' => 'name', 'optionEditPath' => 'civicrm/admin/options/recur_frequency_units', - ) - ) , - 'frequency_interval' => array( + ] + ], + 'frequency_interval' => [ 'name' => 'frequency_interval', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Interval (number of units)') , - 'description' => 'Number of time units for recurrence of payment.', - 'required' => true, - 'html' => array( + 'title' => ts('Interval (number of units)'), + 'description' => ts('Number of time units for recurrence of payment.'), + 'required' => TRUE, + 'table_name' => 'civicrm_contribution_recur', + 'entity' => 'ContributionRecur', + 'bao' => 'CRM_Contribute_BAO_ContributionRecur', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'installments' => array( + ], + ], + 'installments' => [ 'name' => 'installments', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Number of Installments') , - 'description' => 'Total number of payments to be made. Set this to 0 if this is an open-ended commitment i.e. no set end date.', - 'html' => array( + 'title' => ts('Number of Installments'), + 'description' => ts('Total number of payments to be made. Set this to 0 if this is an open-ended commitment i.e. no set end date.'), + 'table_name' => 'civicrm_contribution_recur', + 'entity' => 'ContributionRecur', + 'bao' => 'CRM_Contribute_BAO_ContributionRecur', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'start_date' => array( + ], + ], + 'start_date' => [ 'name' => 'start_date', 'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME, - 'title' => ts('Recurring Contribution Started Date') , - 'description' => 'The date the first scheduled recurring contribution occurs.', - 'required' => true, - 'html' => array( + 'title' => ts('Start Date'), + 'description' => ts('The date the first scheduled recurring contribution occurs.'), + 'required' => TRUE, + 'table_name' => 'civicrm_contribution_recur', + 'entity' => 'ContributionRecur', + 'bao' => 'CRM_Contribute_BAO_ContributionRecur', + 'localizable' => 0, + 'html' => [ 'type' => 'Select Date', - ) , - ) , - 'create_date' => array( + 'formatType' => 'activityDateTime', + ], + ], + 'create_date' => [ 'name' => 'create_date', 'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME, - 'title' => ts('Recurring Contribution Created Date') , - 'description' => 'When this recurring contribution record was created.', - 'required' => true, - 'html' => array( + 'title' => ts('Created Date'), + 'description' => ts('When this recurring contribution record was created.'), + 'required' => TRUE, + 'table_name' => 'civicrm_contribution_recur', + 'entity' => 'ContributionRecur', + 'bao' => 'CRM_Contribute_BAO_ContributionRecur', + 'localizable' => 0, + 'html' => [ 'type' => 'Select Date', - ) , - ) , - 'modified_date' => array( + 'formatType' => 'activityDateTime', + ], + ], + 'modified_date' => [ 'name' => 'modified_date', 'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME, - 'title' => ts('Recurring Contribution Modified Date') , - 'description' => 'Last updated date for this record. mostly the last time a payment was received', - 'html' => array( + 'title' => ts('Modified Date'), + 'description' => ts('Last updated date for this record. mostly the last time a payment was received'), + 'table_name' => 'civicrm_contribution_recur', + 'entity' => 'ContributionRecur', + 'bao' => 'CRM_Contribute_BAO_ContributionRecur', + 'localizable' => 0, + 'html' => [ 'type' => 'Select Date', - ) , - ) , - 'cancel_date' => array( + 'formatType' => 'activityDateTime', + ], + ], + 'cancel_date' => [ 'name' => 'cancel_date', 'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME, - 'title' => ts('Recurring Contribution Cancel Date') , - 'description' => 'Date this recurring contribution was cancelled by contributor- if we can get access to it', - 'html' => array( + 'title' => ts('Cancel Date'), + 'description' => ts('Date this recurring contribution was cancelled by contributor- if we can get access to it'), + 'table_name' => 'civicrm_contribution_recur', + 'entity' => 'ContributionRecur', + 'bao' => 'CRM_Contribute_BAO_ContributionRecur', + 'localizable' => 0, + 'html' => [ 'type' => 'Select Date', - ) , - ) , - 'end_date' => array( + 'formatType' => 'activityDate', + ], + ], + 'contribution_recur_cancel_reason' => [ + 'name' => 'cancel_reason', + 'type' => CRM_Utils_Type::T_TEXT, + 'title' => ts('Cancellation Reason'), + 'description' => ts('Free text field for a reason for cancelling'), + 'table_name' => 'civicrm_contribution_recur', + 'entity' => 'ContributionRecur', + 'bao' => 'CRM_Contribute_BAO_ContributionRecur', + 'localizable' => 0, + 'html' => [ + 'type' => 'Text', + ], + ], + 'end_date' => [ 'name' => 'end_date', 'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME, - 'title' => ts('Recurring Contribution End Date') , - 'description' => 'Date this recurring contribution finished successfully', - 'html' => array( + 'title' => ts('Recurring Contribution End Date'), + 'description' => ts('Date this recurring contribution finished successfully'), + 'table_name' => 'civicrm_contribution_recur', + 'entity' => 'ContributionRecur', + 'bao' => 'CRM_Contribute_BAO_ContributionRecur', + 'localizable' => 0, + 'html' => [ 'type' => 'Select Date', - ) , - ) , - 'processor_id' => array( + 'formatType' => 'activityDate', + ], + ], + 'processor_id' => [ 'name' => 'processor_id', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Processor ID') , - 'description' => 'Possibly needed to store a unique identifier for this recurring payment order - if this is available from the processor??', + 'title' => ts('Processor ID'), + 'description' => ts('Possibly needed to store a unique identifier for this recurring payment order - if this is available from the processor??'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - ) , - 'payment_token_id' => array( + 'table_name' => 'civicrm_contribution_recur', + 'entity' => 'ContributionRecur', + 'bao' => 'CRM_Contribute_BAO_ContributionRecur', + 'localizable' => 0, + 'html' => [ + 'type' => 'Text', + ], + ], + 'payment_token_id' => [ 'name' => 'payment_token_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Payment Token ID') , - 'description' => 'Optionally used to store a link to a payment token used for this recurring contribution.', + 'title' => ts('Payment Token ID'), + 'description' => ts('Optionally used to store a link to a payment token used for this recurring contribution.'), + 'table_name' => 'civicrm_contribution_recur', + 'entity' => 'ContributionRecur', + 'bao' => 'CRM_Contribute_BAO_ContributionRecur', + 'localizable' => 0, 'FKClassName' => 'CRM_Financial_DAO_PaymentToken', - ) , - 'trxn_id' => array( + ], + 'trxn_id' => [ 'name' => 'trxn_id', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Transaction ID') , - 'description' => 'unique transaction id. may be processor id, bank id + trans id, or account number + check number... depending on payment_method', + 'title' => ts('Transaction ID'), + 'description' => ts('unique transaction id. may be processor id, bank id + trans id, or account number + check number... depending on payment_method'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - ) , - 'invoice_id' => array( + 'table_name' => 'civicrm_contribution_recur', + 'entity' => 'ContributionRecur', + 'bao' => 'CRM_Contribute_BAO_ContributionRecur', + 'localizable' => 0, + 'html' => [ + 'type' => 'Text', + ], + ], + 'invoice_id' => [ 'name' => 'invoice_id', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Invoice ID') , - 'description' => 'unique invoice id, system generated or passed in', + 'title' => ts('Invoice ID'), + 'description' => ts('unique invoice id, system generated or passed in'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - ) , - 'contribution_status_id' => array( + 'table_name' => 'civicrm_contribution_recur', + 'entity' => 'ContributionRecur', + 'bao' => 'CRM_Contribute_BAO_ContributionRecur', + 'localizable' => 0, + 'html' => [ + 'type' => 'Text', + ], + ], + 'contribution_status_id' => [ 'name' => 'contribution_status_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Recurring Contribution Status') , - 'import' => true, + 'title' => ts('Status'), + 'import' => TRUE, 'where' => 'civicrm_contribution_recur.contribution_status_id', 'headerPattern' => '', 'dataPattern' => '', - 'export' => true, + 'export' => TRUE, 'default' => '1', - 'pseudoconstant' => array( + 'table_name' => 'civicrm_contribution_recur', + 'entity' => 'ContributionRecur', + 'bao' => 'CRM_Contribute_BAO_ContributionRecur', + 'localizable' => 0, + 'html' => [ + 'type' => 'Select', + ], + 'pseudoconstant' => [ 'optionGroupName' => 'contribution_status', 'optionEditPath' => 'civicrm/admin/options/contribution_status', - ) - ) , - 'is_test' => array( + ] + ], + 'is_test' => [ 'name' => 'is_test', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Test') , - 'import' => true, + 'title' => ts('Test'), + 'import' => TRUE, 'where' => 'civicrm_contribution_recur.is_test', 'headerPattern' => '', 'dataPattern' => '', - 'export' => true, - 'html' => array( + 'export' => TRUE, + 'default' => '0', + 'table_name' => 'civicrm_contribution_recur', + 'entity' => 'ContributionRecur', + 'bao' => 'CRM_Contribute_BAO_ContributionRecur', + 'localizable' => 0, + 'html' => [ 'type' => 'CheckBox', - ) , - ) , - 'cycle_day' => array( + ], + ], + 'cycle_day' => [ 'name' => 'cycle_day', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Number of Cycle Day') , - 'description' => 'Day in the period when the payment should be charged e.g. 1st of month, 15th etc.', - 'required' => true, + 'title' => ts('Cycle Day'), + 'description' => ts('Day in the period when the payment should be charged e.g. 1st of month, 15th etc.'), + 'required' => TRUE, 'default' => '1', - 'html' => array( + 'table_name' => 'civicrm_contribution_recur', + 'entity' => 'ContributionRecur', + 'bao' => 'CRM_Contribute_BAO_ContributionRecur', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'next_sched_contribution_date' => array( + ], + ], + 'next_sched_contribution_date' => [ 'name' => 'next_sched_contribution_date', 'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME, - 'title' => ts('Next Scheduled Contribution Date') , - 'description' => 'Next scheduled date', - 'html' => array( + 'title' => ts('Next Scheduled Contribution Date'), + 'description' => ts('Next scheduled date'), + 'table_name' => 'civicrm_contribution_recur', + 'entity' => 'ContributionRecur', + 'bao' => 'CRM_Contribute_BAO_ContributionRecur', + 'localizable' => 0, + 'html' => [ 'type' => 'Select Date', - ) , - ) , - 'failure_count' => array( + 'formatType' => 'activityDate', + ], + ], + 'failure_count' => [ 'name' => 'failure_count', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Number of Failures') , - 'description' => 'Number of failed charge attempts since last success. Business rule could be set to deactivate on more than x failures.', - 'html' => array( + 'title' => ts('Number of Failures'), + 'description' => ts('Number of failed charge attempts since last success. Business rule could be set to deactivate on more than x failures.'), + 'default' => '0', + 'table_name' => 'civicrm_contribution_recur', + 'entity' => 'ContributionRecur', + 'bao' => 'CRM_Contribute_BAO_ContributionRecur', + 'localizable' => 0, + 'html' => [ 'type' => 'Text', - ) , - ) , - 'failure_retry_date' => array( + ], + ], + 'failure_retry_date' => [ 'name' => 'failure_retry_date', 'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME, - 'title' => ts('Retry Failed Attempt Date') , - 'description' => 'Date to retry failed attempt', - 'html' => array( + 'title' => ts('Retry Failed Attempt Date'), + 'description' => ts('Date to retry failed attempt'), + 'table_name' => 'civicrm_contribution_recur', + 'entity' => 'ContributionRecur', + 'bao' => 'CRM_Contribute_BAO_ContributionRecur', + 'localizable' => 0, + 'html' => [ 'type' => 'Select Date', - ) , - ) , - 'auto_renew' => array( + 'formatType' => 'activityDate', + ], + ], + 'auto_renew' => [ 'name' => 'auto_renew', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Auto Renew') , - 'description' => 'Some systems allow contributor to set a number of installments - but then auto-renew the subscription or commitment if they do not cancel.', - 'required' => true, - 'html' => array( + 'title' => ts('Auto Renew'), + 'description' => ts('Some systems allow contributor to set a number of installments - but then auto-renew the subscription or commitment if they do not cancel.'), + 'required' => TRUE, + 'default' => '0', + 'table_name' => 'civicrm_contribution_recur', + 'entity' => 'ContributionRecur', + 'bao' => 'CRM_Contribute_BAO_ContributionRecur', + 'localizable' => 0, + 'html' => [ 'type' => 'CheckBox', - ) , - ) , - 'payment_processor_id' => array( + ], + ], + 'payment_processor_id' => [ 'name' => 'payment_processor_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Payment Processor') , - 'description' => 'Foreign key to civicrm_payment_processor.id', + 'title' => ts('Payment Processor'), + 'description' => ts('Foreign key to civicrm_payment_processor.id'), + 'table_name' => 'civicrm_contribution_recur', + 'entity' => 'ContributionRecur', + 'bao' => 'CRM_Contribute_BAO_ContributionRecur', + 'localizable' => 0, 'FKClassName' => 'CRM_Financial_DAO_PaymentProcessor', - ) , - 'financial_type_id' => array( + 'html' => [ + 'type' => 'Select', + ], + 'pseudoconstant' => [ + 'table' => 'civicrm_payment_processor', + 'keyColumn' => 'id', + 'labelColumn' => 'name', + ] + ], + 'financial_type_id' => [ 'name' => 'financial_type_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Financial Type') , - 'description' => 'FK to Financial Type', - 'export' => false, + 'title' => ts('Financial Type'), + 'description' => ts('FK to Financial Type'), + 'export' => FALSE, 'where' => 'civicrm_contribution_recur.financial_type_id', 'headerPattern' => '', 'dataPattern' => '', + 'table_name' => 'civicrm_contribution_recur', + 'entity' => 'ContributionRecur', + 'bao' => 'CRM_Contribute_BAO_ContributionRecur', + 'localizable' => 0, 'FKClassName' => 'CRM_Financial_DAO_FinancialType', - 'pseudoconstant' => array( + 'html' => [ + 'type' => 'Select', + ], + 'pseudoconstant' => [ 'table' => 'civicrm_financial_type', 'keyColumn' => 'id', 'labelColumn' => 'name', - ) - ) , - 'payment_instrument_id' => array( + ] + ], + 'payment_instrument_id' => [ 'name' => 'payment_instrument_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Payment Method') , - 'description' => 'FK to Payment Instrument', - 'html' => array( + 'title' => ts('Payment Method'), + 'description' => ts('FK to Payment Instrument'), + 'table_name' => 'civicrm_contribution_recur', + 'entity' => 'ContributionRecur', + 'bao' => 'CRM_Contribute_BAO_ContributionRecur', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'optionGroupName' => 'payment_instrument', 'optionEditPath' => 'civicrm/admin/options/payment_instrument', - ) - ) , - 'contribution_campaign_id' => array( + ] + ], + 'contribution_campaign_id' => [ 'name' => 'campaign_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Campaign') , - 'description' => 'The campaign for which this contribution has been triggered.', - 'import' => true, + 'title' => ts('Campaign'), + 'description' => ts('The campaign for which this contribution has been triggered.'), + 'import' => TRUE, 'where' => 'civicrm_contribution_recur.campaign_id', 'headerPattern' => '', 'dataPattern' => '', - 'export' => true, + 'export' => TRUE, + 'table_name' => 'civicrm_contribution_recur', + 'entity' => 'ContributionRecur', + 'bao' => 'CRM_Contribute_BAO_ContributionRecur', + 'localizable' => 0, 'FKClassName' => 'CRM_Campaign_DAO_Campaign', - 'pseudoconstant' => array( + 'html' => [ + 'type' => 'Select', + ], + 'pseudoconstant' => [ 'table' => 'civicrm_campaign', 'keyColumn' => 'id', 'labelColumn' => 'title', - ) - ) , - 'is_email_receipt' => array( + ] + ], + 'is_email_receipt' => [ 'name' => 'is_email_receipt', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Send email Receipt?') , - 'description' => 'if true, receipt is automatically emailed to contact on each successful payment', + 'title' => ts('Send email Receipt?'), + 'description' => ts('if true, receipt is automatically emailed to contact on each successful payment'), 'default' => '1', - 'html' => array( + 'table_name' => 'civicrm_contribution_recur', + 'entity' => 'ContributionRecur', + 'bao' => 'CRM_Contribute_BAO_ContributionRecur', + 'localizable' => 0, + 'html' => [ 'type' => 'CheckBox', - ) , - ) , - ); + ], + ], + ]; CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } return Civi::$statics[__CLASS__]['fields']; } + /** * Return a mapping from field-name to the corresponding key (as used in fields()). * * @return array * Array(string $name => string $uniqueName). */ - static function &fieldKeys() { + public static function &fieldKeys() { if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); } return Civi::$statics[__CLASS__]['fieldKeys']; } + /** * Returns the names of this table * * @return string */ - static function getTableName() { + public static function getTableName() { return self::$_tableName; } + /** * Returns if this table needs to be logged * - * @return boolean + * @return bool */ - function getLog() { + public function getLog() { return self::$_log; } + /** * Returns the list of fields that can be imported * @@ -589,10 +770,11 @@ function getLog() { * * @return array */ - static function &import($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'contribution_recur', $prefix, array()); + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'contribution_recur', $prefix, []); return $r; } + /** * Returns the list of fields that can be exported * @@ -600,8 +782,56 @@ static function &import($prefix = false) { * * @return array */ - static function &export($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'contribution_recur', $prefix, array()); + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'contribution_recur', $prefix, []); return $r; } + + /** + * Returns the list of indices + * + * @param bool $localize + * + * @return array + */ + public static function indices($localize = TRUE) { + $indices = [ + 'UI_contrib_trxn_id' => [ + 'name' => 'UI_contrib_trxn_id', + 'field' => [ + 0 => 'trxn_id', + ], + 'localizable' => FALSE, + 'unique' => TRUE, + 'sig' => 'civicrm_contribution_recur::1::trxn_id', + ], + 'UI_contrib_invoice_id' => [ + 'name' => 'UI_contrib_invoice_id', + 'field' => [ + 0 => 'invoice_id', + ], + 'localizable' => FALSE, + 'unique' => TRUE, + 'sig' => 'civicrm_contribution_recur::1::invoice_id', + ], + 'index_contribution_status' => [ + 'name' => 'index_contribution_status', + 'field' => [ + 0 => 'contribution_status_id', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_contribution_recur::0::contribution_status_id', + ], + 'UI_contribution_recur_payment_instrument_id' => [ + 'name' => 'UI_contribution_recur_payment_instrument_id', + 'field' => [ + 0 => 'payment_instrument_id', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_contribution_recur::0::payment_instrument_id', + ], + ]; + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + } diff --git a/CRM/Contribute/DAO/ContributionSoft.php b/CRM/Contribute/DAO/ContributionSoft.php index 02d4c47f59f5..ae009420f0b6 100644 --- a/CRM/Contribute/DAO/ContributionSoft.php +++ b/CRM/Contribute/DAO/ContributionSoft.php @@ -1,288 +1,323 @@ __table = 'civicrm_contribution_soft'; parent::__construct(); } + /** - * Returns foreign keys and entity references + * Returns foreign keys and entity references. * * @return array * [CRM_Core_Reference_Interface] */ - static function getReferenceColumns() { + public static function getReferenceColumns() { if (!isset(Civi::$statics[__CLASS__]['links'])) { - Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'contribution_id', 'civicrm_contribution', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'contact_id', 'civicrm_contact', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'pcp_id', 'civicrm_pcp', 'id'); + Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'contribution_id', 'civicrm_contribution', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'contact_id', 'civicrm_contact', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'pcp_id', 'civicrm_pcp', 'id'); CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']); } return Civi::$statics[__CLASS__]['links']; } + /** * Returns all the column names of this table * * @return array */ - static function &fields() { + public static function &fields() { if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = array( - 'contribution_soft_id' => array( + Civi::$statics[__CLASS__]['fields'] = [ + 'contribution_soft_id' => [ 'name' => 'id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Soft Contribution ID') , - 'description' => 'Soft Contribution ID', - 'required' => true, - 'import' => true, + 'title' => ts('Soft Contribution ID'), + 'description' => ts('Soft Contribution ID'), + 'required' => TRUE, + 'import' => TRUE, 'where' => 'civicrm_contribution_soft.id', 'headerPattern' => '', 'dataPattern' => '', - 'export' => true, - ) , - 'contribution_id' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contribution_soft', + 'entity' => 'ContributionSoft', + 'bao' => 'CRM_Contribute_BAO_ContributionSoft', + 'localizable' => 0, + ], + 'contribution_id' => [ 'name' => 'contribution_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Soft Contribution - Contribution') , - 'description' => 'FK to contribution table.', - 'required' => true, + 'title' => ts('Soft Contribution - Contribution'), + 'description' => ts('FK to contribution table.'), + 'required' => TRUE, + 'table_name' => 'civicrm_contribution_soft', + 'entity' => 'ContributionSoft', + 'bao' => 'CRM_Contribute_BAO_ContributionSoft', + 'localizable' => 0, 'FKClassName' => 'CRM_Contribute_DAO_Contribution', - ) , - 'contribution_soft_contact_id' => array( + ], + 'contribution_soft_contact_id' => [ 'name' => 'contact_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Contact ID') , - 'description' => 'FK to Contact ID', - 'required' => true, - 'import' => true, + 'title' => ts('Contact ID'), + 'description' => ts('FK to Contact ID'), + 'required' => TRUE, + 'import' => TRUE, 'where' => 'civicrm_contribution_soft.contact_id', 'headerPattern' => '/contact(.?id)?/i', 'dataPattern' => '/^\d+$/', - 'export' => true, + 'export' => TRUE, + 'table_name' => 'civicrm_contribution_soft', + 'entity' => 'ContributionSoft', + 'bao' => 'CRM_Contribute_BAO_ContributionSoft', + 'localizable' => 0, 'FKClassName' => 'CRM_Contact_DAO_Contact', - ) , - 'amount' => array( + ], + 'amount' => [ 'name' => 'amount', 'type' => CRM_Utils_Type::T_MONEY, - 'title' => ts('Soft Contribution Amount') , - 'description' => 'Amount of this soft contribution.', - 'required' => true, - 'precision' => array( + 'title' => ts('Soft Contribution Amount'), + 'description' => ts('Amount of this soft contribution.'), + 'required' => TRUE, + 'precision' => [ 20, 2 - ) , - 'import' => true, + ], + 'import' => TRUE, 'where' => 'civicrm_contribution_soft.amount', 'headerPattern' => '/total(.?am(ou)?nt)?/i', 'dataPattern' => '/^\d+(\.\d{2})?$/', - 'export' => true, - ) , - 'currency' => array( + 'export' => TRUE, + 'table_name' => 'civicrm_contribution_soft', + 'entity' => 'ContributionSoft', + 'bao' => 'CRM_Contribute_BAO_ContributionSoft', + 'localizable' => 0, + ], + 'currency' => [ 'name' => 'currency', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Soft Contribution Currency') , - 'description' => '3 character string, value from config setting or input via user.', + 'title' => ts('Soft Contribution Currency'), + 'description' => ts('3 character string, value from config setting or input via user.'), 'maxlength' => 3, 'size' => CRM_Utils_Type::FOUR, 'default' => 'NULL', - 'html' => array( + 'table_name' => 'civicrm_contribution_soft', + 'entity' => 'ContributionSoft', + 'bao' => 'CRM_Contribute_BAO_ContributionSoft', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'table' => 'civicrm_currency', 'keyColumn' => 'name', 'labelColumn' => 'full_name', 'nameColumn' => 'name', - ) - ) , - 'pcp_id' => array( + ] + ], + 'pcp_id' => [ 'name' => 'pcp_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Soft Contribution PCP') , - 'description' => 'FK to civicrm_pcp.id', + 'title' => ts('Soft Contribution PCP'), + 'description' => ts('FK to civicrm_pcp.id'), 'default' => 'NULL', + 'table_name' => 'civicrm_contribution_soft', + 'entity' => 'ContributionSoft', + 'bao' => 'CRM_Contribute_BAO_ContributionSoft', + 'localizable' => 0, 'FKClassName' => 'CRM_PCP_DAO_PCP', - 'pseudoconstant' => array( + 'pseudoconstant' => [ 'table' => 'civicrm_pcp', 'keyColumn' => 'id', 'labelColumn' => 'title', - ) - ) , - 'pcp_display_in_roll' => array( + ] + ], + 'pcp_display_in_roll' => [ 'name' => 'pcp_display_in_roll', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Soft Contribution Display on PCP') , - ) , - 'pcp_roll_nickname' => array( + 'title' => ts('Soft Contribution Display on PCP'), + 'default' => '0', + 'table_name' => 'civicrm_contribution_soft', + 'entity' => 'ContributionSoft', + 'bao' => 'CRM_Contribute_BAO_ContributionSoft', + 'localizable' => 0, + ], + 'pcp_roll_nickname' => [ 'name' => 'pcp_roll_nickname', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Soft Contribution PCP Nickname') , + 'title' => ts('Soft Contribution PCP Nickname'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, 'default' => 'NULL', - ) , - 'pcp_personal_note' => array( + 'table_name' => 'civicrm_contribution_soft', + 'entity' => 'ContributionSoft', + 'bao' => 'CRM_Contribute_BAO_ContributionSoft', + 'localizable' => 0, + ], + 'pcp_personal_note' => [ 'name' => 'pcp_personal_note', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Soft Contribution PCP Note') , + 'title' => ts('Soft Contribution PCP Note'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, 'default' => 'NULL', - ) , - 'soft_credit_type_id' => array( + 'table_name' => 'civicrm_contribution_soft', + 'entity' => 'ContributionSoft', + 'bao' => 'CRM_Contribute_BAO_ContributionSoft', + 'localizable' => 0, + 'html' => [ + 'type' => 'TextArea', + ], + ], + 'soft_credit_type_id' => [ 'name' => 'soft_credit_type_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Soft Credit Type') , - 'description' => 'Soft Credit Type ID.Implicit FK to civicrm_option_value where option_group = soft_credit_type.', + 'title' => ts('Soft Credit Type'), + 'description' => ts('Soft Credit Type ID.Implicit FK to civicrm_option_value where option_group = soft_credit_type.'), 'default' => 'NULL', - 'pseudoconstant' => array( + 'table_name' => 'civicrm_contribution_soft', + 'entity' => 'ContributionSoft', + 'bao' => 'CRM_Contribute_BAO_ContributionSoft', + 'localizable' => 0, + 'pseudoconstant' => [ 'optionGroupName' => 'soft_credit_type', 'optionEditPath' => 'civicrm/admin/options/soft_credit_type', - ) - ) , - ); + ] + ], + ]; CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } return Civi::$statics[__CLASS__]['fields']; } + /** * Return a mapping from field-name to the corresponding key (as used in fields()). * * @return array * Array(string $name => string $uniqueName). */ - static function &fieldKeys() { + public static function &fieldKeys() { if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); } return Civi::$statics[__CLASS__]['fieldKeys']; } + /** * Returns the names of this table * * @return string */ - static function getTableName() { + public static function getTableName() { return self::$_tableName; } + /** * Returns if this table needs to be logged * - * @return boolean + * @return bool */ - function getLog() { + public function getLog() { return self::$_log; } + /** * Returns the list of fields that can be imported * @@ -290,10 +325,11 @@ function getLog() { * * @return array */ - static function &import($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'contribution_soft', $prefix, array()); + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'contribution_soft', $prefix, []); return $r; } + /** * Returns the list of fields that can be exported * @@ -301,8 +337,30 @@ static function &import($prefix = false) { * * @return array */ - static function &export($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'contribution_soft', $prefix, array()); + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'contribution_soft', $prefix, []); return $r; } + + /** + * Returns the list of indices + * + * @param bool $localize + * + * @return array + */ + public static function indices($localize = TRUE) { + $indices = [ + 'index_id' => [ + 'name' => 'index_id', + 'field' => [ + 0 => 'pcp_id', + ], + 'localizable' => FALSE, + 'sig' => 'civicrm_contribution_soft::0::pcp_id', + ], + ]; + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + } diff --git a/CRM/Contribute/DAO/Premium.php b/CRM/Contribute/DAO/Premium.php index 76db38743d31..643bf1dbafdb 100644 --- a/CRM/Contribute/DAO/Premium.php +++ b/CRM/Contribute/DAO/Premium.php @@ -1,259 +1,296 @@ at top of Premiums section of page. Text and HTML allowed. * * @var text */ public $premiums_intro_text; + /** * This email address is included in receipts if it is populated and a premium has been selected. * * @var string */ public $premiums_contact_email; + /** * This phone number is included in receipts if it is populated and a premium has been selected. * * @var string */ public $premiums_contact_phone; + /** * Boolean. Should we automatically display minimum contribution amount text after the premium descriptions. * * @var boolean */ public $premiums_display_min_contribution; + /** * Label displayed for No Thank-you option in premiums block (e.g. No thank you) * * @var string */ public $premiums_nothankyou_label; + /** - * * @var int unsigned */ public $premiums_nothankyou_position; + /** - * class constructor - * - * @return civicrm_premiums + * Class constructor. */ - function __construct() { + public function __construct() { $this->__table = 'civicrm_premiums'; parent::__construct(); } + /** - * Returns foreign keys and entity references + * Returns foreign keys and entity references. * * @return array * [CRM_Core_Reference_Interface] */ - static function getReferenceColumns() { + public static function getReferenceColumns() { if (!isset(Civi::$statics[__CLASS__]['links'])) { - Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Dynamic(self::getTableName() , 'entity_id', NULL, 'id', 'entity_table'); + Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Dynamic(self::getTableName(), 'entity_id', NULL, 'id', 'entity_table'); CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']); } return Civi::$statics[__CLASS__]['links']; } + /** * Returns all the column names of this table * * @return array */ - static function &fields() { + public static function &fields() { if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = array( - 'id' => array( + Civi::$statics[__CLASS__]['fields'] = [ + 'id' => [ 'name' => 'id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Premium ID') , - 'required' => true, - ) , - 'entity_table' => array( + 'title' => ts('Premium ID'), + 'required' => TRUE, + 'table_name' => 'civicrm_premiums', + 'entity' => 'Premium', + 'bao' => 'CRM_Contribute_BAO_Premium', + 'localizable' => 0, + ], + 'entity_table' => [ 'name' => 'entity_table', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Premium Entity') , - 'description' => 'Joins these premium settings to another object. Always civicrm_contribution_page for now.', - 'required' => true, + 'title' => ts('Premium Entity'), + 'description' => ts('Joins these premium settings to another object. Always civicrm_contribution_page for now.'), + 'required' => TRUE, 'maxlength' => 64, 'size' => CRM_Utils_Type::BIG, - ) , - 'entity_id' => array( + 'table_name' => 'civicrm_premiums', + 'entity' => 'Premium', + 'bao' => 'CRM_Contribute_BAO_Premium', + 'localizable' => 0, + ], + 'entity_id' => [ 'name' => 'entity_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Premium entity ID') , - 'required' => true, - ) , - 'premiums_active' => array( + 'title' => ts('Premium entity ID'), + 'required' => TRUE, + 'table_name' => 'civicrm_premiums', + 'entity' => 'Premium', + 'bao' => 'CRM_Contribute_BAO_Premium', + 'localizable' => 0, + ], + 'premiums_active' => [ 'name' => 'premiums_active', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Is Premium Active?') , - 'description' => 'Is the Premiums feature enabled for this page?', - 'required' => true, - ) , - 'premiums_intro_title' => array( + 'title' => ts('Is Premium Active?'), + 'description' => ts('Is the Premiums feature enabled for this page?'), + 'required' => TRUE, + 'default' => '0', + 'table_name' => 'civicrm_premiums', + 'entity' => 'Premium', + 'bao' => 'CRM_Contribute_BAO_Premium', + 'localizable' => 0, + ], + 'premiums_intro_title' => [ 'name' => 'premiums_intro_title', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Title for Premiums section') , - 'description' => 'Title for Premiums section.', + 'title' => ts('Title for Premiums section'), + 'description' => ts('Title for Premiums section.'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - ) , - 'premiums_intro_text' => array( + 'table_name' => 'civicrm_premiums', + 'entity' => 'Premium', + 'bao' => 'CRM_Contribute_BAO_Premium', + 'localizable' => 1, + ], + 'premiums_intro_text' => [ 'name' => 'premiums_intro_text', 'type' => CRM_Utils_Type::T_TEXT, - 'title' => ts('Premium Introductory Text') , - 'description' => 'Displayed in
    at top of Premiums section of page. Text and HTML allowed.', - ) , - 'premiums_contact_email' => array( + 'title' => ts('Premium Introductory Text'), + 'description' => ts('Displayed in
    at top of Premiums section of page. Text and HTML allowed.'), + 'table_name' => 'civicrm_premiums', + 'entity' => 'Premium', + 'bao' => 'CRM_Contribute_BAO_Premium', + 'localizable' => 1, + ], + 'premiums_contact_email' => [ 'name' => 'premiums_contact_email', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Premium Contact Email') , - 'description' => 'This email address is included in receipts if it is populated and a premium has been selected.', + 'title' => ts('Premium Contact Email'), + 'description' => ts('This email address is included in receipts if it is populated and a premium has been selected.'), 'maxlength' => 100, 'size' => CRM_Utils_Type::HUGE, - ) , - 'premiums_contact_phone' => array( + 'table_name' => 'civicrm_premiums', + 'entity' => 'Premium', + 'bao' => 'CRM_Contribute_BAO_Premium', + 'localizable' => 0, + ], + 'premiums_contact_phone' => [ 'name' => 'premiums_contact_phone', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Premiums Contact Phone') , - 'description' => 'This phone number is included in receipts if it is populated and a premium has been selected.', + 'title' => ts('Premiums Contact Phone'), + 'description' => ts('This phone number is included in receipts if it is populated and a premium has been selected.'), 'maxlength' => 50, 'size' => CRM_Utils_Type::BIG, - ) , - 'premiums_display_min_contribution' => array( + 'table_name' => 'civicrm_premiums', + 'entity' => 'Premium', + 'bao' => 'CRM_Contribute_BAO_Premium', + 'localizable' => 0, + ], + 'premiums_display_min_contribution' => [ 'name' => 'premiums_display_min_contribution', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Display Minimum Contribution?') , - 'description' => 'Boolean. Should we automatically display minimum contribution amount text after the premium descriptions.', - 'required' => true, - ) , - 'premiums_nothankyou_label' => array( + 'title' => ts('Display Minimum Contribution?'), + 'description' => ts('Boolean. Should we automatically display minimum contribution amount text after the premium descriptions.'), + 'required' => TRUE, + 'table_name' => 'civicrm_premiums', + 'entity' => 'Premium', + 'bao' => 'CRM_Contribute_BAO_Premium', + 'localizable' => 0, + ], + 'premiums_nothankyou_label' => [ 'name' => 'premiums_nothankyou_label', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('No Thank-you Text') , - 'description' => 'Label displayed for No Thank-you option in premiums block (e.g. No thank you)', + 'title' => ts('No Thank-you Text'), + 'description' => ts('Label displayed for No Thank-you option in premiums block (e.g. No thank you)'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - ) , - 'premiums_nothankyou_position' => array( + 'table_name' => 'civicrm_premiums', + 'entity' => 'Premium', + 'bao' => 'CRM_Contribute_BAO_Premium', + 'localizable' => 1, + ], + 'premiums_nothankyou_position' => [ 'name' => 'premiums_nothankyou_position', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('No Thank-you Position') , + 'title' => ts('No Thank-you Position'), 'default' => '1', - ) , - ); + 'table_name' => 'civicrm_premiums', + 'entity' => 'Premium', + 'bao' => 'CRM_Contribute_BAO_Premium', + 'localizable' => 0, + ], + ]; CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } return Civi::$statics[__CLASS__]['fields']; } + /** * Return a mapping from field-name to the corresponding key (as used in fields()). * * @return array * Array(string $name => string $uniqueName). */ - static function &fieldKeys() { + public static function &fieldKeys() { if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); } return Civi::$statics[__CLASS__]['fieldKeys']; } + /** * Returns the names of this table * * @return string */ - static function getTableName() { + public static function getTableName() { return CRM_Core_DAO::getLocaleTableName(self::$_tableName); } + /** * Returns if this table needs to be logged * - * @return boolean + * @return bool */ - function getLog() { + public function getLog() { return self::$_log; } + /** * Returns the list of fields that can be imported * @@ -261,10 +298,11 @@ function getLog() { * * @return array */ - static function &import($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'premiums', $prefix, array()); + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'premiums', $prefix, []); return $r; } + /** * Returns the list of fields that can be exported * @@ -272,8 +310,21 @@ static function &import($prefix = false) { * * @return array */ - static function &export($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'premiums', $prefix, array()); + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'premiums', $prefix, []); return $r; } + + /** + * Returns the list of indices + * + * @param bool $localize + * + * @return array + */ + public static function indices($localize = TRUE) { + $indices = []; + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + } diff --git a/CRM/Contribute/DAO/PremiumsProduct.php b/CRM/Contribute/DAO/PremiumsProduct.php index 4dd16782a118..7f319b0669d1 100644 --- a/CRM/Contribute/DAO/PremiumsProduct.php +++ b/CRM/Contribute/DAO/PremiumsProduct.php @@ -1,190 +1,198 @@ __table = 'civicrm_premiums_product'; parent::__construct(); } + /** - * Returns foreign keys and entity references + * Returns foreign keys and entity references. * * @return array * [CRM_Core_Reference_Interface] */ - static function getReferenceColumns() { + public static function getReferenceColumns() { if (!isset(Civi::$statics[__CLASS__]['links'])) { - Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'premiums_id', 'civicrm_premiums', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'product_id', 'civicrm_product', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'financial_type_id', 'civicrm_financial_type', 'id'); + Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'premiums_id', 'civicrm_premiums', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'product_id', 'civicrm_product', 'id'); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'financial_type_id', 'civicrm_financial_type', 'id'); CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']); } return Civi::$statics[__CLASS__]['links']; } + /** * Returns all the column names of this table * * @return array */ - static function &fields() { + public static function &fields() { if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = array( - 'id' => array( + Civi::$statics[__CLASS__]['fields'] = [ + 'id' => [ 'name' => 'id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Premium Product ID') , - 'description' => 'Contribution ID', - 'required' => true, - ) , - 'premiums_id' => array( + 'title' => ts('Premium Product ID'), + 'description' => ts('Contribution ID'), + 'required' => TRUE, + 'table_name' => 'civicrm_premiums_product', + 'entity' => 'PremiumsProduct', + 'bao' => 'CRM_Contribute_DAO_PremiumsProduct', + 'localizable' => 0, + ], + 'premiums_id' => [ 'name' => 'premiums_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Premium') , - 'description' => 'Foreign key to premiums settings record.', - 'required' => true, + 'title' => ts('Premium'), + 'description' => ts('Foreign key to premiums settings record.'), + 'required' => TRUE, + 'table_name' => 'civicrm_premiums_product', + 'entity' => 'PremiumsProduct', + 'bao' => 'CRM_Contribute_DAO_PremiumsProduct', + 'localizable' => 0, 'FKClassName' => 'CRM_Contribute_DAO_Premium', - ) , - 'product_id' => array( + ], + 'product_id' => [ 'name' => 'product_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Product') , - 'description' => 'Foreign key to each product object.', - 'required' => true, + 'title' => ts('Product'), + 'description' => ts('Foreign key to each product object.'), + 'required' => TRUE, + 'table_name' => 'civicrm_premiums_product', + 'entity' => 'PremiumsProduct', + 'bao' => 'CRM_Contribute_DAO_PremiumsProduct', + 'localizable' => 0, 'FKClassName' => 'CRM_Contribute_DAO_Product', - ) , - 'weight' => array( + ], + 'weight' => [ 'name' => 'weight', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Order') , - 'required' => true, - ) , - 'financial_type_id' => array( + 'title' => ts('Order'), + 'required' => TRUE, + 'table_name' => 'civicrm_premiums_product', + 'entity' => 'PremiumsProduct', + 'bao' => 'CRM_Contribute_DAO_PremiumsProduct', + 'localizable' => 0, + ], + 'financial_type_id' => [ 'name' => 'financial_type_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Financial Type') , - 'description' => 'FK to Financial Type.', + 'title' => ts('Financial Type'), + 'description' => ts('FK to Financial Type.'), 'default' => 'NULL', + 'table_name' => 'civicrm_premiums_product', + 'entity' => 'PremiumsProduct', + 'bao' => 'CRM_Contribute_DAO_PremiumsProduct', + 'localizable' => 0, 'FKClassName' => 'CRM_Financial_DAO_FinancialType', - 'pseudoconstant' => array( + 'pseudoconstant' => [ 'table' => 'civicrm_financial_type', 'keyColumn' => 'id', 'labelColumn' => 'name', - ) - ) , - ); + ] + ], + ]; CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } return Civi::$statics[__CLASS__]['fields']; } + /** * Return a mapping from field-name to the corresponding key (as used in fields()). * * @return array * Array(string $name => string $uniqueName). */ - static function &fieldKeys() { + public static function &fieldKeys() { if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); } return Civi::$statics[__CLASS__]['fieldKeys']; } + /** * Returns the names of this table * * @return string */ - static function getTableName() { + public static function getTableName() { return self::$_tableName; } + /** * Returns if this table needs to be logged * - * @return boolean + * @return bool */ - function getLog() { + public function getLog() { return self::$_log; } + /** * Returns the list of fields that can be imported * @@ -192,10 +200,11 @@ function getLog() { * * @return array */ - static function &import($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'premiums_product', $prefix, array()); + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'premiums_product', $prefix, []); return $r; } + /** * Returns the list of fields that can be exported * @@ -203,8 +212,21 @@ static function &import($prefix = false) { * * @return array */ - static function &export($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'premiums_product', $prefix, array()); + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'premiums_product', $prefix, []); return $r; } + + /** + * Returns the list of indices + * + * @param bool $localize + * + * @return array + */ + public static function indices($localize = TRUE) { + $indices = []; + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + } diff --git a/CRM/Contribute/DAO/Product.php b/CRM/Contribute/DAO/Product.php index 8701532f3786..d4e1e75ef531 100644 --- a/CRM/Contribute/DAO/Product.php +++ b/CRM/Contribute/DAO/Product.php @@ -1,130 +1,122 @@ we would set start/end for 1/1/06 thru 12/31/06 for any premium chosen in 2006) @@ -132,290 +124,375 @@ class CRM_Contribute_DAO_Product extends CRM_Core_DAO { * @var string */ public $period_type; + /** * Month and day (MMDD) that fixed period type subscription or membership starts. * * @var int */ public $fixed_period_start_day; + /** - * * @var string */ public $duration_unit; + /** * Number of units for total duration of subscription, service, membership (e.g. 12 Months). * * @var int */ public $duration_interval; + /** * Frequency unit and interval allow option to store actual delivery frequency for a subscription or service. * * @var string */ public $frequency_unit; + /** * Number of units for delivery frequency of subscription, service, membership (e.g. every 3 Months). * * @var int */ public $frequency_interval; + /** - * class constructor - * - * @return civicrm_product + * Class constructor. */ - function __construct() { + public function __construct() { $this->__table = 'civicrm_product'; parent::__construct(); } + /** - * Returns foreign keys and entity references + * Returns foreign keys and entity references. * * @return array * [CRM_Core_Reference_Interface] */ - static function getReferenceColumns() { + public static function getReferenceColumns() { if (!isset(Civi::$statics[__CLASS__]['links'])) { - Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'financial_type_id', 'civicrm_financial_type', 'id'); + Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'financial_type_id', 'civicrm_financial_type', 'id'); CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']); } return Civi::$statics[__CLASS__]['links']; } + /** * Returns all the column names of this table * * @return array */ - static function &fields() { + public static function &fields() { if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = array( - 'id' => array( + Civi::$statics[__CLASS__]['fields'] = [ + 'id' => [ 'name' => 'id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Product ID') , - 'required' => true, - ) , - 'product_name' => array( + 'title' => ts('Product ID'), + 'required' => TRUE, + 'table_name' => 'civicrm_product', + 'entity' => 'Product', + 'bao' => 'CRM_Contribute_BAO_Product', + 'localizable' => 0, + ], + 'product_name' => [ 'name' => 'name', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Product Name') , - 'description' => 'Required product/premium name', - 'required' => true, + 'title' => ts('Product Name'), + 'description' => ts('Required product/premium name'), + 'required' => TRUE, 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - 'export' => true, + 'export' => TRUE, 'where' => 'civicrm_product.name', 'headerPattern' => '', 'dataPattern' => '', - ) , - 'description' => array( + 'table_name' => 'civicrm_product', + 'entity' => 'Product', + 'bao' => 'CRM_Contribute_BAO_Product', + 'localizable' => 1, + ], + 'description' => [ 'name' => 'description', 'type' => CRM_Utils_Type::T_TEXT, - 'title' => ts('Description') , - 'description' => 'Optional description of the product/premium.', - ) , - 'sku' => array( + 'title' => ts('Description'), + 'description' => ts('Optional description of the product/premium.'), + 'table_name' => 'civicrm_product', + 'entity' => 'Product', + 'bao' => 'CRM_Contribute_BAO_Product', + 'localizable' => 1, + ], + 'sku' => [ 'name' => 'sku', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('SKU') , - 'description' => 'Optional product sku or code.', + 'title' => ts('SKU'), + 'description' => ts('Optional product sku or code.'), 'maxlength' => 50, 'size' => CRM_Utils_Type::BIG, - 'export' => true, + 'export' => TRUE, 'where' => 'civicrm_product.sku', 'headerPattern' => '', 'dataPattern' => '', - ) , - 'options' => array( + 'table_name' => 'civicrm_product', + 'entity' => 'Product', + 'bao' => 'CRM_Contribute_BAO_Product', + 'localizable' => 0, + ], + 'options' => [ 'name' => 'options', 'type' => CRM_Utils_Type::T_TEXT, - 'title' => ts('Options') , - 'description' => 'Store comma-delimited list of color, size, etc. options for the product.', - ) , - 'image' => array( + 'title' => ts('Options'), + 'description' => ts('Store comma-delimited list of color, size, etc. options for the product.'), + 'table_name' => 'civicrm_product', + 'entity' => 'Product', + 'bao' => 'CRM_Contribute_BAO_Product', + 'localizable' => 1, + ], + 'image' => [ 'name' => 'image', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Image') , - 'description' => 'Full or relative URL to uploaded image - fullsize.', + 'title' => ts('Image'), + 'description' => ts('Full or relative URL to uploaded image - fullsize.'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - ) , - 'thumbnail' => array( + 'table_name' => 'civicrm_product', + 'entity' => 'Product', + 'bao' => 'CRM_Contribute_BAO_Product', + 'localizable' => 0, + ], + 'thumbnail' => [ 'name' => 'thumbnail', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Thumbnail') , - 'description' => 'Full or relative URL to image thumbnail.', + 'title' => ts('Thumbnail'), + 'description' => ts('Full or relative URL to image thumbnail.'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - ) , - 'price' => array( + 'table_name' => 'civicrm_product', + 'entity' => 'Product', + 'bao' => 'CRM_Contribute_BAO_Product', + 'localizable' => 0, + ], + 'price' => [ 'name' => 'price', 'type' => CRM_Utils_Type::T_MONEY, - 'title' => ts('Price') , - 'description' => 'Sell price or market value for premiums. For tax-deductible contributions, this will be stored as non_deductible_amount in the contribution record.', - 'precision' => array( + 'title' => ts('Price'), + 'description' => ts('Sell price or market value for premiums. For tax-deductible contributions, this will be stored as non_deductible_amount in the contribution record.'), + 'precision' => [ 20, 2 - ) , - ) , - 'currency' => array( + ], + 'table_name' => 'civicrm_product', + 'entity' => 'Product', + 'bao' => 'CRM_Contribute_BAO_Product', + 'localizable' => 0, + ], + 'currency' => [ 'name' => 'currency', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Currency') , - 'description' => '3 character string, value from config setting or input via user.', + 'title' => ts('Currency'), + 'description' => ts('3 character string, value from config setting or input via user.'), 'maxlength' => 3, 'size' => CRM_Utils_Type::FOUR, 'default' => 'NULL', - 'html' => array( + 'table_name' => 'civicrm_product', + 'entity' => 'Product', + 'bao' => 'CRM_Contribute_BAO_Product', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'table' => 'civicrm_currency', 'keyColumn' => 'name', 'labelColumn' => 'full_name', 'nameColumn' => 'name', - ) - ) , - 'financial_type_id' => array( + ] + ], + 'financial_type_id' => [ 'name' => 'financial_type_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Financial Type') , - 'description' => 'FK to Financial Type.', + 'title' => ts('Financial Type'), + 'description' => ts('FK to Financial Type.'), 'default' => 'NULL', + 'table_name' => 'civicrm_product', + 'entity' => 'Product', + 'bao' => 'CRM_Contribute_BAO_Product', + 'localizable' => 0, 'FKClassName' => 'CRM_Financial_DAO_FinancialType', - 'pseudoconstant' => array( + 'pseudoconstant' => [ 'table' => 'civicrm_financial_type', 'keyColumn' => 'id', 'labelColumn' => 'name', - ) - ) , - 'min_contribution' => array( + ] + ], + 'min_contribution' => [ 'name' => 'min_contribution', 'type' => CRM_Utils_Type::T_MONEY, - 'title' => ts('Minimum Contribution') , - 'description' => 'Minimum contribution required to be eligible to select this premium.', - 'precision' => array( + 'title' => ts('Minimum Contribution'), + 'description' => ts('Minimum contribution required to be eligible to select this premium.'), + 'precision' => [ 20, 2 - ) , - ) , - 'cost' => array( + ], + 'table_name' => 'civicrm_product', + 'entity' => 'Product', + 'bao' => 'CRM_Contribute_BAO_Product', + 'localizable' => 0, + ], + 'cost' => [ 'name' => 'cost', 'type' => CRM_Utils_Type::T_MONEY, - 'title' => ts('Cost') , - 'description' => 'Actual cost of this product. Useful to determine net return from sale or using this as an incentive.', - 'precision' => array( + 'title' => ts('Cost'), + 'description' => ts('Actual cost of this product. Useful to determine net return from sale or using this as an incentive.'), + 'precision' => [ 20, 2 - ) , - ) , - 'is_active' => array( + ], + 'table_name' => 'civicrm_product', + 'entity' => 'Product', + 'bao' => 'CRM_Contribute_BAO_Product', + 'localizable' => 0, + ], + 'is_active' => [ 'name' => 'is_active', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Is Active') , - 'description' => 'Disabling premium removes it from the premiums_premium join table below.', - 'required' => true, - ) , - 'period_type' => array( + 'title' => ts('Is Active'), + 'description' => ts('Disabling premium removes it from the premiums_premium join table below.'), + 'required' => TRUE, + 'table_name' => 'civicrm_product', + 'entity' => 'Product', + 'bao' => 'CRM_Contribute_BAO_Product', + 'localizable' => 0, + ], + 'period_type' => [ 'name' => 'period_type', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Period Type') , - 'description' => 'Rolling means we set start/end based on current day, fixed means we set start/end for current year or month - (e.g. 1 year + fixed -> we would set start/end for 1/1/06 thru 12/31/06 for any premium chosen in 2006) ', + 'title' => ts('Period Type'), + 'description' => ts('Rolling means we set start/end based on current day, fixed means we set start/end for current year or month + (e.g. 1 year + fixed -> we would set start/end for 1/1/06 thru 12/31/06 for any premium chosen in 2006) '), 'maxlength' => 8, 'size' => CRM_Utils_Type::EIGHT, 'default' => 'rolling', - 'html' => array( + 'table_name' => 'civicrm_product', + 'entity' => 'Product', + 'bao' => 'CRM_Contribute_BAO_Product', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'callback' => 'CRM_Core_SelectValues::periodType', - ) - ) , - 'fixed_period_start_day' => array( + ] + ], + 'fixed_period_start_day' => [ 'name' => 'fixed_period_start_day', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Fixed Period Start Day') , - 'description' => 'Month and day (MMDD) that fixed period type subscription or membership starts.', + 'title' => ts('Fixed Period Start Day'), + 'description' => ts('Month and day (MMDD) that fixed period type subscription or membership starts.'), 'default' => '0101', - ) , - 'duration_unit' => array( + 'table_name' => 'civicrm_product', + 'entity' => 'Product', + 'bao' => 'CRM_Contribute_BAO_Product', + 'localizable' => 0, + ], + 'duration_unit' => [ 'name' => 'duration_unit', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Duration Unit') , + 'title' => ts('Duration Unit'), 'maxlength' => 8, 'size' => CRM_Utils_Type::EIGHT, 'default' => 'year', - 'html' => array( + 'table_name' => 'civicrm_product', + 'entity' => 'Product', + 'bao' => 'CRM_Contribute_BAO_Product', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'callback' => 'CRM_Core_SelectValues::getPremiumUnits', - ) - ) , - 'duration_interval' => array( + ] + ], + 'duration_interval' => [ 'name' => 'duration_interval', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Duration Interval') , - 'description' => 'Number of units for total duration of subscription, service, membership (e.g. 12 Months).', - ) , - 'frequency_unit' => array( + 'title' => ts('Duration Interval'), + 'description' => ts('Number of units for total duration of subscription, service, membership (e.g. 12 Months).'), + 'table_name' => 'civicrm_product', + 'entity' => 'Product', + 'bao' => 'CRM_Contribute_BAO_Product', + 'localizable' => 0, + ], + 'frequency_unit' => [ 'name' => 'frequency_unit', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Frequency Unit') , - 'description' => 'Frequency unit and interval allow option to store actual delivery frequency for a subscription or service.', + 'title' => ts('Frequency Unit'), + 'description' => ts('Frequency unit and interval allow option to store actual delivery frequency for a subscription or service.'), 'maxlength' => 8, 'size' => CRM_Utils_Type::EIGHT, 'default' => 'month', - 'html' => array( + 'table_name' => 'civicrm_product', + 'entity' => 'Product', + 'bao' => 'CRM_Contribute_BAO_Product', + 'localizable' => 0, + 'html' => [ 'type' => 'Select', - ) , - 'pseudoconstant' => array( + ], + 'pseudoconstant' => [ 'callback' => 'CRM_Core_SelectValues::getPremiumUnits', - ) - ) , - 'frequency_interval' => array( + ] + ], + 'frequency_interval' => [ 'name' => 'frequency_interval', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Frequency Interval') , - 'description' => 'Number of units for delivery frequency of subscription, service, membership (e.g. every 3 Months).', - ) , - ); + 'title' => ts('Frequency Interval'), + 'description' => ts('Number of units for delivery frequency of subscription, service, membership (e.g. every 3 Months).'), + 'table_name' => 'civicrm_product', + 'entity' => 'Product', + 'bao' => 'CRM_Contribute_BAO_Product', + 'localizable' => 0, + ], + ]; CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } return Civi::$statics[__CLASS__]['fields']; } + /** * Return a mapping from field-name to the corresponding key (as used in fields()). * * @return array * Array(string $name => string $uniqueName). */ - static function &fieldKeys() { + public static function &fieldKeys() { if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); } return Civi::$statics[__CLASS__]['fieldKeys']; } + /** * Returns the names of this table * * @return string */ - static function getTableName() { + public static function getTableName() { return CRM_Core_DAO::getLocaleTableName(self::$_tableName); } + /** * Returns if this table needs to be logged * - * @return boolean + * @return bool */ - function getLog() { + public function getLog() { return self::$_log; } + /** * Returns the list of fields that can be imported * @@ -423,10 +500,11 @@ function getLog() { * * @return array */ - static function &import($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'product', $prefix, array()); + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'product', $prefix, []); return $r; } + /** * Returns the list of fields that can be exported * @@ -434,8 +512,21 @@ static function &import($prefix = false) { * * @return array */ - static function &export($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'product', $prefix, array()); + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'product', $prefix, []); return $r; } + + /** + * Returns the list of indices + * + * @param bool $localize + * + * @return array + */ + public static function indices($localize = TRUE) { + $indices = []; + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + } diff --git a/CRM/Contribute/DAO/Widget.php b/CRM/Contribute/DAO/Widget.php index 4fb38b170fc4..fb7eb5e58eda 100644 --- a/CRM/Contribute/DAO/Widget.php +++ b/CRM/Contribute/DAO/Widget.php @@ -1,331 +1,391 @@ __table = 'civicrm_contribution_widget'; parent::__construct(); } + /** - * Returns foreign keys and entity references + * Returns foreign keys and entity references. * * @return array * [CRM_Core_Reference_Interface] */ - static function getReferenceColumns() { + public static function getReferenceColumns() { if (!isset(Civi::$statics[__CLASS__]['links'])) { - Civi::$statics[__CLASS__]['links'] = static ::createReferenceColumns(__CLASS__); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName() , 'contribution_page_id', 'civicrm_contribution_page', 'id'); + Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'contribution_page_id', 'civicrm_contribution_page', 'id'); CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']); } return Civi::$statics[__CLASS__]['links']; } + /** * Returns all the column names of this table * * @return array */ - static function &fields() { + public static function &fields() { if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = array( - 'id' => array( + Civi::$statics[__CLASS__]['fields'] = [ + 'id' => [ 'name' => 'id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Widget ID') , - 'description' => 'Contribution Id', - 'required' => true, - ) , - 'contribution_page_id' => array( + 'title' => ts('Widget ID'), + 'description' => ts('Contribution Id'), + 'required' => TRUE, + 'table_name' => 'civicrm_contribution_widget', + 'entity' => 'Widget', + 'bao' => 'CRM_Contribute_BAO_Widget', + 'localizable' => 0, + ], + 'contribution_page_id' => [ 'name' => 'contribution_page_id', 'type' => CRM_Utils_Type::T_INT, - 'title' => ts('Contribution Page') , - 'description' => 'The Contribution Page which triggered this contribution', + 'title' => ts('Contribution Page'), + 'description' => ts('The Contribution Page which triggered this contribution'), + 'table_name' => 'civicrm_contribution_widget', + 'entity' => 'Widget', + 'bao' => 'CRM_Contribute_BAO_Widget', + 'localizable' => 0, 'FKClassName' => 'CRM_Contribute_DAO_ContributionPage', - ) , - 'is_active' => array( + ], + 'is_active' => [ 'name' => 'is_active', 'type' => CRM_Utils_Type::T_BOOLEAN, - 'title' => ts('Enabled?') , - 'description' => 'Is this property active?', - ) , - 'title' => array( + 'title' => ts('Enabled?'), + 'description' => ts('Is this property active?'), + 'table_name' => 'civicrm_contribution_widget', + 'entity' => 'Widget', + 'bao' => 'CRM_Contribute_BAO_Widget', + 'localizable' => 0, + ], + 'title' => [ 'name' => 'title', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Widget Title') , - 'description' => 'Widget title.', + 'title' => ts('Widget Title'), + 'description' => ts('Widget title.'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - ) , - 'url_logo' => array( + 'table_name' => 'civicrm_contribution_widget', + 'entity' => 'Widget', + 'bao' => 'CRM_Contribute_BAO_Widget', + 'localizable' => 0, + ], + 'url_logo' => [ 'name' => 'url_logo', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Widget Image Url') , - 'description' => 'URL to Widget logo', + 'title' => ts('Widget Image Url'), + 'description' => ts('URL to Widget logo'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - ) , - 'button_title' => array( + 'table_name' => 'civicrm_contribution_widget', + 'entity' => 'Widget', + 'bao' => 'CRM_Contribute_BAO_Widget', + 'localizable' => 0, + ], + 'button_title' => [ 'name' => 'button_title', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Button Title') , - 'description' => 'Button title.', + 'title' => ts('Button Title'), + 'description' => ts('Button title.'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - ) , - 'about' => array( + 'table_name' => 'civicrm_contribution_widget', + 'entity' => 'Widget', + 'bao' => 'CRM_Contribute_BAO_Widget', + 'localizable' => 0, + ], + 'about' => [ 'name' => 'about', 'type' => CRM_Utils_Type::T_TEXT, - 'title' => ts('Description') , - 'description' => 'About description.', - ) , - 'url_homepage' => array( + 'title' => ts('Description'), + 'description' => ts('About description.'), + 'table_name' => 'civicrm_contribution_widget', + 'entity' => 'Widget', + 'bao' => 'CRM_Contribute_BAO_Widget', + 'localizable' => 0, + ], + 'url_homepage' => [ 'name' => 'url_homepage', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Homepage Url') , - 'description' => 'URL to Homepage.', + 'title' => ts('Homepage Url'), + 'description' => ts('URL to Homepage.'), 'maxlength' => 255, 'size' => CRM_Utils_Type::HUGE, - ) , - 'color_title' => array( + 'table_name' => 'civicrm_contribution_widget', + 'entity' => 'Widget', + 'bao' => 'CRM_Contribute_BAO_Widget', + 'localizable' => 0, + ], + 'color_title' => [ 'name' => 'color_title', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Title Color') , + 'title' => ts('Title Color'), 'maxlength' => 10, 'size' => CRM_Utils_Type::TWELVE, - ) , - 'color_button' => array( + 'table_name' => 'civicrm_contribution_widget', + 'entity' => 'Widget', + 'bao' => 'CRM_Contribute_BAO_Widget', + 'localizable' => 0, + ], + 'color_button' => [ 'name' => 'color_button', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Button Colour') , + 'title' => ts('Button Colour'), 'maxlength' => 10, 'size' => CRM_Utils_Type::TWELVE, - ) , - 'color_bar' => array( + 'table_name' => 'civicrm_contribution_widget', + 'entity' => 'Widget', + 'bao' => 'CRM_Contribute_BAO_Widget', + 'localizable' => 0, + ], + 'color_bar' => [ 'name' => 'color_bar', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Bar Color') , + 'title' => ts('Bar Color'), 'maxlength' => 10, 'size' => CRM_Utils_Type::TWELVE, - ) , - 'color_main_text' => array( + 'table_name' => 'civicrm_contribution_widget', + 'entity' => 'Widget', + 'bao' => 'CRM_Contribute_BAO_Widget', + 'localizable' => 0, + ], + 'color_main_text' => [ 'name' => 'color_main_text', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Main Text Color') , + 'title' => ts('Main Text Color'), 'maxlength' => 10, 'size' => CRM_Utils_Type::TWELVE, - ) , - 'color_main' => array( + 'table_name' => 'civicrm_contribution_widget', + 'entity' => 'Widget', + 'bao' => 'CRM_Contribute_BAO_Widget', + 'localizable' => 0, + ], + 'color_main' => [ 'name' => 'color_main', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Main Colour') , + 'title' => ts('Main Colour'), 'maxlength' => 10, 'size' => CRM_Utils_Type::TWELVE, - ) , - 'color_main_bg' => array( + 'table_name' => 'civicrm_contribution_widget', + 'entity' => 'Widget', + 'bao' => 'CRM_Contribute_BAO_Widget', + 'localizable' => 0, + ], + 'color_main_bg' => [ 'name' => 'color_main_bg', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Backgroup Color') , + 'title' => ts('Backgroup Color'), 'maxlength' => 10, 'size' => CRM_Utils_Type::TWELVE, - ) , - 'color_bg' => array( + 'table_name' => 'civicrm_contribution_widget', + 'entity' => 'Widget', + 'bao' => 'CRM_Contribute_BAO_Widget', + 'localizable' => 0, + ], + 'color_bg' => [ 'name' => 'color_bg', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Other Backgroun Colour') , + 'title' => ts('Other Backgroun Colour'), 'maxlength' => 10, 'size' => CRM_Utils_Type::TWELVE, - ) , - 'color_about_link' => array( + 'table_name' => 'civicrm_contribution_widget', + 'entity' => 'Widget', + 'bao' => 'CRM_Contribute_BAO_Widget', + 'localizable' => 0, + ], + 'color_about_link' => [ 'name' => 'color_about_link', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('About Link Colour') , + 'title' => ts('About Link Colour'), 'maxlength' => 10, 'size' => CRM_Utils_Type::TWELVE, - ) , - 'color_homepage_link' => array( + 'table_name' => 'civicrm_contribution_widget', + 'entity' => 'Widget', + 'bao' => 'CRM_Contribute_BAO_Widget', + 'localizable' => 0, + ], + 'color_homepage_link' => [ 'name' => 'color_homepage_link', 'type' => CRM_Utils_Type::T_STRING, - 'title' => ts('Homepage Link Colour') , + 'title' => ts('Homepage Link Colour'), 'maxlength' => 10, 'size' => CRM_Utils_Type::TWELVE, - ) , - ); + 'table_name' => 'civicrm_contribution_widget', + 'entity' => 'Widget', + 'bao' => 'CRM_Contribute_BAO_Widget', + 'localizable' => 0, + ], + ]; CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } return Civi::$statics[__CLASS__]['fields']; } + /** * Return a mapping from field-name to the corresponding key (as used in fields()). * * @return array * Array(string $name => string $uniqueName). */ - static function &fieldKeys() { + public static function &fieldKeys() { if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); } return Civi::$statics[__CLASS__]['fieldKeys']; } + /** * Returns the names of this table * * @return string */ - static function getTableName() { + public static function getTableName() { return self::$_tableName; } + /** * Returns if this table needs to be logged * - * @return boolean + * @return bool */ - function getLog() { + public function getLog() { return self::$_log; } + /** * Returns the list of fields that can be imported * @@ -333,10 +393,11 @@ function getLog() { * * @return array */ - static function &import($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'contribution_widget', $prefix, array()); + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'contribution_widget', $prefix, []); return $r; } + /** * Returns the list of fields that can be exported * @@ -344,8 +405,21 @@ static function &import($prefix = false) { * * @return array */ - static function &export($prefix = false) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'contribution_widget', $prefix, array()); + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'contribution_widget', $prefix, []); return $r; } + + /** + * Returns the list of indices + * + * @param bool $localize + * + * @return array + */ + public static function indices($localize = TRUE) { + $indices = []; + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + } diff --git a/CRM/Contribute/Exception/CheckLineItemsException.php b/CRM/Contribute/Exception/CheckLineItemsException.php new file mode 100644 index 000000000000..c1c0d3a91427 --- /dev/null +++ b/CRM/Contribute/Exception/CheckLineItemsException.php @@ -0,0 +1,13 @@ +id = $id; + CRM_Core_Error::debug_log_message('Access to contribution page with start date in future attempted - page number ' . $id); + } + + /** + * Get Contribution page ID. + * + * @return int + */ + public function getID() { + return $this->id; + } + +} diff --git a/CRM/Contribute/Exception/PastContributionPageException.php b/CRM/Contribute/Exception/PastContributionPageException.php new file mode 100644 index 000000000000..fc7c8b3182ea --- /dev/null +++ b/CRM/Contribute/Exception/PastContributionPageException.php @@ -0,0 +1,25 @@ +id = $id; + CRM_Core_Error::debug_log_message('Access to contribution page with past end date attempted - page number ' . $id); + } + + /** + * Get Contribution page ID. + * + * @return int + */ + public function getID() { + return $this->id; + } + +} diff --git a/CRM/Contribute/Form.php b/CRM/Contribute/Form.php index 320f28f209b8..745b0d7c66cb 100644 --- a/CRM/Contribute/Form.php +++ b/CRM/Contribute/Form.php @@ -1,9 +1,9 @@ _id)) { - $params = array('id' => $this->_id); + $params = ['id' => $this->_id]; if (!empty($this->_BAOName)) { $baoName = $this->_BAOName; $baoName::retrieve($params, $defaults); diff --git a/CRM/Contribute/Form/AbstractEditPayment.php b/CRM/Contribute/Form/AbstractEditPayment.php index c5f8855dc0ac..1eacda32bb1f 100644 --- a/CRM/Contribute/Form/AbstractEditPayment.php +++ b/CRM/Contribute/Form/AbstractEditPayment.php @@ -1,9 +1,9 @@ array($id => $label) @@ -79,7 +82,7 @@ class CRM_Contribute_Form_AbstractEditPayment extends CRM_Contact_Form_Task { * Available payment processors with full details including the key 'object' indexed by their id * @var array */ - protected $_paymentProcessors = array(); + protected $_paymentProcessors = []; /** * Instance of the payment processor object. @@ -95,6 +98,15 @@ class CRM_Contribute_Form_AbstractEditPayment extends CRM_Contact_Form_Task { */ public $_id; + /** + * Entity that $this->_id relates to. + * + * If set the contact id is not required in the url. + * + * @var string + */ + protected $entity; + /** * The id of the premium that we are proceessing. * @@ -166,11 +178,13 @@ class CRM_Contribute_Form_AbstractEditPayment extends CRM_Contact_Form_Task { /** * The contribution values if an existing contribution + * @var array */ public $_values; /** * The pledge values if this contribution is associated with pledge + * @var array */ public $_pledgeValues; @@ -182,18 +196,33 @@ class CRM_Contribute_Form_AbstractEditPayment extends CRM_Contact_Form_Task { /** * Store the line items if price set used. + * @var array */ public $_lineItems; /** * Is this a backoffice form - * (this will affect whether paypal express code is displayed) + * * @var bool */ public $isBackOffice = TRUE; protected $_formType; + /** + * Payment instrument id for the transaction. + * + * @var int + */ + public $paymentInstrumentID; + + /** + * Component - event, membership or contribution. + * + * @var string + */ + protected $_component; + /** * Array of fields to display on billingBlock.tpl - this is not fully implemented but basically intent is the panes/fieldsets on this page should * be all in this array in order like @@ -203,16 +232,33 @@ class CRM_Contribute_Form_AbstractEditPayment extends CRM_Contact_Form_Task { * such that both the fields and the order can be more easily altered by payment processors & other extensions * @var array */ - public $billingFieldSets = array(); + public $billingFieldSets = []; + + /** + * Monetary fields that may be submitted. + * + * These should get a standardised format in the beginPostProcess function. + * + * These fields are common to many forms. Some may override this. + * @var array + */ + protected $submittableMoneyFields = ['total_amount', 'net_amount', 'non_deductible_amount', 'fee_amount']; /** * Pre process function with common actions. */ public function preProcess() { $this->_contactID = CRM_Utils_Request::retrieve('cid', 'Positive', $this); + if (empty($this->_contactID) && !empty($this->_id) && $this->entity) { + $this->_contactID = civicrm_api3($this->entity, 'getvalue', ['id' => $this->_id, 'return' => 'contact_id']); + } $this->assign('contactID', $this->_contactID); - CRM_Core_Resources::singleton()->addVars('coreForm', array('contact_id' => (int) $this->_contactID)); + CRM_Core_Resources::singleton()->addVars('coreForm', ['contact_id' => (int) $this->_contactID]); $this->_action = CRM_Utils_Request::retrieve('action', 'String', $this, FALSE, 'add'); + $this->_mode = empty($this->_mode) ? CRM_Utils_Request::retrieve('mode', 'Alphanumeric', $this) : $this->_mode; + $this->assign('isBackOffice', $this->isBackOffice); + $this->assignContactEmailDetails(); + $this->assignPaymentRelatedVariables(); } /** @@ -225,7 +271,7 @@ public function showRecordLinkMesssage($id) { $recordPaymentLink = CRM_Utils_System::url('civicrm/payment', "reset=1&id={$pid}&cid={$this->_contactID}&action=add&component=event" ); - CRM_Core_Session::setStatus(ts('Please use the Record Payment form if you have received an additional payment for this Partially paid contribution record.', array(1 => $recordPaymentLink)), ts('Notice'), 'alert'); + CRM_Core_Session::setStatus(ts('Please use the Record Payment form if you have received an additional payment for this Partially paid contribution record.', [1 => $recordPaymentLink]), ts('Notice'), 'alert'); } } } @@ -235,8 +281,8 @@ public function showRecordLinkMesssage($id) { * @param $values */ public function buildValuesAndAssignOnline_Note_Type($id, &$values) { - $ids = array(); - $params = array('id' => $id); + $ids = []; + $params = ['id' => $id]; CRM_Contribute_BAO_Contribution::getValues($params, $values, $ids); //Check if this is an online transaction (financial_trxn.payment_processor_id NOT NULL) @@ -306,8 +352,7 @@ public function assignPremiumProduct($id) { * @throws Exception */ public function getValidProcessors() { - $defaultID = NULL; - $capabilities = array('BackOffice'); + $capabilities = ['BackOffice']; if ($this->_mode) { $capabilities[] = (ucfirst($this->_mode) . 'Mode'); } @@ -322,53 +367,60 @@ public function getValidProcessors() { public function assignProcessors() { //ensure that processor has a valid config //only valid processors get display to user - - if ($this->_mode) { - $this->assign('processorSupportsFutureStartDate', CRM_Financial_BAO_PaymentProcessor::hasPaymentProcessorSupporting(array('FutureRecurStartDate'))); - $this->_paymentProcessors = $this->getValidProcessors(); - if (!isset($this->_paymentProcessor['id'])) { - // if the payment processor isn't set yet (as indicated by the presence of an id,) we'll grab the first one which should be the default - $this->_paymentProcessor = reset($this->_paymentProcessors); - } - if (empty($this->_paymentProcessors)) { - throw new CRM_Core_Exception(ts('You will need to configure the %1 settings for your Payment Processor before you can submit a credit card transactions.', array(1 => $this->_mode))); - } - $this->_processors = array(); - foreach ($this->_paymentProcessors as $id => $processor) { - // @todo review this. The inclusion of this IF was to address test processors being incorrectly loaded. - // However the function $this->getValidProcessors() is expected to only return the processors relevant - // to the mode (using the actual id - ie. the id of the test processor for the test processor). - // for some reason there was a need to filter here per commit history - but this indicates a problem - // somewhere else. - if ($processor['is_test'] == ($this->_mode == 'test')) { - $this->_processors[$id] = ts($processor['name']); - if (!empty($processor['description'])) { - $this->_processors[$id] .= ' : ' . ts($processor['description']); - } - if ($processor['is_recur']) { - $this->_recurPaymentProcessors[$id] = $this->_processors[$id]; - } + $this->assign('processorSupportsFutureStartDate', CRM_Financial_BAO_PaymentProcessor::hasPaymentProcessorSupporting(['FutureRecurStartDate'])); + $this->_paymentProcessors = $this->getValidProcessors(); + if (!isset($this->_paymentProcessor['id'])) { + // if the payment processor isn't set yet (as indicated by the presence of an id,) we'll grab the first one which should be the default + $this->_paymentProcessor = reset($this->_paymentProcessors); + } + if (!$this->_mode) { + $this->_paymentProcessor = $this->_paymentProcessors[0]; + } + elseif (empty($this->_paymentProcessors) || array_keys($this->_paymentProcessors) === [0]) { + throw new CRM_Core_Exception(ts('You will need to configure the %1 settings for your Payment Processor before you can submit a credit card transactions.', [1 => $this->_mode])); + } + //Assign submitted processor value if it is different from the loaded one. + if (!empty($this->_submitValues['payment_processor_id']) + && $this->_paymentProcessor['id'] != $this->_submitValues['payment_processor_id']) { + $this->_paymentProcessor = CRM_Financial_BAO_PaymentProcessor::getPayment($this->_submitValues['payment_processor_id']); + } + $this->_processors = []; + foreach ($this->_paymentProcessors as $id => $processor) { + // @todo review this. The inclusion of this IF was to address test processors being incorrectly loaded. + // However the function $this->getValidProcessors() is expected to only return the processors relevant + // to the mode (using the actual id - ie. the id of the test processor for the test processor). + // for some reason there was a need to filter here per commit history - but this indicates a problem + // somewhere else. + if ($processor['is_test'] == ($this->_mode == 'test')) { + $this->_processors[$id] = ts($processor['name']); + if (!empty($processor['description'])) { + $this->_processors[$id] .= ' : ' . ts($processor['description']); + } + if ($processor['is_recur']) { + $this->_recurPaymentProcessors[$id] = $this->_processors[$id]; } } - CRM_Financial_Form_Payment::addCreditCardJs(); } + // CRM-21002: pass the default payment processor ID whose credit card type icons should be populated first + CRM_Financial_Form_Payment::addCreditCardJs($this->_paymentProcessor['id']); + $this->assign('recurringPaymentProcessorIds', empty($this->_recurPaymentProcessors) ? '' : implode(',', array_keys($this->_recurPaymentProcessors)) ); // this required to show billing block // @todo remove this assignment the billing block is now designed to be always included but will not show fieldsets unless those sets of fields are assigned - $this->assign_by_ref('paymentProcessor', $processor); + $this->assign_by_ref('paymentProcessor', $this->_paymentProcessor); } /** * Get current currency from DB or use default currency. * - * @param $submittedValues + * @param array $submittedValues * - * @return mixed + * @return string */ - public function getCurrency($submittedValues) { + public function getCurrency($submittedValues = []) { $config = CRM_Core_Config::singleton(); $currentCurrency = CRM_Utils_Array::value('currency', @@ -384,38 +436,11 @@ public function getCurrency($submittedValues) { return $result; } - /** - * @param int $financialTypeId - * - * @return array - */ - public function getFinancialAccounts($financialTypeId) { - $financialAccounts = array(); - CRM_Core_PseudoConstant::populate($financialAccounts, - 'CRM_Financial_DAO_EntityFinancialAccount', - $all = TRUE, - $retrieve = 'financial_account_id', - $filter = NULL, - " entity_id = {$financialTypeId} ", NULL, 'account_relationship'); - return $financialAccounts; - } - - /** - * @param int $financialTypeId - * @param int $relationTypeId - * - * @return mixed - */ - public function getFinancialAccount($financialTypeId, $relationTypeId) { - $financialAccounts = $this->getFinancialAccounts($financialTypeId); - return CRM_Utils_Array::value($relationTypeId, $financialAccounts); - } - public function preProcessPledge() { //get the payment values associated with given pledge payment id OR check for payments due. - $this->_pledgeValues = array(); + $this->_pledgeValues = []; if ($this->_ppID) { - $payParams = array('id' => $this->_ppID); + $payParams = ['id' => $this->_ppID]; CRM_Pledge_BAO_PledgePayment::retrieve($payParams, $this->_pledgeValues['pledgePayment']); $this->_pledgeID = CRM_Utils_Array::value('pledge_id', $this->_pledgeValues['pledgePayment']); @@ -430,8 +455,8 @@ public function preProcessPledge() { //get the pledge values associated with given pledge payment. - $ids = array(); - $pledgeParams = array('id' => $this->_pledgeID); + $ids = []; + $pledgeParams = ['id' => $this->_pledgeID]; CRM_Pledge_BAO_Pledge::getValues($pledgeParams, $this->_pledgeValues, $ids); $this->assign('ppID', $this->_ppID); } @@ -460,7 +485,7 @@ public function preProcessPledge() { $pledgeTab = CRM_Utils_System::url('civicrm/contact/view', "reset=1&force=1&cid={$this->_contactID}&selectedChild=pledge" ); - CRM_Core_Session::setStatus(ts('This contact has pending or overdue pledge payments. Click here to view their Pledges tab and verify whether this contribution should be applied as a pledge payment.', array(1 => $pledgeTab)), ts('Notice'), 'alert'); + CRM_Core_Session::setStatus(ts('This contact has pending or overdue pledge payments. Click here to view their Pledges tab and verify whether this contribution should be applied as a pledge payment.', [1 => $pledgeTab]), ts('Notice'), 'alert'); } elseif ($paymentsDue) { // Show user link to oldest Pending or Overdue pledge payment @@ -476,11 +501,11 @@ public function preProcessPledge() { "reset=1&action=add&cid={$this->_contactID}&ppid={$payments['id']}&context=pledge" ); } - CRM_Core_Session::setStatus(ts('This contact has a pending or overdue pledge payment of %2 which is scheduled for %3. Click here to enter a pledge payment.', array( + CRM_Core_Session::setStatus(ts('This contact has a pending or overdue pledge payment of %2 which is scheduled for %3. Click here to enter a pledge payment.', [ 1 => $ppUrl, 2 => $ppAmountDue, 3 => $ppSchedDate, - )), ts('Notice'), 'alert'); + ]), ts('Notice'), 'alert'); } } } @@ -494,7 +519,7 @@ public function preProcessPledge() { */ public function unsetCreditCardFields($submittedValues) { //Offline Contribution. - $unsetParams = array( + $unsetParams = [ 'payment_processor_id', "email-{$this->_bltID}", 'hidden_buildCreditCard', @@ -511,7 +536,7 @@ public function unsetCreditCardFields($submittedValues) { 'cvv2', 'credit_card_exp_date', 'credit_card_type', - ); + ]; foreach ($unsetParams as $key) { if (isset($submittedValues[$key])) { unset($submittedValues[$key]); @@ -526,20 +551,12 @@ public function unsetCreditCardFields($submittedValues) { */ protected function assignPaymentRelatedVariables() { try { - if ($this->_contactID) { - list($this->userDisplayName, $this->userEmail) = CRM_Contact_BAO_Contact_Location::getEmailDetails($this->_contactID); - $this->assign('displayName', $this->userDisplayName); - } - if ($this->_mode) { - $this->assignProcessors(); - - $this->assignBillingType(); - - CRM_Core_Payment_Form::setPaymentFieldsByProcessor($this, $this->_paymentProcessor, FALSE, TRUE); - } + $this->assignProcessors(); + $this->assignBillingType(); + CRM_Core_Payment_Form::setPaymentFieldsByProcessor($this, $this->_paymentProcessor, FALSE, TRUE, CRM_Utils_Request::retrieve('payment_instrument_id', 'Integer', $this)); } catch (CRM_Core_Exception $e) { - CRM_Core_Error::fatal($e->getMessage()); + CRM_Core_Error::statusBounce($e->getMessage()); } } @@ -568,8 +585,37 @@ protected function beginPostProcess() { $this->assign('credit_card_type', CRM_Utils_Array::value('credit_card_type', $this->_params)); } $this->_params['ip_address'] = CRM_Utils_System::ipAddress(); + + self::formatCreditCardDetails($this->_params); + foreach ($this->submittableMoneyFields as $moneyField) { + if (isset($this->_params[$moneyField])) { + $this->_params[$moneyField] = CRM_Utils_Rule::cleanMoney($this->_params[$moneyField]); + } + } + if (!empty($this->_params['contact_id']) && empty($this->_contactID)) { + // Contact ID has been set in the standalone form. + $this->_contactID = $this->_params['contact_id']; + $this->assignContactEmailDetails(); + } } + /** + * Format credit card details like: + * 1. Retrieve last 4 digit from credit card number as pan_truncation + * 2. Retrieve credit card type id from name + * + * @param array $params + * + * @return void + */ + public static function formatCreditCardDetails(&$params) { + if (in_array('credit_card_type', array_keys($params))) { + $params['card_type_id'] = CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_FinancialTrxn', 'card_type_id', $params['credit_card_type']); + } + if (!empty($params['credit_card_number']) && empty($params['pan_truncation'])) { + $params['pan_truncation'] = substr($params['credit_card_number'], -4); + } + } /** * Add the billing address to the contact who paid. @@ -579,7 +625,7 @@ protected function beginPostProcess() { * for pay later. */ protected function processBillingAddress() { - $fields = array(); + $fields = []; $fields['email-Primary'] = 1; $this->_params['email-5'] = $this->_params['email-Primary'] = $this->_contributorEmail; @@ -613,6 +659,8 @@ protected function processBillingAddress() { CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $this->_contactID, 'contact_type') ); } + + $this->assignBillingName($this->_params); } /** @@ -641,4 +689,78 @@ protected function getBillingDefaults($defaults) { return array_merge($defaults, $billingDefaults); } + /** + * Get the default payment instrument id. + * + * @return int + */ + protected function getDefaultPaymentInstrumentId() { + $paymentInstrumentID = CRM_Utils_Request::retrieve('payment_instrument_id', 'Integer'); + if ($paymentInstrumentID) { + return $paymentInstrumentID; + } + return key(CRM_Core_OptionGroup::values('payment_instrument', FALSE, FALSE, FALSE, 'AND is_default = 1')); + } + + /** + * Add the payment processor select to the form. + * + * @param bool $isRequired + * Is it a mandatory field. + * @param bool $isBuildRecurBlock + * True if we want to build recur on change + * @param bool $isBuildAutoRenewBlock + * True if we want to build autorenew on change. + */ + protected function addPaymentProcessorSelect($isRequired, $isBuildRecurBlock = FALSE, $isBuildAutoRenewBlock = FALSE) { + if (!$this->_mode) { + return; + } + $js = ($isBuildRecurBlock ? ['onChange' => "buildRecurBlock( this.value ); return false;"] : NULL); + if ($isBuildAutoRenewBlock) { + $js = ['onChange' => "buildAutoRenew( null, this.value, '{$this->_mode}');"]; + } + $element = $this->add('select', + 'payment_processor_id', + ts('Payment Processor'), + array_diff_key($this->_processors, [0 => 1]), + $isRequired, + $js + ); + // The concept of _online is not really explained & the code is old + // @todo figure out & document. + if ($this->_online) { + $element->freeze(); + } + } + + /** + * Assign the values to build the payment info block. + * + * @return string + * Block title. + */ + protected function assignPaymentInfoBlock() { + $paymentInfo = CRM_Contribute_BAO_Contribution::getPaymentInfo($this->_id, $this->_component, TRUE); + $title = ts('View Payment'); + if (!empty($this->_component) && $this->_component == 'event') { + $info = CRM_Event_BAO_Participant::participantDetails($this->_id); + $title .= " - {$info['title']}"; + } + $this->assign('transaction', TRUE); + $this->assign('payments', $paymentInfo['transaction']); + $this->assign('paymentLinks', $paymentInfo['payment_links']); + return $title; + } + + protected function assignContactEmailDetails() { + if ($this->_contactID) { + list($this->userDisplayName, $this->userEmail) = CRM_Contact_BAO_Contact_Location::getEmailDetails($this->_contactID); + if (empty($this->userDisplayName)) { + $this->userDisplayName = civicrm_api3('contact', 'getvalue', ['id' => $this->_contactID, 'return' => 'display_name']); + } + $this->assign('displayName', $this->userDisplayName); + } + } + } diff --git a/CRM/Contribute/Form/AdditionalInfo.php b/CRM/Contribute/Form/AdditionalInfo.php index 0609f843b5b5..eb68ed514c4c 100644 --- a/CRM/Contribute/Form/AdditionalInfo.php +++ b/CRM/Contribute/Form/AdditionalInfo.php @@ -1,9 +1,9 @@ add('hidden', 'hidden_Premium', 1); - $sel1 = $sel2 = array(); + $sel1 = $sel2 = []; $dao = new CRM_Contribute_DAO_Product(); $dao->is_active = 1; $dao->find(); - $min_amount = array(); + $min_amount = []; $sel1[0] = ts('-select product-'); while ($dao->fetch()) { $sel1[$dao->id] = $dao->name . " ( " . $dao->sku . " )"; @@ -77,11 +77,11 @@ public static function buildPremium(&$form) { } } - $sel->setOptions(array($sel1, $sel2)); + $sel->setOptions([$sel1, $sel2]); $js .= "\n"; $form->assign('initHideBoxes', $js); - $form->addDate('fulfilled_date', ts('Fulfilled'), FALSE, array('formatType' => 'activityDate')); + $form->add('datepicker', 'fulfilled_date', ts('Fulfilled'), [], FALSE, ['time' => FALSE]); $form->addElement('text', 'min_amount', ts('Minimum Contribution Amount')); } @@ -96,7 +96,7 @@ public static function buildAdditionalDetail(&$form) { $attributes = CRM_Core_DAO::getAttribute('CRM_Contribute_DAO_Contribution'); - $form->addDateTime('thankyou_date', ts('Thank-you Sent'), FALSE, array('formatType' => 'activityDateTime')); + $form->addField('thankyou_date', ['entity' => 'contribution'], FALSE, FALSE); // add various amounts $nonDeductAmount = &$form->add('text', 'non_deductible_amount', ts('Non-deductible Amount'), @@ -115,13 +115,6 @@ public static function buildAdditionalDetail(&$form) { $feeAmount->freeze(); } - $netAmount = &$form->add('text', 'net_amount', ts('Net Amount'), - $attributes['net_amount'] - ); - $form->addRule('net_amount', ts('Please enter a valid monetary value for Net Amount.'), 'money'); - if ($form->_online) { - $netAmount->freeze(); - } $element = &$form->add('text', 'invoice_id', ts('Invoice ID'), $attributes['invoice_id'] ); @@ -132,7 +125,7 @@ public static function buildAdditionalDetail(&$form) { $form->addRule('invoice_id', ts('This Invoice ID already exists in the database.'), 'objectExists', - array('CRM_Contribute_DAO_Contribution', $form->_id, 'invoice_id') + ['CRM_Contribute_DAO_Contribution', $form->_id, 'invoice_id'] ); } $element = $form->add('text', 'creditnote_id', ts('Credit Note ID'), @@ -145,23 +138,22 @@ public static function buildAdditionalDetail(&$form) { $form->addRule('creditnote_id', ts('This Credit Note ID already exists in the database.'), 'objectExists', - array('CRM_Contribute_DAO_Contribution', $form->_id, 'creditnote_id') + ['CRM_Contribute_DAO_Contribution', $form->_id, 'creditnote_id'] ); } $form->add('select', 'contribution_page_id', ts('Online Contribution Page'), - array( + [ '' => ts('- select -'), - ) + + ] + CRM_Contribute_PseudoConstant::contributionPage() ); - $form->add('textarea', 'note', ts('Notes'), array("rows" => 4, "cols" => 60)); + $form->add('textarea', 'note', ts('Notes'), ["rows" => 4, "cols" => 60]); $statusName = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name'); if ($form->_id && $form->_values['contribution_status_id'] == array_search('Cancelled', $statusName)) { - $netAmount->freeze(); $feeAmount->freeze(); } @@ -177,11 +169,11 @@ public static function buildAdditionalDetail(&$form) { public static function buildPaymentReminders(&$form) { //PaymentReminders section $form->add('hidden', 'hidden_PaymentReminders', 1); - $form->add('text', 'initial_reminder_day', ts('Send Initial Reminder'), array('size' => 3)); + $form->add('text', 'initial_reminder_day', ts('Send Initial Reminder'), ['size' => 3]); $form->addRule('initial_reminder_day', ts('Please enter a valid reminder day.'), 'positiveInteger'); - $form->add('text', 'max_reminders', ts('Send up to'), array('size' => 3)); + $form->add('text', 'max_reminders', ts('Send up to'), ['size' => 3]); $form->addRule('max_reminders', ts('Please enter a valid No. of reminders.'), 'positiveInteger'); - $form->add('text', 'additional_reminder_day', ts('Send additional reminders'), array('size' => 3)); + $form->add('text', 'additional_reminder_day', ts('Send additional reminders'), ['size' => 3]); $form->addRule('additional_reminder_day', ts('Please enter a valid additional reminder day.'), 'positiveInteger'); } @@ -193,23 +185,23 @@ public static function buildPaymentReminders(&$form) { * @param int $premiumID * @param array $options */ - public static function processPremium($params, $contributionID, $premiumID = NULL, $options = array()) { + public static function processPremium($params, $contributionID, $premiumID = NULL, $options = []) { $selectedProductID = $params['product_name'][0]; $selectedProductOptionID = CRM_Utils_Array::value(1, $params['product_name']); $dao = new CRM_Contribute_DAO_ContributionProduct(); $dao->contribution_id = $contributionID; $dao->product_id = $selectedProductID; - $dao->fulfilled_date = CRM_Utils_Date::processDate($params['fulfilled_date'], NULL, TRUE); + $dao->fulfilled_date = $params['fulfilled_date']; $isDeleted = FALSE; //CRM-11106 - $premiumParams = array( + $premiumParams = [ 'id' => $selectedProductID, - ); + ]; - $productDetails = array(); - CRM_Contribute_BAO_ManagePremiums::retrieve($premiumParams, $productDetails); + $productDetails = []; + CRM_Contribute_BAO_Product::retrieve($premiumParams, $productDetails); $dao->financial_type_id = CRM_Utils_Array::value('financial_type_id', $productDetails); if (!empty($options[$selectedProductID])) { $dao->product_option = $options[$selectedProductID][$selectedProductOptionID]; @@ -230,12 +222,12 @@ public static function processPremium($params, $contributionID, $premiumID = NUL $dao->save(); //CRM-11106 if ($premiumID == NULL || $isDeleted) { - $premiumParams = array( + $premiumParams = [ 'cost' => CRM_Utils_Array::value('cost', $productDetails), 'currency' => CRM_Utils_Array::value('currency', $productDetails), 'financial_type_id' => CRM_Utils_Array::value('financial_type_id', $productDetails), 'contributionId' => $contributionID, - ); + ]; if ($isDeleted) { $premiumParams['oldPremium']['product_id'] = $ContributionProduct->product_id; $premiumParams['oldPremium']['contribution_id'] = $ContributionProduct->contribution_id; @@ -254,16 +246,20 @@ public static function processPremium($params, $contributionID, $premiumID = NUL * @param int $contributionNoteID */ public static function processNote($params, $contactID, $contributionID, $contributionNoteID = NULL) { + if (CRM_Utils_System::isNull($params['note']) && $contributionNoteID) { + CRM_Core_BAO_Note::del($contributionNoteID); + return; + } //process note - $noteParams = array( + $noteParams = [ 'entity_table' => 'civicrm_contribution', 'note' => $params['note'], 'entity_id' => $contributionID, 'contact_id' => $contactID, - ); - $noteID = array(); + ]; + $noteID = []; if ($contributionNoteID) { - $noteID = array("id" => $contributionNoteID); + $noteID = ["id" => $contributionNoteID]; $noteParams['note'] = $noteParams['note'] ? $noteParams['note'] : "null"; } CRM_Core_BAO_Note::add($noteParams, $noteID); @@ -277,17 +273,16 @@ public static function processNote($params, $contactID, $contributionID, $contri * @param CRM_Core_Form $form */ public static function postProcessCommon(&$params, &$formatted, &$form) { - $fields = array( + $fields = [ 'non_deductible_amount', 'total_amount', 'fee_amount', - 'net_amount', 'trxn_id', 'invoice_id', 'creditnote_id', 'campaign_id', 'contribution_page_id', - ); + ]; foreach ($fields as $f) { $formatted[$f] = CRM_Utils_Array::value($f, $params); } @@ -344,12 +339,12 @@ public static function emailReceipt(&$form, &$params, $ccContribution = FALSE) { // retrieve individual prefix value for honoree if (isset($params['soft_credit'])) { - $softCreditTypes = $softCredits = array(); + $softCreditTypes = $softCredits = []; foreach ($params['soft_credit'] as $key => $softCredit) { - $softCredits[$key] = array( + $softCredits[$key] = [ 'Name' => $softCredit['contact_name'], 'Amount' => CRM_Utils_Money::format($softCredit['amount'], $softCredit['currency']), - ); + ]; $softCreditTypes[$key] = $softCredit['soft_credit_type_label']; } $form->assign('softCreditTypes', $softCreditTypes); @@ -374,37 +369,18 @@ public static function emailReceipt(&$form, &$params, $ccContribution = FALSE) { $params['product_option'] = $form->_options[$productDAO->id][$productOptionID]; } } - if (!empty($params['fulfilled_date'])) { - $form->assign('fulfilled_date', CRM_Utils_Date::processDate($params['fulfilled_date'])); + $form->assign('fulfilled_date', $params['fulfilled_date']); } } $form->assign('ccContribution', $ccContribution); if ($ccContribution) { - //build the name. - $name = CRM_Utils_Array::value('billing_first_name', $params); - if (!empty($params['billing_middle_name'])) { - $name .= " {$params['billing_middle_name']}"; - } - $name .= ' ' . CRM_Utils_Array::value('billing_last_name', $params); - $name = trim($name); - $form->assign('billingName', $name); - - //assign the address formatted up for display - $addressParts = array( - "street_address" => "billing_street_address-{$form->_bltID}", - "city" => "billing_city-{$form->_bltID}", - "postal_code" => "billing_postal_code-{$form->_bltID}", - "state_province" => "state_province-{$form->_bltID}", - "country" => "country-{$form->_bltID}", - ); - - $addressFields = array(); - foreach ($addressParts as $name => $field) { - $addressFields[$name] = CRM_Utils_Array::value($field, $params); - } - $form->assign('address', CRM_Utils_Address::format($addressFields)); + $form->assignBillingName($params); + $form->assign('address', CRM_Utils_Address::getFormattedBillingAddressFieldsFromParameters( + $params, + $form->_bltID + )); $date = CRM_Utils_Date::format($params['credit_card_exp_date']); $date = CRM_Utils_Date::mysqlToIso($date); @@ -432,16 +408,16 @@ public static function emailReceipt(&$form, &$params, $ccContribution = FALSE) { //handle custom data if (!empty($params['hidden_custom'])) { - $contribParams = array(array('contribution_id', '=', $params['contribution_id'], 0, 0)); + $contribParams = [['contribution_id', '=', $params['contribution_id'], 0, 0]]; if ($form->_mode == 'test') { - $contribParams[] = array('contribution_test', '=', 1, 0, 0); + $contribParams[] = ['contribution_test', '=', 1, 0, 0]; } //retrieve custom data - $customGroup = array(); + $customGroup = []; foreach ($form->_groupTree as $groupID => $group) { - $customFields = $customValues = array(); + $customFields = $customValues = []; if ($groupID == 'info') { continue; } @@ -486,7 +462,7 @@ public static function emailReceipt(&$form, &$params, $ccContribution = FALSE) { } list($sendReceipt, $subject, $message, $html) = CRM_Core_BAO_MessageTemplate::sendTemplate( - array( + [ 'groupName' => 'msg_tpl_workflow_contribution', 'valueName' => 'contribution_offline_receipt', 'contactId' => $params['contact_id'], @@ -497,7 +473,7 @@ public static function emailReceipt(&$form, &$params, $ccContribution = FALSE) { 'isTest' => $form->_mode == 'test', 'PDFFilename' => ts('receipt') . '.pdf', 'isEmailPdf' => $isEmailPdf, - ) + ] ); return $sendReceipt; diff --git a/CRM/Contribute/Form/AdditionalPayment.php b/CRM/Contribute/Form/AdditionalPayment.php index bbfcc709c569..1c37915b5803 100644 --- a/CRM/Contribute/Form/AdditionalPayment.php +++ b/CRM/Contribute/Form/AdditionalPayment.php @@ -1,9 +1,9 @@ contactID + */ protected $_contactId = NULL; protected $_contributorDisplayName = NULL; @@ -74,40 +74,36 @@ class CRM_Contribute_Form_AdditionalPayment extends CRM_Contribute_Form_Abstract public $_action = NULL; public function preProcess() { + $this->_id = CRM_Utils_Request::retrieve('id', 'Positive', $this, TRUE); - $this->_contactId = CRM_Utils_Request::retrieve('cid', 'Positive', $this, TRUE); - $this->_component = CRM_Utils_Request::retrieve('component', 'String', $this, TRUE); + parent::preProcess(); + $this->_contactId = $this->_contactID; + $this->_component = CRM_Utils_Request::retrieve('component', 'String', $this, FALSE, 'contribution'); $this->_view = CRM_Utils_Request::retrieve('view', 'String', $this, FALSE); - $this->_action = CRM_Utils_Request::retrieve('action', 'String', $this, FALSE); $this->assign('component', $this->_component); $this->assign('id', $this->_id); $this->assign('suppressPaymentFormButtons', $this->isBeingCalledFromSelectorContext()); if ($this->_view == 'transaction' && ($this->_action & CRM_Core_Action::BROWSE)) { - $paymentInfo = CRM_Contribute_BAO_Contribution::getPaymentInfo($this->_id, $this->_component, TRUE); - $transactionRows = $paymentInfo['transaction']; - $title = ts('View Payment'); - if ($this->_component == 'event') { - $info = CRM_Event_BAO_Participant::participantDetails($this->_id); - $title .= " - {$info['title']}"; - } + $title = $this->assignPaymentInfoBlock(); CRM_Utils_System::setTitle($title); - $this->assign('transaction', TRUE); - $this->assign('rows', $transactionRows); return; } $this->_fromEmails = CRM_Core_BAO_Email::getFromEmail(); - $this->_formType = CRM_Utils_Array::value('formType', $_GET); - $enitityType = NULL; + $entityType = 'contribution'; if ($this->_component == 'event') { - $enitityType = 'participant'; + $entityType = 'participant'; $this->_contributionId = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_ParticipantPayment', $this->_id, 'contribution_id', 'participant_id'); + $eventId = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_Participant', $this->_id, 'event_id', 'id'); + $this->_fromEmails = CRM_Event_BAO_Event::getFromEmailIds($eventId); + } + else { + $this->_contributionId = $this->_id; + $this->_fromEmails['from_email_id'] = CRM_Core_BAO_Email::getFromEmail(); } - $eventId = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_Participant', $this->_id, 'event_id', 'id'); - $this->_fromEmails = CRM_Event_BAO_Event::getFromEmailIds($eventId); - $paymentInfo = CRM_Core_BAO_FinancialTrxn::getPartialPaymentWithType($this->_id, $enitityType); + $paymentInfo = CRM_Core_BAO_FinancialTrxn::getPartialPaymentWithType($this->_id, $entityType); $paymentDetails = CRM_Contribute_BAO_Contribution::getPaymentInfo($this->_id, $this->_component, FALSE, TRUE); $this->_amtPaid = $paymentDetails['paid']; @@ -125,19 +121,14 @@ public function preProcess() { CRM_Core_Error::fatal(ts('No payment information found for this record')); } - //set the payment mode - _mode property is defined in parent class - $this->_mode = CRM_Utils_Request::retrieve('mode', 'String', $this); - if (!empty($this->_mode) && $this->_paymentType == 'refund') { CRM_Core_Error::fatal(ts('Credit card payment is not for Refund payments use')); } - list($this->_contributorDisplayName, $this->_contributorEmail) = CRM_Contact_BAO_Contact_Location::getEmailDetails($this->_contactId); - - $this->assignPaymentRelatedVariables(); + list($this->_contributorDisplayName, $this->_contributorEmail) = CRM_Contact_BAO_Contact_Location::getEmailDetails($this->_contactID); $this->assign('contributionMode', $this->_mode); - $this->assign('contactId', $this->_contactId); + $this->assign('contactId', $this->_contactID); $this->assign('paymentType', $this->_paymentType); $this->assign('paymentAmt', abs($paymentAmt)); @@ -162,6 +153,7 @@ protected function isBeingCalledFromSelectorContext() { * @return array * reference to the array of default values */ + /** * @return array */ @@ -169,34 +161,21 @@ public function setDefaultValues() { if ($this->_view == 'transaction' && ($this->_action & CRM_Core_Action::BROWSE)) { return NULL; } - $defaults = array(); + $defaults = []; if ($this->_mode) { - $defaults = $this->_values; - - $config = CRM_Core_Config::singleton(); - // set default country from config if no country set - if (empty($defaults["billing_country_id-{$this->_bltID}"])) { - $defaults["billing_country_id-{$this->_bltID}"] = $config->defaultContactCountry; - } - - if (empty($defaults["billing_state_province_id-{$this->_bltID}"])) { - $defaults["billing_state_province_id-{$this->_bltID}"] = $config->defaultContactStateProvince; - } - - $billingDefaults = $this->getProfileDefaults('Billing', $this->_contactId); - $defaults = array_merge($defaults, $billingDefaults); + CRM_Core_Payment_Form::setDefaultValues($this, $this->_contactId); + $defaults = array_merge($defaults, $this->_defaults); } - if (empty($defaults['trxn_date']) && empty($defaults['trxn_date_time'])) { - list($defaults['trxn_date'], $defaults['trxn_date_time']) - = CRM_Utils_Date::setDateDefaults( - CRM_Utils_Array::value('register_date', $defaults), - 'activityDateTime' - ); + if (empty($defaults['trxn_date'])) { + $defaults['trxn_date'] = date('Y-m-d H:i:s'); } if ($this->_refund) { - $defaults['total_amount'] = abs($this->_refund); + $defaults['total_amount'] = CRM_Utils_Money::format(abs($this->_refund), NULL, NULL, TRUE); + } + elseif ($this->_owed) { + $defaults['total_amount'] = CRM_Utils_Money::formatLocaleNumericRoundedForDefaultCurrency($this->_owed); } // Set $newCredit variable in template to control whether link to credit card mode is included @@ -204,89 +183,35 @@ public function setDefaultValues() { return $defaults; } + /** + * Build the form object. + */ public function buildQuickForm() { if ($this->_view == 'transaction' && ($this->_action & CRM_Core_Action::BROWSE)) { - $this->addButtons(array( - array( - 'type' => 'cancel', - 'name' => ts('Done'), - 'spacing' => '         ', - 'isDefault' => TRUE, - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'cancel', + 'name' => ts('Done'), + 'spacing' => '         ', + 'isDefault' => TRUE, + ], + ]); return; } - $ccPane = NULL; - if ($this->_mode) { - if (CRM_Utils_Array::value('payment_type', $this->_processors) & CRM_Core_Payment::PAYMENT_TYPE_DIRECT_DEBIT - ) { - $ccPane = array(ts('Direct Debit Information') => 'DirectDebit'); - } - else { - $ccPane = array(ts('Credit Card Information') => 'CreditCard'); - } - $defaults = $this->_values; - $showAdditionalInfo = FALSE; - - foreach ($ccPane as $name => $type) { - if ($this->_formType == $type || !empty($_POST["hidden_{$type}"]) || - CRM_Utils_Array::value("hidden_{$type}", $defaults) - ) { - $showAdditionalInfo = TRUE; - $allPanes[$name]['open'] = 'true'; - } - - $urlParams = "snippet=4&formType={$type}"; - if ($this->_mode) { - $urlParams .= "&mode={$this->_mode}"; - } - $open = 'false'; - if ($type == 'CreditCard' || - $type == 'DirectDebit' - ) { - $open = 'true'; - } - - $allPanes[$name] = array( - 'url' => CRM_Utils_System::url('civicrm/payment/add', $urlParams), - 'open' => $open, - 'id' => $type, - ); - - CRM_Core_Payment_Form::buildPaymentForm($this, $this->_paymentProcessor, FALSE, TRUE); - - $qfKey = $this->controller->_key; - $this->assign('qfKey', $qfKey); - $this->assign('allPanes', $allPanes); - $this->assign('showAdditionalInfo', $showAdditionalInfo); - - if ($this->_formType) { - $this->assign('formType', $this->_formType); - return; - } - } - } - $attributes = CRM_Core_DAO::getAttribute('CRM_Financial_DAO_FinancialTrxn'); + CRM_Core_Payment_Form::buildPaymentForm($this, $this->_paymentProcessor, FALSE, TRUE, CRM_Utils_Request::retrieve('payment_instrument_id', 'Integer')); $this->add('select', 'payment_processor_id', ts('Payment Processor'), $this->_processors, NULL); + + $attributes = CRM_Core_DAO::getAttribute('CRM_Financial_DAO_FinancialTrxn'); + $label = ($this->_refund) ? ts('Refund Amount') : ts('Payment Amount'); $this->addMoney('total_amount', $label, - FALSE, + TRUE, $attributes['total_amount'], TRUE, 'currency', NULL ); - $this->add('select', 'payment_instrument_id', - ts('Payment Method'), - array('' => ts('- select -')) + CRM_Contribute_PseudoConstant::paymentInstrument(), - TRUE, array('onChange' => "return showHideByValue('payment_instrument_id','4','checkNumber','table-row','select',false);") - ); - - $this->add('text', 'check_number', ts('Check Number'), $attributes['financial_trxn_check_number']); - $this->add('text', 'trxn_id', ts('Transaction ID'), array('class' => 'twelve') + $attributes['trxn_id']); - //add receipt for offline contribution $this->addElement('checkbox', 'is_email_receipt', ts('Send Receipt?')); @@ -294,9 +219,8 @@ public function buildQuickForm() { $this->add('textarea', 'receipt_text', ts('Confirmation Message')); - // add various dates $dateLabel = ($this->_refund) ? ts('Refund Date') : ts('Date Received'); - $this->addDateTime('trxn_date', $dateLabel, FALSE, array('formatType' => 'activityDateTime')); + $this->addField('trxn_date', ['entity' => 'FinancialTrxn', 'label' => $dateLabel, 'context' => 'Contribution'], FALSE, FALSE); if ($this->_contactId && $this->_id) { if ($this->_component == 'event') { @@ -310,39 +234,49 @@ public function buildQuickForm() { $this->assign('component', $this->_component); $this->assign('email', $this->_contributorEmail); - $this->add('text', 'fee_amount', ts('Fee Amount'), - $attributes['fee_amount'] - ); - $this->addRule('fee_amount', ts('Please enter a valid monetary value for Fee Amount.'), 'money'); - - $this->add('text', 'net_amount', ts('Net Amount'), - $attributes['net_amount'] - ); - $this->addRule('net_amount', ts('Please enter a valid monetary value for Net Amount.'), 'money'); - $js = NULL; + // render backoffice payment fields only on offline mode if (!$this->_mode) { - $js = array('onclick' => "return verify( );"); + $js = ['onclick' => "return verify( );"]; + + $this->add('select', 'payment_instrument_id', + ts('Payment Method'), + ['' => ts('- select -')] + CRM_Contribute_PseudoConstant::paymentInstrument(), + TRUE, + ['onChange' => "return showHideByValue('payment_instrument_id','4','checkNumber','table-row','select',false);"] + ); + + $this->add('text', 'check_number', ts('Check Number'), $attributes['financial_trxn_check_number']); + $this->add('text', 'trxn_id', ts('Transaction ID'), ['class' => 'twelve'] + $attributes['trxn_id']); + + $this->add('text', 'fee_amount', ts('Fee Amount'), + $attributes['fee_amount'] + ); + $this->addRule('fee_amount', ts('Please enter a valid monetary value for Fee Amount.'), 'money'); + + $this->add('text', 'net_amount', ts('Net Amount'), + $attributes['net_amount'] + ); + $this->addRule('net_amount', ts('Please enter a valid monetary value for Net Amount.'), 'money'); } $buttonName = $this->_refund ? 'Record Refund' : 'Record Payment'; - $this->addButtons(array( - array( - 'type' => 'upload', - 'name' => ts('%1', array(1 => $buttonName)), - 'js' => $js, - 'isDefault' => TRUE, - ), - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'upload', + 'name' => ts('%1', [1 => $buttonName]), + 'js' => $js, + 'isDefault' => TRUE, + ], + [ + 'type' => 'cancel', + 'name' => ts('Cancel'), + ], + ]); $mailingInfo = Civi::settings()->get('mailing_backend'); $this->assign('outBound_option', $mailingInfo['outBound_option']); - $this->addFormRule(array('CRM_Contribute_Form_AdditionalPayment', 'formRule'), $this); + $this->addFormRule(['CRM_Contribute_Form_AdditionalPayment', 'formRule'], $this); } /** @@ -353,157 +287,128 @@ public function buildQuickForm() { * @return array */ public static function formRule($fields, $files, $self) { - $errors = array(); + $errors = []; if ($self->_paymentType == 'owed' && $fields['total_amount'] > $self->_owed) { $errors['total_amount'] = ts('Payment amount cannot be greater than owed amount'); } if ($self->_paymentType == 'refund' && $fields['total_amount'] != abs($self->_refund)) { $errors['total_amount'] = ts('Refund amount must equal refund due amount.'); } - $netAmt = $fields['total_amount'] - $fields['fee_amount']; + $netAmt = $fields['total_amount'] - CRM_Utils_Array::value('fee_amount', $fields, 0); if (!empty($fields['net_amount']) && $netAmt != $fields['net_amount']) { $errors['net_amount'] = ts('Net amount should be equal to the difference between payment amount and fee amount.'); } + if ($self->_paymentProcessor['id'] === 0 && empty($fields['payment_instrument_id'])) { + $errors['payment_instrument_id'] = ts('Payment method is a required field'); + } + return $errors; } + /** + * Process the form submission. + */ public function postProcess() { + $submittedValues = $this->controller->exportValues($this->_name); + $this->submit($submittedValues); + $childTab = 'contribute'; + if ($this->_component == 'event') { + $childTab = 'participant'; + } + $session = CRM_Core_Session::singleton(); + $session->replaceUserContext(CRM_Utils_System::url('civicrm/contact/view', + "reset=1&cid={$this->_contactId}&selectedChild={$childTab}" + )); + } + + /** + * Process Payments. + * @param array $submittedValues + * + */ + public function submit($submittedValues) { + $this->_params = $submittedValues; + $this->beginPostProcess(); + $this->_contributorContactID = $this->_contactID; + $this->processBillingAddress(); $participantId = NULL; if ($this->_component == 'event') { $participantId = $this->_id; } - $submittedValues = $this->controller->exportValues($this->_name); - $submittedValues['confirm_email_text'] = CRM_Utils_Array::value('receipt_text', $submittedValues); + $contributionStatuses = CRM_Core_PseudoConstant::get('CRM_Contribute_DAO_Contribution', + 'contribution_status_id', + ['labelColumn' => 'name'] + ); + $contributionStatusID = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_Contribution', $this->_contributionId, 'contribution_status_id'); + if ($contributionStatuses[$contributionStatusID] == 'Pending') { + civicrm_api3('Contribution', 'create', + [ + 'id' => $this->_contributionId, + 'contribution_status_id' => array_search('Partially paid', $contributionStatuses), + 'is_pay_later' => 0, + ] + ); + } - $submittedValues['trxn_date'] = CRM_Utils_Date::processDate($submittedValues['trxn_date'], $submittedValues['trxn_date_time']); if ($this->_mode) { // process credit card $this->assign('contributeMode', 'direct'); - $this->processCreditCard($submittedValues); - } - else { - $defaults = array(); - $contribution = civicrm_api3('Contribution', 'getsingle', array( - 'return' => array("contribution_status_id"), - 'id' => $this->_contributionId, - )); - $contributionStatusId = CRM_Utils_Array::value('contribution_status_id', $contribution); - $result = CRM_Contribute_BAO_Contribution::recordAdditionalPayment($this->_contributionId, $submittedValues, $this->_paymentType, $participantId); - // Fetch the contribution & do proportional line item assignment - $params = array('id' => $this->_contributionId); - $contribution = CRM_Contribute_BAO_Contribution::retrieve($params, $defaults, $params); - $lineItems = CRM_Price_BAO_LineItem::getLineItemsByContributionID($this->_contributionId); - if (!empty($lineItems)) { - CRM_Contribute_BAO_Contribution::addPayments($lineItems, array($contribution), $contributionStatusId); + $this->processCreditCard(); + } + + $defaults = []; + $contribution = civicrm_api3('Contribution', 'getsingle', [ + 'return' => ["contribution_status_id"], + 'id' => $this->_contributionId, + ]); + $contributionStatusId = CRM_Utils_Array::value('contribution_status_id', $contribution); + $result = CRM_Contribute_BAO_Contribution::recordAdditionalPayment($this->_contributionId, $this->_params, $this->_paymentType, $participantId); + // Fetch the contribution & do proportional line item assignment + $params = ['id' => $this->_contributionId]; + $contribution = CRM_Contribute_BAO_Contribution::retrieve($params, $defaults, $params); + CRM_Contribute_BAO_Contribution::addPayments([$contribution], $contributionStatusId); + if ($this->_contributionId && CRM_Core_Permission::access('CiviMember')) { + $membershipPaymentCount = civicrm_api3('MembershipPayment', 'getCount', ['contribution_id' => $this->_contributionId]); + if ($membershipPaymentCount) { + $this->ajaxResponse['updateTabs']['#tab_member'] = CRM_Contact_BAO_Contact::getCountComponent('membership', $this->_contactID); } - - // email sending - if (!empty($result) && !empty($submittedValues['is_email_receipt'])) { - $submittedValues['contact_id'] = $this->_contactId; - $submittedValues['contribution_id'] = $this->_contributionId; - - // to get 'from email id' for send receipt - $this->fromEmailId = $submittedValues['from_email_address']; - $sendReceipt = $this->emailReceipt($submittedValues); + } + if ($this->_contributionId && CRM_Core_Permission::access('CiviEvent')) { + $participantPaymentCount = civicrm_api3('ParticipantPayment', 'getCount', ['contribution_id' => $this->_contributionId]); + if ($participantPaymentCount) { + $this->ajaxResponse['updateTabs']['#tab_participant'] = CRM_Contact_BAO_Contact::getCountComponent('participant', $this->_contactID); } + } - $statusMsg = ts('The payment record has been processed.'); - if (!empty($submittedValues['is_email_receipt']) && $sendReceipt) { + $statusMsg = ts('The payment record has been processed.'); + // send email + if (!empty($result) && !empty($this->_params['is_email_receipt'])) { + $sendResult = civicrm_api3('Payment', 'sendconfirmation', ['id' => $result->id])['values'][$result->id]; + if ($sendResult['is_sent']) { $statusMsg .= ' ' . ts('A receipt has been emailed to the contributor.'); } - - CRM_Core_Session::setStatus($statusMsg, ts('Saved'), 'success'); - - $session = CRM_Core_Session::singleton(); - $session->replaceUserContext(CRM_Utils_System::url('civicrm/contact/view', - "reset=1&cid={$this->_contactId}&selectedChild=participant" - )); } + + CRM_Core_Session::setStatus($statusMsg, ts('Saved'), 'success'); } - /** - * @param $submittedValues - */ - public function processCreditCard($submittedValues) { + public function processCreditCard() { $config = CRM_Core_Config::singleton(); $session = CRM_Core_Session::singleton(); - $unsetParams = array( - 'trxn_id', - 'payment_instrument_id', - 'contribution_status_id', - ); - foreach ($unsetParams as $key) { - if (isset($submittedValues[$key])) { - unset($submittedValues[$key]); - } - } - - // Get the required fields value only. - $params = $this->_params = $submittedValues; - - //get the payment processor id as per mode. - //@todo unclear relevance of mode - seems like a lot of duplicated params here! - $this->_params['payment_processor'] = $params['payment_processor_id'] - = $this->_params['payment_processor_id'] = $submittedValues['payment_processor_id'] = $this->_paymentProcessor['id']; - $now = date('YmdHis'); - $fields = array(); + $fields = []; // we need to retrieve email address - if ($this->_context == 'standalone' && !empty($submittedValues['is_email_receipt'])) { + if ($this->_context == 'standalone' && !empty($this->_params['is_email_receipt'])) { list($this->userDisplayName, $this->userEmail ) = CRM_Contact_BAO_Contact_Location::getEmailDetails($this->_contactId); $this->assign('displayName', $this->userDisplayName); } - //set email for primary location. - $fields['email-Primary'] = 1; - $params['email-Primary'] = $this->_contributorEmail; - - // now set the values for the billing location. - foreach ($this->_fields as $name => $dontCare) { - $fields[$name] = 1; - } - - // also add location name to the array - $params["address_name-{$this->_bltID}"] = CRM_Utils_Array::value('billing_first_name', $params) . ' ' . CRM_Utils_Array::value('billing_middle_name', $params) . ' ' . CRM_Utils_Array::value('billing_last_name', $params); - $params["address_name-{$this->_bltID}"] = trim($params["address_name-{$this->_bltID}"]); - $fields["address_name-{$this->_bltID}"] = 1; - - $ctype = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', - $this->_contactId, - 'contact_type' - ); - - $nameFields = array('first_name', 'middle_name', 'last_name'); - foreach ($nameFields as $name) { - $fields[$name] = 1; - if (array_key_exists("billing_$name", $params)) { - $params[$name] = $params["billing_{$name}"]; - $params['preserveDBName'] = TRUE; - } - } - - if (!empty($params['source'])) { - unset($params['source']); - } - $contactID = CRM_Contact_BAO_Contact::createProfileContact($params, $fields, - $this->_contactId, - NULL, NULL, - $ctype - ); + $this->formatParamsForPaymentProcessor($this->_params); - // Add all the additional payment params we need. - $this->_params["state_province-{$this->_bltID}"] = $this->_params["billing_state_province-{$this->_bltID}"] = CRM_Core_PseudoConstant::stateProvinceAbbreviation($this->_params["billing_state_province_id-{$this->_bltID}"]); - $this->_params["country-{$this->_bltID}"] = $this->_params["billing_country-{$this->_bltID}"] = CRM_Core_PseudoConstant::countryIsoCode($this->_params["billing_country_id-{$this->_bltID}"]); - - if ($this->_paymentProcessor['payment_type'] & CRM_Core_Payment::PAYMENT_TYPE_CREDIT_CARD) { - $this->_params['year'] = CRM_Core_Payment_Form::getCreditCardExpirationYear($this->_params); - $this->_params['month'] = CRM_Core_Payment_Form::getCreditCardExpirationMonth($this->_params); - } - $this->_params['ip_address'] = CRM_Utils_System::ipAddress(); $this->_params['amount'] = $this->_params['total_amount']; // @todo - stop setting amount level in this function & call the CRM_Price_BAO_PriceSet::getAmountLevel // function to get correct amount level consistently. Remove setting of the amount level in @@ -516,7 +421,11 @@ public function processCreditCard($submittedValues) { ); if (!empty($this->_params['trxn_date'])) { - $this->_params['receive_date'] = CRM_Utils_Date::processDate($this->_params['trxn_date'], $this->_params['trxn_date_time']); + $this->_params['receive_date'] = $this->_params['trxn_date']; + } + + if (empty($this->_params['receive_date'])) { + $this->_params['receive_date'] = date('YmdHis'); } if (empty($this->_params['invoice_id'])) { @@ -526,37 +435,13 @@ public function processCreditCard($submittedValues) { $this->_params['invoiceID'] = $this->_params['invoice_id']; } - // billing name and Address - $name = CRM_Utils_Array::value('billing_first_name', $params); - if (!empty($params['billing_middle_name'])) { - $name .= " {$params['billing_middle_name']}"; - } - $name .= ' ' . CRM_Utils_Array::value('billing_last_name', $params); - $name = trim($name); - $this->assign('billingName', $name); - - //assign the address formatted up for display - $addressParts = array( - "street_address" => "billing_street_address-{$this->_bltID}", - "city" => "billing_city-{$this->_bltID}", - "postal_code" => "billing_postal_code-{$this->_bltID}", - "state_province" => "state_province-{$this->_bltID}", - "country" => "country-{$this->_bltID}", - ); - $addressFields = array(); - foreach ($addressParts as $name => $field) { - $addressFields[$name] = CRM_Utils_Array::value($field, $params); - } - $this->assign('address', CRM_Utils_Address::format($addressFields)); - $date = CRM_Utils_Date::format($params['credit_card_exp_date']); - $date = CRM_Utils_Date::mysqlToIso($date); - $this->assign('credit_card_type', CRM_Utils_Array::value('credit_card_type', $params)); - $this->assign('credit_card_exp_date', $date); - $this->assign('credit_card_number', - CRM_Utils_System::mungeCreditCard($params['credit_card_number']) - ); + $this->assign('address', CRM_Utils_Address::getFormattedBillingAddressFieldsFromParameters( + $this->_params, + $this->_bltID + )); //Add common data to formatted params + $params = $this->_params; CRM_Contribute_Form_AdditionalInfo::postProcessCommon($params, $this->_params, $this); // at this point we've created a contact and stored its address etc // all the payment processors expect the name and address to be in the @@ -565,23 +450,13 @@ public function processCreditCard($submittedValues) { $paymentParams['contactID'] = $this->_contactId; CRM_Core_Payment_Form::mapParams($this->_bltID, $this->_params, $paymentParams, TRUE); - // add some financial type details to the params list - // if folks need to use it - $paymentParams['contributionType_name'] = $this->_params['contributionType_name'] = $contributionType->name; $paymentParams['contributionPageID'] = NULL; if (!empty($this->_params['is_email_receipt'])) { $paymentParams['email'] = $this->_contributorEmail; - $paymentParams['is_email_receipt'] = 1; + $paymentParams['is_email_receipt'] = TRUE; } else { - $paymentParams['is_email_receipt'] = 0; - $this->_params['is_email_receipt'] = 0; - } - if (!empty($this->_params['receive_date'])) { - $paymentParams['receive_date'] = $this->_params['receive_date']; - } - if (!empty($this->_params['receive_date'])) { - $paymentParams['receive_date'] = $this->_params['receive_date']; + $paymentParams['is_email_receipt'] = $this->_params['is_email_receipt'] = FALSE; } $result = NULL; @@ -593,17 +468,13 @@ public function processCreditCard($submittedValues) { $result = $payment->doPayment($paymentParams); } catch (\Civi\Payment\Exception\PaymentProcessorException $e) { - //set the contribution mode. - $urlParams = "action=add&cid={$this->_contactId}&id={$this->_id}&component={$this->_component}"; - if ($this->_mode) { - $urlParams .= "&mode={$this->_mode}"; - } - CRM_Core_Error::displaySessionError($result); - CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/payment/add', $urlParams)); + Civi::log()->error('Payment processor exception: ' . $e->getMessage()); + $urlParams = "action=add&cid={$this->_contactId}&id={$this->_contributionId}&component={$this->_component}&mode={$this->_mode}"; + CRM_Core_Error::statusBounce(CRM_Utils_System::url($e->getMessage(), 'civicrm/payment/add', $urlParams)); } } - if ($result) { + if (!empty($result)) { $this->_params = array_merge($this->_params, $result); } @@ -619,117 +490,58 @@ public function processCreditCard($submittedValues) { $userSortName = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $userID, 'sort_name' ); - $this->_params['source'] = ts('Submit Credit Card Payment by: %1', array(1 => $userSortName)); - } - - // process the additional payment - $participantId = NULL; - if ($this->_component == 'event') { - $participantId = $this->_id; - } - $trxnRecord = CRM_Contribute_BAO_Contribution::recordAdditionalPayment($this->_contributionId, $submittedValues, $this->_paymentType, $participantId); - - if ($trxnRecord->id && !empty($this->_params['is_email_receipt'])) { - $sendReceipt = $this->emailReceipt($this->_params); - } - - if ($trxnRecord->id) { - $statusMsg = ts('The payment record has been processed.'); - if (!empty($this->_params['is_email_receipt']) && $sendReceipt) { - $statusMsg .= ' ' . ts('A receipt has been emailed to the contributor.'); - } - - CRM_Core_Session::setStatus($statusMsg, ts('Complete'), 'success'); - $session->replaceUserContext(CRM_Utils_System::url('civicrm/contact/view', - "reset=1&cid={$this->_contactId}&selectedChild=participant" - )); + $this->_params['source'] = ts('Submit Credit Card Payment by: %1', [1 => $userSortName]); } } /** + * Wrapper for unit testing the post process submit function. + * * @param array $params + * @param string|null $creditCardMode + * @param string $entityType * - * @return mixed + * @throws \CiviCRM_API3_Exception */ - public function emailReceipt(&$params) { - // email receipt sending - // send message template - if ($this->_component == 'event') { - $eventId = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_Participant', $this->_id, 'event_id', 'id'); + public function testSubmit($params, $creditCardMode = NULL, $entityType = 'contribute') { + $this->_bltID = 5; + // Required because processCreditCard calls set method on this. + $_SERVER['REQUEST_METHOD'] = 'GET'; + $this->controller = new CRM_Core_Controller(); - $returnProperties = array('fee_label', 'start_date', 'end_date', 'is_show_location', 'title'); - CRM_Core_DAO::commonRetrieveAll('CRM_Event_DAO_Event', 'id', $eventId, $events, $returnProperties); - $event = $events[$eventId]; - unset($event['start_date']); - unset($event['end_date']); - - $this->assign('event', $event); - $this->assign('isShowLocation', $event['is_show_location']); - if (CRM_Utils_Array::value('is_show_location', $event) == 1) { - $locationParams = array( - 'entity_id' => $eventId, - 'entity_table' => 'civicrm_event', - ); - $location = CRM_Core_BAO_Location::getValues($locationParams, TRUE); - $this->assign('location', $location); - } - } + $this->assignPaymentRelatedVariables(); - // assign payment info here - $paymentConfig['confirm_email_text'] = CRM_Utils_Array::value('confirm_email_text', $params); - $this->assign('paymentConfig', $paymentConfig); - $isRefund = ($this->_paymentType == 'refund') ? TRUE : FALSE; - $this->assign('isRefund', $isRefund); - if ($isRefund) { - $this->assign('totalPaid', $this->_amtPaid); - $this->assign('totalAmount', $this->_amtTotal); - $this->assign('refundAmount', $params['total_amount']); - } - else { - $balance = $this->_amtTotal - ($this->_amtPaid + $params['total_amount']); - $paymentsComplete = ($balance == 0) ? 1 : 0; - $this->assign('amountOwed', $balance); - $this->assign('totalAmount', $this->_amtTotal); - $this->assign('paymentAmount', $params['total_amount']); - $this->assign('paymentsComplete', $paymentsComplete); - } - $this->assign('contactDisplayName', $this->_contributorDisplayName); - - // assign trxn details - $this->assign('trxn_id', CRM_Utils_Array::value('trxn_id', $params)); - $this->assign('receive_date', CRM_Utils_Array::value('trxn_date', $params)); - $paymentInstrument = CRM_Contribute_PseudoConstant::paymentInstrument(); - if (array_key_exists('payment_instrument_id', $params)) { - $this->assign('paidBy', - CRM_Utils_Array::value($params['payment_instrument_id'], - $paymentInstrument - ) - ); - } - $this->assign('checkNumber', CRM_Utils_Array::value('check_number', $params)); + if (!empty($params['contribution_id'])) { + $this->_contributionId = $params['contribution_id']; - $sendTemplateParams = array( - 'groupName' => 'msg_tpl_workflow_contribution', - 'valueName' => 'payment_or_refund_notification', - 'contactId' => $this->_contactId, - 'PDFFilename' => ts('notification') . '.pdf', - ); + $paymentInfo = CRM_Core_BAO_FinancialTrxn::getPartialPaymentWithType($this->_contributionId, $entityType); + $paymentDetails = CRM_Contribute_BAO_Contribution::getPaymentInfo($this->_contributionId, $entityType, FALSE, TRUE); - // try to send emails only if email id is present - // and the do-not-email option is not checked for that contact - if ($this->_contributorEmail && !$this->_toDoNotEmail) { - if (array_key_exists($params['from_email_address'], $this->_fromEmails['from_email_id'])) { - $receiptFrom = $params['from_email_address']; + $this->_amtPaid = $paymentDetails['paid']; + $this->_amtTotal = $paymentDetails['total']; + + if (!empty($paymentInfo['refund_due'])) { + $this->_refund = $paymentInfo['refund_due']; + $this->_paymentType = 'refund'; + } + elseif (!empty($paymentInfo['amount_owed'])) { + $this->_owed = $paymentInfo['amount_owed']; + $this->_paymentType = 'owed'; } + } + + if (!empty($params['contact_id'])) { + $this->_contactId = $params['contact_id']; + } - $sendTemplateParams['from'] = $receiptFrom; - $sendTemplateParams['toName'] = $this->_contributorDisplayName; - $sendTemplateParams['toEmail'] = $this->_contributorEmail; - $sendTemplateParams['cc'] = CRM_Utils_Array::value('cc', $this->_fromEmails); - $sendTemplateParams['bcc'] = CRM_Utils_Array::value('bcc', $this->_fromEmails); + if ($creditCardMode) { + $this->_mode = $creditCardMode; } - list($mailSent, $subject, $message, $html) = CRM_Core_BAO_MessageTemplate::sendTemplate($sendTemplateParams); - return $mailSent; + + $this->_fields = []; + $this->set('cid', $this->_contactId); + parent::preProcess(); + $this->submit($params); } } diff --git a/CRM/Contribute/Form/CancelSubscription.php b/CRM/Contribute/Form/CancelSubscription.php index 817fc58af879..91bbe384604b 100644 --- a/CRM/Contribute/Form/CancelSubscription.php +++ b/CRM/Contribute/Form/CancelSubscription.php @@ -1,9 +1,9 @@ _mid = CRM_Utils_Request::retrieve('mid', 'Integer', $this, FALSE); - - $this->_crid = CRM_Utils_Request::retrieve('crid', 'Integer', $this, FALSE); + parent::preProcess(); if ($this->_crid) { $this->_paymentProcessorObj = CRM_Financial_BAO_PaymentProcessor::getProcessorForEntity($this->_crid, 'recur', 'obj'); $this->_subscriptionDetails = CRM_Contribute_BAO_ContributionRecur::getSubscriptionDetails($this->_crid); @@ -84,7 +76,6 @@ public function preProcess() { $this->assign('membershipType', CRM_Utils_Array::value($membershipTypeId, $membershipTypes)); } - $this->_coid = CRM_Utils_Request::retrieve('coid', 'Integer', $this, FALSE); if ($this->_coid) { if (CRM_Contribute_BAO_Contribution::isSubscriptionCancelled($this->_coid)) { CRM_Core_Error::fatal(ts('The recurring contribution looks to have been cancelled already.')); @@ -100,15 +91,14 @@ public function preProcess() { if ( (!$this->_crid && !$this->_coid && !$this->_mid) || - ($this->_subscriptionDetails == CRM_Core_DAO::$_nullObject) + (!$this->_subscriptionDetails) ) { CRM_Core_Error::fatal('Required information missing.'); } if (!CRM_Core_Permission::check('edit contributions')) { - $userChecksum = CRM_Utils_Request::retrieve('cs', 'String', $this, FALSE); - if (!CRM_Contact_BAO_Contact_Utils::validChecksum($this->_subscriptionDetails->contact_id, $userChecksum)) { - CRM_Core_Error::fatal(ts('You do not have permission to cancel this recurring contribution.')); + if ($this->_subscriptionDetails->contact_id != $this->getContactID()) { + CRM_Core_Error::statusBounce(ts('You do not have permission to cancel this recurring contribution.')); } $this->_selfService = TRUE; } @@ -133,7 +123,7 @@ public function buildQuickForm() { // Determine if we can cancel recurring contribution via API with this processor $cancelSupported = $this->_paymentProcessorObj->supports('CancelRecurring'); if ($cancelSupported) { - $searchRange = array(); + $searchRange = []; $searchRange[] = $this->createElement('radio', NULL, NULL, ts('Yes'), '1'); $searchRange[] = $this->createElement('radio', NULL, NULL, ts('No'), '0'); @@ -141,7 +131,7 @@ public function buildQuickForm() { $searchRange, 'send_cancel_request', ts('Send cancellation request to %1 ?', - array(1 => $this->_paymentProcessorObj->_processorName)) + [1 => $this->_paymentProcessorObj->_processorName]) ); } $this->assign('cancelSupported', $cancelSupported); @@ -161,19 +151,18 @@ public function buildQuickForm() { $type = 'submit'; } - $this->addButtons(array( - array( - 'type' => $type, - 'name' => $cancelButton, - 'spacing' => '         ', - 'isDefault' => TRUE, - ), - array( - 'type' => 'cancel', - 'name' => ts('Not Now'), - ), - ) - ); + $this->addButtons([ + [ + 'type' => $type, + 'name' => $cancelButton, + 'spacing' => '         ', + 'isDefault' => TRUE, + ], + [ + 'type' => 'cancel', + 'name' => ts('Not Now'), + ], + ]); } /** @@ -183,10 +172,10 @@ public function buildQuickForm() { * array of default values */ public function setDefaultValues() { - return array( + return [ 'is_notify' => 1, 'send_cancel_request' => 1, - ); + ]; } /** @@ -209,7 +198,7 @@ public function postProcess() { } if (CRM_Utils_Array::value('send_cancel_request', $params) == 1) { - $cancelParams = array('subscriptionId' => $this->_subscriptionDetails->subscription_id); + $cancelParams = ['subscriptionId' => $this->_subscriptionDetails->subscription_id]; $cancelSubscription = $this->_paymentProcessorObj->cancelSubscription($message, $cancelParams); } @@ -217,28 +206,20 @@ public function postProcess() { CRM_Core_Error::displaySessionError($cancelSubscription); } elseif ($cancelSubscription) { - $activityParams - = array( - 'subject' => $this->_mid ? ts('Auto-renewal membership cancelled') : ts('Recurring contribution cancelled'), - 'details' => $message, - ); $cancelStatus = CRM_Contribute_BAO_ContributionRecur::cancelRecurContribution( - $this->_subscriptionDetails->recur_id, - CRM_Core_DAO::$_nullObject, - $activityParams - ); + ['id' => $this->_subscriptionDetails->recur_id, 'membership_id' => $this->_mid, 'processor_message' => $message]); if ($cancelStatus) { - $tplParams = array(); + $tplParams = []; if ($this->_mid) { - $inputParams = array('id' => $this->_mid); + $inputParams = ['id' => $this->_mid]; CRM_Member_BAO_Membership::getValues($inputParams, $tplParams); $tplParams = $tplParams[$this->_mid]; $tplParams['membership_status'] = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipStatus', $tplParams['status_id']); $tplParams['membershipType'] = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType', $tplParams['membership_type_id']); - $status = ts('The automatic renewal of your %1 membership has been cancelled as requested. This does not affect the status of your membership - you will receive a separate notification when your membership is up for renewal.', array(1 => $tplParams['membershipType'])); + $status = ts('The automatic renewal of your %1 membership has been cancelled as requested. This does not affect the status of your membership - you will receive a separate notification when your membership is up for renewal.', [1 => $tplParams['membershipType']]); $msgTitle = 'Membership Renewal Cancelled'; $msgType = 'info'; } @@ -246,13 +227,13 @@ public function postProcess() { $tplParams['recur_frequency_interval'] = $this->_subscriptionDetails->frequency_interval; $tplParams['recur_frequency_unit'] = $this->_subscriptionDetails->frequency_unit; $tplParams['amount'] = $this->_subscriptionDetails->amount; - $tplParams['contact'] = array('display_name' => $this->_donorDisplayName); + $tplParams['contact'] = ['display_name' => $this->_donorDisplayName]; $status = ts('The recurring contribution of %1, every %2 %3 has been cancelled.', - array( + [ 1 => $this->_subscriptionDetails->amount, 2 => $this->_subscriptionDetails->frequency_interval, 3 => $this->_subscriptionDetails->frequency_unit, - ) + ] ); $msgTitle = 'Contribution Cancelled'; $msgType = 'success'; @@ -265,7 +246,7 @@ public function postProcess() { 'id', $this->_subscriptionDetails->contribution_page_id, $value, - array('title', 'receipt_from_name', 'receipt_from_email') + ['title', 'receipt_from_name', 'receipt_from_email'] ); $receiptFrom = '"' . CRM_Utils_Array::value('receipt_from_name', $value[$this->_subscriptionDetails->contribution_page_id]) . @@ -280,7 +261,7 @@ public function postProcess() { // send notification $sendTemplateParams - = array( + = [ 'groupName' => $this->_mode == 'auto_renew' ? 'msg_tpl_workflow_membership' : 'msg_tpl_workflow_contribution', 'valueName' => $this->_mode == 'auto_renew' ? 'membership_autorenew_cancelled' : 'contribution_recurring_cancelled', 'contactId' => $this->_subscriptionDetails->contact_id, @@ -290,7 +271,7 @@ public function postProcess() { 'from' => $receiptFrom, 'toName' => $this->_donorDisplayName, 'toEmail' => $this->_donorEmail, - ); + ]; list($sent) = CRM_Core_BAO_MessageTemplate::sendTemplate($sendTemplateParams); } } diff --git a/CRM/Contribute/Form/CloseAccPeriod.php b/CRM/Contribute/Form/CloseAccPeriod.php deleted file mode 100644 index 9a0670a1916a..000000000000 --- a/CRM/Contribute/Form/CloseAccPeriod.php +++ /dev/null @@ -1,133 +0,0 @@ -get('closing_date'); - if (empty($period)) { - $prior = CRM_Contribute_BAO_Contribution::checkContributeSettings('prior_financial_period'); - } - else { - $defaults['closing_date'] = $period; - return $defaults; - } - if (!empty($prior)) { - $period = array( - 'M' => date('n', strtotime($prior)), - 'd' => date('j', strtotime($prior)), - ); - if ($period['M'] == 1) { - $period['M'] = 12; - } - else { - $period['M']--; - } - $defaults['closing_date'] = $period; - } - else { - $defaults['closing_date'] = array( - 'M' => date('n', strtotime("-1 month")), - 'd' => date('j'), - ); - } - return $defaults; - } - - /** - * Build the form object. - */ - public function buildQuickForm() { - $this->add('date', 'closing_date', ts('Accounting Period to Close'), CRM_Core_SelectValues::date(NULL, 'M d'), TRUE); - $confirmClose = ts('Are you sure you want to close accounting period?'); - $this->addButtons(array( - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - ), - array( - 'type' => 'upload', - 'name' => ts('Close Accounting Period'), - 'js' => array('onclick' => 'return confirm(\'' . $confirmClose . '\');'), - ), - ) - ); - } - - /** - * Global form rule. - * - * @param array $fields - * The input form values. - * @param array $files - * The uploaded files if any. - * @param $self - * - */ - public static function formRule($fields, $files, $self) { - } - - /** - * Process the form submission. - */ - public function postProcess() { - // store the submitted values in an array - $params = $this->controller->exportValues($this->_name); - // Create activity - $activityType = CRM_Core_OptionGroup::getValue('activity_type', - 'Close Accounting Period', - 'name' - ); - $activityParams = array( - 'source_contact_id' => CRM_Core_Session::singleton()->get('userID'), - 'assignee_contact_id' => CRM_Core_Session::singleton()->get('userID'), - 'activity_type_id' => $activityType, - 'subject' => ts('Close Accounting Period'), - 'status_id' => CRM_Core_OptionGroup::getValue('activity_status', - 'Completed', - 'name' - ), - 'activity_date_time' => date('YmdHis'), - ); - CRM_Activity_BAO_Activity::create($activityParams); - // Set Prior Financial Period - $priorFinPeriod = $params['closing_date']['M'] . '/' . $params['closing_date']['d'] . '/' . date('Y'); - Civi::settings()->set('prior_financial_period', date('m/d/Y', strtotime($priorFinPeriod))); - // Set closing date - Civi::settings()->set('closing_date', $params['closing_date']); - CRM_Core_Session::setStatus(ts("Accounting Period has been closed successfully!"), ts('Success'), 'success'); - } - -} diff --git a/CRM/Contribute/Form/Contribution.php b/CRM/Contribute/Form/Contribution.php index f8f9baf8fdcd..ab46198a0b7a 100644 --- a/CRM/Contribute/Form/Contribution.php +++ b/CRM/Contribute/Form/Contribution.php @@ -1,9 +1,9 @@ _action)) { + // @todo replace with throw new CRM_Core_Exception(). CRM_Core_Error::fatal(ts('You do not have permission to access this page.')); } @@ -231,10 +248,12 @@ public function preProcess() { // Get the contribution id if update $this->_id = CRM_Utils_Request::retrieve('id', 'Positive', $this); if (!empty($this->_id)) { + $this->assignPaymentInfoBlock(); $this->assign('contribID', $this->_id); + $this->assign('isUsePaymentBlock', TRUE); } - $this->_context = CRM_Utils_Request::retrieve('context', 'String', $this); + $this->_context = CRM_Utils_Request::retrieve('context', 'Alphanumeric', $this); $this->assign('context', $this->_context); $this->_compId = CRM_Utils_Request::retrieve('compId', 'Positive', $this); @@ -242,17 +261,14 @@ public function preProcess() { $this->_compContext = CRM_Utils_Request::retrieve('compContext', 'String', $this); //set the contribution mode. - $this->_mode = CRM_Utils_Request::retrieve('mode', 'String', $this); + $this->_mode = CRM_Utils_Request::retrieve('mode', 'Alphanumeric', $this); $this->assign('contributionMode', $this->_mode); if ($this->_action & CRM_Core_Action::DELETE) { return; } - $this->assign('showCheckNumber', TRUE); - $this->_fromEmails = CRM_Core_BAO_Email::getFromEmail(); - $this->assignPaymentRelatedVariables(); if (in_array('CiviPledge', CRM_Core_Config::singleton()->enableComponents) && !$this->_formType) { $this->preProcessPledge(); @@ -261,7 +277,7 @@ public function preProcess() { if ($this->_id) { $this->showRecordLinkMesssage($this->_id); } - $this->_values = array(); + $this->_values = []; // Current contribution id. if ($this->_id) { @@ -274,7 +290,7 @@ public function preProcess() { $this->applyCustomData('Contribution', CRM_Utils_Array::value('financial_type_id', $_POST), $this->_id); } - $this->_lineItems = array(); + $this->_lineItems = []; if ($this->_id) { if (!empty($this->_compId) && $this->_compContext == 'participant') { $this->assign('compId', $this->_compId); @@ -283,10 +299,11 @@ public function preProcess() { else { $lineItem = CRM_Price_BAO_LineItem::getLineItems($this->_id, 'contribution', 1, TRUE, TRUE); } + // wtf? empty($lineItem) ? NULL : $this->_lineItems[] = $lineItem; } - $this->assign('lineItem', empty($this->_lineItems) ? FALSE : $this->_lineItems); + $this->assign('lineItem', empty($lineItem) ? FALSE : [$lineItem]); // Set title if ($this->_mode && $this->_id) { @@ -300,10 +317,6 @@ public function preProcess() { else { $this->setPageTitle($this->_ppID ? ts('Pledge Payment') : ts('Contribution')); } - - if ($this->_id) { - CRM_Contribute_Form_SoftCredit::preprocess($this); - } } /** @@ -312,7 +325,6 @@ public function preProcess() { * @return array */ public function setDefaultValues() { - $defaults = $this->_values; // Set defaults for pledge payment. @@ -358,27 +370,21 @@ public function setDefaultValues() { // Fix the display of the monetary value, CRM-4038. if (isset($defaults['total_amount'])) { + $total_value = $defaults['total_amount']; + $defaults['total_amount'] = CRM_Utils_Money::format($total_value, NULL, '%a'); if (!empty($defaults['tax_amount'])) { $componentDetails = CRM_Contribute_BAO_Contribution::getComponentDetails($this->_id); if (!(CRM_Utils_Array::value('membership', $componentDetails) || CRM_Utils_Array::value('participant', $componentDetails))) { - $defaults['total_amount'] = CRM_Utils_Money::format($defaults['total_amount'] - $defaults['tax_amount'], NULL, '%a'); + $defaults['total_amount'] = CRM_Utils_Money::format($total_value - $defaults['tax_amount'], NULL, '%a'); } } - else { - $defaults['total_amount'] = CRM_Utils_Money::format($defaults['total_amount'], NULL, '%a'); - } - } - - if (isset($defaults['non_deductible_amount'])) { - $defaults['non_deductible_amount'] = CRM_Utils_Money::format($defaults['non_deductible_amount'], NULL, '%a'); - } - - if (isset($defaults['fee_amount'])) { - $defaults['fee_amount'] = CRM_Utils_Money::format($defaults['fee_amount'], NULL, '%a'); } - if (isset($defaults['net_amount'])) { - $defaults['net_amount'] = CRM_Utils_Money::format($defaults['net_amount'], NULL, '%a'); + $amountFields = ['non_deductible_amount', 'fee_amount']; + foreach ($amountFields as $amt) { + if (isset($defaults[$amt])) { + $defaults[$amt] = CRM_Utils_Money::format($defaults[$amt], NULL, '%a'); + } } if ($this->_contributionType) { @@ -386,7 +392,7 @@ public function setDefaultValues() { } if (empty($defaults['payment_instrument_id'])) { - $defaults['payment_instrument_id'] = key(CRM_Core_OptionGroup::values('payment_instrument', FALSE, FALSE, FALSE, 'AND is_default = 1')); + $defaults['payment_instrument_id'] = $this->getDefaultPaymentInstrumentId(); } if (!empty($defaults['is_test'])) { @@ -403,13 +409,13 @@ public function setDefaultValues() { } $options_key = CRM_Utils_Array::key($this->_productDAO->product_option, $options); if ($options_key) { - $defaults['product_name'] = array($this->_productDAO->product_id, trim($options_key)); + $defaults['product_name'] = [$this->_productDAO->product_id, trim($options_key)]; } else { - $defaults['product_name'] = array($this->_productDAO->product_id); + $defaults['product_name'] = [$this->_productDAO->product_id]; } if ($this->_productDAO->fulfilled_date) { - list($defaults['fulfilled_date']) = CRM_Utils_Date::setDateDefaults($this->_productDAO->fulfilled_date); + $defaults['fulfilled_date'] = $this->_productDAO->fulfilled_date; } } @@ -424,35 +430,18 @@ public function setDefaultValues() { if (!empty($defaults['contribution_status_id']) && in_array( CRM_Contribute_PseudoConstant::contributionStatus($defaults['contribution_status_id'], 'name'), // Historically not 'Cancelled' hence not using CRM_Contribute_BAO_Contribution::isContributionStatusNegative. - array('Refunded', 'Chargeback') + ['Refunded', 'Chargeback'] )) { $defaults['refund_trxn_id'] = CRM_Core_BAO_FinancialTrxn::getRefundTransactionTrxnID($this->_id); } else { $defaults['refund_trxn_id'] = isset($defaults['trxn_id']) ? $defaults['trxn_id'] : NULL; } - $dates = array( - 'receive_date', - 'receipt_date', - 'cancel_date', - 'thankyou_date', - ); - foreach ($dates as $key) { - if (!empty($defaults[$key])) { - list($defaults[$key], $defaults[$key . '_time']) - = CRM_Utils_Date::setDateDefaults(CRM_Utils_Array::value($key, $defaults), 'activityDateTime'); - } - } if (!$this->_id && empty($defaults['receive_date'])) { - list($defaults['receive_date'], - $defaults['receive_date_time'] - ) = CRM_Utils_Date::setDateDefaults(NULL, 'activityDateTime'); + $defaults['receive_date'] = date('Y-m-d H:i:s'); } - $this->assign('receive_date', CRM_Utils_Date::processDate(CRM_Utils_Array::value('receive_date', $defaults), - CRM_Utils_Array::value('receive_date_time', $defaults) - )); $currency = CRM_Utils_Array::value('currency', $defaults); $this->assign('currency', $currency); // Hack to get currency info to the js layer. CRM-11440. @@ -473,6 +462,22 @@ public function setDefaultValues() { * Build the form object. */ public function buildQuickForm() { + if ($this->_action & CRM_Core_Action::DELETE) { + $this->addButtons([ + [ + 'type' => 'next', + 'name' => ts('Delete'), + 'spacing' => '         ', + 'isDefault' => TRUE, + ], + [ + 'type' => 'cancel', + 'name' => ts('Cancel'), + ], + ]); + return; + } + // FIXME: This probably needs to be done in preprocess if (CRM_Financial_BAO_FinancialType::isACLFinancialTypeStatus() && $this->_action & CRM_Core_Action::UPDATE @@ -484,18 +489,19 @@ public function buildQuickForm() { CRM_Core_Error::fatal(ts('You do not have permission to access this page.')); } } - $allPanes = array(); - $recurJs = NULL; + $allPanes = []; + //tax rate from financialType $this->assign('taxRates', json_encode(CRM_Core_PseudoConstant::getTaxRates())); $this->assign('currencies', json_encode(CRM_Core_OptionGroup::values('currencies_enabled'))); // build price set form. $buildPriceSet = FALSE; - $invoiceSettings = Civi::settings()->get('contribution_invoice_settings'); - $invoicing = CRM_Utils_Array::value('invoicing', $invoiceSettings); + $invoicing = CRM_Invoicing_Utils::isInvoicingEnabled(); $this->assign('invoicing', $invoicing); + $buildRecurBlock = FALSE; + // display tax amount on edit contribution page if ($invoicing && $this->_action & CRM_Core_Action::UPDATE && isset($this->_values['tax_amount'])) { $this->assign('totalTaxAmount', $this->_values['tax_amount']); @@ -523,14 +529,13 @@ public function buildQuickForm() { $this->assign('buildPriceSet', $buildPriceSet); $defaults = $this->_values; - $additionalDetailFields = array( + $additionalDetailFields = [ 'note', 'thankyou_date', 'invoice_id', 'non_deductible_amount', 'fee_amount', - 'net_amount', - ); + ]; foreach ($additionalDetailFields as $key) { if (!empty($defaults[$key])) { $defaults['hidden_AdditionalDetail'] = 1; @@ -545,12 +550,12 @@ public function buildQuickForm() { } if ($this->_noteID && - isset($this->_values['note']) + !CRM_Utils_System::isNull($this->_values['note']) ) { $defaults['hidden_AdditionalDetail'] = 1; } - $paneNames = array(); + $paneNames = []; if (empty($this->_payNow)) { $paneNames[ts('Additional Details')] = 'AdditionalDetail'; } @@ -563,29 +568,28 @@ public function buildQuickForm() { $paneNames[ts('Premium Information')] = 'Premium'; } - if ($this->_mode) { - if (CRM_Core_Payment_Form::buildPaymentForm($this, $this->_paymentProcessor, FALSE, TRUE) == TRUE) { - if (!empty($this->_recurPaymentProcessors)) { - $buildRecurBlock = TRUE; - if ($this->_ppID) { - // ppID denotes a pledge payment. - foreach ($this->_paymentProcessors as $processor) { - if (!empty($processor['is_recur']) && !empty($processor['object']) && $processor['object']->supports('recurContributionsForPledges')) { - $buildRecurBlock = TRUE; - break; - } - $buildRecurBlock = FALSE; + $this->payment_instrument_id = CRM_Utils_Array::value('payment_instrument_id', $defaults, $this->getDefaultPaymentInstrumentId()); + if (CRM_Core_Payment_Form::buildPaymentForm($this, $this->_paymentProcessor, FALSE, TRUE, $this->payment_instrument_id) == TRUE) { + if (!empty($this->_recurPaymentProcessors)) { + $buildRecurBlock = TRUE; + if ($this->_ppID) { + // ppID denotes a pledge payment. + foreach ($this->_paymentProcessors as $processor) { + if (!empty($processor['is_recur']) && !empty($processor['object']) && $processor['object']->supports('recurContributionsForPledges')) { + $buildRecurBlock = TRUE; + break; } + $buildRecurBlock = FALSE; } - if ($buildRecurBlock) { - CRM_Contribute_Form_Contribution_Main::buildRecur($this); - $this->setDefaults(array('is_recur' => 0)); - $this->assign('buildRecurBlock', TRUE); - $recurJs = array('onChange' => "buildRecurBlock( this.value ); return false;"); - } + } + if ($buildRecurBlock) { + CRM_Contribute_Form_Contribution_Main::buildRecur($this); + $this->setDefaults(['is_recur' => 0]); + $this->assign('buildRecurBlock', TRUE); } } } + $this->addPaymentProcessorSelect(FALSE, $buildRecurBlock); foreach ($paneNames as $name => $type) { $allPanes[$name] = $this->generatePane($type, $defaults); @@ -595,7 +599,7 @@ public function buildQuickForm() { $this->assign('qfKey', $qfKey); $this->assign('allPanes', $allPanes); - $this->addFormRule(array('CRM_Contribute_Form_Contribution', 'formRule'), $this); + $this->addFormRule(['CRM_Contribute_Form_Contribution', 'formRule'], $this); if ($this->_formType) { $this->assign('formType', $this->_formType); @@ -604,33 +608,16 @@ public function buildQuickForm() { $this->applyFilter('__ALL__', 'trim'); - if ($this->_action & CRM_Core_Action::DELETE) { - $this->addButtons(array( - array( - 'type' => 'next', - 'name' => ts('Delete'), - 'spacing' => '         ', - 'isDefault' => TRUE, - ), - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - ), - ) - ); - return; - } - //need to assign custom data type and subtype to the template $this->assign('customDataType', 'Contribution'); $this->assign('customDataSubType', $this->_contributionType); $this->assign('entityID', $this->_id); if ($this->_context == 'standalone') { - $this->addEntityRef('contact_id', ts('Contact'), array( - 'create' => TRUE, - 'api' => array('extra' => array('email')), - ), TRUE); + $this->addEntityRef('contact_id', ts('Contact'), [ + 'create' => TRUE, + 'api' => ['extra' => ['email']], + ], TRUE); } $attributes = CRM_Core_DAO::getAttribute('CRM_Contribute_DAO_Contribution'); @@ -642,113 +629,55 @@ public function buildQuickForm() { } $financialType = $this->add('select', 'financial_type_id', ts('Financial Type'), - array('' => ts('- select -')) + $financialTypes, + ['' => ts('- select -')] + $financialTypes, TRUE, - array('onChange' => "CRM.buildCustomData( 'Contribution', this.value );") + ['onChange' => "CRM.buildCustomData( 'Contribution', this.value );"] ); $paymentInstrument = FALSE; if (!$this->_mode) { + // payment_instrument isn't required in edit and will not be present when payment block is enabled. + $required = $this->_id ? FALSE : TRUE; + $checkPaymentID = array_search('Check', CRM_Contribute_PseudoConstant::paymentInstrument('name')); $paymentInstrument = $this->add('select', 'payment_instrument_id', ts('Payment Method'), - array('' => ts('- select -')) + CRM_Contribute_PseudoConstant::paymentInstrument(), - TRUE, array('onChange' => "return showHideByValue('payment_instrument_id','4','checkNumber','table-row','select',false);") + ['' => ts('- select -')] + CRM_Contribute_PseudoConstant::paymentInstrument(), + $required, ['onChange' => "return showHideByValue('payment_instrument_id','{$checkPaymentID}','checkNumber','table-row','select',false);"] ); } - $trxnId = $this->add('text', 'trxn_id', ts('Transaction ID'), array('class' => 'twelve') + $attributes['trxn_id']); + $trxnId = $this->add('text', 'trxn_id', ts('Transaction ID'), ['class' => 'twelve'] + $attributes['trxn_id']); //add receipt for offline contribution $this->addElement('checkbox', 'is_email_receipt', ts('Send Receipt?')); $this->add('select', 'from_email_address', ts('Receipt From'), $this->_fromEmails); - $status = CRM_Contribute_PseudoConstant::contributionStatus(); - - // suppressing contribution statuses that are NOT relevant to pledges (CRM-5169) - $statusName = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name'); - if ($this->_ppID) { - foreach (array( - 'Cancelled', - 'Failed', - 'In Progress', - ) as $suppress) { - unset($status[CRM_Utils_Array::key($suppress, $statusName)]); - } - } - elseif ((!$this->_ppID && $this->_id) || !$this->_id) { - $suppressFlag = FALSE; - if ($this->_id) { - $componentDetails = CRM_Contribute_BAO_Contribution::getComponentDetails($this->_id); - if (CRM_Utils_Array::value('membership', $componentDetails) || CRM_Utils_Array::value('participant', $componentDetails)) { - $suppressFlag = TRUE; - } - } - if (!$suppressFlag) { - foreach (array( - 'Overdue', - 'In Progress', - ) as $suppress) { - unset($status[CRM_Utils_Array::key($suppress, $statusName)]); - } + $component = 'contribution'; + $componentDetails = []; + if ($this->_id) { + $componentDetails = CRM_Contribute_BAO_Contribution::getComponentDetails($this->_id); + if (CRM_Utils_Array::value('membership', $componentDetails)) { + $component = 'membership'; } - else { - unset($status[CRM_Utils_Array::key('Overdue', $statusName)]); + elseif (CRM_Utils_Array::value('participant', $componentDetails)) { + $component = 'participant'; } } + if ($this->_ppID) { + $component = 'pledge'; + } + $status = CRM_Contribute_BAO_Contribution_Utils::getContributionStatuses($component, $this->_id); // define the status IDs that show the cancellation info, see CRM-17589 - $cancelInfo_show_ids = array(); - foreach (array_keys($statusName) as $status_id) { + $cancelInfo_show_ids = []; + foreach (array_keys($status) as $status_id) { if (CRM_Contribute_BAO_Contribution::isContributionStatusNegative($status_id)) { $cancelInfo_show_ids[] = "'$status_id'"; } } $this->assign('cancelInfo_show_ids', implode(',', $cancelInfo_show_ids)); - if ($this->_id) { - $contributionStatus = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_Contribution', $this->_id, 'contribution_status_id'); - $name = CRM_Utils_Array::value($contributionStatus, $statusName); - switch ($name) { - case 'Completed': - // [CRM-17498] Removing unsupported status change options. - unset($status[CRM_Utils_Array::key('Pending', $statusName)]); - unset($status[CRM_Utils_Array::key('Failed', $statusName)]); - unset($status[CRM_Utils_Array::key('Partially paid', $statusName)]); - unset($status[CRM_Utils_Array::key('Pending refund', $statusName)]); - case 'Cancelled': - case 'Chargeback': - case 'Refunded': - unset($status[CRM_Utils_Array::key('In Progress', $statusName)]); - unset($status[CRM_Utils_Array::key('Pending', $statusName)]); - unset($status[CRM_Utils_Array::key('Failed', $statusName)]); - break; - - case 'Pending': - case 'In Progress': - unset($status[CRM_Utils_Array::key('Refunded', $statusName)]); - unset($status[CRM_Utils_Array::key('Chargeback', $statusName)]); - break; - - case 'Failed': - foreach (array( - 'Pending', - 'Refunded', - 'Chargeback', - 'Completed', - 'In Progress', - 'Cancelled', - ) as $suppress) { - unset($status[CRM_Utils_Array::key($suppress, $statusName)]); - } - break; - } - } - else { - unset($status[CRM_Utils_Array::key('Refunded', $statusName)]); - unset($status[CRM_Utils_Array::key('Chargeback', $statusName)]); - } - $statusElement = $this->add('select', 'contribution_status_id', ts('Contribution Status'), $status, @@ -764,33 +693,22 @@ public function buildQuickForm() { // CRM-16189, add Revenue Recognition Date if (CRM_Contribute_BAO_Contribution::checkContributeSettings('deferred_revenue_enabled')) { - $this->add('date', 'revenue_recognition_date', ts('Revenue Recognition Date'), CRM_Core_SelectValues::date(NULL, 'M Y', NULL, 5)); + $revenueDate = $this->add('date', 'revenue_recognition_date', ts('Revenue Recognition Date'), CRM_Core_SelectValues::date(NULL, 'M Y', NULL, 5)); + if ($this->_id && !CRM_Contribute_BAO_Contribution::allowUpdateRevenueRecognitionDate($this->_id)) { + $revenueDate->freeze(); + } } // add various dates - $this->addDateTime('receive_date', ts('Received'), FALSE, array('formatType' => 'activityDateTime')); + $this->addField('receive_date', ['entity' => 'contribution'], !$this->_mode, FALSE); + $this->addField('receipt_date', ['entity' => 'contribution'], FALSE, FALSE); + $this->addField('cancel_date', ['entity' => 'contribution', 'label' => ts('Cancelled / Refunded Date')], FALSE, FALSE); if ($this->_online) { $this->assign('hideCalender', TRUE); } - $checkNumber = $this->add('text', 'check_number', ts('Check Number'), $attributes['check_number']); - - $this->addDateTime('receipt_date', ts('Receipt Date'), FALSE, array('formatType' => 'activityDateTime')); - $this->addDateTime('cancel_date', ts('Cancelled / Refunded Date'), FALSE, array('formatType' => 'activityDateTime')); $this->add('textarea', 'cancel_reason', ts('Cancellation / Refund Reason'), $attributes['cancel_reason']); - $this->add('text', 'refund_trxn_id', ts('Transaction ID for the refund payment')); - $element = $this->add('select', - 'payment_processor_id', - ts('Payment Processor'), - $this->_processors, - NULL, - $recurJs - ); - - if ($this->_online) { - $element->freeze(); - } $totalAmount = NULL; if (empty($this->_lineItems)) { @@ -803,7 +721,6 @@ public function buildQuickForm() { // don't allow price set for contribution if it is related to participant, or if it is a pledge payment // and if we already have line items for that participant. CRM-5095 if ($buildPriceSet && $this->_id) { - $componentDetails = CRM_Contribute_BAO_Contribution::getComponentDetails($this->_id); $pledgePaymentId = CRM_Core_DAO::getFieldValue('CRM_Pledge_DAO_PledgePayment', $this->_id, 'id', @@ -827,10 +744,10 @@ public function buildQuickForm() { // instead of selecting manually $financialTypeIds = CRM_Price_BAO_PriceSet::getAssoc(FALSE, 'CiviContribute', 'financial_type_id'); $element = $this->add('select', 'price_set_id', ts('Choose price set'), - array( + [ '' => ts('Choose price set'), - ) + $priceSets, - NULL, array('onchange' => "buildAmount( this.value, " . json_encode($financialTypeIds) . ");") + ] + $priceSets, + NULL, ['onchange' => "buildAmount( this.value, " . json_encode($financialTypeIds) . ");"] ); if ($this->_online && !($this->_action & CRM_Core_Action::UPDATE)) { $element->freeze(); @@ -839,18 +756,18 @@ public function buildQuickForm() { $this->assign('hasPriceSets', $hasPriceSets); if (!($this->_action & CRM_Core_Action::UPDATE)) { if ($this->_online || $this->_ppID) { - $attributes['total_amount'] = array_merge($attributes['total_amount'], array( + $attributes['total_amount'] = array_merge($attributes['total_amount'], [ 'READONLY' => TRUE, 'style' => "background-color:#EBECE4", - )); - $optionTypes = array( + ]); + $optionTypes = [ '1' => ts('Adjust Pledge Payment Schedule?'), '2' => ts('Adjust Total Pledge Amount?'), - ); + ]; $this->addRadio('option_type', NULL, $optionTypes, - array(), '
    ' + [], '
    ' ); $currencyFreeze = TRUE; @@ -876,50 +793,49 @@ public function buildQuickForm() { $js = NULL; if (!$this->_mode) { - $js = array('onclick' => "return verify( );"); + $js = ['onclick' => "return verify( );"]; } $mailingInfo = Civi::settings()->get('mailing_backend'); $this->assign('outBound_option', $mailingInfo['outBound_option']); - $this->addButtons(array( - array( - 'type' => 'upload', - 'name' => ts('Save'), - 'js' => $js, - 'isDefault' => TRUE, - ), - array( - 'type' => 'upload', - 'name' => ts('Save and New'), - 'js' => $js, - 'subName' => 'new', - ), - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - ), - ) - ); - - // if status is Cancelled freeze Amount, Payment Instrument, Check #, Financial Type, - // Net and Fee Amounts are frozen in AdditionalInfo::buildAdditionalDetail - if ($this->_id && $this->_values['contribution_status_id'] == array_search('Cancelled', $statusName)) { - if ($totalAmount) { - $totalAmount->freeze(); - } - $checkNumber->freeze(); - $paymentInstrument->freeze(); - $trxnId->freeze(); - $financialType->freeze(); - } + $this->addButtons([ + [ + 'type' => 'upload', + 'name' => ts('Save'), + 'js' => $js, + 'isDefault' => TRUE, + ], + [ + 'type' => 'upload', + 'name' => ts('Save and New'), + 'js' => $js, + 'subName' => 'new', + ], + [ + 'type' => 'cancel', + 'name' => ts('Cancel'), + ], + ]); // if contribution is related to membership or participant freeze Financial Type, Amount - if ($this->_id && isset($this->_values['tax_amount'])) { + if ($this->_id) { $componentDetails = CRM_Contribute_BAO_Contribution::getComponentDetails($this->_id); - if (CRM_Utils_Array::value('membership', $componentDetails) || CRM_Utils_Array::value('participant', $componentDetails)) { + $isCancelledStatus = ($this->_values['contribution_status_id'] == CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Cancelled')); + + if (CRM_Utils_Array::value('membership', $componentDetails) || + CRM_Utils_Array::value('participant', $componentDetails) || + // if status is Cancelled freeze Amount, Payment Instrument, Check #, Financial Type, + // Net and Fee Amounts are frozen in AdditionalInfo::buildAdditionalDetail + $isCancelledStatus + ) { if ($totalAmount) { $totalAmount->freeze(); + $this->getElement('currency')->freeze(); + } + if ($isCancelledStatus) { + $paymentInstrument->freeze(); + $trxnId->freeze(); } $financialType->freeze(); $this->assign('freezeFinancialType', TRUE); @@ -944,7 +860,7 @@ public function buildQuickForm() { * true if no errors, else array of errors */ public static function formRule($fields, $files, $self) { - $errors = array(); + $errors = []; // Check for Credit Card Contribution. if ($self->_mode) { if (empty($fields['payment_processor_id'])) { @@ -965,23 +881,6 @@ public static function formRule($fields, $files, $self) { $softErrors = CRM_Contribute_Form_SoftCredit::formRule($fields, $errors, $self); - if (!empty($fields['total_amount']) && (!empty($fields['net_amount']) || !empty($fields['fee_amount']))) { - $sum = CRM_Utils_Rule::cleanMoney($fields['net_amount']) + CRM_Utils_Rule::cleanMoney($fields['fee_amount']); - // For taxable contribution we need to deduct taxable amount from - // (net amount + fee amount) before comparing it with total amount - if (!empty($self->_values['tax_amount'])) { - $componentDetails = CRM_Contribute_BAO_Contribution::getComponentDetails($self->_id); - if (!(CRM_Utils_Array::value('membership', $componentDetails) || - CRM_Utils_Array::value('participant', $componentDetails)) - ) { - $sum = CRM_Utils_Money::format($sum - $self->_values['tax_amount'], NULL, '%a'); - } - } - if (CRM_Utils_Rule::cleanMoney($fields['total_amount']) != $sum) { - $errors['total_amount'] = ts('The sum of fee amount and net amount must be equal to total amount'); - } - } - //CRM-16285 - Function to handle validation errors on form, for recurring contribution field. CRM_Contribute_BAO_ContributionRecur::validateRecurContribution($fields, $files, $self, $errors); @@ -1006,15 +905,15 @@ public static function formRule($fields, $files, $self) { // $trxn_id must be unique CRM-13919 if (!empty($fields['trxn_id'])) { - $queryParams = array(1 => array($fields['trxn_id'], 'String')); + $queryParams = [1 => [$fields['trxn_id'], 'String']]; $query = 'select count(*) from civicrm_contribution where trxn_id = %1'; if ($self->_id) { - $queryParams[2] = array((int) $self->_id, 'Integer'); + $queryParams[2] = [(int) $self->_id, 'Integer']; $query .= ' and id !=%2'; } $tCnt = CRM_Core_DAO::singleValueQuery($query, $queryParams); if ($tCnt) { - $errors['trxn_id'] = ts('Transaction ID\'s must be unique. Transaction \'%1\' already exists in your database.', array(1 => $fields['trxn_id'])); + $errors['trxn_id'] = ts('Transaction ID\'s must be unique. Transaction \'%1\' already exists in your database.', [1 => $fields['trxn_id']]); } } if (!empty($fields['revenue_recognition_date']) @@ -1092,6 +991,18 @@ public function postProcess() { if (empty($this->_id) && !empty($contribution->id)) { $this->_id = $contribution->id; } + if (!empty($this->_id) && CRM_Core_Permission::access('CiviMember')) { + $membershipPaymentCount = civicrm_api3('MembershipPayment', 'getCount', ['contribution_id' => $this->_id]); + if ($membershipPaymentCount) { + $this->ajaxResponse['updateTabs']['#tab_member'] = CRM_Contact_BAO_Contact::getCountComponent('membership', $this->_contactID); + } + } + if (!empty($this->_id) && CRM_Core_Permission::access('CiviEvent')) { + $participantPaymentCount = civicrm_api3('ParticipantPayment', 'getCount', ['contribution_id' => $this->_id]); + if ($participantPaymentCount) { + $this->ajaxResponse['updateTabs']['#tab_participant'] = CRM_Contact_BAO_Contact::getCountComponent('participant', $this->_contactID); + } + } } /** @@ -1126,7 +1037,7 @@ protected function processCreditCard($submittedValues, $lineItem, $contactID) { $userSortName = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $userID, 'sort_name' ); - $submittedValues['source'] = ts('Submit Credit Card Payment by: %1', array(1 => $userSortName)); + $submittedValues['source'] = ts('Submit Credit Card Payment by: %1', [1 => $userSortName]); } $params = $submittedValues; @@ -1137,14 +1048,6 @@ protected function processCreditCard($submittedValues, $lineItem, $contactID) { $now = date('YmdHis'); - // we need to retrieve email address - if ($this->_context == 'standalone' && !empty($submittedValues['is_email_receipt'])) { - list($this->userDisplayName, - $this->userEmail - ) = CRM_Contact_BAO_Contact_Location::getEmailDetails($contactID); - $this->assign('displayName', $this->userDisplayName); - } - $this->_contributorEmail = $this->userEmail; $this->_contributorContactID = $contactID; $this->processBillingAddress(); @@ -1164,10 +1067,6 @@ protected function processCreditCard($submittedValues, $lineItem, $contactID) { CRM_Core_Config::singleton()->defaultCurrency ); - if (!empty($this->_params['receive_date'])) { - $this->_params['receive_date'] = CRM_Utils_Date::processDate($this->_params['receive_date'], $this->_params['receive_date_time']); - } - $this->_params['pcp_display_in_roll'] = CRM_Utils_Array::value('pcp_display_in_roll', $params); $this->_params['pcp_roll_nickname'] = CRM_Utils_Array::value('pcp_roll_nickname', $params); $this->_params['pcp_personal_note'] = CRM_Utils_Array::value('pcp_personal_note', $params); @@ -1210,16 +1109,9 @@ protected function processCreditCard($submittedValues, $lineItem, $contactID) { $paymentParams['receive_date'] = $this->_params['receive_date']; } - $this->_params['receive_date'] = $now; - if (!empty($this->_params['is_email_receipt'])) { $this->_params['receipt_date'] = $now; } - else { - $this->_params['receipt_date'] = CRM_Utils_Date::processDate($this->_params['receipt_date'], - $params['receipt_date_time'], TRUE - ); - } $this->set('params', $this->_params); @@ -1231,7 +1123,7 @@ protected function processCreditCard($submittedValues, $lineItem, $contactID) { $this->assign('is_deductible', TRUE); $this->set('is_deductible', TRUE); } - $contributionParams = array( + $contributionParams = [ 'id' => CRM_Utils_Array::value('contribution_id', $this->_params), 'contact_id' => $contactID, 'line_item' => $lineItem, @@ -1240,12 +1132,8 @@ protected function processCreditCard($submittedValues, $lineItem, $contactID) { 'contribution_page_id' => CRM_Utils_Array::value('contribution_page_id', $this->_params), 'source' => CRM_Utils_Array::value('source', $paymentParams, CRM_Utils_Array::value('description', $paymentParams)), 'thankyou_date' => CRM_Utils_Array::value('thankyou_date', $this->_params), - ); - - if (empty($paymentParams['is_pay_later'])) { - // @todo look up payment_instrument_id on payment processor table. - $contributionParams['payment_instrument_id'] = 1; - } + ]; + $contributionParams['payment_instrument_id'] = $this->_paymentProcessor['payment_instrument_id']; $contribution = CRM_Contribute_Form_Contribution_Confirm::processFormContribution($this, $this->_params, @@ -1267,7 +1155,7 @@ protected function processCreditCard($submittedValues, $lineItem, $contactID) { // NOTE - I expect this is obsolete. $payment = Civi\Payment\System::singleton()->getByProcessor($this->_paymentProcessor); try { - $statuses = CRM_Contribute_BAO_Contribution::buildOptions('contribution_status_id'); + $completeStatusId = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'); $result = $payment->doPayment($paymentParams, 'contribute'); $this->assign('trxn_id', $result['trxn_id']); $contribution->trxn_id = $result['trxn_id']; @@ -1281,15 +1169,18 @@ protected function processCreditCard($submittedValues, $lineItem, $contactID) { * as historically we have had to guess from the context - ie doDirectPayment * = error or success, unless it is a recurring contribution in which case it is pending. */ - if ($result['payment_status_id'] == array_search('Completed', $statuses)) { + if ($result['payment_status_id'] == $completeStatusId) { try { - civicrm_api3('contribution', 'completetransaction', array( + civicrm_api3('contribution', 'completetransaction', [ 'id' => $contribution->id, 'trxn_id' => $result['trxn_id'], 'payment_processor_id' => $this->_paymentProcessor['id'], 'is_transactional' => FALSE, 'fee_amount' => CRM_Utils_Array::value('fee_amount', $result), - )); + 'card_type_id' => CRM_Utils_Array::value('card_type_id', $paymentParams), + 'pan_truncation' => CRM_Utils_Array::value('pan_truncation', $paymentParams), + 'is_email_receipt' => FALSE, + ]); // This has now been set to 1 in the DB - declare it here also $contribution->contribution_status_id = 1; } @@ -1352,11 +1243,11 @@ protected function generatePane($type, $defaults) { $open = 'true'; } - $pane = array( + $pane = [ 'url' => CRM_Utils_System::url('civicrm/contact/view/contribution', $urlParams), 'open' => $open, 'id' => $type, - ); + ]; // See if we need to include this paneName in the current form. if ($this->_formType == $type || !empty($_POST["hidden_{$type}"]) || @@ -1391,23 +1282,29 @@ protected function generatePane($type, $defaults) { * @throws \CiviCRM_API3_Exception */ public function testSubmit($params, $action, $creditCardMode = NULL) { - $defaults = array( - 'soft_credit_contact_id' => array(), + $defaults = [ + 'soft_credit_contact_id' => [], + 'receive_date' => date('Y-m-d H:i:s'), 'receipt_date' => '', - 'receipt_date_time' => '', 'cancel_date' => '', - 'cancel_date_time' => '', 'hidden_Premium' => 1, - ); + ]; $this->_bltID = 5; if (!empty($params['id'])) { - $existingContribution = civicrm_api3('contribution', 'getsingle', array( + $existingContribution = civicrm_api3('contribution', 'getsingle', [ 'id' => $params['id'], - )); + ]); $this->_id = $params['id']; + $this->_values = $existingContribution; + if (CRM_Contribute_BAO_Contribution::checkContributeSettings('invoicing')) { + $this->_values['tax_amount'] = civicrm_api3('contribution', 'getvalue', [ + 'id' => $params['id'], + 'return' => 'tax_amount', + ]); + } } else { - $existingContribution = array(); + $existingContribution = []; } $this->_defaults['contribution_status_id'] = CRM_Utils_Array::value('contribution_status_id', @@ -1428,8 +1325,8 @@ public function testSubmit($params, $action, $creditCardMode = NULL) { CRM_Contribute_Form_AdditionalInfo::buildPremium($this); - $this->_fields = array(); - $this->submit(array_merge($defaults, $params), $action, CRM_Utils_Array::value('pledge_payment_id', $params)); + $this->_fields = []; + return $this->submit(array_merge($defaults, $params), $action, CRM_Utils_Array::value('pledge_payment_id', $params)); } @@ -1446,10 +1343,11 @@ public function testSubmit($params, $action, $creditCardMode = NULL) { * @throws \Exception */ protected function submit($submittedValues, $action, $pledgePaymentID) { - $softParams = $softIDs = array(); $pId = $contribution = $isRelatedId = FALSE; $this->_params = $submittedValues; $this->beginPostProcess(); + // reassign submitted form values if the any information is formatted via beginPostProcess + $submittedValues = $this->_params; if (!empty($submittedValues['price_set_id']) && $action & CRM_Core_Action::UPDATE) { $line = CRM_Price_BAO_LineItem::getLineItems($this->_id, 'contribution'); @@ -1464,7 +1362,7 @@ protected function submit($submittedValues, $action, $pledgePaymentID) { } // Process price set and get total amount and line items. - $lineItem = array(); + $lineItem = []; $priceSetId = CRM_Utils_Array::value('price_set_id', $submittedValues); if (empty($priceSetId) && !$this->_id) { $this->_priceSetId = $priceSetId = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', 'default_contribution_amount', 'id', 'name'); @@ -1480,7 +1378,7 @@ protected function submit($submittedValues, $action, $pledgePaymentID) { // as a point of fragility rather than a logical 'if' clause. if ($priceSetId) { CRM_Price_BAO_PriceSet::processAmount($this->_priceSet['fields'], - $submittedValues, $lineItem[$priceSetId]); + $submittedValues, $lineItem[$priceSetId], NULL, $priceSetId); // Unset tax amount for offline 'is_quick_config' contribution. // @todo WHY - quick config was conceived as a quick way to configure contribution forms. // this is an example of 'other' functionality being hung off it. @@ -1491,6 +1389,7 @@ protected function submit($submittedValues, $action, $pledgePaymentID) { } $submittedValues['total_amount'] = CRM_Utils_Array::value('amount', $submittedValues); } + if ($this->_id) { if ($this->_compId) { if ($this->_context == 'participant') { @@ -1526,13 +1425,13 @@ protected function submit($submittedValues, $action, $pledgePaymentID) { $entityTable = 'participant'; $entityID = $pId; $isRelatedId = FALSE; - $participantParams = array( + $participantParams = [ 'fee_amount' => $submittedValues['total_amount'], 'id' => $entityID, - ); + ]; CRM_Event_BAO_Participant::add($participantParams); if (empty($this->_lineItems)) { - $this->_lineItems[] = CRM_Price_BAO_LineItem::getLineItems($entityID, 'participant', 1); + $this->_lineItems[] = CRM_Price_BAO_LineItem::getLineItems($entityID, 'participant', TRUE); } } else { @@ -1540,7 +1439,7 @@ protected function submit($submittedValues, $action, $pledgePaymentID) { $entityID = $this->_id; } - $lineItems = CRM_Price_BAO_LineItem::getLineItems($entityID, $entityTable, NULL, TRUE, $isRelatedId); + $lineItems = CRM_Price_BAO_LineItem::getLineItems($entityID, $entityTable, FALSE, TRUE, $isRelatedId); foreach (array_keys($lineItems) as $id) { $lineItems[$id]['id'] = $id; } @@ -1600,13 +1499,16 @@ protected function submit($submittedValues, $action, $pledgePaymentID) { if (!isset($submittedValues['total_amount'])) { $submittedValues['total_amount'] = CRM_Utils_Array::value('total_amount', $this->_values); + // Avoid tax amount deduction on edit form and keep it original, because this will lead to error described in CRM-20676 + if (!$this->_id) { + $submittedValues['total_amount'] -= CRM_Utils_Array::value('tax_amount', $this->_values, 0); + } } $this->assign('lineItem', !empty($lineItem) && !$isQuickConfig ? $lineItem : FALSE); $isEmpty = array_keys(array_flip($submittedValues['soft_credit_contact_id'])); if ($this->_id && count($isEmpty) == 1 && key($isEmpty) == NULL) { - //Delete existing soft credit records if soft credit list is empty on update - CRM_Contribute_BAO_ContributionSoft::del(array('contribution_id' => $this->_id, 'pcp_id' => 0)); + civicrm_api3('ContributionSoft', 'get', ['contribution_id' => $this->_id, 'pcp_id' => NULL, 'api.ContributionSoft.delete' => 1]); } // set the contact, when contact is selected @@ -1618,13 +1520,13 @@ protected function submit($submittedValues, $action, $pledgePaymentID) { // Credit Card Contribution. if ($this->_mode) { - $paramsSetByPaymentProcessingSubsystem = array( + $paramsSetByPaymentProcessingSubsystem = [ 'trxn_id', 'payment_instrument_id', 'contribution_status_id', 'cancel_date', 'cancel_reason', - ); + ]; foreach ($paramsSetByPaymentProcessingSubsystem as $key) { if (isset($formValues[$key])) { unset($formValues[$key]); @@ -1641,31 +1543,31 @@ protected function submit($submittedValues, $action, $pledgePaymentID) { // get the required field value only. - $params = $ids = array(); - - $params['contact_id'] = $this->_contactID; - $params['currency'] = $this->getCurrency($submittedValues); + $params = [ + 'contact_id' => $this->_contactID, + 'currency' => $this->getCurrency($submittedValues), + 'skipCleanMoney' => TRUE, + 'id' => $this->_id, + ]; //format soft-credit/pcp param first CRM_Contribute_BAO_ContributionSoft::formatSoftCreditParams($submittedValues, $this); $params = array_merge($params, $submittedValues); - $fields = array( + $fields = [ 'financial_type_id', 'contribution_status_id', 'payment_instrument_id', 'cancel_reason', 'source', 'check_number', - ); + 'card_type_id', + 'pan_truncation', + ]; foreach ($fields as $f) { $params[$f] = CRM_Utils_Array::value($f, $formValues); } - // CRM-5740 if priceset is used, no need to cleanup money. - if ($priceSetId) { - $params['skipCleanMoney'] = 1; - } $params['revenue_recognition_date'] = NULL; if (!empty($formValues['revenue_recognition_date']) && count(array_filter($formValues['revenue_recognition_date'])) == 2 @@ -1674,17 +1576,6 @@ protected function submit($submittedValues, $action, $pledgePaymentID) { '01-' . implode('-', $formValues['revenue_recognition_date']) ); } - $dates = array( - 'receive_date', - 'receipt_date', - 'cancel_date', - ); - - foreach ($dates as $d) { - if (isset($formValues[$d])) { - $params[$d] = CRM_Utils_Date::processDate($formValues[$d], CRM_Utils_Array::value($d . '_time', $formValues), TRUE); - } - } if (!empty($formValues['is_email_receipt'])) { $params['receipt_date'] = date("Y-m-d"); @@ -1702,15 +1593,13 @@ protected function submit($submittedValues, $action, $pledgePaymentID) { // Set is_pay_later flag for back-office offline Pending status contributions CRM-8996 // else if contribution_status is changed to Completed is_pay_later flag is changed to 0, CRM-15041 - if ($params['contribution_status_id'] == CRM_Core_OptionGroup::getValue('contribution_status', 'Pending', 'name')) { + if ($params['contribution_status_id'] == CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Pending')) { $params['is_pay_later'] = 1; } - elseif ($params['contribution_status_id'] == CRM_Core_OptionGroup::getValue('contribution_status', 'Completed', 'name')) { + elseif ($params['contribution_status_id'] == CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed')) { $params['is_pay_later'] = 0; } - $ids['contribution'] = $params['id'] = $this->_id; - // Add Additional common information to formatted params. CRM_Contribute_Form_AdditionalInfo::postProcessCommon($formValues, $params, $this); if ($pId) { @@ -1723,9 +1612,7 @@ protected function submit($submittedValues, $action, $pledgePaymentID) { } $params['line_item'] = $lineItem; $params['payment_processor_id'] = $params['payment_processor'] = CRM_Utils_Array::value('id', $this->_paymentProcessor); - if (isset($submittedValues['tax_amount'])) { - $params['tax_amount'] = $submittedValues['tax_amount']; - } + $params['tax_amount'] = CRM_Utils_Array::value('tax_amount', $submittedValues, CRM_Utils_Array::value('tax_amount', $this->_values)); //create contribution. if ($isQuickConfig) { $params['is_quick_config'] = 1; @@ -1736,7 +1623,7 @@ protected function submit($submittedValues, $action, $pledgePaymentID) { if (!empty($params['note']) && !empty($submittedValues['note'])) { unset($params['note']); } - $contribution = CRM_Contribute_BAO_Contribution::create($params, $ids); + $contribution = CRM_Contribute_BAO_Contribution::create($params); // process associated membership / participant, CRM-4395 if ($contribution->id && $action & CRM_Core_Action::UPDATE) { @@ -1761,7 +1648,7 @@ protected function submit($submittedValues, $action, $pledgePaymentID) { $formValues += CRM_Contribute_BAO_ContributionSoft::getSoftContribution($contribution->id); // to get 'from email id' for send receipt - $this->fromEmailId = $formValues['from_email_address']; + $this->fromEmailId = CRM_Utils_Array::value('from_email_address', $formValues); if (CRM_Contribute_Form_AdditionalInfo::emailReceipt($this, $formValues)) { $this->statusMessage[] = ts('A receipt has been emailed to the contributor.'); } @@ -1777,7 +1664,7 @@ protected function submit($submittedValues, $action, $pledgePaymentID) { ); } - if ($contribution->id && !empty($submittedValues['note'])) { + if ($contribution->id && array_key_exists('note', $submittedValues)) { CRM_Contribute_Form_AdditionalInfo::processNote($submittedValues, $this->_contactID, $contribution->id, $this->_noteID); } @@ -1809,15 +1696,10 @@ protected function invoicingPostProcessHook($submittedValues, $action, $lineItem if (!CRM_Utils_Array::value('invoicing', $invoiceSettings)) { return; } - $taxRate = array(); + $taxRate = []; $getTaxDetails = FALSE; - if ($action & CRM_Core_Action::ADD) { - $line = $lineItem; - } - elseif ($action & CRM_Core_Action::UPDATE) { - $line = $this->_lineItems; - } - foreach ($line as $key => $value) { + + foreach ($lineItem as $key => $value) { foreach ($value as $v) { if (isset($taxRate[(string) CRM_Utils_Array::value('tax_rate', $v)])) { $taxRate[(string) $v['tax_rate']] = $taxRate[(string) $v['tax_rate']] + CRM_Utils_Array::value('tax_amount', $v); @@ -1872,6 +1754,15 @@ protected function calculateNonDeductibleAmount($params, $formValues) { return $params['non_deductible_amount']; } + $priceSetId = CRM_Utils_Array::value('price_set_id', $params); + // return non-deductible amount if it is set at the price field option level + if ($priceSetId && !empty($params['line_item'])) { + $nonDeductibleAmount = CRM_Price_BAO_PriceSet::getNonDeductibleAmountFromPriceSet($priceSetId, $params['line_item']); + if (!empty($nonDeductibleAmount)) { + return $nonDeductibleAmount; + } + } + $financialType = new CRM_Financial_DAO_FinancialType(); $financialType->id = $params['financial_type_id']; $financialType->find(TRUE); diff --git a/CRM/Contribute/Form/Contribution/Confirm.php b/CRM/Contribute/Form/Contribution/Confirm.php index 67daa503cb8d..9add6a4ec12f 100644 --- a/CRM/Contribute/Form/Contribution/Confirm.php +++ b/CRM/Contribute/Form/Contribution/Confirm.php @@ -1,9 +1,9 @@ $dontCare) { + $scheduledAmount = CRM_Core_DAO::getFieldValue( + 'CRM_Pledge_DAO_PledgePayment', + $paymentId, + 'scheduled_amount', + 'id' + ); + + $pledgePayment = ($amount >= $scheduledAmount) ? $scheduledAmount : $amount; + if ($pledgePayment > 0) { + $pledgePaymentParams[] = [ + 'id' => $paymentId, + 'contribution_id' => $contribution->id, + 'status_id' => $contribution->contribution_status_id, + 'actual_amount' => $pledgePayment, + ]; + $amount -= $pledgePayment; + } + } + if ($amount > 0 && count($pledgePaymentParams)) { + $pledgePaymentParams[count($pledgePaymentParams) - 1]['actual_amount'] += $amount; + } + foreach ($pledgePaymentParams as $p) { + CRM_Pledge_BAO_PledgePayment::add($p); + } + + //update pledge status according to the new payment statuses + CRM_Pledge_BAO_PledgePayment::updatePledgePaymentStatus($pledgeID); + return $form; + } + else { + //when user creating pledge record. + $pledgeParams = []; + $pledgeParams['contact_id'] = $contribution->contact_id; + $pledgeParams['installment_amount'] = $pledgeParams['actual_amount'] = $contribution->total_amount; + $pledgeParams['contribution_id'] = $contribution->id; + $pledgeParams['contribution_page_id'] = $contribution->contribution_page_id; + $pledgeParams['financial_type_id'] = $contribution->financial_type_id; + $pledgeParams['frequency_interval'] = $params['pledge_frequency_interval']; + $pledgeParams['installments'] = $params['pledge_installments']; + $pledgeParams['frequency_unit'] = $params['pledge_frequency_unit']; + if ($pledgeParams['frequency_unit'] == 'month') { + $pledgeParams['frequency_day'] = intval(date("d")); + } + else { + $pledgeParams['frequency_day'] = 1; + } + $pledgeParams['create_date'] = $pledgeParams['start_date'] = $pledgeParams['scheduled_date'] = date("Ymd"); + if (CRM_Utils_Array::value('start_date', $params)) { + $pledgeParams['frequency_day'] = intval(date("d", strtotime(CRM_Utils_Array::value('start_date', $params)))); + $pledgeParams['start_date'] = $pledgeParams['scheduled_date'] = date('Ymd', strtotime(CRM_Utils_Array::value('start_date', $params))); + } + $pledgeParams['status_id'] = $contribution->contribution_status_id; + $pledgeParams['max_reminders'] = $form->_values['max_reminders']; + $pledgeParams['initial_reminder_day'] = $form->_values['initial_reminder_day']; + $pledgeParams['additional_reminder_day'] = $form->_values['additional_reminder_day']; + $pledgeParams['is_test'] = $contribution->is_test; + $pledgeParams['acknowledge_date'] = date('Ymd'); + $pledgeParams['original_installment_amount'] = $pledgeParams['installment_amount']; + + //inherit campaign from contirb page. + $pledgeParams['campaign_id'] = CRM_Utils_Array::value('campaign_id', $contributionParams); + + $pledge = CRM_Pledge_BAO_Pledge::create($pledgeParams); + + $form->_params['pledge_id'] = $pledge->id; + + //send acknowledgment email. only when pledge is created + if ($pledge->id && $isEmailReceipt) { + //build params to send acknowledgment. + $pledgeParams['id'] = $pledge->id; + $pledgeParams['receipt_from_name'] = $form->_values['receipt_from_name']; + $pledgeParams['receipt_from_email'] = $form->_values['receipt_from_email']; + + //scheduled amount will be same as installment_amount. + $pledgeParams['scheduled_amount'] = $pledgeParams['installment_amount']; + + //get total pledge amount. + $pledgeParams['total_pledge_amount'] = $pledge->amount; + + CRM_Pledge_BAO_Pledge::sendAcknowledgment($form, $pledgeParams); + return $form; + } + return $form; + } + } + /** * Set the parameters to be passed to contribution create function. * * @param array $params * @param int $financialTypeID - * @param float $nonDeductibleAmount - * @param bool $pending * @param array $paymentProcessorOutcome * @param string $receiptDate * @param int $recurringContributionID @@ -65,13 +166,11 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr * @return array */ public static function getContributionParams( - $params, $financialTypeID, $nonDeductibleAmount, $pending, + $params, $financialTypeID, $paymentProcessorOutcome, $receiptDate, $recurringContributionID) { - $contributionParams = array( + $contributionParams = [ 'financial_type_id' => $financialTypeID, 'receive_date' => (CRM_Utils_Array::value('receive_date', $params)) ? CRM_Utils_Date::processDate($params['receive_date']) : date('YmdHis'), - 'non_deductible_amount' => $nonDeductibleAmount, - 'total_amount' => $params['amount'], 'tax_amount' => CRM_Utils_Array::value('tax_amount', $params), 'amount_level' => CRM_Utils_Array::value('amount_level', $params), 'invoice_id' => $params['invoiceID'], @@ -84,31 +183,22 @@ public static function getContributionParams( '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), - ); + ]; if ($paymentProcessorOutcome) { $contributionParams['payment_processor'] = CRM_Utils_Array::value('payment_processor', $paymentProcessorOutcome); } - if (!$pending && $paymentProcessorOutcome) { - $contributionParams += array( - 'fee_amount' => CRM_Utils_Array::value('fee_amount', $paymentProcessorOutcome), - 'net_amount' => CRM_Utils_Array::value('net_amount', $paymentProcessorOutcome, $params['amount']), - 'trxn_id' => $paymentProcessorOutcome['trxn_id'], + if (!empty($params["is_email_receipt"])) { + $contributionParams += [ 'receipt_date' => $receiptDate, - // also add financial_trxn details as part of fix for CRM-4724 - 'trxn_result_code' => CRM_Utils_Array::value('trxn_result_code', $paymentProcessorOutcome), - ); + ]; } - // CRM-4038: for non-en_US locales, CRM_Contribute_BAO_Contribution::add() expects localised amounts - $contributionParams['non_deductible_amount'] = trim(CRM_Utils_Money::format($contributionParams['non_deductible_amount'], ' ')); - $contributionParams['total_amount'] = trim(CRM_Utils_Money::format($contributionParams['total_amount'], ' ')); - if ($recurringContributionID) { $contributionParams['contribution_recur_id'] = $recurringContributionID; } - $contributionParams['contribution_status_id'] = $pending ? 2 : 1; + $contributionParams['contribution_status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Pending'); if (isset($contributionParams['invoice_id'])) { $contributionParams['id'] = CRM_Core_DAO::getFieldValue( 'CRM_Contribute_DAO_Contribution', @@ -133,13 +223,23 @@ public static function getContributionParams( * @param array $params * @param CRM_Financial_BAO_FinancialType $financialType * @param bool $online + * @param CRM_Contribute_Form_Contribution_Confirm $form * * @return array */ - protected static function getNonDeductibleAmount($params, $financialType, $online) { + protected static function getNonDeductibleAmount($params, $financialType, $online, $form) { if (isset($params['non_deductible_amount']) && (!empty($params['non_deductible_amount']))) { return $params['non_deductible_amount']; } + $priceSetId = CRM_Utils_Array::value('priceSetId', $params); + // return non-deductible amount if it is set at the price field option level + if ($priceSetId && !empty($form->_lineItem)) { + $nonDeductibleAmount = CRM_Price_BAO_PriceSet::getNonDeductibleAmountFromPriceSet($priceSetId, $form->_lineItem); + } + + if (!empty($nonDeductibleAmount)) { + return $nonDeductibleAmount; + } else { if ($financialType->is_deductible) { if ($online && isset($params['selectProduct'])) { @@ -187,7 +287,7 @@ public function preProcess() { // lineItem isn't set until Register postProcess $this->_lineItem = $this->get('lineItem'); $this->_ccid = $this->get('ccid'); - $this->_paymentProcessor = $this->get('paymentProcessor'); + $this->_params = $this->controller->exportValues('Main'); $this->_params['ip_address'] = CRM_Utils_System::ipAddress(); $this->_params['amount'] = $this->get('amount'); @@ -220,13 +320,15 @@ public function preProcess() { $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', $this->_values['pay_later_receipt']); + $this->assign('pay_later_receipt', CRM_Utils_Array::value('pay_later_receipt', $this->_values)); } // if onbehalf-of-organization if (!empty($this->_values['onbehalf_profile_id']) && !empty($this->_params['onbehalf']['organization_name'])) { - $this->_params['organization_id'] = CRM_Utils_Array::value('onbehalfof_id', $this->_params); + if (empty($this->_params['org_option']) && empty($this->_params['organization_id'])) { + $this->_params['organization_id'] = CRM_Utils_Array::value('onbehalfof_id', $this->_params); + } $this->_params['organization_name'] = $this->_params['onbehalf']['organization_name']; - $addressBlocks = array( + $addressBlocks = [ 'street_address', 'city', 'state_province', @@ -239,9 +341,9 @@ public function preProcess() { 'geo_code_1', 'geo_code_2', 'address_name', - ); + ]; - $blocks = array('email', 'phone', 'im', 'url', 'openid'); + $blocks = ['email', 'phone', 'im', 'url', 'openid']; foreach ($this->_params['onbehalf'] as $loc => $value) { $field = $typeId = NULL; if (strstr($loc, '-')) { @@ -286,7 +388,7 @@ public function preProcess() { $locationValue = $locType; } $locTypeId = ''; - $phoneExtField = array(); + $phoneExtField = []; if ($field == 'url') { $blockName = 'website'; @@ -305,25 +407,25 @@ public function preProcess() { //check if extension field exists $extField = str_replace('phone', 'phone_ext', $loc); if (isset($this->_params['onbehalf'][$extField])) { - $phoneExtField = array('phone_ext' => $this->_params['onbehalf'][$extField]); + $phoneExtField = ['phone_ext' => $this->_params['onbehalf'][$extField]]; } } $isPrimary = 1; - if (isset ($this->_params['onbehalf_location'][$blockName]) + if (isset($this->_params['onbehalf_location'][$blockName]) && count($this->_params['onbehalf_location'][$blockName]) > 0 ) { $isPrimary = 0; } if ($locationValue) { - $blockValues = array( + $blockValues = [ $fieldName => $value, $locationType => $locationValue, 'is_primary' => $isPrimary, - ); + ]; if ($locTypeId) { - $blockValues = array_merge($blockValues, array($locTypeId => $typeId)); + $blockValues = array_merge($blockValues, [$locTypeId => $typeId]); } if (!empty($phoneExtField)) { $blockValues = array_merge($blockValues, $phoneExtField); @@ -352,11 +454,11 @@ public function preProcess() { elseif (!empty($this->_values['is_for_organization'])) { // no on behalf of an organization, CRM-5519 // so reset loc blocks from main params. - foreach (array( - 'phone', - 'email', - 'address', - ) as $blk) { + foreach ([ + 'phone', + 'email', + 'address', + ] as $blk) { if (isset($this->_params[$blk])) { unset($this->_params[$blk]); } @@ -386,6 +488,7 @@ public function preProcess() { * Build the form object. */ public function buildQuickForm() { + // FIXME: Some of this code is identical to Thankyou.php and should be broken out into a shared function $this->assignToTemplate(); $params = $this->_params; @@ -397,7 +500,7 @@ public function buildQuickForm() { $this->assign('soft_credit_type', $softCreditTypes[$params['soft_credit_type_id']]); CRM_Contribute_BAO_ContributionSoft::formatHonoreeProfileFields($this, $params['honor']); - $fieldTypes = array('Contact'); + $fieldTypes = ['Contact']; $fieldTypes[] = CRM_Core_BAO_UFGroup::getContactType($this->_values['honoree_profile_id']); $this->buildCustom($this->_values['honoree_profile_id'], 'honoreeProfileFields', TRUE, 'honor', $fieldTypes); } @@ -405,24 +508,27 @@ public function buildQuickForm() { $amount_block_is_active = $this->get('amount_block_is_active'); $this->assign('amount_block_is_active', $amount_block_is_active); - $invoiceSettings = Civi::settings()->get('contribution_invoice_settings'); - $invoicing = CRM_Utils_Array::value('invoicing', $invoiceSettings); - if ($invoicing) { - $getTaxDetails = FALSE; - $taxTerm = CRM_Utils_Array::value('tax_term', $invoiceSettings); - foreach ($this->_lineItem as $key => $value) { - foreach ($value as $v) { - if (isset($v['tax_rate'])) { - if ($v['tax_rate'] != '') { - $getTaxDetails = TRUE; - } - } - } - } - $this->assign('getTaxDetails', $getTaxDetails); - $this->assign('taxTerm', $taxTerm); + // Make a copy of line items array to use for display only + $tplLineItems = $this->_lineItem; + if (CRM_Invoicing_Utils::isInvoicingEnabled()) { + // @todo $params seems like exactly the wrong place to get totalTaxAmount from + // this is a calculated variable so we it should be transparent how we + // calculated it rather than coming from 'params' $this->assign('totalTaxAmount', $params['tax_amount']); } + $this->assignLineItemsToTemplate($tplLineItems); + + $isDisplayLineItems = $this->_priceSetId && !CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', $this->_priceSetId, 'is_quick_config'); + + $this->assign('isDisplayLineItems', $isDisplayLineItems); + + if (!$isDisplayLineItems) { + // quickConfig is deprecated in favour of isDisplayLineItems. Lots of logic has been harnessed to quick config + // whereas isDisplayLineItems is specific & clear. + $this->assign('is_quick_config', 1); + $this->_params['is_quick_config'] = 1; + } + if (!empty($params['selectProduct']) && $params['selectProduct'] != 'no_thanks') { $option = CRM_Utils_Array::value('options_' . $params['selectProduct'], $params); $productID = $params['selectProduct']; @@ -462,14 +568,14 @@ public function buildQuickForm() { !empty($params['is_for_organization']) ) && empty($this->_ccid) ) { - $fieldTypes = array('Contact', 'Organization'); + $fieldTypes = ['Contact', 'Organization']; $contactSubType = CRM_Contact_BAO_ContactType::subTypes('Organization'); $fieldTypes = array_merge($fieldTypes, $contactSubType); if (is_array($this->_membershipBlock) && !empty($this->_membershipBlock)) { - $fieldTypes = array_merge($fieldTypes, array('Membership')); + $fieldTypes = array_merge($fieldTypes, ['Membership']); } else { - $fieldTypes = array_merge($fieldTypes, array('Contribution')); + $fieldTypes = array_merge($fieldTypes, ['Contribution']); } $this->buildCustom($this->_values['onbehalf_profile_id'], 'onbehalfProfile', TRUE, 'onbehalf', $fieldTypes); @@ -477,49 +583,47 @@ public function buildQuickForm() { $this->_separateMembershipPayment = $this->get('separateMembershipPayment'); $this->assign('is_separate_payment', $this->_separateMembershipPayment); - if ($this->_priceSetId && !CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', $this->_priceSetId, 'is_quick_config')) { - $this->assign('lineItem', $this->_lineItem); - } - else { - $this->assign('is_quick_config', 1); - $this->_params['is_quick_config'] = 1; - } + $this->assign('priceSetID', $this->_priceSetId); // 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 ($this->_contributeMode == 'notify' || - $this->_amount < 0.0 || $this->_params['is_pay_later'] || - ($this->_separateMembershipPayment && $this->_amount <= 0.0) + $this->_amount <= 0.0 || $this->_params['is_pay_later'] ) { $contribButton = ts('Continue'); - $this->assign('button', ts('Continue')); } elseif (!empty($this->_ccid)) { $contribButton = ts('Make Payment'); - $this->assign('button', ts('Make Payment')); } else { $contribButton = ts('Make Contribution'); - $this->assign('button', ts('Make Contribution')); - } - $this->addButtons(array( - array( - 'type' => 'next', - 'name' => $contribButton, - 'spacing' => '         ', - 'isDefault' => TRUE, - 'js' => array('onclick' => "return submitOnce(this,'" . $this->_name . "','" . ts('Processing') . "');"), - ), - array( - 'type' => 'back', - 'name' => ts('Go Back'), - ), - ) + } + $this->assign('button', $contribButton); + + $this->assign('continueText', + $this->getPaymentProcessorObject()->getText('contributionPageContinueText', [ + 'is_payment_to_existing' => !empty($this->_ccid), + 'amount' => $this->_amount, + ]) ); - $defaults = array(); + $this->addButtons([ + [ + 'type' => 'next', + 'name' => $contribButton, + 'spacing' => '         ', + 'isDefault' => TRUE, + 'js' => ['onclick' => "return submitOnce(this,'" . $this->_name . "','" . ts('Processing') . "');"], + ], + [ + 'type' => 'back', + 'name' => ts('Go Back'), + ], + ]); + + $defaults = []; $fields = array_fill_keys(array_keys($this->_fields), 1); $fields["billing_state_province-{$this->_bltID}"] = $fields["billing_country-{$this->_bltID}"] = $fields["email-{$this->_bltID}"] = 1; @@ -528,10 +632,10 @@ public function buildQuickForm() { // Recursively set defaults for nested fields if (isset($contact[$name]) && is_array($contact[$name]) && ($name == 'onbehalf' || $name == 'honor')) { foreach ($contact[$name] as $fieldName => $fieldValue) { - if (is_array($fieldValue) && !in_array($this->_fields[$name][$fieldName]['html_type'], array( - 'Multi-Select', - 'AdvMulti-Select', - )) + if (is_array($fieldValue) && !in_array($this->_fields[$name][$fieldName]['html_type'], [ + 'Multi-Select', + 'AdvMulti-Select', + ]) ) { foreach ($fieldValue as $key => $value) { $defaults["{$name}[{$fieldName}][{$key}]"] = $value; @@ -553,11 +657,11 @@ public function buildQuickForm() { $defaults["{$name}_id"] = $contact["{$name}_id"]; } } - elseif (in_array($name, array( - 'addressee', - 'email_greeting', - 'postal_greeting', - )) && !empty($contact[$name . '_custom']) + elseif (in_array($name, [ + 'addressee', + 'email_greeting', + 'postal_greeting', + ]) && !empty($contact[$name . '_custom']) ) { $defaults[$name . '_custom'] = $contact[$name . '_custom']; } @@ -601,14 +705,15 @@ public function setDefaultValues() { */ public function postProcess() { $contactID = $this->getContactID(); - $result = $this->processFormSubmission($contactID); + try { + $result = $this->processFormSubmission($contactID); + } + catch (CRM_Core_Exception $e) { + $this->bounceOnError($e->getMessage()); + } + if (is_array($result) && !empty($result['is_payment_failure'])) { - // We will probably have the function that gets this error throw an exception on the next round of refactoring. - CRM_Core_Session::singleton()->setStatus(ts("Payment Processor Error message :") . - $result['error']->getMessage()); - CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contribute/transact', - "_qf_Main_display=true&qfKey={$this->_params['qfKey']}" - )); + $this->bounceOnError($result['error']->getMessage()); } // Presumably this is for hooks to access? Not quite clear & perhaps not required. $this->set('params', $this->_params); @@ -622,21 +727,18 @@ public function postProcess() { * * Comments from previous refactor indicate doubt as to what was going on. * - * @param int $contributionTypeId + * @param int $financialTypeID * * @return null|string */ - public function wrangleFinancialTypeID($contributionTypeId) { - if (isset($paymentParams['financial_type'])) { - $contributionTypeId = $paymentParams['financial_type']; - } - elseif (!empty($this->_values['pledge_id'])) { - $contributionTypeId = CRM_Core_DAO::getFieldValue('CRM_Pledge_DAO_Pledge', + public function wrangleFinancialTypeID($financialTypeID) { + if (empty($financialTypeID) && !empty($this->_values['pledge_id'])) { + $financialTypeID = CRM_Core_DAO::getFieldValue('CRM_Pledge_DAO_Pledge', $this->_values['pledge_id'], 'financial_type_id' ); } - return $contributionTypeId; + return $financialTypeID; } /** @@ -718,14 +820,14 @@ protected function postProcessPremium($premiumParams, $contribution) { $this->assign('contact_email', $dao->premiums_contact_email); //create Premium record - $params = array( + $params = [ 'product_id' => $premiumParams['selectProduct'], 'contribution_id' => $contribution->id, 'product_option' => CRM_Utils_Array::value('options_' . $premiumParams['selectProduct'], $premiumParams), 'quantity' => 1, 'start_date' => CRM_Utils_Date::customFormat($startDate, '%Y%m%d'), 'end_date' => CRM_Utils_Date::customFormat($endDate, '%Y%m%d'), - ); + ]; if (!empty($premiumParams['selectProduct'])) { $daoPremiumsProduct = new CRM_Contribute_DAO_PremiumsProduct(); $daoPremiumsProduct->product_id = $premiumParams['selectProduct']; @@ -742,12 +844,12 @@ protected function postProcessPremium($premiumParams, $contribution) { CRM_Contribute_BAO_Contribution::addPremium($params); if ($productDAO->cost && !empty($params['financial_type_id'])) { - $trxnParams = array( + $trxnParams = [ 'cost' => $productDAO->cost, 'currency' => $productDAO->currency, 'financial_type_id' => $params['financial_type_id'], 'contributionId' => $contribution->id, - ); + ]; CRM_Core_BAO_FinancialTrxn::createPremiumTrxn($trxnParams); } } @@ -831,8 +933,8 @@ public static function processFormContribution( $params['is_email_receipt'] = $isEmailReceipt; } $params['is_recur'] = $isRecur; + $params['payment_instrument_id'] = CRM_Utils_Array::value('payment_instrument_id', $contributionParams); $recurringContributionID = self::processRecurringContribution($form, $params, $contactID, $financialType); - $nonDeductibleAmount = self::getNonDeductibleAmount($params, $financialType, $online); $now = date('YmdHis'); $receiptDate = CRM_Utils_Array::value('receipt_date', $params); @@ -842,16 +944,22 @@ public static function processFormContribution( if (isset($params['amount'])) { $contributionParams = array_merge(self::getContributionParams( - $params, $financialType->id, $nonDeductibleAmount, TRUE, + $params, $financialType->id, $result, $receiptDate, $recurringContributionID), $contributionParams ); + $contributionParams['non_deductible_amount'] = self::getNonDeductibleAmount($params, $financialType, $online, $form); + $contributionParams['skipCleanMoney'] = TRUE; + // @todo this is the wrong place for this - it should be done as close to form submission + // as possible + $contributionParams['total_amount'] = $params['amount']; + $contribution = CRM_Contribute_BAO_Contribution::add($contributionParams); $invoiceSettings = Civi::settings()->get('contribution_invoice_settings'); $invoicing = CRM_Utils_Array::value('invoicing', $invoiceSettings); if ($invoicing) { - $dataArray = array(); + $dataArray = []; // @todo - interrogate the line items passed in on the params array. // No reason to assume line items will be set on the form. foreach ($form->_lineItem as $lineItemKey => $lineItemValue) { @@ -870,10 +978,6 @@ public static function processFormContribution( $smarty->assign('dataArray', $dataArray); $smarty->assign('totalTaxAmount', $params['tax_amount']); } - if (is_a($contribution, 'CRM_Core_Error')) { - $message = CRM_Core_Error::getMessages($contribution); - CRM_Core_Error::fatal($message); - } // lets store it in the form variable so postProcess hook can get to this and use it $form->_contributionID = $contribution->id; @@ -885,96 +989,8 @@ public static function processFormContribution( //CRM-13981, processing honor contact into soft-credit contribution CRM_Contribute_BAO_ContributionSoft::processSoftContribution($params, $contribution); - //handle pledge stuff. if ($isPledge) { - if ($pledgeID) { - //when user doing pledge payments. - //update the schedule when payment(s) are made - $amount = $params['amount']; - $pledgePaymentParams = array(); - foreach ($params['pledge_amount'] as $paymentId => $dontCare) { - $scheduledAmount = CRM_Core_DAO::getFieldValue( - 'CRM_Pledge_DAO_PledgePayment', - $paymentId, - 'scheduled_amount', - 'id' - ); - - $pledgePayment = ($amount >= $scheduledAmount) ? $scheduledAmount : $amount; - if ($pledgePayment > 0) { - $pledgePaymentParams[] = array( - 'id' => $paymentId, - 'contribution_id' => $contribution->id, - 'status_id' => $contribution->contribution_status_id, - 'actual_amount' => $pledgePayment, - ); - $amount -= $pledgePayment; - } - } - if ($amount > 0 && count($pledgePaymentParams)) { - $pledgePaymentParams[count($pledgePaymentParams) - 1]['actual_amount'] += $amount; - } - foreach ($pledgePaymentParams as $p) { - CRM_Pledge_BAO_PledgePayment::add($p); - } - - //update pledge status according to the new payment statuses - CRM_Pledge_BAO_PledgePayment::updatePledgePaymentStatus($pledgeID); - } - else { - //when user creating pledge record. - $pledgeParams = array(); - $pledgeParams['contact_id'] = $contribution->contact_id; - $pledgeParams['installment_amount'] = $pledgeParams['actual_amount'] = $contribution->total_amount; - $pledgeParams['contribution_id'] = $contribution->id; - $pledgeParams['contribution_page_id'] = $contribution->contribution_page_id; - $pledgeParams['financial_type_id'] = $contribution->financial_type_id; - $pledgeParams['frequency_interval'] = $params['pledge_frequency_interval']; - $pledgeParams['installments'] = $params['pledge_installments']; - $pledgeParams['frequency_unit'] = $params['pledge_frequency_unit']; - if ($pledgeParams['frequency_unit'] == 'month') { - $pledgeParams['frequency_day'] = intval(date("d")); - } - else { - $pledgeParams['frequency_day'] = 1; - } - $pledgeParams['create_date'] = $pledgeParams['start_date'] = $pledgeParams['scheduled_date'] = date("Ymd"); - $pledgeBlock = CRM_Pledge_BAO_PledgeBlock::getPledgeBlock($contribution->contribution_page_id); - if (CRM_Utils_Array::value('start_date', $params) || !CRM_Utils_Array::value('is_pledge_start_date_visible', $pledgeBlock)) { - $pledgeStartDate = CRM_Utils_Array::value('start_date', $params, NULL); - $pledgeParams['start_date'] = $pledgeParams['scheduled_date'] = CRM_Pledge_BAO_Pledge::getPledgeStartDate($pledgeStartDate, $pledgeBlock); - } - $pledgeParams['status_id'] = $contribution->contribution_status_id; - $pledgeParams['max_reminders'] = $form->_values['max_reminders']; - $pledgeParams['initial_reminder_day'] = $form->_values['initial_reminder_day']; - $pledgeParams['additional_reminder_day'] = $form->_values['additional_reminder_day']; - $pledgeParams['is_test'] = $contribution->is_test; - $pledgeParams['acknowledge_date'] = date('Ymd'); - $pledgeParams['original_installment_amount'] = $pledgeParams['installment_amount']; - - //inherit campaign from contirb page. - $pledgeParams['campaign_id'] = CRM_Utils_Array::value('campaign_id', $contributionParams); - - $pledge = CRM_Pledge_BAO_Pledge::create($pledgeParams); - - $form->_params['pledge_id'] = $pledge->id; - - //send acknowledgment email. only when pledge is created - if ($pledge->id && $isEmailReceipt) { - //build params to send acknowledgment. - $pledgeParams['id'] = $pledge->id; - $pledgeParams['receipt_from_name'] = $form->_values['receipt_from_name']; - $pledgeParams['receipt_from_email'] = $form->_values['receipt_from_email']; - - //scheduled amount will be same as installment_amount. - $pledgeParams['scheduled_amount'] = $pledgeParams['installment_amount']; - - //get total pledge amount. - $pledgeParams['total_pledge_amount'] = $pledge->amount; - - CRM_Pledge_BAO_Pledge::sendAcknowledgment($form, $pledgeParams); - } - } + $form = self::handlePledge($form, $params, $contributionParams, $pledgeID, $contribution, $isEmailReceipt); } if ($online && $contribution) { @@ -988,23 +1004,22 @@ public static function processFormContribution( //handle custom data. $params['contribution_id'] = $contribution->id; if (!empty($params['custom']) && - is_array($params['custom']) && - !is_a($contribution, 'CRM_Core_Error') + is_array($params['custom']) ) { CRM_Core_BAO_CustomValueTable::store($params['custom'], 'civicrm_contribution', $contribution->id); } } // Save note if ($contribution && !empty($params['contribution_note'])) { - $noteParams = array( + $noteParams = [ 'entity_table' => 'civicrm_contribution', 'note' => $params['contribution_note'], 'entity_id' => $contribution->id, 'contact_id' => $contribution->contact_id, 'modified_date' => date('Ymd'), - ); + ]; - CRM_Core_BAO_Note::add($noteParams, array()); + CRM_Core_BAO_Note::add($noteParams, []); } if (isset($params['related_contact'])) { @@ -1053,7 +1068,7 @@ public static function processRecurringContribution(&$form, &$params, $contactID return NULL; } - $recurParams = array('contact_id' => $contactID); + $recurParams = ['contact_id' => $contactID]; $recurParams['amount'] = CRM_Utils_Array::value('amount', $params); $recurParams['auto_renew'] = CRM_Utils_Array::value('auto_renew', $params); $recurParams['frequency_unit'] = CRM_Utils_Array::value('frequency_unit', $params); @@ -1061,6 +1076,7 @@ public static function processRecurringContribution(&$form, &$params, $contactID $recurParams['installments'] = CRM_Utils_Array::value('installments', $params); $recurParams['financial_type_id'] = CRM_Utils_Array::value('financial_type_id', $params); $recurParams['currency'] = CRM_Utils_Array::value('currency', $params); + $recurParams['payment_instrument_id'] = $params['payment_instrument_id']; // CRM-14354: For an auto-renewing membership with an additional contribution, // if separate payments is not enabled, make sure only the membership fee recurs @@ -1084,7 +1100,7 @@ public static function processRecurringContribution(&$form, &$params, $contactID $recurParams['start_date'] = $recurParams['create_date'] = $recurParams['modified_date'] = date('YmdHis'); if (!empty($params['receive_date'])) { - $recurParams['start_date'] = $params['receive_date']; + $recurParams['start_date'] = date('YmdHis', strtotime($params['receive_date'])); } $recurParams['invoice_id'] = CRM_Utils_Array::value('invoiceID', $params); $recurParams['contribution_status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Pending'); @@ -1095,13 +1111,8 @@ public static function processRecurringContribution(&$form, &$params, $contactID $recurParams['trxn_id'] = CRM_Utils_Array::value('trxn_id', $params, $params['invoiceID']); $recurParams['financial_type_id'] = $contributionType->id; - if (!empty($form->_values['is_monetary'])) { - $recurParams['payment_instrument_id'] = 1; - } - $campaignId = CRM_Utils_Array::value('campaign_id', $params, CRM_Utils_Array::value('campaign_id', $form->_values)); $recurParams['campaign_id'] = $campaignId; - $recurring = CRM_Contribute_BAO_ContributionRecur::add($recurParams); if (is_a($recurring, 'CRM_Core_Error')) { CRM_Core_Error::displaySessionError($recurring); @@ -1144,7 +1155,7 @@ public static function processRecurringContribution(&$form, &$params, $contactID */ public static function processOnBehalfOrganization(&$behalfOrganization, &$contactID, &$values, &$params, $fields = NULL) { $isNotCurrentEmployer = FALSE; - $dupeIDs = array(); + $dupeIDs = []; $orgID = NULL; if (!empty($behalfOrganization['organization_id'])) { $orgID = $behalfOrganization['organization_id']; @@ -1168,9 +1179,7 @@ public static function processOnBehalfOrganization(&$behalfOrganization, &$conta if (!$orgID) { // check if matching organization contact exists - $dedupeParams = CRM_Dedupe_Finder::formatParams($behalfOrganization, 'Organization'); - $dedupeParams['check_permission'] = FALSE; - $dupeIDs = CRM_Dedupe_Finder::dupesByParams($dedupeParams, 'Organization', 'Unsupervised'); + $dupeIDs = CRM_Contact_BAO_Contact::getDuplicateContacts($behalfOrganization, 'Organization', 'Unsupervised', [], FALSE); // CRM-6243 says to pick the first org even if more than one match if (count($dupeIDs) >= 1) { @@ -1198,7 +1207,7 @@ public static function processOnBehalfOrganization(&$behalfOrganization, &$conta // create relationship if ($isNotCurrentEmployer) { $relParams['contact_check'][$orgID] = 1; - $cid = array('contact' => $contactID); + $cid = ['contact' => $contactID]; CRM_Contact_BAO_Relationship::legacyCreateMultiple($relParams, $cid); } @@ -1251,14 +1260,15 @@ public static function processOnBehalfOrganization(&$behalfOrganization, &$conta * Contribution object. */ public static function pcpNotifyOwner($contribution, $contributionSoft) { - $params = array('id' => $contributionSoft->pcp_id); + $params = ['id' => $contributionSoft->pcp_id]; CRM_Core_DAO::commonRetrieve('CRM_PCP_DAO_PCP', $params, $pcpInfo); $ownerNotifyID = CRM_Core_DAO::getFieldValue('CRM_PCP_DAO_PCPBlock', $pcpInfo['pcp_block_id'], 'owner_notify_id'); + $ownerNotifyOption = CRM_Core_PseudoConstant::getName('CRM_PCP_DAO_PCPBlock', 'owner_notify_id', $ownerNotifyID); - if ($ownerNotifyID != CRM_Core_OptionGroup::getValue('pcp_owner_notify', 'no_notifications', 'name') && - (($ownerNotifyID == CRM_Core_OptionGroup::getValue('pcp_owner_notify', 'owner_chooses', 'name') && + if ($ownerNotifyOption != 'no_notifications' && + (($ownerNotifyOption == 'owner_chooses' && CRM_Core_DAO::getFieldValue('CRM_PCP_DAO_PCP', $contributionSoft->pcp_id, 'is_notify')) || - $ownerNotifyID == CRM_Core_OptionGroup::getValue('pcp_owner_notify', 'all_owners', 'name'))) { + $ownerNotifyOption == 'all_owners')) { $pcpInfoURL = CRM_Utils_System::url('civicrm/pcp/info', "reset=1&id={$contributionSoft->pcp_id}", TRUE, NULL, FALSE, TRUE @@ -1274,7 +1284,7 @@ public static function pcpNotifyOwner($contribution, $contributionSoft) { list($donorName, $email) = CRM_Contact_BAO_Contact_Location::getEmailDetails($contribution->contact_id); } list($ownerName, $ownerEmail) = CRM_Contact_BAO_Contact_Location::getEmailDetails($contributionSoft->contact_id); - $tplParams = array( + $tplParams = [ 'page_title' => $pcpInfo['title'], 'receive_date' => $contribution->receive_date, 'total_amount' => $contributionSoft->amount, @@ -1283,9 +1293,9 @@ public static function pcpNotifyOwner($contribution, $contributionSoft) { 'pcpInfoURL' => $pcpInfoURL, 'is_honor_roll_enabled' => $contributionSoft->pcp_display_in_roll, 'currency' => $contributionSoft->currency, - ); + ]; $domainValues = CRM_Core_BAO_Domain::getNameAndEmail(); - $sendTemplateParams = array( + $sendTemplateParams = [ 'groupName' => 'msg_tpl_workflow_contribution', 'valueName' => 'pcp_owner_notify', 'contactId' => $contributionSoft->contact_id, @@ -1294,7 +1304,7 @@ public static function pcpNotifyOwner($contribution, $contributionSoft) { 'from' => "$domainValues[0] <$domainValues[1]>", 'tplParams' => $tplParams, 'PDFFilename' => 'receipt.pdf', - ); + ]; CRM_Core_BAO_MessageTemplate::sendTemplate($sendTemplateParams); } } @@ -1320,12 +1330,12 @@ public static function processPcp(&$page, $params) { else { $params['pcp_is_anonymous'] = 0; } - foreach (array( - 'pcp_display_in_roll', - 'pcp_is_anonymous', - 'pcp_roll_nickname', - 'pcp_personal_note', - ) as $val) { + foreach ([ + 'pcp_display_in_roll', + 'pcp_is_anonymous', + 'pcp_roll_nickname', + 'pcp_personal_note', + ] as $val) { if (!empty($params[$val])) { $page->assign($val, $params[$val]); } @@ -1344,14 +1354,13 @@ public static function processPcp(&$page, $params) { * @param array $premiumParams * @param array $membershipLineItems * Line items specifically relating to memberships. - * @param bool $isPayLater */ protected function processMembership($membershipParams, $contactID, $customFieldsFormatted, $fieldTypes, $premiumParams, - $membershipLineItems, $isPayLater) { + $membershipLineItems) { $membershipTypeIDs = (array) $membershipParams['selectMembership']; $membershipTypes = CRM_Member_BAO_Membership::buildMembershipTypeValues($this, $membershipTypeIDs); - $membershipType = empty($membershipTypes) ? array() : reset($membershipTypes); + $membershipType = empty($membershipTypes) ? [] : reset($membershipTypes); $isPending = $this->getIsPending(); $this->assign('membership_name', CRM_Utils_Array::value('name', $membershipType)); @@ -1380,7 +1389,7 @@ protected function processMembership($membershipParams, $contactID, $customField $this->postProcessMembership($membershipParams, $contactID, $this, $premiumParams, $customFieldsFormatted, $fieldTypes, $membershipType, $membershipTypeIDs, $isPaidMembership, $this->_membershipId, $isProcessSeparateMembershipTransaction, $financialTypeID, - $membershipLineItems, $isPayLater, $isPending); + $membershipLineItems, $isPending); $this->assign('membership_assign', TRUE); $this->set('membershipTypeID', $membershipParams['selectMembership']); @@ -1410,9 +1419,8 @@ protected function processMembership($membershipParams, $contactID, $customField * @param bool $isProcessSeparateMembershipTransaction * * @param int $financialTypeID - * @param array $membershipLineItems - * Line items specific to membership payment that is separate to contribution. - * @param bool $isPayLater + * @param array $unprocessedLineItems + * Line items for payment options chosen on the form. * @param bool $isPending * * @throws \CRM_Core_Exception @@ -1420,12 +1428,14 @@ protected function processMembership($membershipParams, $contactID, $customField protected function postProcessMembership( $membershipParams, $contactID, &$form, $premiumParams, $customFieldsFormatted = NULL, $includeFieldTypes = NULL, $membershipDetails, $membershipTypeIDs, $isPaidMembership, $membershipID, - $isProcessSeparateMembershipTransaction, $financialTypeID, $membershipLineItems, $isPayLater, $isPending) { + $isProcessSeparateMembershipTransaction, $financialTypeID, $unprocessedLineItems, $isPending) { + $membershipContribution = NULL; $isTest = CRM_Utils_Array::value('is_test', $membershipParams, FALSE); - $errors = $paymentResults = array(); + $errors = $paymentResults = []; $form->_values['isMembership'] = TRUE; - $isRecurForFirstTransaction = CRM_Utils_Array::value('is_recur', $form->_values, CRM_Utils_Array::value('is_recur', $membershipParams)); + $isRecurForFirstTransaction = CRM_Utils_Array::value('is_recur', $form->_params, CRM_Utils_Array::value('is_recur', $membershipParams)); + $totalAmount = $membershipParams['amount']; if ($isPaidMembership) { @@ -1437,15 +1447,27 @@ protected function postProcessMembership( } } - $paymentResult = CRM_Contribute_BAO_Contribution_Utils::processConfirm($form, $membershipParams, + if (!$isProcessSeparateMembershipTransaction) { + // Skip line items in the contribution processing transaction. + // We will create them with the membership for proper linking. + $membershipParams['skipLineItem'] = 1; + } + else { + $membershipParams['total_amount'] = $totalAmount; + $membershipParams['skipLineItem'] = 0; + CRM_Price_BAO_LineItem::getLineItemArray($membershipParams); + + } + $paymentResult = CRM_Contribute_BAO_Contribution_Utils::processConfirm( + $form, + $membershipParams, $contactID, $financialTypeID, - 'membership', $isTest, $isRecurForFirstTransaction ); - if (!empty($paymentResult['contribution'])) { + $paymentResults[] = ['contribution_id' => $paymentResult['contribution']->id, 'result' => $paymentResult]; $this->postProcessPremium($premiumParams, $paymentResult['contribution']); //note that this will be over-written if we are using a separate membership transaction. Otherwise there is only one $membershipContribution = $paymentResult['contribution']; @@ -1457,13 +1479,14 @@ protected function postProcessMembership( if ($isProcessSeparateMembershipTransaction) { try { - $form->_lineItem = $membershipLineItems; + $form->_lineItem = $unprocessedLineItems; if (empty($form->_params['auto_renew']) && !empty($membershipParams['is_recur'])) { unset($membershipParams['is_recur']); } - list($membershipContribution, $secondPaymentResult) = $this->processSecondaryFinancialTransaction($contactID, $form, $membershipParams, - $isTest, $membershipLineItems, CRM_Utils_Array::value('minimum_fee', $membershipDetails, 0), CRM_Utils_Array::value('financial_type_id', $membershipDetails)); - $paymentResults[] = array('contribution_id' => $membershipContribution->id, 'result' => $secondPaymentResult); + 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)); + $paymentResults[] = ['contribution_id' => $membershipContribution->id, 'result' => $secondPaymentResult]; + $totalAmount = $membershipContribution->total_amount; } catch (CRM_Core_Exception $e) { $errors[2] = $e->getMessage(); @@ -1482,11 +1505,32 @@ protected function postProcessMembership( } //@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, array()); + $typesTerms = CRM_Utils_Array::value('types_terms', $membershipParams, []); + + $membershipLines = $nonMembershipLines = []; + foreach ($unprocessedLineItems as $priceSetID => $lines) { + foreach ($lines as $line) { + if (!empty($line['membership_type_id'])) { + $membershipLines[$line['membership_type_id']] = $line['price_field_value_id']; + } + } + } + + $i = 1; + $form->_params['createdMembershipIDs'] = []; foreach ($membershipTypeIDs as $memType) { + $membershipLineItems = []; + if ($i < count($membershipTypeIDs)) { + $membershipLineItems[$priceSetID][$membershipLines[$memType]] = $unprocessedLineItems[$priceSetID][$membershipLines[$memType]]; + unset($unprocessedLineItems[$priceSetID][$membershipLines[$memType]]); + } + else { + $membershipLineItems = $unprocessedLineItems; + } + $i++; $numTerms = CRM_Utils_Array::value($memType, $typesTerms, 1); if (!empty($membershipContribution)) { - $pendingStatus = CRM_Core_OptionGroup::getValue('contribution_status', 'Pending', 'name'); + $pendingStatus = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Pending'); $pending = ($membershipContribution->contribution_status_id == $pendingStatus) ? TRUE : FALSE; } else { @@ -1513,12 +1557,13 @@ protected function postProcessMembership( } } - list($membership, $renewalMode, $dates) = CRM_Member_BAO_Membership::renewMembership( + list($membership, $renewalMode, $dates) = CRM_Member_BAO_Membership::processMembership( $contactID, $memType, $isTest, date('YmdHis'), CRM_Utils_Array::value('cms_contactID', $membershipParams), $customFieldsFormatted, $numTerms, $membershipID, $pending, - $contributionRecurID, $membershipSource, $isPayLater, $campaignId + $contributionRecurID, $membershipSource, $isPayLater, $campaignId, [], $membershipContribution, + $membershipLineItems ); $form->set('renewal_mode', $renewalMode); @@ -1530,8 +1575,18 @@ protected function postProcessMembership( if (!empty($membershipContribution)) { // update recurring id for membership record CRM_Member_BAO_Membership::updateRecurMembership($membership, $membershipContribution); + // Next line is probably redundant. Checksprevent it happening twice. CRM_Member_BAO_Membership::linkMembershipPayment($membership, $membershipContribution); } + if ($membership) { + CRM_Core_BAO_CustomValueTable::postProcess($form->_params, 'civicrm_membership', $membership->id, 'Membership'); + $form->_params['createdMembershipIDs'][] = $membership->id; + $form->_params['membershipID'] = $membership->id; + + //CRM-15232: Check if membership is created and on the basis of it use + //membership receipt template to send payment receipt + $form->_values['isMembership'] = TRUE; + } } if ($form->_priceSetId && !empty($form->_useForMember) && !empty($form->_lineItem)) { foreach ($form->_lineItem[$form->_priceSetId] as & $priceFieldOp) { @@ -1553,23 +1608,7 @@ protected function postProcessMembership( $message = $this->compileErrorMessage($errors); throw new CRM_Core_Exception($message); } - $form->_params['createdMembershipIDs'] = array(); - // CRM-7851 - Moved after processing Payment Errors - //@todo - the reasoning for this being here seems a little outdated - CRM_Core_BAO_CustomValueTable::postProcess($form->_params, 'civicrm_membership', $membership->id, 'Membership'); - $form->_params['createdMembershipIDs'][] = $membership->id; - - if ($membership) { - //presumably this is only relevant for exactly 1 membership - $form->_params['membershipID'] = $membership->id; - } - - //CRM-15232: Check if membership is created and on the basis of it use - //membership receipt template to send payment receipt - if ($membership) { - $form->_values['isMembership'] = TRUE; - } if (isset($membershipContributionID)) { $form->_values['contribution_id'] = $membershipContributionID; } @@ -1586,9 +1625,17 @@ protected function postProcessMembership( // The contribution_other_id is effectively the ID for the only contribution or the non-membership contribution. // Since we have called the membership contribution (in a 2 contribution scenario) this is out // primary-contribution compared to that - but let's face it - it's all just too hard & confusing at the moment! - $paymentParams = array_merge($form->_params, array('contributionID' => $form->_values['contribution_other_id'])); - $paymentActionResult = $payment->doPayment($paymentParams, 'contribute'); - $paymentResults[] = array('contribution_id' => $paymentResult['contribution']->id, 'result' => $paymentActionResult); + $paymentParams = array_merge($form->_params, ['contributionID' => $form->_values['contribution_other_id']]); + + // CRM-19792 : set necessary fields for payment processor + CRM_Core_Payment_Form::mapParams($form->_bltID, $paymentParams, $paymentParams, TRUE); + + // If this is a single membership-related contribution, it won't have + // be performed yet, so do it now. + if ($isPaidMembership && !$isProcessSeparateMembershipTransaction) { + $paymentActionResult = $payment->doPayment($paymentParams, 'contribute'); + $paymentResults[] = ['contribution_id' => $paymentResult['contribution']->id, 'result' => $paymentActionResult]; + } // Do not send an email if Recurring transaction is done via Direct Mode // Email will we sent when the IPN is received. foreach ($paymentResults as $result) { @@ -1602,6 +1649,8 @@ protected function postProcessMembership( $emailValues = array_merge($membershipParams, $form->_values); $emailValues['membership_assign'] = 1; + $emailValues['useForMember'] = !empty($form->_useForMember); + // Finally send an email receipt for pay-later scenario (although it might sometimes be caught above!) if ($totalAmount == 0) { // This feels like a bizarre hack as the variable name doesn't seem to be directly connected to it's use in the template. @@ -1612,13 +1661,16 @@ protected function postProcessMembership( // also it reset any payment processor selection result into pending free membership // so its a kind of hack to complete free membership at this point since there is no $form->_paymentProcessor info if (!empty($membershipContribution) && !is_a($membershipContribution, 'CRM_Core_Error')) { - $paymentProcessorIDs = explode(CRM_Core_DAO::VALUE_SEPARATOR, CRM_Utils_Array::value('payment_processor', $this->_values)); - if (empty($form->_paymentProcessor) && !empty($paymentProcessorIDs)) { + 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)); $this->_paymentProcessor['id'] = $paymentProcessorIDs[0]; } - $result = array('payment_status_id' => 1, 'contribution' => $membershipContribution); + $result = ['payment_status_id' => 1, 'contribution' => $membershipContribution]; $this->completeTransaction($result, $result['contribution']->id); } + // return as completeTransaction() already sends the receipt mail. + return; } CRM_Contribute_BAO_ContributionPage::sendMail($contactID, @@ -1690,7 +1742,7 @@ protected function processSecondaryFinancialTransaction($contactID, &$form, $tem //so for differentiating membership contribution from //main contribution. $form->_params['separate_membership_payment'] = 1; - $contributionParams = array( + $contributionParams = [ 'contact_id' => $contactID, 'line_item' => $lineItems, 'is_test' => $isTest, @@ -1698,13 +1750,17 @@ protected function processSecondaryFinancialTransaction($contactID, &$form, $tem $form->_values)), 'contribution_page_id' => $form->_id, 'source' => CRM_Utils_Array::value('source', $tempParams, CRM_Utils_Array::value('description', $tempParams)), - ); + ]; $isMonetary = !empty($form->_values['is_monetary']); if ($isMonetary) { if (empty($paymentParams['is_pay_later'])) { $contributionParams['payment_instrument_id'] = $form->_paymentProcessor['payment_instrument_id']; } } + + // CRM-19792 : set necessary fields for payment processor + CRM_Core_Payment_Form::mapParams($form->_bltID, $form->_params, $tempParams, TRUE); + $membershipContribution = CRM_Contribute_Form_Contribution_Confirm::processFormContribution($form, $tempParams, $tempParams, @@ -1715,7 +1771,20 @@ protected function processSecondaryFinancialTransaction($contactID, &$form, $tem $isRecur ); - $result = array(); + $result = []; + + // We're not processing the line item here because we are processing a membership. + // To ensure processing of the correct parameters, replace relevant parameters + // in $tempParams with those in $membershipContribution. + $tempParams['amount_level'] = $membershipContribution->amount_level; + $tempParams['total_amount'] = $membershipContribution->total_amount; + $tempParams['tax_amount'] = $membershipContribution->tax_amount; + $tempParams['contactID'] = $membershipContribution->contact_id; + $tempParams['financialTypeID'] = $membershipContribution->financial_type_id; + $tempParams['invoiceID'] = $membershipContribution->invoice_id; + $tempParams['trxn_id'] = $membershipContribution->trxn_id; + $tempParams['contributionID'] = $membershipContribution->id; + if ($form->_values['is_monetary'] && !$form->_params['is_pay_later'] && $minimumFee > 0.0) { // At the moment our tests are calling this form in a way that leaves 'object' empty. For // now we compensate here. @@ -1730,7 +1799,7 @@ protected function processSecondaryFinancialTransaction($contactID, &$form, $tem $form->assign('membership_trx_id', $result['trxn_id']); } - return array($membershipContribution, $result); + return [$membershipContribution, $result]; } /** @@ -1745,8 +1814,7 @@ 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']) + if (((isset($this->_contributeMode)) || !empty($this->_params['is_pay_later']) ) && (($this->_values['is_monetary'] && $this->_amount > 0.0)) ) { @@ -1760,7 +1828,7 @@ protected function getIsPending() { * * Ie the membership block supports a separate transactions AND the contribution form has been configured for a * contribution - * transaction AND a membership transaction AND the payment processor supports double financial transactions (ie. NOT doTransferPayment style) + * transaction AND a membership transaction AND the payment processor supports double financial transactions (ie. NOT doTransferCheckout style) * * @param int $formID * @param bool $amountBlockActiveOnForm @@ -1867,6 +1935,7 @@ public static function submit($params) { $form->_fields['billing_last_name'] = 1; // CRM-18854 - Set form values to allow pledge to be created for api test. if (CRM_Utils_Array::value('pledge_block_id', $params)) { + $form->_values['pledge_id'] = CRM_Utils_Array::value('pledge_id', $params, NULL); $form->_values['pledge_block_id'] = $params['pledge_block_id']; $pledgeBlock = CRM_Pledge_BAO_PledgeBlock::getPledgeBlock($params['id']); $form->_values['max_reminders'] = $pledgeBlock['max_reminders']; @@ -1880,10 +1949,17 @@ public static function submit($params) { $form->_values['fee'] = $priceSetFields['fields']; $form->_priceSetId = $priceSetID; $form->setFormAmountFields($priceSetID); + $capabilities = []; + if ($form->_mode) { + $capabilities[] = (ucfirst($form->_mode) . 'Mode'); + } + $form->_paymentProcessors = CRM_Financial_BAO_PaymentProcessor::getPaymentProcessors($capabilities); + $form->_params['payment_processor_id'] = isset($params['payment_processor_id']) ? $params['payment_processor_id'] : 0; + if ($form->_params['payment_processor_id'] !== '') { + // It can be blank with a $0 transaction - then no processor needs to be selected + $form->_paymentProcessor = $form->_paymentProcessors[$form->_params['payment_processor_id']]; + } if (!empty($params['payment_processor_id'])) { - $form->_paymentProcessor = civicrm_api3('payment_processor', 'getsingle', array( - 'id' => $params['payment_processor_id'], - )); // The concept of contributeMode is deprecated as is the billing_mode concept. if ($form->_paymentProcessor['billing_mode'] == 1) { $form->_contributeMode = 'direct'; @@ -1892,12 +1968,22 @@ public static function submit($params) { $form->_contributeMode = 'notify'; } } - else { - $form->_params['payment_processor_id'] = 0; - } + $priceFields = $priceFields[$priceSetID]['fields']; - CRM_Price_BAO_PriceSet::processAmount($priceFields, $paramsProcessedForForm, $lineItems, 'civicrm_contribution'); - $form->_lineItem = array($priceSetID => $lineItems); + $lineItems = []; + CRM_Price_BAO_PriceSet::processAmount($priceFields, $paramsProcessedForForm, $lineItems, 'civicrm_contribution', $priceSetID); + $form->_lineItem = [$priceSetID => $lineItems]; + $membershipPriceFieldIDs = []; + foreach ((array) $lineItems as $lineItem) { + if (!empty($lineItem['membership_type_id'])) { + $form->set('useForMember', 1); + $form->_useForMember = 1; + $membershipPriceFieldIDs['id'] = $priceSetID; + $membershipPriceFieldIDs[] = $lineItem['price_field_value_id']; + } + } + $form->set('memberPriceFieldIDS', $membershipPriceFieldIDs); + $form->setRecurringMembershipParams(); $form->processFormSubmission(CRM_Utils_Array::value('contact_id', $params)); } @@ -1917,11 +2003,11 @@ public static function getFormParams($id, array $params) { if (!empty($params['payment_processor_id'])) { $params['is_pay_later'] = 0; } - else { - $params['is_pay_later'] = civicrm_api3('contribution_page', 'getvalue', array( + elseif ($params['amount'] !== 0) { + $params['is_pay_later'] = civicrm_api3('contribution_page', 'getvalue', [ 'id' => $id, 'return' => 'is_pay_later', - )); + ]); } } if (empty($params['price_set_id'])) { @@ -1940,7 +2026,6 @@ public static function getFormParams($id, array $params) { * @return array */ protected function processFormSubmission($contactID) { - $isPayLater = $this->_params['is_pay_later']; if (!isset($this->_params['payment_processor_id'])) { // If there is no processor we are using the pay-later manual pseudo-processor. // (note it might make sense to make this a row in the processor table in the db). @@ -1953,6 +2038,14 @@ protected function processFormSubmission($contactID) { if (!empty($this->_ccid)) { $this->_params['contribution_id'] = $this->_ccid; } + //Set email-bltID if pre/post profile contains an email. + if ($this->_emailExists == TRUE) { + foreach ($this->_params as $key => $val) { + if (substr($key, 0, 6) == 'email-' && empty($this->_params["email-{$this->_bltID}"])) { + $this->_params["email-{$this->_bltID}"] = $this->_params[$key]; + } + } + } // add a description field at the very beginning $this->_params['description'] = ts('Online Contribution') . ': ' . (($this->_pcpInfo['title']) ? $this->_pcpInfo['title'] : $this->_values['title']); @@ -1961,10 +2054,13 @@ protected function processFormSubmission($contactID) { // fix currency ID $this->_params['currencyID'] = CRM_Core_Config::singleton()->defaultCurrency; + CRM_Contribute_Form_AbstractEditPayment::formatCreditCardDetails($this->_params); + // CRM-18854 - if (CRM_Utils_Array::value('adjust_recur_start_date', $this->_values)) { + if (CRM_Utils_Array::value('is_pledge', $this->_params) && !CRM_Utils_Array::value('pledge_id', $this->_values) && CRM_Utils_Array::value('adjust_recur_start_date', $this->_values)) { $pledgeBlock = CRM_Pledge_BAO_PledgeBlock::getPledgeBlock($this->_id); - if (CRM_Utils_Array::value('start_date', $this->_params) || !CRM_Utils_Array::value('is_pledge_start_date_visible', $pledgeBlock)) { + if (CRM_Utils_Array::value('start_date', $this->_params) || !CRM_Utils_Array::value('is_pledge_start_date_visible', $pledgeBlock) + || !CRM_Utils_Array::value('is_pledge_start_date_editable', $pledgeBlock)) { $pledgeStartDate = CRM_Utils_Array::value('start_date', $this->_params, NULL); $this->_params['receive_date'] = CRM_Pledge_BAO_Pledge::getPledgeStartDate($pledgeStartDate, $pledgeBlock); $recurParams = CRM_Pledge_BAO_Pledge::buildRecurParams($this->_params); @@ -1982,10 +2078,10 @@ protected function processFormSubmission($contactID) { CRM_Contact_BAO_Contact::processImageParams($params); } - $fields = array('email-Primary' => 1); + $fields = ['email-Primary' => 1]; // get the add to groups - $addToGroups = array(); + $addToGroups = []; // now set the values for the billing location. foreach ($this->_fields as $name => $value) { @@ -2007,8 +2103,8 @@ protected function processFormSubmission($contactID) { // normal behavior is continued. And use that variable to // process on-behalf-of functionality. if (!empty($this->_values['onbehalf_profile_id']) && empty($this->_ccid)) { - $behalfOrganization = array(); - $orgFields = array('organization_name', 'organization_id', 'org_option'); + $behalfOrganization = []; + $orgFields = ['organization_name', 'organization_id', 'org_option']; foreach ($orgFields as $fld) { if (array_key_exists($fld, $params)) { $behalfOrganization[$fld] = $params[$fld]; @@ -2022,10 +2118,10 @@ protected function processFormSubmission($contactID) { $behalfOrganization[$fld] = $values; } elseif (!(strstr($fld, '-'))) { - if (in_array($fld, array( + if (in_array($fld, [ 'contribution_campaign_id', 'member_campaign_id', - ))) { + ])) { $fld = 'campaign_id'; } else { @@ -2086,12 +2182,7 @@ protected function processFormSubmission($contactID) { unset($dupeParams['honor']); } - $dedupeParams = CRM_Dedupe_Finder::formatParams($dupeParams, 'Individual'); - $dedupeParams['check_permission'] = FALSE; - $ids = CRM_Dedupe_Finder::dupesByParams($dedupeParams, 'Individual'); - - // if we find more than one contact, use the first one - $contactID = CRM_Utils_Array::value(0, $ids); + $contactID = CRM_Contact_BAO_Contact::getFirstDuplicateContact($dupeParams, 'Individual', 'Unsupervised', [], FALSE); // Fetch default greeting id's if creating a contact if (!$contactID) { @@ -2123,7 +2214,7 @@ protected function processFormSubmission($contactID) { $this->_contactID = $contactID; //get email primary first if exist - $subscriptionEmail = array('email' => CRM_Utils_Array::value('email-Primary', $params)); + $subscriptionEmail = ['email' => CRM_Utils_Array::value('email-Primary', $params)]; if (!$subscriptionEmail['email']) { $subscriptionEmail['email'] = CRM_Utils_Array::value("email-{$this->_bltID}", $params); } @@ -2140,7 +2231,7 @@ protected function processFormSubmission($contactID) { !empty($this->_params['is_for_organization']) ) ) { - $ufFields = array(); + $ufFields = []; foreach ($this->_fields['onbehalf'] as $name => $value) { $ufFields[$name] = 1; } @@ -2187,19 +2278,28 @@ protected function processFormSubmission($contactID) { $membershipParams['is_pay_later'] = 1; } + if (isset($this->_params['onbehalf_contact_id'])) { + $membershipParams['onbehalf_contact_id'] = $this->_params['onbehalf_contact_id']; + } //inherit campaign from contribution page. if (!array_key_exists('campaign_id', $membershipParams)) { $membershipParams['campaign_id'] = CRM_Utils_Array::value('campaign_id', $this->_values); } - CRM_Core_Payment_Form::mapParams($this->_bltID, $this->_params, $membershipParams, TRUE); - $this->doMembershipProcessing($contactID, $membershipParams, $premiumParams, $isPayLater); + $this->_params = CRM_Core_Payment_Form::mapParams($this->_bltID, $this->_params, $membershipParams, TRUE); + $this->doMembershipProcessing($contactID, $membershipParams, $premiumParams, $this->_lineItem); } else { // at this point we've created a contact and stored its address etc // all the payment processors expect the name and address to be in the // so we copy stuff over to first_name etc. $paymentParams = $this->_params; + // Make it explict that we are letting the processConfirm function figure out the line items. + $paymentParams['skipLineItem'] = 0; + + if (!isset($paymentParams['line_item'])) { + $paymentParams['line_item'] = $this->_lineItem; + } if (!empty($paymentParams['onbehalf']) && is_array($paymentParams['onbehalf']) @@ -2214,7 +2314,6 @@ protected function processFormSubmission($contactID) { $result = CRM_Contribute_BAO_Contribution_Utils::processConfirm($this, $paymentParams, $contactID, $this->wrangleFinancialTypeID($this->_values['financial_type_id']), - 'contribution', ($this->_mode == 'test') ? 1 : 0, CRM_Utils_Array::value('is_recur', $paymentParams) ); @@ -2224,7 +2323,7 @@ protected function processFormSubmission($contactID) { $this->postProcessPremium($premiumParams, $result['contribution']); } if (!empty($result['contribution'])) { - // Not quite sure why it would be empty at this stage but tests show it can be ... at least in tests. + // It seems this line is hit when there is a zero dollar transaction & in tests, not sure when else. $this->completeTransaction($result, $result['contribution']->id); } return $result; @@ -2239,9 +2338,9 @@ protected function processFormSubmission($contactID) { * @param int $contactID * @param array $membershipParams * @param array $premiumParams - * @param bool $isPayLater + * @param array $formLineItems */ - protected function doMembershipProcessing($contactID, $membershipParams, $premiumParams, $isPayLater) { + protected function doMembershipProcessing($contactID, $membershipParams, $premiumParams, $formLineItems) { // This could be set by a hook. if (!empty($this->_params['installments'])) { $membershipParams['installments'] = $this->_params['installments']; @@ -2260,7 +2359,7 @@ protected function doMembershipProcessing($contactID, $membershipParams, $premiu $this->_params['campaign_id'] = $membershipParams['onbehalf']['member_campaign_id']; } - $customFieldsFormatted = $fieldTypes = array(); + $customFieldsFormatted = $fieldTypes = []; if (!empty($membershipParams['onbehalf']) && is_array($membershipParams['onbehalf']) ) { @@ -2277,38 +2376,32 @@ protected function doMembershipProcessing($contactID, $membershipParams, $premiu ); } } - $fieldTypes = array('Contact', 'Organization', 'Membership'); + $fieldTypes = ['Contact', 'Organization', 'Membership']; } $priceFieldIds = $this->get('memberPriceFieldIDS'); if (!empty($priceFieldIds)) { - $contributionTypeID = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', $priceFieldIds['id'], 'financial_type_id'); + $membershipParams['financial_type_id'] = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', $priceFieldIds['id'], 'financial_type_id'); unset($priceFieldIds['id']); - $membershipTypeIds = array(); - $membershipTypeTerms = array(); + $membershipTypeIds = []; + $membershipTypeTerms = []; foreach ($priceFieldIds as $priceFieldId) { - if ($id = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceFieldValue', $priceFieldId, 'membership_type_id')) { - $membershipTypeIds[] = $id; - //@todo the value for $term is immediately overwritten. It is unclear from the code whether it was intentional to - // do this or a double = was intended (this ambiguity is the reason many IDEs complain about 'assignment in condition' - $term = 1; - if ($term = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceFieldValue', $priceFieldId, 'membership_num_terms')) { - $membershipTypeTerms[$id] = ($term > 1) ? $term : 1; - } - else { - $membershipTypeTerms[$id] = 1; - } + $membershipTypeId = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceFieldValue', $priceFieldId, 'membership_type_id'); + if ($membershipTypeId) { + $membershipTypeIds[] = $membershipTypeId; + $term = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceFieldValue', $priceFieldId, 'membership_num_terms') ?: 1; + $membershipTypeTerms[$membershipTypeId] = ($term > 1) ? $term : 1; } } $membershipParams['selectMembership'] = $membershipTypeIds; - $membershipParams['financial_type_id'] = $contributionTypeID; $membershipParams['types_terms'] = $membershipTypeTerms; } if (!empty($membershipParams['selectMembership'])) { // CRM-12233 - $membershipLineItems = array(); + $membershipLineItems = $formLineItems; if ($this->_separateMembershipPayment && $this->_values['amount_block_is_active']) { + $membershipLineItems = []; foreach ($this->_values['fee'] as $key => $feeValues) { if ($feeValues['name'] == 'membership_amount') { $fieldId = $this->_params['price_' . $key]; @@ -2319,7 +2412,15 @@ protected function doMembershipProcessing($contactID, $membershipParams, $premiu } } try { - $this->processMembership($membershipParams, $contactID, $customFieldsFormatted, $fieldTypes, $premiumParams, $membershipLineItems, $isPayLater); + $this->processMembership($membershipParams, $contactID, $customFieldsFormatted, $fieldTypes, $premiumParams, $membershipLineItems); + } + catch (\Civi\Payment\Exception\PaymentProcessorException $e) { + CRM_Core_Session::singleton()->setStatus($e->getMessage()); + if (!empty($this->_contributionID)) { + CRM_Contribute_BAO_Contribution::failPayment($this->_contributionID, + $contactID, $e->getMessage()); + } + CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contribute/transact', "_qf_Main_display=true&qfKey={$this->_params['qfKey']}")); } catch (CRM_Core_Exception $e) { CRM_Core_Session::singleton()->setStatus($e->getMessage()); @@ -2352,15 +2453,16 @@ protected function doMembershipProcessing($contactID, $membershipParams, $premiu protected function completeTransaction($result, $contributionID) { if (CRM_Utils_Array::value('payment_status_id', $result) == 1) { try { - civicrm_api3('contribution', 'completetransaction', array( - 'id' => $contributionID, - 'trxn_id' => CRM_Utils_Array::value('trxn_id', $result), - 'payment_processor_id' => $this->_paymentProcessor['id'], - 'is_transactional' => FALSE, - 'fee_amount' => CRM_Utils_Array::value('fee_amount', $result), - 'receive_date' => CRM_Utils_Array::value('receive_date', $result), - ) - ); + civicrm_api3('contribution', 'completetransaction', [ + 'id' => $contributionID, + 'trxn_id' => CRM_Utils_Array::value('trxn_id', $result), + 'payment_processor_id' => CRM_Utils_Array::value('payment_processor_id', $result, $this->_paymentProcessor['id']), + 'is_transactional' => FALSE, + 'fee_amount' => CRM_Utils_Array::value('fee_amount', $result), + 'receive_date' => CRM_Utils_Array::value('receive_date', $result), + 'card_type_id' => CRM_Utils_Array::value('card_type_id', $result), + 'pan_truncation' => CRM_Utils_Array::value('pan_truncation', $result), + ]); } catch (CiviCRM_API3_Exception $e) { if ($e->getErrorCode() != 'contribution_completed') { @@ -2370,4 +2472,18 @@ protected function completeTransaction($result, $contributionID) { } } + /** + * Bounce the user back to retry when an error occurs. + * + * @param string $message + */ + protected function bounceOnError($message) { + CRM_Core_Session::singleton() + ->setStatus(ts("Payment Processor Error message :") . + $message); + CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contribute/transact', + "_qf_Main_display=true&qfKey={$this->_params['qfKey']}" + )); + } + } diff --git a/CRM/Contribute/Form/Contribution/Main.php b/CRM/Contribute/Form/Contribution/Main.php index 76cdb228808b..601c8e08fd2e 100644 --- a/CRM/Contribute/Form/Contribution/Main.php +++ b/CRM/Contribute/Form/Contribution/Main.php @@ -1,9 +1,9 @@ _paymentProcessors = $this->get('paymentProcessors'); $this->preProcessPaymentOptions(); - if (!empty($this->_ccid)) { - $payment = CRM_Contribute_BAO_Contribution::getPaymentInfo($this->_ccid, 'contribution'); - //bounce if the contribution is not pending. - if (empty($payment['balance'])) { - CRM_Core_Error::statusBounce(ts("Returning since contribution has already been handled.")); - } - if (!empty($payment['total'])) { - $this->_pendingAmount = $payment['total']; - $this->assign('pendingAmount', $this->_pendingAmount); - } - $lineItems = CRM_Price_BAO_LineItem::getLineItemsByContributionID($this->_ccid); - foreach (array_keys($lineItems) as $id) { - $lineItems[$id]['id'] = $id; - } - $itemId = key($lineItems); - if ($itemId && !empty($lineItems[$itemId]['price_field_id'])) { - $this->_priceSetId = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceField', $lineItems[$itemId]['price_field_id'], 'price_set_id'); - } - - if (!empty($lineItems[$itemId]['price_field_id'])) { - $this->_lineItem[$this->_priceSetId] = $lineItems; - } - $isQuickConfig = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', $this->_priceSetId, 'is_quick_config'); - $this->assign('lineItem', $this->_lineItem); - $this->assign('is_quick_config', $isQuickConfig); - $this->assign('priceSetID', $this->_priceSetId); - } + $this->assignFormVariablesByContributionID(); // Make the contributionPageID available to the template $this->assign('contributionPageID', $this->_id); @@ -99,7 +74,7 @@ public function preProcess() { $this->assign('isShare', CRM_Utils_Array::value('is_share', $this->_values)); $this->assign('isConfirmEnabled', CRM_Utils_Array::value('is_confirm_enabled', $this->_values)); - $this->assign('reset', CRM_Utils_Request::retrieve('reset', 'Boolean', CRM_Core_DAO::$_nullObject)); + $this->assign('reset', CRM_Utils_Request::retrieve('reset', 'Boolean')); $this->assign('mainDisplay', CRM_Utils_Request::retrieve('_qf_Main_display', 'Boolean', CRM_Core_DAO::$_nullObject)); @@ -129,8 +104,8 @@ public function setDefaultValues() { $contactID = $this->getContactID(); if (!empty($contactID)) { - $fields = array(); - $removeCustomFieldTypes = array('Contribution', 'Membership'); + $fields = []; + $removeCustomFieldTypes = ['Contribution', 'Membership']; $contribFields = CRM_Contribute_BAO_Contribution::getContributionFields(); // remove component related fields @@ -178,7 +153,7 @@ public function setDefaultValues() { //build set default for pledge overdue payment. if (!empty($this->_values['pledge_id'])) { //used to record completed pledge payment ids used later for honor default - $completedContributionIds = array(); + $completedContributionIds = []; $pledgePayments = CRM_Pledge_BAO_PledgePayment::getPledgePayments($this->_values['pledge_id']); $paymentAmount = 0; @@ -200,7 +175,7 @@ public function setDefaultValues() { $this->_defaults['price_' . $this->_priceSetId] = $paymentAmount; if (count($completedContributionIds)) { - $softCredit = array(); + $softCredit = []; foreach ($completedContributionIds as $id) { $softCredit = CRM_Contribute_BAO_ContributionSoft::getSoftContribution($id); } @@ -243,7 +218,7 @@ public function setDefaultValues() { $entityId = $memtypeID = NULL; if ($this->_priceSetId) { if (($this->_useForMember && !empty($this->_currentMemberships)) || $this->_defaultMemTypeId) { - $selectedCurrentMemTypes = array(); + $selectedCurrentMemTypes = []; foreach ($this->_priceSet['fields'] as $key => $val) { foreach ($val['options'] as $keys => $values) { $opMemTypeId = CRM_Utils_Array::value('membership_type_id', $values); @@ -264,14 +239,10 @@ public function setDefaultValues() { CRM_Price_BAO_PriceSet::setDefaultPriceSetField($priceFieldName, $keys, $val['html_type'], $this->_defaults); $memtypeID = $selectedCurrentMemTypes[] = $values['membership_type_id']; } - elseif (!empty($values['is_default']) && - !$opMemTypeId && - (!isset($this->_defaults[$priceFieldName]) || - ($val['html_type'] == 'CheckBox' && - !isset($this->_defaults[$priceFieldName][$keys])) - )) { - CRM_Price_BAO_PriceSet::setDefaultPriceSetField($priceFieldName, $keys, $val['html_type'], $this->_defaults); - $memtypeID = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceFieldValue', $this->_defaults[$priceFieldName], 'membership_type_id'); + elseif (!empty($values['is_default']) && !$opMemTypeId && (!isset($this->_defaults[$priceFieldName]) || + ($val['html_type'] == 'CheckBox' && !isset($this->_defaults[$priceFieldName][$keys])))) { + CRM_Price_BAO_PriceSet::setDefaultPriceSetField($priceFieldName, $keys, $val['html_type'], $this->_defaults); + $memtypeID = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceFieldValue', $this->_defaults[$priceFieldName], 'membership_type_id'); } } } @@ -328,6 +299,15 @@ public function buildQuickForm() { $this->buildComponentForm($this->_id, $this); } + if (count($this->_paymentProcessors) >= 1 && !isset($this->_paymentProcessors[0]) && !$this->get_template_vars("isCaptcha") && $this->hasToAddForcefully()) { + if (!$this->_userID) { + $this->enableCaptchaOnForm(); + } + else { + $this->displayCaptchaWarning(); + } + } + // Build payment processor form CRM_Core_Payment_ProcessorForm::buildQuickForm($this); @@ -341,18 +321,21 @@ public function buildQuickForm() { $this->applyFilter('__ALL__', 'trim'); if (empty($this->_ccid)) { - $this->add('text', "email-{$this->_bltID}", - ts('Email Address'), - array('size' => 30, 'maxlength' => 60, 'class' => 'email'), - TRUE - ); - $this->addRule("email-{$this->_bltID}", ts('Email is not valid.'), 'email'); + if ($this->_emailExists == FALSE) { + $this->add('text', "email-{$this->_bltID}", + ts('Email Address'), + ['size' => 30, 'maxlength' => 60, 'class' => 'email'], + TRUE + ); + $this->assign('showMainEmail', TRUE); + $this->addRule("email-{$this->_bltID}", ts('Email is not valid.'), 'email'); + } } else { $this->addElement('hidden', "email-{$this->_bltID}", 1); - $this->add('text', 'total_amount', ts('Total Amount'), array('readonly' => TRUE), FALSE); + $this->add('text', 'total_amount', ts('Total Amount'), ['readonly' => TRUE], FALSE); } - $pps = array(); + $pps = []; //@todo - this should be replaced by a check as to whether billing fields are set $onlinePaymentProcessorEnabled = FALSE; if (!empty($this->_paymentProcessors)) { @@ -465,7 +448,7 @@ public function buildQuickForm() { $this->_values['custom_post_id'] ) { if (!is_array($this->_values['custom_post_id'])) { - $profileIDs = array($this->_values['custom_post_id']); + $profileIDs = [$this->_values['custom_post_id']]; } else { $profileIDs = $this->_values['custom_post_id']; @@ -485,7 +468,7 @@ public function buildQuickForm() { } if ($this->_pcpId && empty($this->_ccid)) { if ($pcpSupporter = CRM_PCP_BAO_PCP::displayName($this->_pcpId)) { - $pcp_supporter_text = ts('This contribution is being made thanks to the effort of %1, who supports our campaign.', array(1 => $pcpSupporter)); + $pcp_supporter_text = ts('This contribution is being made thanks to the effort of %1, who supports our campaign.', [1 => $pcpSupporter]); // Only tell people that can also create a PCP if the contribution page has a non-empty value in the "Create Personal Campaign Page link" field. $text = CRM_PCP_BAO_PCP::getPcpBlockStatus($this->_id, 'contribute'); if (!empty($text)) { @@ -493,21 +476,21 @@ public function buildQuickForm() { } $this->assign('pcpSupporterText', $pcp_supporter_text); } - $prms = array('id' => $this->_pcpId); + $prms = ['id' => $this->_pcpId]; CRM_Core_DAO::commonRetrieve('CRM_PCP_DAO_PCP', $prms, $pcpInfo); if ($pcpInfo['is_honor_roll']) { $this->assign('isHonor', TRUE); $this->add('checkbox', 'pcp_display_in_roll', ts('Show my contribution in the public honor roll'), NULL, NULL, - array('onclick' => "showHideByValue('pcp_display_in_roll','','nameID|nickID|personalNoteID','block','radio',false); pcpAnonymous( );") + ['onclick' => "showHideByValue('pcp_display_in_roll','','nameID|nickID|personalNoteID','block','radio',false); pcpAnonymous( );"] ); - $extraOption = array('onclick' => "return pcpAnonymous( );"); - $elements = array(); + $extraOption = ['onclick' => "return pcpAnonymous( );"]; + $elements = []; $elements[] = &$this->createElement('radio', NULL, '', ts('Include my name and message'), 0, $extraOption); $elements[] = &$this->createElement('radio', NULL, '', ts('List my contribution anonymously'), 1, $extraOption); $this->addGroup($elements, 'pcp_is_anonymous', NULL, '   '); - $this->add('text', 'pcp_roll_nickname', ts('Name'), array('maxlength' => 30)); - $this->add('textarea', 'pcp_personal_note', ts('Personal Note'), array('style' => 'height: 3em; width: 40em;')); + $this->add('text', 'pcp_roll_nickname', ts('Name'), ['maxlength' => 30]); + $this->addField('pcp_personal_note', ['entity' => 'ContributionSoft', 'context' => 'create', 'style' => 'height: 3em; width: 40em;']); } } if (empty($this->_values['fee']) && empty($this->_ccid)) { @@ -534,24 +517,24 @@ public function buildQuickForm() { } if (!($allAreBillingModeProcessors && !$this->_values['is_pay_later'])) { - $submitButton = array( + $submitButton = [ 'type' => 'upload', 'name' => CRM_Utils_Array::value('is_confirm_enabled', $this->_values) ? ts('Confirm Contribution') : ts('Contribute'), 'spacing' => '         ', 'isDefault' => TRUE, - ); + ]; // Add submit-once behavior when confirm page disabled if (empty($this->_values['is_confirm_enabled'])) { - $submitButton['js'] = array('onclick' => "return submitOnce(this,'" . $this->_name . "','" . ts('Processing') . "');"); + $submitButton['js'] = ['onclick' => "return submitOnce(this,'" . $this->_name . "','" . ts('Processing') . "');"]; } //change button name for updating contribution if (!empty($this->_ccid)) { $submitButton['name'] = ts('Confirm Payment'); } - $this->addButtons(array($submitButton)); + $this->addButtons([$submitButton]); } - $this->addFormRule(array('CRM_Contribute_Form_Contribution_Main', 'formRule'), $this); + $this->addFormRule(['CRM_Contribute_Form_Contribution_Main', 'formRule'], $this); } /** @@ -566,11 +549,18 @@ public static function buildRecur(&$form) { $form->assign('is_recur_interval', CRM_Utils_Array::value('is_recur_interval', $form->_values)); $form->assign('is_recur_installments', CRM_Utils_Array::value('is_recur_installments', $form->_values)); + $paymentObject = $form->getVar('_paymentObject'); + if ($paymentObject) { + $form->assign('recurringHelpText', $paymentObject->getText('contributionPageRecurringHelp', [ + 'is_recur_installments' => !empty($form->_values['is_recur_installments']), + 'is_email_receipt' => !empty($form->_values['is_email_receipt']), + ])); + } $form->add('checkbox', 'is_recur', ts('I want to contribute this amount'), NULL); if (!empty($form->_values['is_recur_interval']) || $className == 'CRM_Contribute_Form_Contribution') { - $form->add('text', 'frequency_interval', ts('Every'), $attributes['frequency_interval']); + $form->add('text', 'frequency_interval', ts('Every'), $attributes['frequency_interval'] + ['aria-label' => ts('Every')]); $form->addRule('frequency_interval', ts('Frequency must be a whole number (EXAMPLE: Every 3 months).'), 'integer'); } else { @@ -601,7 +591,7 @@ public static function buildRecur(&$form) { } else { $form->assign('one_frequency_unit', FALSE); - $units = array(); + $units = []; $frequencyUnits = CRM_Core_OptionGroup::values('recur_frequency_units', FALSE, FALSE, TRUE); foreach ($unitVals as $key => $val) { if (array_key_exists($val, $frequencyUnits)) { @@ -611,7 +601,7 @@ public static function buildRecur(&$form) { } } } - $frequencyUnit = &$form->add('select', 'frequency_unit', NULL, $units); + $frequencyUnit = &$form->addElement('select', 'frequency_unit', NULL, $units, ['aria-label' => ts('Frequency Unit')]); } // FIXME: Ideally we should freeze select box if there is only @@ -633,13 +623,13 @@ public static function buildRecur(&$form) { * The input form values. * @param array $files * The uploaded files if any. - * @param CRM_Core_Form $self + * @param \CRM_Contribute_Form_Contribution_Main $self * * @return bool|array * true if no errors, else array of errors */ public static function formRule($fields, $files, $self) { - $errors = array(); + $errors = []; $amount = self::computeAmount($fields, $self->_values); if (CRM_Utils_Array::value('auto_renew', $fields) && CRM_Utils_Array::value('payment_processor_id', $fields) == 0 @@ -659,7 +649,7 @@ public static function formRule($fields, $files, $self) { $membershipOrgDetails = CRM_Member_BAO_MembershipType::getMembershipTypeOrganization(); - $unallowedOrgs = array(); + $unallowedOrgs = []; foreach (array_keys($lifeMember) as $memTypeId) { $unallowedOrgs[] = $membershipOrgDetails[$memTypeId]; } @@ -672,12 +662,12 @@ public static function formRule($fields, $files, $self) { $priceField->orderBy('weight'); $priceField->find(); - $check = array(); + $check = []; $membershipIsActive = TRUE; $previousId = $otherAmount = FALSE; while ($priceField->fetch()) { - if ($self->_quickConfig && ($priceField->name == 'contribution_amount' || $priceField->name == 'membership_amount')) { + if ($self->isQuickConfig() && ($priceField->name == 'contribution_amount' || $priceField->name == 'membership_amount')) { $previousId = $priceField->id; if ($priceField->name == 'membership_amount' && !$priceField->is_active) { $membershipIsActive = FALSE; @@ -695,12 +685,12 @@ public static function formRule($fields, $files, $self) { $max = CRM_Utils_Array::value('max_amount', $self->_values); if ($min && $otherAmountVal < $min) { $errors["price_{$priceField->id}"] = ts('Contribution amount must be at least %1', - array(1 => $min) + [1 => $min] ); } if ($max && $otherAmountVal > $max) { $errors["price_{$priceField->id}"] = ts('Contribution amount cannot be more than %1.', - array(1 => $max) + [1 => $max] ); } } @@ -720,11 +710,7 @@ public static function formRule($fields, $files, $self) { // For anonymous user check using dedupe rule // if user has Cancelled Membership if (!$memContactID) { - $dedupeParams = CRM_Dedupe_Finder::formatParams($fields, 'Individual'); - $dedupeParams['check_permission'] = FALSE; - $ids = CRM_Dedupe_Finder::dupesByParams($dedupeParams, 'Individual'); - // if we find more than one contact, use the first one - $memContactID = CRM_Utils_Array::value(0, $ids); + $memContactID = CRM_Contact_BAO_Contact::getFirstDuplicateContact($fields, 'Individual', 'Unsupervised', [], FALSE); } $currentMemberships = CRM_Member_BAO_Membership::getContactsCancelledMembership($memContactID, $is_test @@ -737,7 +723,7 @@ public static function formRule($fields, $files, $self) { if (array_key_exists('membership_type_id', $fieldValue['options'][$fields['price_' . $fieldKey]]) && in_array($fieldValue['options'][$fields['price_' . $fieldKey]]['membership_type_id'], $currentMemberships) ) { - $errors['price_' . $fieldKey] = ts($errorText, array(1 => CRM_Member_PseudoConstant::membershipType($fieldValue['options'][$fields['price_' . $fieldKey]]['membership_type_id']))); + $errors['price_' . $fieldKey] = ts($errorText, [1 => CRM_Member_PseudoConstant::membershipType($fieldValue['options'][$fields['price_' . $fieldKey]]['membership_type_id'])]); } } else { @@ -746,7 +732,7 @@ public static function formRule($fields, $files, $self) { if (array_key_exists('membership_type_id', $fieldValue['options'][$key]) && in_array($fieldValue['options'][$key]['membership_type_id'], $currentMemberships) ) { - $errors['price_' . $fieldKey] = ts($errorText, array(1 => CRM_Member_PseudoConstant::membershipType($fieldValue['options'][$key]['membership_type_id']))); + $errors['price_' . $fieldKey] = ts($errorText, [1 => CRM_Member_PseudoConstant::membershipType($fieldValue['options'][$key]['membership_type_id'])]); } } } @@ -796,8 +782,8 @@ public static function formRule($fields, $files, $self) { } if ($self->_useForMember == 1 && !empty($check) && $membershipIsActive) { - $priceFieldIDS = array(); - $priceFieldMemTypes = array(); + $priceFieldIDS = []; + $priceFieldMemTypes = []; foreach ($self->_priceSet['fields'] as $priceId => $value) { if (!empty($fields['price_' . $priceId]) || ($self->_quickConfig && $value['name'] == 'membership_amount' && empty($self->_membershipBlock['is_required']))) { @@ -861,9 +847,16 @@ public static function formRule($fields, $files, $self) { $fields, $lineItem ); + $minAmt = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', $fields['priceSetId'], 'min_amount'); if ($fields['amount'] < 0) { $errors['_qf_default'] = ts('Contribution can not be less than zero. Please select the options accordingly'); } + elseif (!empty($minAmt) && $fields['amount'] < $minAmt) { + $errors['_qf_default'] = ts('A minimum amount of %1 should be selected from Contribution(s).', [ + 1 => CRM_Utils_Money::format($minAmt), + ]); + } + $amount = $fields['amount']; } @@ -876,7 +869,7 @@ public static function formRule($fields, $files, $self) { $min_amount = $productDAO->min_contribution; if ($amount < $min_amount) { - $errors['selectProduct'] = ts('The premium you have selected requires a minimum contribution of %1', array(1 => CRM_Utils_Money::format($min_amount))); + $errors['selectProduct'] = ts('The premium you have selected requires a minimum contribution of %1', [1 => CRM_Utils_Money::format($min_amount)]); CRM_Core_Session::setStatus($errors['selectProduct']); } } @@ -901,7 +894,7 @@ public static function formRule($fields, $files, $self) { // return if this is express mode $config = CRM_Core_Config::singleton(); if ($self->_paymentProcessor && - $self->_paymentProcessor['billing_mode'] & CRM_Core_Payment::BILLING_MODE_BUTTON + (int) $self->_paymentProcessor['billing_mode'] & CRM_Core_Payment::BILLING_MODE_BUTTON ) { if (!empty($fields[$self->_expressButtonName . '_x']) || !empty($fields[$self->_expressButtonName . '_y']) || CRM_Utils_Array::value($self->_expressButtonName, $fields) @@ -955,7 +948,7 @@ public static function formRule($fields, $files, $self) { return $errors; } - if (CRM_Utils_Array::value('payment_processor_id', $fields) == NULL) { + if (CRM_Utils_Array::value('payment_processor_id', $fields) === NULL) { $errors['payment_processor_id'] = ts('Payment Method is a required field.'); } else { @@ -969,10 +962,10 @@ public static function formRule($fields, $files, $self) { foreach (CRM_Contact_BAO_Contact::$_greetingTypes as $greeting) { if ($greetingType = CRM_Utils_Array::value($greeting, $fields)) { - $customizedValue = CRM_Core_OptionGroup::getValue($greeting, 'Customized', 'name'); + $customizedValue = CRM_Core_PseudoConstant::getKey('CRM_Contact_BAO_Contact', $greeting . '_id', 'Customized'); if ($customizedValue == $greetingType && empty($fielse[$greeting . '_custom'])) { $errors[$greeting . '_custom'] = ts('Custom %1 is a required field if %1 is of type Customized.', - array(1 => ucwords(str_replace('_', " ", $greeting))) + [1 => ucwords(str_replace('_', " ", $greeting))] ); } } @@ -1054,7 +1047,7 @@ public function submit($params) { $params['currencyID'] = CRM_Core_Config::singleton()->defaultCurrency; - $is_quick_config = 0; + // @todo refactor this & leverage it from the unit tests. if (!empty($params['priceSetId'])) { $is_quick_config = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', $this->_priceSetId, 'is_quick_config'); if ($is_quick_config) { @@ -1063,7 +1056,7 @@ public function submit($params) { $priceField->orderBy('weight'); $priceField->find(); - $priceOptions = array(); + $priceOptions = []; while ($priceField->fetch()) { CRM_Price_BAO_PriceFieldValue::getValues($priceField->id, $priceOptions); if (($selectedPriceOptionID = CRM_Utils_Array::value("price_{$priceField->id}", $params)) != FALSE && $selectedPriceOptionID > 0) { @@ -1118,7 +1111,7 @@ public function submit($params) { } } //If the membership & contribution is used in contribution page & not separate payment - $fieldId = $memPresent = $membershipLabel = $fieldOption = $is_quick_config = NULL; + $memPresent = $membershipLabel = $fieldOption = $is_quick_config = NULL; $proceFieldAmount = 0; if (property_exists($this, '_separateMembershipPayment') && $this->_separateMembershipPayment == 0) { $is_quick_config = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', $this->_priceSetId, 'is_quick_config'); @@ -1155,7 +1148,7 @@ public function submit($params) { $this->set('lineItem', $this->_lineItem); } elseif ($priceSetId = CRM_Utils_Array::value('priceSetId', $params)) { - $lineItem = array(); + $lineItem = []; $is_quick_config = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', $priceSetId, 'is_quick_config'); if ($is_quick_config) { foreach ($this->_values['fee'] as $key => & $val) { @@ -1178,7 +1171,7 @@ public function submit($params) { $component = 'membership'; } - CRM_Price_BAO_PriceSet::processAmount($this->_values['fee'], $params, $lineItem[$priceSetId], $component); + CRM_Price_BAO_PriceSet::processAmount($this->_values['fee'], $params, $lineItem[$priceSetId], $component, $priceSetId); if ($params['tax_amount']) { $this->set('tax_amount', $params['tax_amount']); } @@ -1215,7 +1208,7 @@ public function submit($params) { $this->assign('is_pay_later', $params['is_pay_later']); if ($params['is_pay_later']) { $this->assign('pay_later_text', $this->_values['pay_later_text']); - $this->assign('pay_later_receipt', $this->_values['pay_later_receipt']); + $this->assign('pay_later_receipt', CRM_Utils_Array::value('pay_later_receipt', $this->_values)); } if ($this->_membershipBlock['is_separate_payment'] && !empty($params['separate_amount'])) { @@ -1306,6 +1299,50 @@ protected function skipToThankYouPage() { CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contribute/transact', "_qf_ThankYou_display=1&qfKey=$qfKey", TRUE, NULL, FALSE)); } + /** + * Set form variables if contribution ID is found + */ + public function assignFormVariablesByContributionID() { + if (empty($this->_ccid)) { + return; + } + if (!$this->getContactID()) { + CRM_Core_Error::statusBounce(ts("Returning since there is no contact attached to this contribution id.")); + } + + $paymentBalance = CRM_Contribute_BAO_Contribution::getContributionBalance($this->_ccid); + //bounce if the contribution is not pending. + if ((int) $paymentBalance <= 0) { + CRM_Core_Error::statusBounce(ts("Returning since contribution has already been handled.")); + } + if (!empty($paymentBalance)) { + $this->_pendingAmount = $paymentBalance; + $this->assign('pendingAmount', $this->_pendingAmount); + } + + if ($taxAmount = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_Contribution', $this->_ccid, 'tax_amount')) { + $this->set('tax_amount', $taxAmount); + $this->assign('taxAmount', $taxAmount); + } + + $lineItems = CRM_Price_BAO_LineItem::getLineItemsByContributionID($this->_ccid); + foreach (array_keys($lineItems) as $id) { + $lineItems[$id]['id'] = $id; + } + $itemId = key($lineItems); + if ($itemId && !empty($lineItems[$itemId]['price_field_id'])) { + $this->_priceSetId = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceField', $lineItems[$itemId]['price_field_id'], 'price_set_id'); + } + + if (!empty($lineItems[$itemId]['price_field_id'])) { + $this->_lineItem[$this->_priceSetId] = $lineItems; + } + $isQuickConfig = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', $this->_priceSetId, 'is_quick_config'); + $this->assign('lineItem', $this->_lineItem); + $this->assign('is_quick_config', $isQuickConfig); + $this->assign('priceSetID', $this->_priceSetId); + } + /** * Function for unit tests on the postProcess function. * diff --git a/CRM/Contribute/Form/Contribution/ThankYou.php b/CRM/Contribute/Form/Contribution/ThankYou.php index 37c7b281a8e1..842feec3b5a7 100644 --- a/CRM/Contribute/Form/Contribution/ThankYou.php +++ b/CRM/Contribute/Form/Contribution/ThankYou.php @@ -1,9 +1,9 @@ _params = $this->get('params'); $this->_lineItem = $this->get('lineItem'); + $this->_useForMember = $this->get('useForMember'); $is_deductible = $this->get('is_deductible'); $this->assign('is_deductible', $is_deductible); $this->assign('thankyou_title', CRM_Utils_Array::value('thankyou_title', $this->_values)); @@ -88,6 +96,7 @@ public function getAction() { * Build the form object. */ public function buildQuickForm() { + // FIXME: Some of this code is identical to Confirm.php and should be broken out into a shared function $this->assignToTemplate(); $this->_ccid = $this->get('ccid'); $productID = $this->get('productID'); @@ -98,46 +107,49 @@ public function buildQuickForm() { if ($productID) { CRM_Contribute_BAO_Premium::buildPremiumBlock($this, $this->_id, FALSE, $productID, $option); } - if ($this->_priceSetId && !CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', $this->_priceSetId, 'is_quick_config')) { - $this->assign('lineItem', $this->_lineItem); - } - else { - if (is_array($membershipTypeID)) { - $membershipTypeID = current($membershipTypeID); - } - $this->assign('is_quick_config', 1); - $this->_params['is_quick_config'] = 1; - } - $this->assign('priceSetID', $this->_priceSetId); - $this->assign('useForMember', $this->get('useForMember')); $params = $this->_params; - $invoiceSettings = Civi::settings()->get('contribution_invoice_settings'); - $invoicing = CRM_Utils_Array::value('invoicing', $invoiceSettings); + $invoicing = CRM_Invoicing_Utils::isInvoicingEnabled(); + // Make a copy of line items array to use for display only + $tplLineItems = $this->_lineItem; if ($invoicing) { $getTaxDetails = FALSE; - $taxTerm = CRM_Utils_Array::value('tax_term', $invoiceSettings); foreach ($this->_lineItem as $key => $value) { - foreach ($value as $v) { + foreach ($value as $k => $v) { if (isset($v['tax_rate'])) { if ($v['tax_rate'] != '') { $getTaxDetails = TRUE; + // Cast to float to display without trailing zero decimals + $tplLineItems[$key][$k]['tax_rate'] = (float) $v['tax_rate']; } } } } $this->assign('getTaxDetails', $getTaxDetails); - $this->assign('taxTerm', $taxTerm); + $this->assign('taxTerm', CRM_Invoicing_Utils::getTaxTerm()); $this->assign('totalTaxAmount', $params['tax_amount']); } + + if ($this->_priceSetId && !CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', $this->_priceSetId, 'is_quick_config')) { + $this->assign('lineItem', $tplLineItems); + } + else { + if (is_array($membershipTypeID)) { + $membershipTypeID = current($membershipTypeID); + } + $this->assign('is_quick_config', 1); + $this->_params['is_quick_config'] = 1; + } + $this->assign('priceSetID', $this->_priceSetId); + $this->assign('useForMember', $this->get('useForMember')); + if (!empty($this->_values['honoree_profile_id']) && !empty($params['soft_credit_type_id'])) { - $honorName = NULL; $softCreditTypes = CRM_Core_OptionGroup::values("soft_credit_type", FALSE); $this->assign('soft_credit_type', $softCreditTypes[$params['soft_credit_type_id']]); CRM_Contribute_BAO_ContributionSoft::formatHonoreeProfileFields($this, $params['honor']); - $fieldTypes = array('Contact'); + $fieldTypes = ['Contact']; $fieldTypes[] = CRM_Core_BAO_UFGroup::getContactType($this->_values['honoree_profile_id']); $this->buildCustom($this->_values['honoree_profile_id'], 'honoreeProfileFields', TRUE, 'honor', $fieldTypes); } @@ -147,12 +159,12 @@ public function buildQuickForm() { if ($this->_pcpId) { $qParams .= "&pcpId={$this->_pcpId}"; $this->assign('pcpBlock', TRUE); - foreach (array( - 'pcp_display_in_roll', - 'pcp_is_anonymous', - 'pcp_roll_nickname', - 'pcp_personal_note', - ) as $val) { + foreach ([ + 'pcp_display_in_roll', + 'pcp_is_anonymous', + 'pcp_roll_nickname', + 'pcp_personal_note', + ] as $val) { if (!empty($this->_params[$val])) { $this->assign($val, $this->_params[$val]); } @@ -195,30 +207,29 @@ public function buildQuickForm() { !empty($params['is_for_organization']) ) && empty($this->_ccid) ) { - $fieldTypes = array('Contact', 'Organization'); + $fieldTypes = ['Contact', 'Organization']; $contactSubType = CRM_Contact_BAO_ContactType::subTypes('Organization'); $fieldTypes = array_merge($fieldTypes, $contactSubType); if (is_array($this->_membershipBlock) && !empty($this->_membershipBlock)) { - $fieldTypes = array_merge($fieldTypes, array('Membership')); + $fieldTypes = array_merge($fieldTypes, ['Membership']); } else { - $fieldTypes = array_merge($fieldTypes, array('Contribution')); + $fieldTypes = array_merge($fieldTypes, ['Contribution']); } $this->buildCustom($this->_values['onbehalf_profile_id'], 'onbehalfProfile', TRUE, 'onbehalf', $fieldTypes); } - $this->assign('trxn_id', - CRM_Utils_Array::value('trxn_id', - $this->_params - ) - ); + $this->_trxnId = CRM_Utils_Array::value('trxn_id', $this->_params); + + $this->assign('trxn_id', $this->_trxnId); + $this->assign('receive_date', CRM_Utils_Date::mysqlToIso(CRM_Utils_Array::value('receive_date', $this->_params)) ); - $defaults = array(); - $fields = array(); + $defaults = []; + $fields = []; foreach ($this->_fields as $name => $dontCare) { if ($name != 'onbehalf' || $name != 'honor') { $fields[$name] = 1; @@ -236,11 +247,11 @@ public function buildQuickForm() { $defaults[$timeField] = $contact[$timeField]; } } - elseif (in_array($name, array( - 'addressee', - 'email_greeting', - 'postal_greeting', - )) && !empty($contact[$name . '_custom']) + elseif (in_array($name, [ + 'addressee', + 'email_greeting', + 'postal_greeting', + ]) && !empty($contact[$name . '_custom']) ) { $defaults[$name . '_custom'] = $contact[$name . '_custom']; } @@ -284,6 +295,29 @@ public function buildQuickForm() { $this->assign('friendURL', $url); } + $isPendingOutcome = TRUE; + try { + // A payment notification update could have come in at any time. Check at the last minute. + $contributionStatusID = civicrm_api3('Contribution', 'getvalue', [ + 'id' => CRM_Utils_Array::value('contributionID', $params), + 'return' => 'contribution_status_id', + 'is_test' => ($this->_mode == 'test') ? 1 : 0, + 'invoice_id' => CRM_Utils_Array::value('invoiceID', $params), + ]); + if (CRM_Core_PseudoConstant::getName('CRM_Contribute_BAO_Contribution', 'contribution_status_id', $contributionStatusID) === 'Pending' + && !empty($params['payment_processor_id']) + ) { + $isPendingOutcome = TRUE; + } + else { + $isPendingOutcome = FALSE; + } + } + catch (CiviCRM_API3_Exception $e) { + + } + $this->assign('isPendingOutcome', $isPendingOutcome); + $this->freeze(); // can we blow away the session now to prevent hackery diff --git a/CRM/Contribute/Form/ContributionBase.php b/CRM/Contribute/Form/ContributionBase.php index 4f6365e81b15..971479e6332d 100644 --- a/CRM/Contribute/Form/ContributionBase.php +++ b/CRM/Contribute/Form/ContributionBase.php @@ -1,9 +1,9 @@ controller->invalidKeyRedirect(); } + $this->_emailExists = $this->get('emailExists'); // this was used prior to the cleverer this_>getContactID - unsure now - $this->_userID = CRM_Core_Session::singleton()->get('userID'); + $this->_userID = CRM_Core_Session::singleton()->getLoggedInContactID(); $this->_contactID = $this->_membershipContactID = $this->getContactID(); $this->_mid = NULL; @@ -269,13 +301,14 @@ public function preProcess() { $this->_fields = $this->get('fields'); $this->_bltID = $this->get('bltID'); $this->_paymentProcessor = $this->get('paymentProcessor'); + $this->_priceSetId = $this->get('priceSetId'); $this->_priceSet = $this->get('priceSet'); if (!$this->_values) { // get all the values from the dao object - $this->_values = array(); - $this->_fields = array(); + $this->_values = []; + $this->_fields = []; CRM_Contribute_BAO_ContributionPage::setValues($this->_id, $this->_values); if (CRM_Financial_BAO_FinancialType::isACLFinancialTypeStatus() @@ -287,23 +320,38 @@ public function preProcess() { throw new CRM_Contribute_Exception_InactiveContributionPageException(ts('The page you requested is currently unavailable.'), $this->_id); } + $endDate = CRM_Utils_Date::processDate(CRM_Utils_Array::value('end_date', $this->_values)); + $now = date('YmdHis'); + if ($endDate && $endDate < $now) { + throw new CRM_Contribute_Exception_PastContributionPageException(ts('The page you requested has past its end date on ' . CRM_Utils_Date::customFormat($endDate)), $this->_id); + } + + $startDate = CRM_Utils_Date::processDate(CRM_Utils_Array::value('start_date', $this->_values)); + if ($startDate && $startDate > $now) { + throw new CRM_Contribute_Exception_FutureContributionPageException(ts('The page you requested will be active from ' . CRM_Utils_Date::customFormat($startDate)), $this->_id); + } + $this->assignBillingType(); // check for is_monetary status $isMonetary = CRM_Utils_Array::value('is_monetary', $this->_values); $isPayLater = CRM_Utils_Array::value('is_pay_later', $this->_values); - if (!empty($this->_ccid) && $isPayLater) { - $isPayLater = FALSE; - $this->_values['is_pay_later'] = FALSE; + if (!empty($this->_ccid)) { + $this->_values['financial_type_id'] = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_Contribution', + $this->_ccid, + 'financial_type_id' + ); + if ($isPayLater) { + $isPayLater = FALSE; + $this->_values['is_pay_later'] = FALSE; + } } - if ($isMonetary && - (!$isPayLater || !empty($this->_values['payment_processor'])) - ) { - $this->_paymentProcessorIDs = explode( + if ($isMonetary) { + $this->_paymentProcessorIDs = array_filter(explode( CRM_Core_DAO::VALUE_SEPARATOR, CRM_Utils_Array::value('payment_processor', $this->_values) - ); + )); $this->assignPaymentProcessor($isPayLater); } @@ -313,11 +361,11 @@ public function preProcess() { CRM_Price_BAO_PriceSet::initSet($this, $this->_id, 'civicrm_contribution_page'); // this avoids getting E_NOTICE errors in php - $setNullFields = array( + $setNullFields = [ 'amount_block_is_active', 'is_allow_other_amount', 'footer_text', - ); + ]; foreach ($setNullFields as $f) { if (!isset($this->_values[$f])) { $this->_values[$f] = NULL; @@ -423,20 +471,17 @@ public function preProcess() { CRM_Utils_Array::value('cancelSubscriptionUrl', $this->_values) ); - // assigning title to template in case someone wants to use it, also setting CMS page title - if ($this->_pcpId) { - $this->assign('title', $this->_pcpInfo['title']); - CRM_Utils_System::setTitle($this->_pcpInfo['title']); - } - else { - $this->assign('title', $this->_values['title']); - CRM_Utils_System::setTitle($this->_values['title']); - } - $this->_defaults = array(); + $this->setTitle(($this->_pcpId ? $this->_pcpInfo['title'] : $this->_values['title'])); + $this->_defaults = []; $this->_amount = $this->get('amount'); + // Assigning this to the template means it will be passed through to the payment form. + // This can, for example, by used by payment processors using client side encryption + $this->assign('currency', $this->getCurrency()); //CRM-6907 + // these lines exist to support a non-default currenty on the form but are probably + // obsolete & meddling wth the defaultCurrency is not the right approach.... $config = CRM_Core_Config::singleton(); $config->defaultCurrency = CRM_Utils_Array::value('currency', $this->_values, @@ -450,7 +495,7 @@ public function preProcess() { } //do check for cancel recurring and clean db, CRM-7696 - if (CRM_Utils_Request::retrieve('cancel', 'Boolean', CRM_Core_DAO::$_nullObject)) { + if (CRM_Utils_Request::retrieve('cancel', 'Boolean')) { self::cancelRecurring(); } @@ -472,33 +517,26 @@ public function setDefaultValues() { * Assign the minimal set of variables to the template. */ public function assignToTemplate() { - $name = CRM_Utils_Array::value('billing_first_name', $this->_params); - if (!empty($this->_params['billing_middle_name'])) { - $name .= " {$this->_params['billing_middle_name']}"; - } - $name .= ' ' . CRM_Utils_Array::value('billing_last_name', $this->_params); - $name = trim($name); - $this->assign('billingName', $name); - $this->set('name', $name); + $this->set('name', $this->assignBillingName($this->_params)); $this->assign('paymentProcessor', $this->_paymentProcessor); - $vars = array( + $vars = [ 'amount', 'currencyID', 'credit_card_type', 'trxn_id', 'amount_level', - ); + ]; $config = CRM_Core_Config::singleton(); if (isset($this->_values['is_recur']) && !empty($this->_paymentProcessor['is_recur'])) { $this->assign('is_recur_enabled', 1); - $vars = array_merge($vars, array( + $vars = array_merge($vars, [ 'is_recur', 'frequency_interval', 'frequency_unit', 'installments', - )); + ]); } if (in_array('CiviPledge', $config->enableComponents) && @@ -506,12 +544,12 @@ public function assignToTemplate() { ) { $this->assign('pledge_enabled', 1); - $vars = array_merge($vars, array( + $vars = array_merge($vars, [ 'is_pledge', 'pledge_frequency_interval', 'pledge_frequency_unit', 'pledge_installments', - )); + ]); } // @todo - stop setting amount level in this function & call the CRM_Price_BAO_PriceSet::getAmountLevel @@ -531,61 +569,17 @@ public function assignToTemplate() { } } - // assign the address formatted up for display - $addressParts = array( - "street_address-{$this->_bltID}", - "city-{$this->_bltID}", - "postal_code-{$this->_bltID}", - "state_province-{$this->_bltID}", - "country-{$this->_bltID}", - ); - - $addressFields = array(); - foreach ($addressParts as $part) { - list($n, $id) = explode('-', $part); - $addressFields[$n] = CRM_Utils_Array::value('billing_' . $part, $this->_params); - } - - $this->assign('address', CRM_Utils_Address::format($addressFields)); + $this->assign('address', CRM_Utils_Address::getFormattedBillingAddressFieldsFromParameters( + $this->_params, + $this->_bltID + )); if (!empty($this->_params['onbehalf_profile_id']) && !empty($this->_params['onbehalf'])) { $this->assign('onBehalfName', $this->_params['organization_name']); $locTypeId = array_keys($this->_params['onbehalf_location']['email']); $this->assign('onBehalfEmail', $this->_params['onbehalf_location']['email'][$locTypeId[0]]['email']); } - - //fix for CRM-3767 - $assignCCInfo = FALSE; - if ($this->_amount > 0.0) { - $assignCCInfo = TRUE; - } - elseif (!empty($this->_params['selectMembership'])) { - $memFee = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType', $this->_params['selectMembership'], 'minimum_fee'); - if ($memFee > 0.0) { - $assignCCInfo = TRUE; - } - } - - // The concept of contributeMode is deprecated. - // The payment processor object can provide info about the fields it shows. - if ($this->_contributeMode == 'direct' && $assignCCInfo) { - if ($this->_paymentProcessor && - $this->_paymentProcessor['payment_type'] & CRM_Core_Payment::PAYMENT_TYPE_DIRECT_DEBIT - ) { - $this->assign('account_holder', $this->_params['account_holder']); - $this->assign('bank_identification_number', $this->_params['bank_identification_number']); - $this->assign('bank_name', $this->_params['bank_name']); - $this->assign('bank_account_number', $this->_params['bank_account_number']); - } - else { - $date = CRM_Utils_Date::format(CRM_Utils_Array::value('credit_card_exp_date', $this->_params)); - $date = CRM_Utils_Date::mysqlToIso($date); - $this->assign('credit_card_exp_date', $date); - $this->assign('credit_card_number', - CRM_Utils_System::mungeCreditCard(CRM_Utils_Array::value('credit_card_number', $this->_params)) - ); - } - } + $this->assignPaymentFields(); $this->assign('email', $this->controller->exportValue('Main', "email-{$this->_bltID}") @@ -612,7 +606,7 @@ public function buildCustom($id, $name, $viewOnly = FALSE, $profileContactType = // we don't allow conflicting fields to be // configured via profile - CRM 2100 - $fieldsToIgnore = array( + $fieldsToIgnore = [ 'receive_date' => 1, 'trxn_id' => 1, 'invoice_id' => 1, @@ -622,20 +616,26 @@ public function buildCustom($id, $name, $viewOnly = FALSE, $profileContactType = 'total_amount' => 1, 'amount_level' => 1, 'contribution_status_id' => 1, + // @todo replace payment_instrument with payment instrument id. + // both are available now but the id field is the most consistent. 'payment_instrument' => 1, - 'check_number' => 1, + 'payment_instrument_id' => 1, + 'contribution_check_number' => 1, 'financial_type' => 1, - ); + ]; $fields = CRM_Core_BAO_UFGroup::getFields($id, FALSE, CRM_Core_Action::ADD, NULL, NULL, FALSE, NULL, FALSE, NULL, CRM_Core_Permission::CREATE, NULL ); if ($fields) { - // unset any email-* fields since we already collect it, CRM-2888 - foreach (array_keys($fields) as $fieldName) { - if (substr($fieldName, 0, 6) == 'email-' && !in_array($profileContactType, array('honor', 'onbehalf'))) { - unset($fields[$fieldName]); + // determine if email exists in profile so we know if we need to manually insert CRM-2888, CRM-15067 + foreach ($fields as $key => $field) { + if (substr($key, 0, 6) == 'email-' && + !in_array($profileContactType, ['honor', 'onbehalf']) + ) { + $this->_emailExists = TRUE; + $this->set('emailExists', TRUE); } } @@ -644,30 +644,55 @@ public function buildCustom($id, $name, $viewOnly = FALSE, $profileContactType = CRM_Core_Session::setStatus(ts('Some of the profile fields cannot be configured for this page.'), ts('Warning'), 'alert'); } - $fields = array_diff_assoc($fields, $this->_fields); + //remove common fields only if profile is not configured for onbehalf/honor + if (!in_array($profileContactType, ['honor', 'onbehalf'])) { + $fields = array_diff_key($fields, $this->_fields); + } CRM_Core_BAO_Address::checkContactSharedAddressFields($fields, $contactID); $addCaptcha = FALSE; + // fetch file preview when not submitted yet, like in online contribution Confirm and ThankYou page + $viewOnlyFileValues = empty($profileContactType) ? [] : [$profileContactType => []]; foreach ($fields as $key => $field) { if ($viewOnly && isset($field['data_type']) && $field['data_type'] == 'File' || ($viewOnly && $field['name'] == 'image_URL') ) { - // ignore file upload fields - continue; - } + //retrieve file value from submitted values on basis of $profileContactType + $fileValue = CRM_Utils_Array::value($key, $this->_params); + if (!empty($profileContactType) && !empty($this->_params[$profileContactType])) { + $fileValue = CRM_Utils_Array::value($key, $this->_params[$profileContactType]); + } + + if ($fileValue) { + $path = CRM_Utils_Array::value('name', $fileValue); + $fileType = CRM_Utils_Array::value('type', $fileValue); + $fileValue = CRM_Utils_File::getFileURL($path, $fileType); + } + + // format custom file value fetched from submitted value + if ($profileContactType) { + $viewOnlyFileValues[$profileContactType][$key] = $fileValue; + } + else { + $viewOnlyFileValues[$key] = $fileValue; + } + // On viewOnly use-case (as in online contribution Confirm page) we no longer need to set + // required property because being required file is already uploaded while registration + $field['is_required'] = FALSE; + } if ($profileContactType) { //Since we are showing honoree name separately so we are removing it from honoree profile just for display if ($profileContactType == 'honor') { - $honoreeNamefields = array( + $honoreeNamefields = [ 'prefix_id', 'first_name', 'last_name', 'suffix_id', 'organization_name', 'household_name', - ); + ]; if (in_array($field['name'], $honoreeNamefields)) { unset($fields[$field['name']]); continue; @@ -706,15 +731,103 @@ public function buildCustom($id, $name, $viewOnly = FALSE, $profileContactType = $this->assign($name, $fields); + if ($profileContactType && count($viewOnlyFileValues[$profileContactType])) { + $this->assign('viewOnlyPrefixFileValues', $viewOnlyFileValues); + } + elseif (count($viewOnlyFileValues)) { + $this->assign('viewOnlyFileValues', $viewOnlyFileValues); + } + if ($addCaptcha && !$viewOnly) { - $captcha = CRM_Utils_ReCAPTCHA::singleton(); - $captcha->add($this); - $this->assign('isCaptcha', TRUE); + $this->enableCaptchaOnForm(); } } } } + /** + * Enable ReCAPTCHA on Contribution form + */ + protected function enableCaptchaOnForm() { + $captcha = CRM_Utils_ReCAPTCHA::singleton(); + if ($captcha->hasSettingsAvailable()) { + $captcha->add($this); + $this->assign('isCaptcha', TRUE); + } + } + + public function assignPaymentFields() { + //fix for CRM-3767 + $isMonetary = FALSE; + if ($this->_amount > 0.0) { + $isMonetary = TRUE; + } + elseif (!empty($this->_params['selectMembership'])) { + $memFee = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType', $this->_params['selectMembership'], 'minimum_fee'); + if ($memFee > 0.0) { + $isMonetary = TRUE; + } + } + + // The concept of contributeMode is deprecated. + // The payment processor object can provide info about the fields it shows. + if ($isMonetary && is_a($this->_paymentProcessor['object'], 'CRM_Core_Payment')) { + /** @var $paymentProcessorObject \CRM_Core_Payment */ + $paymentProcessorObject = $this->_paymentProcessor['object']; + + $paymentFields = $paymentProcessorObject->getPaymentFormFields(); + foreach ($paymentFields as $index => $paymentField) { + if (!isset($this->_params[$paymentField])) { + unset($paymentFields[$index]); + continue; + } + if ($paymentField === 'credit_card_exp_date') { + $date = CRM_Utils_Date::format(CRM_Utils_Array::value('credit_card_exp_date', $this->_params)); + $date = CRM_Utils_Date::mysqlToIso($date); + $this->assign('credit_card_exp_date', $date); + } + elseif ($paymentField === 'credit_card_number') { + $this->assign('credit_card_number', + CRM_Utils_System::mungeCreditCard(CRM_Utils_Array::value('credit_card_number', $this->_params)) + ); + } + elseif ($paymentField === 'credit_card_type') { + $this->assign('credit_card_type', CRM_Core_PseudoConstant::getLabel( + 'CRM_Core_BAO_FinancialTrxn', + 'card_type_id', + CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_FinancialTrxn', 'card_type_id', $this->_params['credit_card_type']) + )); + } + else { + $this->assign($paymentField, $this->_params[$paymentField]); + } + } + $this->assign('paymentFieldsetLabel', CRM_Core_Payment_Form::getPaymentLabel($paymentProcessorObject)); + $this->assign('paymentFields', $paymentFields); + + } + } + + /** + * Display ReCAPTCHA warning on Contribution form + */ + protected function displayCaptchaWarning() { + if (CRM_Core_Permission::check("administer CiviCRM")) { + $captcha = CRM_Utils_ReCAPTCHA::singleton(); + if (!$captcha->hasSettingsAvailable()) { + $this->assign('displayCaptchaWarning', TRUE); + } + } + } + + /** + * Check if ReCAPTCHA has to be added on Contribution form forcefully. + */ + protected function hasToAddForcefully() { + $captcha = CRM_Utils_ReCAPTCHA::singleton(); + return $captcha->hasToAddForcefully(); + } + /** * Add onbehalf/honoree profile fields and native module fields. * @@ -728,7 +841,7 @@ public function buildComponentForm($id, $form) { $contactID = $this->getContactID(); - foreach (array('soft_credit', 'on_behalf') as $module) { + foreach (['soft_credit', 'on_behalf'] as $module) { if ($module == 'soft_credit') { if (empty($form->_values['honoree_profile_id'])) { continue; @@ -739,17 +852,17 @@ public function buildComponentForm($id, $form) { } $profileContactType = CRM_Core_BAO_UFGroup::getContactType($form->_values['honoree_profile_id']); - $requiredProfileFields = array( - 'Individual' => array('first_name', 'last_name'), - 'Organization' => array('organization_name', 'email'), - 'Household' => array('household_name', 'email'), - ); + $requiredProfileFields = [ + 'Individual' => ['first_name', 'last_name'], + 'Organization' => ['organization_name', 'email'], + 'Household' => ['household_name', 'email'], + ]; $validProfile = CRM_Core_BAO_UFGroup::checkValidProfile($form->_values['honoree_profile_id'], $requiredProfileFields[$profileContactType]); if (!$validProfile) { CRM_Core_Error::fatal(ts('This contribution page has been configured for contribution on behalf of honoree and the required fields of the selected honoree profile are disabled or doesn\'t exist.')); } - foreach (array('honor_block_title', 'honor_block_text') as $name) { + foreach (['honor_block_title', 'honor_block_text'] as $name) { $form->assign($name, $form->_values[$name]); } @@ -795,11 +908,13 @@ public function buildComponentForm($id, $form) { if (empty($member['is_active'])) { $msg = ts('Mixed profile not allowed for on behalf of registration/sign up.'); $onBehalfProfile = CRM_Core_BAO_UFGroup::profileGroups($form->_values['onbehalf_profile_id']); - foreach (array( + foreach ( + [ 'Individual', 'Organization', 'Household', - ) as $contactType) { + ] as $contactType + ) { if (in_array($contactType, $onBehalfProfile) && (in_array('Membership', $onBehalfProfile) || in_array('Contribution', $onBehalfProfile) @@ -816,17 +931,17 @@ public function buildComponentForm($id, $form) { if (count($organizations)) { // Related org url - pass checksum if needed - $args = array( + $args = [ 'ufId' => $form->_values['onbehalf_profile_id'], 'cid' => '', - ); + ]; if (!empty($_GET['cs'])) { - $args = array( + $args = [ 'ufId' => $form->_values['onbehalf_profile_id'], 'uid' => $this->_contactID, 'cs' => $_GET['cs'], 'cid' => '', - ); + ]; } $locDataURL = CRM_Utils_System::url('civicrm/ajax/permlocation', $args, FALSE, NULL, FALSE); $form->assign('locDataURL', $locDataURL); @@ -834,16 +949,16 @@ public function buildComponentForm($id, $form) { if (count($organizations) > 0) { $form->add('select', 'onbehalfof_id', '', CRM_Utils_Array::collect('name', $organizations)); - $orgOptions = array( + $orgOptions = [ 0 => ts('Select an existing organization'), 1 => ts('Enter a new organization'), - ); + ]; $form->addRadio('org_option', ts('options'), $orgOptions); - $form->setDefaults(array('org_option' => 0)); + $form->setDefaults(['org_option' => 0]); } } - $form->assign('fieldSetTitle', ts('Organization Details')); + $form->assign('fieldSetTitle', ts(CRM_Core_BAO_UFGroup::getTitle($form->_values['onbehalf_profile_id']))); if (CRM_Utils_Array::value('is_for_organization', $form->_values)) { if ($form->_values['is_for_organization'] == 2) { @@ -869,17 +984,20 @@ public function buildComponentForm($id, $form) { if (!empty($form->_submitValues['onbehalfof_id'])) { $form->assign('submittedOnBehalf', $form->_submitValues['onbehalfof_id']); } - $form->assign('submittedOnBehalfInfo', json_encode($form->_submitValues['onbehalf'])); + $form->assign('submittedOnBehalfInfo', json_encode(str_replace('"', '\"', $form->_submitValues['onbehalf']), JSON_HEX_APOS)); } - $fieldTypes = array('Contact', 'Organization'); + $fieldTypes = ['Contact', 'Organization']; + if (!empty($form->_membershipBlock)) { + $fieldTypes = array_merge($fieldTypes, ['Membership']); + } $contactSubType = CRM_Contact_BAO_ContactType::subTypes('Organization'); $fieldTypes = array_merge($fieldTypes, $contactSubType); foreach ($profileFields as $name => $field) { if (in_array($field['field_type'], $fieldTypes)) { list($prefixName, $index) = CRM_Utils_System::explode('-', $name, 2); - if (in_array($prefixName, array('organization_name', 'email')) && empty($field['is_required'])) { + if (in_array($prefixName, ['organization_name', 'email']) && empty($field['is_required'])) { $field['is_required'] = 1; } if (count($form->_submitValues) && @@ -947,18 +1065,18 @@ public function authenticatePledgeUser() { $contactID = CRM_Utils_Request::retrieve('cid', 'Positive', $this); //get pledge status and contact id - $pledgeValues = array(); - $pledgeParams = array('id' => $this->_values['pledge_id']); - $returnProperties = array('contact_id', 'status_id'); + $pledgeValues = []; + $pledgeParams = ['id' => $this->_values['pledge_id']]; + $returnProperties = ['contact_id', 'status_id']; CRM_Core_DAO::commonRetrieve('CRM_Pledge_DAO_Pledge', $pledgeParams, $pledgeValues, $returnProperties); //get all status $allStatus = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name'); - $validStatus = array( + $validStatus = [ array_search('Pending', $allStatus), array_search('In Progress', $allStatus), array_search('Overdue', $allStatus), - ); + ]; $validUser = FALSE; if ($this->_userID && @@ -983,7 +1101,7 @@ public function authenticatePledgeUser() { //check for valid pledge status. if (!in_array($pledgeValues['status_id'], $validStatus)) { - CRM_Core_Error::fatal(ts('Oops. You cannot make a payment for this pledge - pledge status is %1.', array(1 => CRM_Utils_Array::value($pledgeValues['status_id'], $allStatus)))); + CRM_Core_Error::fatal(ts('Oops. You cannot make a payment for this pledge - pledge status is %1.', [1 => CRM_Utils_Array::value($pledgeValues['status_id'], $allStatus)])); } } @@ -995,15 +1113,15 @@ public function authenticatePledgeUser() { * lets delete the recurring and related contribution. */ public function cancelRecurring() { - $isCancel = CRM_Utils_Request::retrieve('cancel', 'Boolean', CRM_Core_DAO::$_nullObject); + $isCancel = CRM_Utils_Request::retrieve('cancel', 'Boolean'); if ($isCancel) { - $isRecur = CRM_Utils_Request::retrieve('isRecur', 'Boolean', CRM_Core_DAO::$_nullObject); - $recurId = CRM_Utils_Request::retrieve('recurId', 'Positive', CRM_Core_DAO::$_nullObject); + $isRecur = CRM_Utils_Request::retrieve('isRecur', 'Boolean'); + $recurId = CRM_Utils_Request::retrieve('recurId', 'Positive'); //clean db for recurring contribution. if ($isRecur && $recurId) { CRM_Contribute_BAO_ContributionRecur::deleteRecurContribution($recurId); } - $contribId = CRM_Utils_Request::retrieve('contribId', 'Positive', CRM_Core_DAO::$_nullObject); + $contribId = CRM_Utils_Request::retrieve('contribId', 'Positive'); if ($contribId) { CRM_Contribute_BAO_Contribution::deleteContribution($contribId); } @@ -1018,7 +1136,7 @@ public function cancelRecurring() { * @param bool $isContributionMainPage * Is this the main page? If so add form input fields. * (or better yet don't have this functionality in a function shared with forms that don't share it). - * @param int $selectedMembershipTypeID + * @param int|array $selectedMembershipTypeID * Selected membership id. * @param bool $thankPage * Thank you page. @@ -1037,13 +1155,13 @@ protected function buildMembershipBlock( $separateMembershipPayment = FALSE; if ($this->_membershipBlock) { - $this->_currentMemberships = array(); + $this->_currentMemberships = []; - $membershipTypeIds = $membershipTypes = $radio = array(); + $membershipTypeIds = $membershipTypes = $radio = []; $membershipPriceset = (!empty($this->_priceSetId) && $this->_useForMember) ? TRUE : FALSE; $allowAutoRenewMembership = $autoRenewOption = FALSE; - $autoRenewMembershipTypeOptions = array(); + $autoRenewMembershipTypeOptions = []; $separateMembershipPayment = CRM_Utils_Array::value('is_separate_payment', $this->_membershipBlock); @@ -1079,6 +1197,18 @@ protected function buildMembershipBlock( $membershipTypeValues = CRM_Member_BAO_Membership::buildMembershipTypeValues($this, $membershipTypeIds); $this->_membershipTypeValues = $membershipTypeValues; $endDate = NULL; + + // Check if we support auto-renew on this contribution page + // FIXME: If any of the payment processors do NOT support recurring you cannot setup an + // auto-renew payment even if that processor is not selected. + $allowAutoRenewOpt = TRUE; + if (is_array($this->_paymentProcessors)) { + foreach ($this->_paymentProcessors as $id => $val) { + if ($id && !$val['is_recur']) { + $allowAutoRenewOpt = FALSE; + } + } + } foreach ($membershipTypeIds as $value) { $memType = $membershipTypeValues[$value]; if ($selectedMembershipTypeID != NULL) { @@ -1101,23 +1231,16 @@ protected function buildMembershipBlock( } } elseif ($memType['is_active']) { - $javascriptMethod = NULL; - $allowAutoRenewOpt = (int) $memType['auto_renew']; - if (is_array($this->_paymentProcessors)) { - foreach ($this->_paymentProcessors as $id => $val) { - if ($id && !$val['is_recur']) { - $allowAutoRenewOpt = 0; - continue; - } - } - } - - $javascriptMethod = array('onclick' => "return showHideAutoRenew( this.value );"); - $autoRenewMembershipTypeOptions["autoRenewMembershipType_{$value}"] = (int) $allowAutoRenewOpt * CRM_Utils_Array::value($value, CRM_Utils_Array::value('auto_renew', $this->_membershipBlock));; if ($allowAutoRenewOpt) { + $javascriptMethod = ['onclick' => "return showHideAutoRenew( this.value );"]; + $autoRenewMembershipTypeOptions["autoRenewMembershipType_{$value}"] = (int) $memType['auto_renew'] * CRM_Utils_Array::value($value, CRM_Utils_Array::value('auto_renew', $this->_membershipBlock)); $allowAutoRenewMembership = TRUE; } + else { + $javascriptMethod = NULL; + $autoRenewMembershipTypeOptions["autoRenewMembershipType_{$value}"] = 0; + } //add membership type. $radio[$memType['id']] = $this->createElement('radio', NULL, NULL, NULL, @@ -1172,6 +1295,17 @@ protected function buildMembershipBlock( $takeUserSubmittedAutoRenew = (!empty($_POST) || $this->isSubmitted()) ? TRUE : FALSE; $this->assign('takeUserSubmittedAutoRenew', $takeUserSubmittedAutoRenew); + // Assign autorenew option (0:hide,1:optional,2:required) so we can use it in confirmation etc. + $autoRenewOption = CRM_Price_BAO_PriceSet::checkAutoRenewForPriceSet($this->_priceSetId); + //$selectedMembershipTypeID is retrieved as an array for membership priceset if multiple + //options for different organisation is selected on the contribution page. + if (is_numeric($selectedMembershipTypeID) && isset($membershipTypeValues[$selectedMembershipTypeID]['auto_renew'])) { + $this->assign('autoRenewOption', $membershipTypeValues[$selectedMembershipTypeID]['auto_renew']); + } + else { + $this->assign('autoRenewOption', $autoRenewOption); + } + if ($isContributionMainPage) { if (!$membershipPriceset) { if (!$this->_membershipBlock['is_required']) { @@ -1181,7 +1315,7 @@ protected function buildMembershipBlock( } elseif ($this->_membershipBlock['is_required'] && count($radio) == 1) { $temp = array_keys($radio); - $this->add('hidden', 'selectMembership', $temp[0], array('id' => 'selectMembership')); + $this->add('hidden', 'selectMembership', $temp[0], ['id' => 'selectMembership']); $this->assign('singleMembership', TRUE); $this->assign('showRadio', FALSE); } @@ -1191,13 +1325,14 @@ protected function buildMembershipBlock( $this->addRule('selectMembership', ts('Please select one of the memberships.'), 'required'); } - else { - $autoRenewOption = CRM_Price_BAO_PriceSet::checkAutoRenewForPriceSet($this->_priceSetId); - $this->assign('autoRenewOption', $autoRenewOption); - } if ((!$this->_values['is_pay_later'] || is_array($this->_paymentProcessors)) && ($allowAutoRenewMembership || $autoRenewOption)) { - $this->addElement('checkbox', 'auto_renew', ts('Please renew my membership automatically.')); + if ($autoRenewOption == 2) { + $this->addElement('hidden', 'auto_renew', ts('Please renew my membership automatically.')); + } + else { + $this->addElement('checkbox', 'auto_renew', ts('Please renew my membership automatically.')); + } } } @@ -1257,4 +1392,16 @@ protected function setRecurringMembershipParams() { } } + /** + * Get the payment processor object for the submission, returning the manual one for offline payments. + * + * @return CRM_Core_Payment + */ + protected function getPaymentProcessorObject() { + if (!empty($this->_paymentProcessor)) { + return $this->_paymentProcessor['object']; + } + return new CRM_Core_Payment_Manual(); + } + } diff --git a/CRM/Contribute/Form/ContributionCharts.php b/CRM/Contribute/Form/ContributionCharts.php index 90738e91aac2..ac5866da5bd0 100644 --- a/CRM/Contribute/Form/ContributionCharts.php +++ b/CRM/Contribute/Form/ContributionCharts.php @@ -1,9 +1,9 @@ addElement('select', 'chart_type', ts('Chart Style'), array( - 'bvg' => ts('Bar'), - 'p3' => ts('Pie'), - ) - ); + $this->addElement('select', 'chart_type', ts('Chart Style'), [ + 'bvg' => ts('Bar'), + 'p3' => ts('Pie'), + ]); $defaultValues['chart_type'] = $this->_chartType; $this->setDefaults($defaultValues); //take available years from database to show in drop down $currentYear = date('Y'); - $years = array(); + $years = []; if (!empty($this->_years)) { if (!array_key_exists($currentYear, $this->_years)) { $this->_years[$currentYear] = $currentYear; @@ -87,9 +86,9 @@ public function buildQuickForm() { } $this->addElement('select', 'select_year', ts('Select Year (for monthly breakdown)'), $years); - $this->setDefaults(array( + $this->setDefaults([ 'select_year' => ($this->_year) ? $this->_year : $currentYear, - )); + ]); } /** @@ -109,7 +108,7 @@ public function postProcess() { //take contribution information monthly $chartInfoMonthly = CRM_Contribute_BAO_Contribution_Utils::contributionChartMonthly($selectedYear); - $chartData = $abbrMonthNames = array(); + $chartData = $abbrMonthNames = []; if (is_array($chartInfoMonthly)) { for ($i = 1; $i <= 12; $i++) { $abbrMonthNames[$i] = strftime('%b', mktime(0, 0, 0, $i, 10, 1970)); @@ -186,7 +185,7 @@ public function postProcess() { $urlParams = NULL; if ($chartKey == 'by_month') { $monthPosition = array_search($index, $abbrMonthNames); - $startDate = CRM_Utils_Date::format(array('Y' => $selectedYear, 'M' => $monthPosition)); + $startDate = CRM_Utils_Date::format(['Y' => $selectedYear, 'M' => $monthPosition]); $endDate = date('Ymd', mktime(0, 0, 0, $monthPosition + 1, 0, $selectedYear)); $urlParams = "reset=1&force=1&status=1&start={$startDate}&end={$endDate}&test=0"; } @@ -196,7 +195,7 @@ public function postProcess() { $endDate = date('Ymd', mktime(0, 0, 0, $config->fiscalYearStart['M'], $config->fiscalYearStart['d'], (substr($index, 0, 4)) + 1)); } else { - $startDate = CRM_Utils_Date::format(array('Y' => substr($index, 0, 4))); + $startDate = CRM_Utils_Date::format(['Y' => substr($index, 0, 4)]); $endDate = date('Ymd', mktime(0, 0, 0, 13, 0, substr($index, 0, 4))); } $urlParams = "reset=1&force=1&status=1&start={$startDate}&end={$endDate}&test=0"; @@ -225,7 +224,7 @@ public function postProcess() { $xSize = 150; } } - $values['size'] = array('xSize' => $xSize, 'ySize' => $ySize); + $values['size'] = ['xSize' => $xSize, 'ySize' => $ySize]; } // finally assign this chart data to template. diff --git a/CRM/Contribute/Form/ContributionPage.php b/CRM/Contribute/Form/ContributionPage.php index 07e731b5e526..a7e95aedfe8d 100644 --- a/CRM/Contribute/Form/ContributionPage.php +++ b/CRM/Contribute/Form/ContributionPage.php @@ -1,9 +1,9 @@ _action == CRM_Core_Action::UPDATE) { - CRM_Utils_System::setTitle(ts('Configure Page - %1', array(1 => $title))); + CRM_Utils_System::setTitle(ts('Configure Page - %1', [1 => $title])); } elseif ($this->_action == CRM_Core_Action::VIEW) { - CRM_Utils_System::setTitle(ts('Preview Page - %1', array(1 => $title))); + CRM_Utils_System::setTitle(ts('Preview Page - %1', [1 => $title])); } elseif ($this->_action == CRM_Core_Action::DELETE) { - CRM_Utils_System::setTitle(ts('Delete Page - %1', array(1 => $title))); + CRM_Utils_System::setTitle(ts('Delete Page - %1', [1 => $title])); } //cache values. $this->_values = $this->get('values'); if (!is_array($this->_values)) { - $this->_values = array(); + $this->_values = []; if (isset($this->_id) && $this->_id) { - $params = array('id' => $this->_id); + $params = ['id' => $this->_id]; CRM_Core_DAO::commonRetrieve('CRM_Contribute_DAO_ContributionPage', $params, $this->_values); CRM_Contribute_BAO_ContributionPage::setValues($this->_id, $this->_values); } @@ -150,7 +150,7 @@ public function preProcess() { } // Preload libraries required by the "Profiles" tab - $schemas = array('IndividualModel', 'OrganizationModel', 'ContributionModel'); + $schemas = ['IndividualModel', 'OrganizationModel', 'ContributionModel']; if (in_array('CiviMember', CRM_Core_Config::singleton()->enableComponents)) { $schemas[] = 'MembershipModel'; } @@ -176,53 +176,53 @@ public function buildQuickForm() { } if ($this->_single) { - $buttons = array( - array( + $buttons = [ + [ 'type' => 'next', 'name' => ts('Save'), 'spacing' => '         ', 'isDefault' => TRUE, - ), - array( + ], + [ 'type' => 'upload', 'name' => ts('Save and Done'), 'spacing' => '         ', 'subName' => 'done', - ), - ); + ], + ]; if (!$this->_last) { - $buttons[] = array( + $buttons[] = [ 'type' => 'submit', 'name' => ts('Save and Next'), 'spacing' => '                 ', 'subName' => 'savenext', - ); + ]; } - $buttons[] = array( + $buttons[] = [ 'type' => 'cancel', 'name' => ts('Cancel'), - ); + ]; $this->addButtons($buttons); } else { - $buttons = array(); + $buttons = []; if (!$this->_first) { - $buttons[] = array( + $buttons[] = [ 'type' => 'back', 'name' => ts('Previous'), 'spacing' => '     ', - ); + ]; } - $buttons[] = array( + $buttons[] = [ 'type' => 'next', 'name' => ts('Continue'), 'spacing' => '         ', 'isDefault' => TRUE, - ); - $buttons[] = array( + ]; + $buttons[] = [ 'type' => 'cancel', 'name' => ts('Cancel'), - ); + ]; $this->addButtons($buttons); } @@ -231,7 +231,7 @@ public function buildQuickForm() { // views are implemented as frozen form if ($this->_action & CRM_Core_Action::VIEW) { $this->freeze(); - $this->addElement('button', 'done', ts('Done'), array('onclick' => "location.href='civicrm/admin/custom/group?reset=1&action=browse'")); + $this->addElement('button', 'done', ts('Done'), ['onclick' => "location.href='civicrm/admin/custom/group?reset=1&action=browse'"]); } // don't show option for contribution amounts section if membership price set @@ -251,7 +251,7 @@ public function buildQuickForm() { } } // set value in DOM that membership price set exists - CRM_Core_Resources::singleton()->addSetting(array('memberPriceset' => $hasMembershipBlk)); + CRM_Core_Resources::singleton()->addSetting(['memberPriceset' => $hasMembershipBlk]); } /** @@ -266,9 +266,9 @@ public function setDefaultValues() { //some child classes calling setdefaults directly w/o preprocess. $this->_values = $this->get('values'); if (!is_array($this->_values)) { - $this->_values = array(); + $this->_values = []; if (isset($this->_id) && $this->_id) { - $params = array('id' => $this->_id); + $params = ['id' => $this->_id]; CRM_Core_DAO::commonRetrieve('CRM_Contribute_DAO_ContributionPage', $params, $this->_values); } $this->set('values', $this->_values); @@ -279,16 +279,16 @@ public function setDefaultValues() { if (isset($this->_id)) { //set defaults for pledgeBlock values. - $pledgeBlockParams = array( + $pledgeBlockParams = [ 'entity_id' => $this->_id, 'entity_table' => ts('civicrm_contribution_page'), - ); - $pledgeBlockDefaults = array(); + ]; + $pledgeBlockDefaults = []; CRM_Pledge_BAO_PledgeBlock::retrieve($pledgeBlockParams, $pledgeBlockDefaults); if ($this->_pledgeBlockID = CRM_Utils_Array::value('id', $pledgeBlockDefaults)) { $defaults['is_pledge_active'] = TRUE; } - $pledgeBlock = array( + $pledgeBlock = [ 'is_pledge_interval', 'max_reminders', 'initial_reminder_day', @@ -296,15 +296,15 @@ public function setDefaultValues() { 'pledge_start_date', 'is_pledge_start_date_visible', 'is_pledge_start_date_editable', - ); + ]; foreach ($pledgeBlock as $key) { $defaults[$key] = CRM_Utils_Array::value($key, $pledgeBlockDefaults); if ($key == 'pledge_start_date' && CRM_Utils_Array::value($key, $pledgeBlockDefaults)) { $defaultPledgeDate = (array) json_decode($pledgeBlockDefaults['pledge_start_date']); - $pledgeDateFields = array( + $pledgeDateFields = [ 'pledge_calendar_date' => 'calendar_date', 'pledge_calendar_month' => 'calendar_month', - ); + ]; $defaults['pledge_default_toggle'] = key($defaultPledgeDate); foreach ($pledgeDateFields as $key => $value) { if (array_key_exists($value, $defaultPledgeDate)) { @@ -332,18 +332,13 @@ public function setDefaultValues() { if ($this->_priceSetID) { $defaults['price_set_id'] = $this->_priceSetID; } - - if (!empty($defaults['end_date'])) { - list($defaults['end_date'], $defaults['end_date_time']) = CRM_Utils_Date::setDateDefaults($defaults['end_date']); - } - - if (!empty($defaults['start_date'])) { - list($defaults['start_date'], $defaults['start_date_time']) = CRM_Utils_Date::setDateDefaults($defaults['start_date']); - } } else { $defaults['is_active'] = 1; // set current date as start date + // @todo look to change to $defaults['start_date'] = date('Ymd His'); + // main settings form overrides this to implement above but this is left here + // 'in case' another extending form uses start_date - for now list($defaults['start_date'], $defaults['start_date_time']) = CRM_Utils_Date::setDateDefaults(); } @@ -353,8 +348,8 @@ public function setDefaultValues() { ), '1'); } else { - # CRM 10860 - $defaults['recur_frequency_unit'] = array('month' => 1); + // CRM-10860 + $defaults['recur_frequency_unit'] = ['month' => 1]; } // confirm page starts out enabled @@ -396,8 +391,7 @@ public function endPostProcess() { switch ($className) { case 'Contribute': $attributes = $this->getVar('_attributes'); - $subPage = strtolower(basename(CRM_Utils_Array::value('action', $attributes))); - $subPageName = ucfirst($subPage); + $subPage = CRM_Utils_Request::retrieveComponent($attributes); if ($subPage == 'friend') { $nextPage = 'custom'; } @@ -408,13 +402,11 @@ public function endPostProcess() { case 'MembershipBlock': $subPage = 'membership'; - $subPageName = 'MembershipBlock'; $nextPage = 'thankyou'; break; default: $subPage = strtolower($className); - $subPageName = $className; $nextPage = strtolower($nextPage); if ($subPage == 'amount') { @@ -427,7 +419,7 @@ public function endPostProcess() { } CRM_Core_Session::setStatus(ts("'%1' information has been saved.", - array(1 => $subPageName) + [1 => CRM_Utils_Array::value('title', CRM_Utils_Array::value($subPage, $this->get('tabHeader')), $className)] ), ts('Saved'), 'success'); $this->postProcessHook(); @@ -460,6 +452,7 @@ public function endPostProcess() { * * @return string */ + /** * @return string */ diff --git a/CRM/Contribute/Form/ContributionPage/AddProduct.php b/CRM/Contribute/Form/ContributionPage/AddProduct.php index b858afb07d86..4808e1f73bc3 100644 --- a/CRM/Contribute/Form/ContributionPage/AddProduct.php +++ b/CRM/Contribute/Form/ContributionPage/AddProduct.php @@ -1,9 +1,9 @@ _pid) { $dao = new CRM_Contribute_DAO_PremiumsProduct(); @@ -95,12 +95,12 @@ public function setDefaultValues() { $premiumID = $dao->id; $sql = 'SELECT max( weight ) as max_weight FROM civicrm_premiums_product WHERE premiums_id = %1'; - $params = array(1 => array($premiumID, 'Integer')); + $params = [1 => [$premiumID, 'Integer']]; $dao = CRM_Core_DAO::executeQuery($sql, $params); $dao->fetch(); $defaults['weight'] = $dao->max_weight + 1; } - RETURN $defaults; + return $defaults; } /** @@ -123,32 +123,30 @@ public function buildQuickForm() { CRM_Utils_System::redirect($url); } - $this->addButtons(array( - array( - 'type' => 'next', - 'name' => ts('Delete'), - 'spacing' => '    ', - 'isDefault' => TRUE, - ), - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'next', + 'name' => ts('Delete'), + 'spacing' => '    ', + 'isDefault' => TRUE, + ], + [ + 'type' => 'cancel', + 'name' => ts('Cancel'), + ], + ]); return; } if ($this->_action & CRM_Core_Action::PREVIEW) { CRM_Contribute_BAO_Premium::buildPremiumPreviewBlock($this, NULL, $this->_pid); - $this->addButtons(array( - array( - 'type' => 'next', - 'name' => ts('Done with Preview'), - 'isDefault' => TRUE, - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'next', + 'name' => ts('Done with Preview'), + 'isDefault' => TRUE, + ], + ]); return; } @@ -161,7 +159,7 @@ public function buildQuickForm() { $this->addElement('text', 'weight', ts('Order'), CRM_Core_DAO::getAttribute('CRM_Contribute_DAO_PremiumsProduct', 'weight')); $financialType = CRM_Contribute_PseudoConstant::financialType(); - $premiumFinancialType = array(); + $premiumFinancialType = []; CRM_Core_PseudoConstant::populate( $premiumFinancialType, 'CRM_Financial_DAO_EntityFinancialAccount', @@ -171,7 +169,7 @@ public function buildQuickForm() { 'account_relationship = 8' ); - $costFinancialType = array(); + $costFinancialType = []; CRM_Core_PseudoConstant::populate( $costFinancialType, 'CRM_Financial_DAO_EntityFinancialAccount', @@ -195,25 +193,24 @@ public function buildQuickForm() { 'select', 'financial_type_id', ts('Financial Type'), - array('' => ts('- select -')) + $financialType + ['' => ts('- select -')] + $financialType ); $this->addRule('weight', ts('Please enter integer value for weight'), 'integer'); $session->pushUserContext(CRM_Utils_System::url($urlParams, 'action=update&reset=1&id=' . $this->_id)); if ($this->_single) { - $this->addButtons(array( - array( - 'type' => 'next', - 'name' => ts('Save'), - 'spacing' => '    ', - 'isDefault' => TRUE, - ), - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'next', + 'name' => ts('Save'), + 'spacing' => '    ', + 'isDefault' => TRUE, + ], + [ + 'type' => 'cancel', + 'name' => ts('Cancel'), + ], + ]); } else { parent::buildQuickForm(); @@ -229,15 +226,12 @@ public function postProcess() { $urlParams = 'civicrm/admin/contribute/premium'; if ($this->_action & CRM_Core_Action::PREVIEW) { - $session = CRM_Core_Session::singleton(); $url = CRM_Utils_System::url($urlParams, 'reset=1&action=update&id=' . $this->_id); - $single = $session->get('singleForm'); CRM_Utils_System::redirect($url); return; } if ($this->_action & CRM_Core_Action::DELETE) { - $session = CRM_Core_Session::singleton(); $url = CRM_Utils_System::url($urlParams, 'reset=1&action=update&id=' . $this->_id); $dao = new CRM_Contribute_DAO_PremiumsProduct(); $dao->id = $this->_pid; @@ -246,7 +240,6 @@ public function postProcess() { CRM_Utils_System::redirect($url); } else { - $session = CRM_Core_Session::singleton(); $url = CRM_Utils_System::url($urlParams, 'reset=1&action=update&id=' . $this->_id); if ($this->_pid) { $params['id'] = $this->_pid; @@ -264,7 +257,7 @@ public function postProcess() { } // updateOtherWeights needs to filter on premiums_id - $filter = array('premiums_id' => $params['premiums_id']); + $filter = ['premiums_id' => $params['premiums_id']]; $params['weight'] = CRM_Utils_Weight::updateOtherWeights('CRM_Contribute_DAO_PremiumsProduct', $oldWeight, $params['weight'], $filter); $dao = new CRM_Contribute_DAO_PremiumsProduct(); diff --git a/CRM/Contribute/Form/ContributionPage/Amount.php b/CRM/Contribute/Form/ContributionPage/Amount.php index b646d04b94bf..ed7efb2d82e9 100644 --- a/CRM/Contribute/Form/ContributionPage/Amount.php +++ b/CRM/Contribute/Form/ContributionPage/Amount.php @@ -1,9 +1,9 @@ addElement('checkbox', 'is_allow_other_amount', ts('Allow other amounts'), NULL, array('onclick' => "minMax(this);showHideAmountBlock( this, 'is_allow_other_amount' );")); - $this->add('text', 'min_amount', ts('Minimum Amount'), array('size' => 8, 'maxlength' => 8)); - $this->addRule('min_amount', ts('Please enter a valid money value (e.g. %1).', array(1 => CRM_Utils_Money::format('9.99', ' '))), 'money'); + $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->add('text', 'max_amount', ts('Maximum Amount'), array('size' => 8, 'maxlength' => 8)); - $this->addRule('max_amount', ts('Please enter a valid money value (e.g. %1).', array(1 => CRM_Utils_Money::format('99.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'); //CRM-12055 $this->add('text', 'amount_label', ts('Contribution Amounts Label')); - $default = array($this->createElement('radio', NULL, NULL, NULL, 0)); - $this->add('hidden', "price_field_id", '', array('id' => "price_field_id")); - $this->add('hidden', "price_field_other", '', array('id' => "price_field_option")); + $default = [$this->createElement('radio', NULL, NULL, NULL, 0)]; + $this->add('hidden', "price_field_id", '', ['id' => "price_field_id"]); + $this->add('hidden', "price_field_other", '', ['id' => "price_field_option"]); for ($i = 1; $i <= self::NUM_OPTION; $i++) { // label $this->add('text', "label[$i]", ts('Label'), CRM_Core_DAO::getAttribute('CRM_Core_DAO_OptionValue', 'label')); - $this->add('hidden', "price_field_value[$i]", '', array('id' => "price_field_value[$i]")); + $this->add('hidden', "price_field_value[$i]", '', ['id' => "price_field_value[$i]"]); // 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).', array(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::format('99.99', ' ')]), 'money'); // default $default[] = $this->createElement('radio', NULL, NULL, NULL, $i); @@ -83,12 +83,12 @@ public function buildQuickForm() { $this->addGroup($default, 'default'); - $this->addElement('checkbox', 'amount_block_is_active', ts('Contribution Amounts section enabled'), NULL, array('onclick' => "showHideAmountBlock( this, 'amount_block_is_active' );")); + $this->addElement('checkbox', 'amount_block_is_active', ts('Contribution Amounts section enabled'), NULL, ['onclick' => "showHideAmountBlock( this, 'amount_block_is_active' );"]); $this->addElement('checkbox', 'is_monetary', ts('Execute real-time monetary transactions')); $paymentProcessors = CRM_Financial_BAO_PaymentProcessor::getAllPaymentProcessors('live'); - $recurringPaymentProcessor = $futurePaymentProcessor = $paymentProcessor = array(); + $recurringPaymentProcessor = $futurePaymentProcessor = $paymentProcessor = []; if (!empty($paymentProcessors)) { foreach ($paymentProcessors as $id => $processor) { @@ -116,18 +116,18 @@ public function buildQuickForm() { $this->addCheckBox('payment_processor', ts('Payment Processor'), array_flip($paymentProcessor), NULL, NULL, NULL, NULL, - array('  ', '  ', '  ', '
    ') + ['  ', '  ', '  ', '
    '] ); //check if selected payment processor supports recurring payment if (!empty($recurringPaymentProcessor)) { $this->addElement('checkbox', 'is_recur', ts('Recurring Contributions'), NULL, - array('onclick' => "showHideByValue('is_recur',true,'recurFields','table-row','radio',false);") + ['onclick' => "showHideByValue('is_recur',true,'recurFields','table-row','radio',false);"] ); $this->addCheckBox('recur_frequency_unit', ts('Supported recurring units'), CRM_Core_OptionGroup::values('recur_frequency_units', FALSE, FALSE, TRUE), NULL, NULL, NULL, NULL, - array('  ', '  ', '  ', '
    '), TRUE + ['  ', '  ', '  ', '
    '], TRUE ); $this->addElement('checkbox', 'is_recur_interval', ts('Support recurring intervals')); $this->addElement('checkbox', 'is_recur_installments', ts('Offer installments')); @@ -153,41 +153,41 @@ public function buildQuickForm() { $this->assign('price', TRUE); } $this->add('select', 'price_set_id', ts('Price Set'), - array( + [ '' => ts('- none -'), - ) + $price, - NULL, array('onchange' => "showHideAmountBlock( this.value, 'price_set_id' );") + ] + $price, + NULL, ['onchange' => "showHideAmountBlock( this.value, 'price_set_id' );"] ); //CiviPledge fields. $config = CRM_Core_Config::singleton(); if (in_array('CiviPledge', $config->enableComponents)) { $this->assign('civiPledge', TRUE); $this->addElement('checkbox', 'is_pledge_active', ts('Pledges'), - NULL, array('onclick' => "showHideAmountBlock( this, 'is_pledge_active' ); return showHideByValue('is_pledge_active',true,'pledgeFields','table-row','radio',false);") + NULL, ['onclick' => "showHideAmountBlock( this, 'is_pledge_active' ); return showHideByValue('is_pledge_active',true,'pledgeFields','table-row','radio',false);"] ); $this->addCheckBox('pledge_frequency_unit', ts('Supported pledge frequencies'), CRM_Core_OptionGroup::values('recur_frequency_units', FALSE, FALSE, TRUE), NULL, NULL, NULL, NULL, - array('  ', '  ', '  ', '
    '), TRUE + ['  ', '  ', '  ', '
    '], TRUE ); $this->addElement('checkbox', 'is_pledge_interval', ts('Allow frequency intervals')); - $this->addElement('text', 'initial_reminder_day', ts('Send payment reminder'), array('size' => 3)); - $this->addElement('text', 'max_reminders', ts('Send up to'), array('size' => 3)); - $this->addElement('text', 'additional_reminder_day', ts('Send additional reminders'), array('size' => 3)); + $this->addElement('text', 'initial_reminder_day', ts('Send payment reminder'), ['size' => 3]); + $this->addElement('text', 'max_reminders', ts('Send up to'), ['size' => 3]); + $this->addElement('text', 'additional_reminder_day', ts('Send additional reminders'), ['size' => 3]); if (!empty($futurePaymentProcessor)) { // CRM-18854 $this->addElement('checkbox', 'adjust_recur_start_date', ts('Adjust Recurring Start Date'), NULL, - array('onclick' => "showHideByValue('adjust_recur_start_date',true,'recurDefaults','table-row','radio',false);") + ['onclick' => "showHideByValue('adjust_recur_start_date',true,'recurDefaults','table-row','radio',false);"] ); $this->addDate('pledge_calendar_date', ts('Specific Calendar Date')); $month = CRM_Utils_Date::getCalendarDayOfMonth(); $this->add('select', 'pledge_calendar_month', ts('Specific day of Month'), $month); - $pledgeDefaults = array( + $pledgeDefaults = [ 'contribution_date' => ts('Day of Contribution'), 'calendar_date' => ts('Specific Calendar Date'), 'calendar_month' => ts('Specific day of Month'), - ); - $this->addRadio('pledge_default_toggle', ts('Recurring Contribution Start Date Default'), $pledgeDefaults, array('allowClear' => FALSE), '

    '); + ]; + $this->addRadio('pledge_default_toggle', ts('Recurring Contribution Start Date Default'), $pledgeDefaults, ['allowClear' => FALSE], '

    '); $this->addElement('checkbox', 'is_pledge_start_date_visible', ts('Show Recurring Donation Start Date?'), NULL); $this->addElement('checkbox', 'is_pledge_start_date_editable', ts('Allow Edits to Recurring Donation Start date?'), NULL); } @@ -196,7 +196,7 @@ public function buildQuickForm() { //add currency element. $this->addCurrency('currency', ts('Currency')); - $this->addFormRule(array('CRM_Contribute_Form_ContributionPage_Amount', 'formRule'), $this); + $this->addFormRule(['CRM_Contribute_Form_ContributionPage_Amount', 'formRule'], $this); parent::buildQuickForm(); } @@ -221,14 +221,14 @@ public function setDefaultValues() { if ($isQuick = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', $priceSetId, 'is_quick_config')) { $this->assign('isQuick', $isQuick); //$priceField = CRM_Core_DAO::getFieldValue( 'CRM_Price_DAO_PriceField', $priceSetId, 'id', 'price_set_id' ); - $options = $pFIDs = array(); - $priceFieldParams = array('price_set_id' => $priceSetId); - $priceFields = CRM_Core_DAO::commonRetrieveAll('CRM_Price_DAO_PriceField', 'price_set_id', $priceSetId, $pFIDs, $return = array( - 'html_type', - 'name', - 'is_active', - 'label', - )); + $options = $pFIDs = []; + $priceFieldParams = ['price_set_id' => $priceSetId]; + $priceFields = CRM_Core_DAO::commonRetrieveAll('CRM_Price_DAO_PriceField', 'price_set_id', $priceSetId, $pFIDs, $return = [ + 'html_type', + 'name', + 'is_active', + 'label', + ]); foreach ($priceFields as $priceField) { if ($priceField['id'] && $priceField['html_type'] == 'Radio' && $priceField['name'] == 'contribution_amount') { $defaults['price_field_id'] = $priceField['id']; @@ -304,7 +304,7 @@ public function setDefaultValues() { * true if no errors, else array of errors */ public static function formRule($fields, $files, $self) { - $errors = array(); + $errors = []; //as for separate membership payment we has to have //contribution amount section enabled, hence to disable it need to //check if separate membership payment enabled, @@ -351,7 +351,7 @@ public static function formRule($fields, $files, $self) { } //check for the amount label (mandatory) - if (!empty($fields['amount_block_is_active']) && empty($fields['amount_label'])) { + if (!empty($fields['amount_block_is_active']) && empty($fields['price_set_id']) && empty($fields['amount_label'])) { $errors['amount_label'] = ts('Please enter the contribution amount label.'); } $minAmount = CRM_Utils_Array::value('min_amount', $fields); @@ -372,6 +372,11 @@ public static function formRule($fields, $files, $self) { $errors['pay_later_receipt'] = ts('Please enter the instructions to be sent to the contributor when they choose to \'pay later\'.'); } } + else { + if ($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.'); + } + } // don't allow price set w/ membership signup, CRM-5095 if ($priceSetId = CRM_Utils_Array::value('price_set_id', $fields)) { @@ -423,17 +428,6 @@ public static function formRule($fields, $files, $self) { $errors['payment_processor'] = ts("Financial Account of account relationship of 'Expense Account is' is not configured for Financial Type : ") . $financialType; } - if (!empty($fields['is_recur_interval'])) { - foreach (array_keys($fields['payment_processor']) as $paymentProcessorID) { - $paymentProcessorTypeId = CRM_Core_DAO::getFieldValue( - 'CRM_Financial_DAO_PaymentProcessor', - $paymentProcessorID, - 'payment_processor_type_id' - ); - $paymentProcessorType = CRM_Core_PseudoConstant::paymentProcessorType(FALSE, $paymentProcessorTypeId, 'name'); - } - } - return $errors; } @@ -463,7 +457,7 @@ public function postProcess() { $priceSetID = CRM_Utils_Array::value('price_set_id', $params); // get required fields. - $fields = array( + $fields = [ 'id' => $this->_id, 'is_recur' => FALSE, 'min_amount' => "null", @@ -477,14 +471,14 @@ public function postProcess() { 'default_amount_id' => "null", 'is_allow_other_amount' => FALSE, 'amount_block_is_active' => FALSE, - ); - $resetFields = array(); + ]; + $resetFields = []; if ($priceSetID) { - $resetFields = array('min_amount', 'max_amount', 'is_allow_other_amount'); + $resetFields = ['min_amount', 'max_amount', 'is_allow_other_amount']; } if (empty($params['is_recur'])) { - $resetFields = array_merge($resetFields, array('is_recur_interval', 'recur_frequency_unit')); + $resetFields = array_merge($resetFields, ['is_recur_interval', 'recur_frequency_unit']); } foreach ($fields as $field => $defaultVal) { @@ -493,10 +487,10 @@ public function postProcess() { $val = $defaultVal; } - if (in_array($field, array( + if (in_array($field, [ 'min_amount', 'max_amount', - ))) { + ])) { $val = CRM_Utils_Rule::cleanMoney($val); } @@ -513,17 +507,17 @@ public function postProcess() { if (CRM_Utils_Array::value('adjust_recur_start_date', $params)) { $fieldValue = ''; - $pledgeDateFields = array( + $pledgeDateFields = [ 'calendar_date' => 'pledge_calendar_date', 'calendar_month' => 'pledge_calendar_month', - ); + ]; if ($params['pledge_default_toggle'] == 'contribution_date') { - $fieldValue = json_encode(array('contribution_date' => date('Ymd'))); + $fieldValue = json_encode(['contribution_date' => date('m/d/Y')]); } else { foreach ($pledgeDateFields as $key => $pledgeDateField) { if (CRM_Utils_Array::value($pledgeDateField, $params) && $params['pledge_default_toggle'] == $key) { - $fieldValue = json_encode(array($key => $params[$pledgeDateField])); + $fieldValue = json_encode([$key => $params[$pledgeDateField]]); break; } } @@ -590,18 +584,19 @@ public function postProcess() { $values = CRM_Utils_Array::value('value', $params); $default = CRM_Utils_Array::value('default', $params); - $options = array(); + $options = []; for ($i = 1; $i < self::NUM_OPTION; $i++) { if (isset($values[$i]) && (strlen(trim($values[$i])) > 0) ) { - $options[] = array( + $values[$i] = $params['value'][$i] = CRM_Utils_Rule::cleanMoney(trim($values[$i])); + $options[] = [ 'label' => trim($labels[$i]), - 'value' => CRM_Utils_Rule::cleanMoney(trim($values[$i])), + 'value' => $values[$i], 'weight' => $i, 'is_active' => 1, 'is_default' => $default == $i, - ); + ]; } } /* || !empty($params['price_field_value']) || CRM_Utils_Array::value( 'price_field_other', $params )*/ @@ -655,11 +650,11 @@ public function postProcess() { } CRM_Price_BAO_PriceSet::addTo('civicrm_contribution_page', $this->_id, $priceSetId); if (!empty($options)) { - $editedFieldParams = array( + $editedFieldParams = [ 'price_set_id' => $priceSetId, 'name' => 'contribution_amount', - ); - $editedResults = array(); + ]; + $editedResults = []; $noContriAmount = 1; CRM_Price_BAO_PriceField::retrieve($editedFieldParams, $editedResults); if (empty($editedResults['id'])) { @@ -691,16 +686,16 @@ public function postProcess() { $priceField = CRM_Price_BAO_PriceField::create($fieldParams); } if (!empty($params['is_allow_other_amount']) && empty($params['price_field_other'])) { - $editedFieldParams = array( + $editedFieldParams = [ 'price_set_id' => $priceSetId, 'name' => 'other_amount', - ); - $editedResults = array(); + ]; + $editedResults = []; CRM_Price_BAO_PriceField::retrieve($editedFieldParams, $editedResults); if (!$priceFieldID = CRM_Utils_Array::value('id', $editedResults)) { - $fieldParams = array( + $fieldParams = [ 'name' => 'other_amount', 'label' => ts('Other Amount'), 'price_set_id' => $priceSetId, @@ -708,7 +703,7 @@ public function postProcess() { 'financial_type_id' => CRM_Utils_Array::value('financial_type_id', $this->_values), 'is_display_amounts' => 0, 'weight' => 3, - ); + ]; $fieldParams['option_weight'][1] = 1; $fieldParams['option_amount'][1] = 1; if (!$noContriAmount) { @@ -728,11 +723,11 @@ public function postProcess() { if (!$noContriAmount) { $priceFieldValueID = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceFieldValue', $priceFieldID, 'id', 'price_field_id'); CRM_Core_DAO::setFieldValue('CRM_Price_DAO_PriceFieldValue', $priceFieldValueID, 'label', $params['amount_label']); - $fieldParams = array( + $fieldParams = [ 'is_required' => 1, 'label' => $params['amount_label'], 'id' => $priceFieldID, - ); + ]; } $fieldParams['is_active'] = 1; $priceField = CRM_Price_BAO_PriceField::add($fieldParams); @@ -745,11 +740,11 @@ public function postProcess() { elseif ($priceFieldID = CRM_Utils_Array::value('price_field_other', $params)) { $priceFieldValueID = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceFieldValue', $priceFieldID, 'id', 'price_field_id'); if (!$noContriAmount) { - $fieldParams = array( + $fieldParams = [ 'is_required' => 1, 'label' => $params['amount_label'], 'id' => $priceFieldID, - ); + ]; CRM_Price_BAO_PriceField::add($fieldParams); CRM_Core_DAO::setFieldValue('CRM_Price_DAO_PriceFieldValue', $priceFieldValueID, 'label', $params['amount_label']); } @@ -762,14 +757,14 @@ public function postProcess() { if (!empty($params['is_pledge_active'])) { $deletePledgeBlk = FALSE; - $pledgeBlockParams = array( + $pledgeBlockParams = [ 'entity_id' => $contributionPageID, 'entity_table' => ts('civicrm_contribution_page'), - ); + ]; if ($this->_pledgeBlockID) { $pledgeBlockParams['id'] = $this->_pledgeBlockID; } - $pledgeBlock = array( + $pledgeBlock = [ 'pledge_frequency_unit', 'max_reminders', 'initial_reminder_day', @@ -777,7 +772,7 @@ public function postProcess() { 'pledge_start_date', 'is_pledge_start_date_visible', 'is_pledge_start_date_editable', - ); + ]; foreach ($pledgeBlock as $key) { $pledgeBlockParams[$key] = CRM_Utils_Array::value($key, $params); } diff --git a/CRM/Contribute/Form/ContributionPage/Custom.php b/CRM/Contribute/Form/ContributionPage/Custom.php index 2c23783ae036..94f02e99c194 100644 --- a/CRM/Contribute/Form/ContributionPage/Custom.php +++ b/CRM/Contribute/Form/ContributionPage/Custom.php @@ -1,9 +1,9 @@ 'contact_1', 'entity_type' => 'IndividualModel'); - $allowCoreTypes = array_merge(array('Contact', 'Individual'), CRM_Contact_BAO_ContactType::subTypes('Individual')); - $allowSubTypes = array(); + $entities = []; + $entities[] = ['entity_name' => 'contact_1', 'entity_type' => 'IndividualModel']; + $allowCoreTypes = array_merge(['Contact', 'Individual'], CRM_Contact_BAO_ContactType::subTypes('Individual')); + $allowSubTypes = []; // Register 'contribution_1' $financialTypeId = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_ContributionPage', $this->_id, 'financial_type_id'); $allowCoreTypes[] = 'Contribution'; //CRM-15427 - $allowSubTypes['ContributionType'] = array($financialTypeId); - $entities[] = array( + $allowSubTypes['ContributionType'] = [$financialTypeId]; + $entities[] = [ 'entity_name' => 'contribution_1', 'entity_type' => 'ContributionModel', 'entity_sub_type' => '*', - ); + ]; // If applicable, register 'membership_1' $member = CRM_Member_BAO_Membership::getMembershipBlock($this->_id); if ($member && $member['is_active']) { //CRM-15427 - $entities[] = array( + $entities[] = [ 'entity_name' => 'membership_1', 'entity_type' => 'MembershipModel', 'entity_sub_type' => '*', - ); + ]; $allowCoreTypes[] = 'Membership'; $allowSubTypes['MembershipType'] = explode(',', $member['membership_types']); } @@ -73,7 +73,7 @@ public function buildQuickForm() { $this->addProfileSelector('custom_pre_id', ts('Include Profile') . '
    ' . ts('(top of page)'), $allowCoreTypes, $allowSubTypes, $entities, TRUE); $this->addProfileSelector('custom_post_id', ts('Include Profile') . '
    ' . ts('(bottom of page)'), $allowCoreTypes, $allowSubTypes, $entities, TRUE); - $this->addFormRule(array('CRM_Contribute_Form_ContributionPage_Custom', 'formRule'), $this); + $this->addFormRule(['CRM_Contribute_Form_ContributionPage_Custom', 'formRule'], $this); parent::buildQuickForm(); } @@ -106,12 +106,12 @@ public function postProcess() { $transaction = new CRM_Core_Transaction(); // also update uf join table - $ufJoinParams = array( + $ufJoinParams = [ 'is_active' => 1, 'module' => 'CiviContribute', 'entity_table' => 'civicrm_contribution_page', 'entity_id' => $this->_id, - ); + ]; // first delete all past entries CRM_Core_BAO_UFJoin::deleteAll($ufJoinParams); @@ -156,7 +156,7 @@ public function getTitle() { * true if no errors, else array of errors */ public static function formRule($fields, $files, $form) { - $errors = array(); + $errors = []; $preProfileType = $postProfileType = NULL; // for membership profile make sure Membership section is enabled // get membership section for this contribution page diff --git a/CRM/Contribute/Form/ContributionPage/Delete.php b/CRM/Contribute/Form/ContributionPage/Delete.php index 7ca58ae5789f..a715b136d8b2 100644 --- a/CRM/Contribute/Form/ContributionPage/Delete.php +++ b/CRM/Contribute/Form/ContributionPage/Delete.php @@ -1,9 +1,9 @@ _relatedContributions) { - $buttons[] = array( + $buttons[] = [ 'type' => 'next', 'name' => ts('Delete Contribution Page'), 'isDefault' => TRUE, - ); + ]; } - $buttons[] = array( + $buttons[] = [ 'type' => 'cancel', 'name' => ts('Cancel'), - ); + ]; $this->addButtons($buttons); } @@ -105,10 +106,10 @@ public function postProcess() { // first delete the join entries associated with this contribution page $dao = new CRM_Core_DAO_UFJoin(); - $params = array( + $params = [ 'entity_table' => 'civicrm_contribution_page', 'entity_id' => $this->_id, - ); + ]; $dao->copyValues($params); $dao->delete(); @@ -137,7 +138,7 @@ public function postProcess() { $transaction->commit(); - CRM_Core_Session::setStatus(ts("The contribution page '%1' has been deleted.", array(1 => $this->_title)), ts('Deleted'), 'success'); + CRM_Core_Session::setStatus(ts("The contribution page '%1' has been deleted.", [1 => $this->_title]), ts('Deleted'), 'success'); } } diff --git a/CRM/Contribute/Form/ContributionPage/Premium.php b/CRM/Contribute/Form/ContributionPage/Premium.php index d62fef0fd8ae..109dbc39edda 100644 --- a/CRM/Contribute/Form/ContributionPage/Premium.php +++ b/CRM/Contribute/Form/ContributionPage/Premium.php @@ -1,9 +1,9 @@ _id)) { $dao = new CRM_Contribute_DAO_Premium(); $dao->entity_table = 'civicrm_contribution_page'; @@ -74,7 +74,7 @@ public function buildQuickForm() { // CRM-10999 Control label and position for No Thank-you radio button $this->add('text', 'premiums_nothankyou_label', ts('No Thank-you Label'), $attributes['premiums_nothankyou_label']); - $positions = array(1 => ts('Before Premiums'), 2 => ts('After Premiums')); + $positions = [1 => ts('Before Premiums'), 2 => ts('After Premiums')]; $this->add('select', 'premiums_nothankyou_position', ts('No Thank-you Option'), $positions); $showForm = TRUE; @@ -92,7 +92,7 @@ public function buildQuickForm() { $this->assign('showForm', $showForm); parent::buildQuickForm(); - $this->addFormRule(array('CRM_Contribute_Form_ContributionPage_Premium', 'formRule'), $this); + $this->addFormRule(['CRM_Contribute_Form_ContributionPage_Premium', 'formRule'], $this); $premiumPage = new CRM_Contribute_Page_Premium(); $premiumPage->browse(); @@ -108,7 +108,7 @@ public function buildQuickForm() { * mixed true or array of errors */ public static function formRule($params) { - $errors = array(); + $errors = []; if (!empty($params['premiums_active'])) { if (empty($params['premiums_nothankyou_label'])) { $errors['premiums_nothankyou_label'] = ts('No Thank-you Label is a required field.'); diff --git a/CRM/Contribute/Form/ContributionPage/Settings.php b/CRM/Contribute/Form/ContributionPage/Settings.php index 8c6b784df8e3..0e7ae3ae58f8 100644 --- a/CRM/Contribute/Form/ContributionPage/Settings.php +++ b/CRM/Contribute/Form/ContributionPage/Settings.php @@ -1,9 +1,9 @@ _id) { + $defaults['start_date'] = date('Y-m-d H:i:s'); + unset($defaults['start_time']); + } $soft_credit_types = CRM_Core_OptionGroup::values('soft_credit_type', TRUE, FALSE, FALSE, NULL, 'name'); if ($this->_id) { @@ -53,7 +58,7 @@ public function setDefaultValues() { ); CRM_Utils_System::setTitle(ts('Title and Settings') . " ($title)"); - foreach (array('on_behalf', 'soft_credit') as $module) { + foreach (['on_behalf', 'soft_credit'] as $module) { $ufJoinDAO = new CRM_Core_DAO_UFJoin(); $ufJoinDAO->module = $module; $ufJoinDAO->entity_id = $this->_id; @@ -78,10 +83,10 @@ public function setDefaultValues() { if ($ufGroupDAO->find(TRUE)) { $defaults['honoree_profile'] = $ufGroupDAO->id; } - $defaults['soft_credit_types'] = array( + $defaults['soft_credit_types'] = [ CRM_Utils_Array::value('in_honor_of', $soft_credit_types), CRM_Utils_Array::value('in_memory_of', $soft_credit_types), - ); + ]; } else { $ufGroupDAO = new CRM_Core_DAO_UFGroup(); @@ -101,10 +106,10 @@ public function setDefaultValues() { if ($ufGroupDAO->find(TRUE)) { $defaults['honoree_profile'] = $ufGroupDAO->id; } - $defaults['soft_credit_types'] = array( + $defaults['soft_credit_types'] = [ CRM_Utils_Array::value('in_honor_of', $soft_credit_types), CRM_Utils_Array::value('in_memory_of', $soft_credit_types), - ); + ]; } return $defaults; @@ -120,9 +125,9 @@ public function buildQuickForm() { // financial Type CRM_Financial_BAO_FinancialType::getAvailableFinancialTypes($financialTypes, CRM_Core_Action::ADD); - $financialOptions = array( + $financialOptions = [ 'options' => $financialTypes, - ); + ]; if (!CRM_Core_Permission::check('administer CiviCRM Financial Types')) { $financialOptions['context'] = 'search'; } @@ -139,42 +144,42 @@ public function buildQuickForm() { $this->add('wysiwyg', 'footer_text', ts('Footer Message'), $attributes['footer_text']); //Register schema which will be used for OnBehalOf and HonorOf profile Selector - CRM_UF_Page_ProfileEditor::registerSchemas(array('OrganizationModel', 'HouseholdModel')); + CRM_UF_Page_ProfileEditor::registerSchemas(['OrganizationModel', 'HouseholdModel']); // is on behalf of an organization ? - $this->addElement('checkbox', 'is_organization', ts('Allow individuals to contribute and / or signup for membership on behalf of an organization?'), NULL, array('onclick' => "showHideByValue('is_organization',true,'for_org_text','table-row','radio',false);showHideByValue('is_organization',true,'for_org_option','table-row','radio',false);")); + $this->addElement('checkbox', 'is_organization', ts('Allow individuals to contribute and / or signup for membership on behalf of an organization?'), NULL, ['onclick' => "showHideByValue('is_organization',true,'for_org_text','table-row','radio',false);showHideByValue('is_organization',true,'for_org_option','table-row','radio',false);"]); //CRM-15787 - If applicable, register 'membership_1' $member = CRM_Member_BAO_Membership::getMembershipBlock($this->_id); - $coreTypes = array('Contact', 'Organization'); + $coreTypes = ['Contact', 'Organization']; - $entities[] = array( - 'entity_name' => array('contact_1'), + $entities[] = [ + 'entity_name' => ['contact_1'], 'entity_type' => 'OrganizationModel', - ); + ]; if ($member && $member['is_active']) { $coreTypes[] = 'Membership'; - $entities[] = array( - 'entity_name' => array('membership_1'), + $entities[] = [ + 'entity_name' => ['membership_1'], 'entity_type' => 'MembershipModel', - ); + ]; } $allowCoreTypes = array_merge($coreTypes, CRM_Contact_BAO_ContactType::subTypes('Organization')); - $allowSubTypes = array(); + $allowSubTypes = []; $this->addProfileSelector('onbehalf_profile_id', ts('Organization Profile'), $allowCoreTypes, $allowSubTypes, $entities); - $options = array(); + $options = []; $options[] = $this->createElement('radio', NULL, NULL, ts('Optional'), 1); $options[] = $this->createElement('radio', NULL, NULL, ts('Required'), 2); $this->addGroup($options, 'is_for_organization', ''); - $this->add('textarea', 'for_organization', ts('On behalf of Label'), array('rows' => 2, 'cols' => 50)); + $this->add('textarea', 'for_organization', ts('On behalf of Label'), ['rows' => 2, 'cols' => 50]); // collect goal amount - $this->add('text', 'goal_amount', ts('Goal Amount'), array('size' => 8, 'maxlength' => 12)); - $this->addRule('goal_amount', ts('Please enter a valid money value (e.g. %1).', array(1 => CRM_Utils_Money::format('99.99', ' '))), 'money'); + $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'); // is confirmation page enabled? $this->addElement('checkbox', 'is_confirm_enabled', ts('Use a confirmation page?')); @@ -186,34 +191,34 @@ public function buildQuickForm() { $this->addElement('checkbox', 'is_active', ts('Is this Online Contribution Page Active?')); // should the honor be enabled - $this->addElement('checkbox', 'honor_block_is_active', ts('Honoree Section Enabled'), NULL, array('onclick' => "showHonor()")); + $this->addElement('checkbox', 'honor_block_is_active', ts('Honoree Section Enabled'), NULL, ['onclick' => "showHonor()"]); - $this->add('text', 'honor_block_title', ts('Honoree Section Title'), array('maxlength' => 255, 'size' => 45)); + $this->add('text', 'honor_block_title', ts('Honoree Section Title'), ['maxlength' => 255, 'size' => 45]); - $this->add('textarea', 'honor_block_text', ts('Honoree Introductory Message'), array('rows' => 2, 'cols' => 50)); + $this->add('textarea', 'honor_block_text', ts('Honoree Introductory Message'), ['rows' => 2, 'cols' => 50]); - $this->addSelect('soft_credit_types', array( + $this->addSelect('soft_credit_types', [ 'label' => ts('Honor Types'), 'entity' => 'ContributionSoft', 'field' => 'soft_credit_type_id', 'multiple' => TRUE, 'class' => 'huge', - )); + ]); - $entities = array( - array( + $entities = [ + [ 'entity_name' => 'contact_1', 'entity_type' => 'IndividualModel', - ), - ); + ], + ]; - $allowCoreTypes = array_merge(array( - 'Contact', - 'Individual', - 'Organization', - 'Household', - ), CRM_Contact_BAO_ContactType::subTypes('Individual')); - $allowSubTypes = array(); + $allowCoreTypes = array_merge([ + 'Contact', + 'Individual', + 'Organization', + 'Household', + ], CRM_Contact_BAO_ContactType::subTypes('Individual')); + $allowSubTypes = []; $this->addProfileSelector('honoree_profile', ts('Honoree Profile'), $allowCoreTypes, $allowSubTypes, $entities); @@ -223,10 +228,10 @@ public function buildQuickForm() { } // add optional start and end dates - $this->addDateTime('start_date', ts('Start Date')); - $this->addDateTime('end_date', ts('End Date')); + $this->add('datepicker', 'start_date', ts('Start Date')); + $this->add('datepicker', 'end_date', ts('End Date')); - $this->addFormRule(array('CRM_Contribute_Form_ContributionPage_Settings', 'formRule'), $this); + $this->addFormRule(['CRM_Contribute_Form_ContributionPage_Settings', 'formRule'], $this); parent::buildQuickForm(); } @@ -244,7 +249,7 @@ public function buildQuickForm() { * list of errors to be posted back to the form */ public static function formRule($values, $files, $self) { - $errors = array(); + $errors = []; $contributionPageId = $self->_id; //CRM-4286 if (strstr($values['title'], '/')) { @@ -257,7 +262,7 @@ public static function formRule($values, $files, $self) { $errors['onbehalf_profile_id'] = ts('Please select a profile to collect organization information on this contribution page.'); } else { - $requiredProfileFields = array('organization_name', 'email'); + $requiredProfileFields = ['organization_name', 'email']; if (!CRM_Core_BAO_UFGroup::checkValidProfile($values['onbehalf_profile_id'], $requiredProfileFields)) { $errors['onbehalf_profile_id'] = ts('Profile does not contain the minimum required fields for an On Behalf Of Organization'); } @@ -278,11 +283,11 @@ public static function formRule($values, $files, $self) { //dont allow on behalf of save when //pre or post profile consists of membership fields if ($contributionPageId && !empty($values['is_organization'])) { - $ufJoinParams = array( + $ufJoinParams = [ 'module' => 'CiviContribute', 'entity_table' => 'civicrm_contribution_page', 'entity_id' => $contributionPageId, - ); + ]; list($contributionProfiles['custom_pre_id'], $contributionProfiles['custom_post_id'] @@ -303,7 +308,7 @@ public static function formRule($values, $files, $self) { } } if (!empty($conProfileType)) { - $errors['is_organization'] = ts("You should move the membership related fields configured in %1 to the 'On Behalf' profile for this Contribution Page", array(1 => $conProfileType)); + $errors['is_organization'] = ts("You should move the membership related fields configured in %1 to the 'On Behalf' profile for this Contribution Page", [1 => $conProfileType]); } } return $errors; @@ -334,10 +339,6 @@ public function postProcess() { $params['is_credit_card_only'] = CRM_Utils_Array::value('is_credit_card_only', $params, FALSE); $params['honor_block_is_active'] = CRM_Utils_Array::value('honor_block_is_active', $params, FALSE); $params['is_for_organization'] = !empty($params['is_organization']) ? CRM_Utils_Array::value('is_for_organization', $params, FALSE) : 0; - - $params['start_date'] = CRM_Utils_Date::processDate($params['start_date'], $params['start_date_time'], TRUE); - $params['end_date'] = CRM_Utils_Date::processDate($params['end_date'], $params['end_date_time'], TRUE); - $params['goal_amount'] = CRM_Utils_Rule::cleanMoney($params['goal_amount']); if (!$params['honor_block_is_active']) { @@ -347,18 +348,18 @@ public function postProcess() { $dao = CRM_Contribute_BAO_ContributionPage::create($params); - $ufJoinParams = array( - 'is_organization' => array( + $ufJoinParams = [ + 'is_organization' => [ 'module' => 'on_behalf', 'entity_table' => 'civicrm_contribution_page', 'entity_id' => $dao->id, - ), - 'honor_block_is_active' => array( + ], + 'honor_block_is_active' => [ 'module' => 'soft_credit', 'entity_table' => 'civicrm_contribution_page', 'entity_id' => $dao->id, - ), - ); + ], + ]; foreach ($ufJoinParams as $index => $ufJoinParam) { if (!empty($params[$index])) { @@ -406,7 +407,7 @@ public function postProcess() { $url = 'civicrm/admin/contribute'; $urlParams = 'reset=1'; CRM_Core_Session::setStatus(ts("'%1' information has been saved.", - array(1 => $this->getTitle()) + [1 => $this->getTitle()] ), ts('Saved'), 'success'); } diff --git a/CRM/Contribute/Form/ContributionPage/TabHeader.php b/CRM/Contribute/Form/ContributionPage/TabHeader.php index 754805c53c3e..c24edb0726e8 100644 --- a/CRM/Contribute/Form/ContributionPage/TabHeader.php +++ b/CRM/Contribute/Form/ContributionPage/TabHeader.php @@ -1,9 +1,9 @@ assign_by_ref('tabHeader', $tabs); CRM_Core_Resources::singleton() ->addScriptFile('civicrm', 'templates/CRM/common/TabHeader.js', 1, 'html-header') - ->addSetting(array( - 'tabSettings' => array( + ->addSetting([ + 'tabSettings' => [ 'active' => self::getCurrentTab($tabs), - ), - )); + ], + ]); return $tabs; } @@ -67,74 +68,74 @@ public static function process(&$form) { return NULL; } - $tabs = array( - 'settings' => array( + $tabs = [ + 'settings' => [ 'title' => ts('Title'), 'link' => NULL, 'valid' => FALSE, 'active' => FALSE, 'current' => FALSE, - ), - 'amount' => array( + ], + 'amount' => [ 'title' => ts('Amounts'), 'link' => NULL, 'valid' => FALSE, 'active' => FALSE, 'current' => FALSE, - ), - 'membership' => array( + ], + 'membership' => [ 'title' => ts('Memberships'), 'link' => NULL, 'valid' => FALSE, 'active' => FALSE, 'current' => FALSE, - ), - 'thankyou' => array( + ], + 'thankyou' => [ 'title' => ts('Receipt'), 'link' => NULL, 'valid' => FALSE, 'active' => FALSE, 'current' => FALSE, - ), - 'friend' => array( + ], + 'friend' => [ 'title' => ts('Tell a Friend'), 'link' => NULL, 'valid' => FALSE, 'active' => FALSE, 'current' => FALSE, - ), - 'custom' => array( + ], + 'custom' => [ 'title' => ts('Profiles'), 'link' => NULL, 'valid' => FALSE, 'active' => FALSE, 'current' => FALSE, - ), - 'premium' => array( + ], + 'premium' => [ 'title' => ts('Premiums'), 'link' => NULL, 'valid' => FALSE, 'active' => FALSE, 'current' => FALSE, - ), - 'widget' => array( + ], + 'widget' => [ 'title' => ts('Widgets'), 'link' => NULL, 'valid' => FALSE, 'active' => FALSE, 'current' => FALSE, - ), - 'pcp' => array( + ], + 'pcp' => [ 'title' => ts('Personal Campaigns'), 'link' => NULL, 'valid' => FALSE, 'active' => FALSE, 'current' => FALSE, - ), - ); + ], + ]; $contribPageId = $form->getVar('_id'); - CRM_Utils_Hook::tabset('civicrm/admin/contribute', $tabs, array('contribution_page_id' => $contribPageId)); + CRM_Utils_Hook::tabset('civicrm/admin/contribute', $tabs, ['contribution_page_id' => $contribPageId]); $fullName = $form->getVar('_name'); $className = CRM_Utils_String::getClassName($fullName); @@ -142,7 +143,7 @@ public static function process(&$form) { switch ($className) { case 'Contribute': $attributes = $form->getVar('_attributes'); - $class = strtolower(basename(CRM_Utils_Array::value('action', $attributes))); + $class = CRM_Utils_Request::retrieveComponent($attributes); break; case 'MembershipBlock': @@ -177,7 +178,7 @@ public static function process(&$form) { $tabs[$key]['active'] = $tabs[$key]['valid'] = TRUE; } //get all section info. - $contriPageInfo = CRM_Contribute_BAO_ContributionPage::getSectionInfo(array($contribPageId)); + $contriPageInfo = CRM_Contribute_BAO_ContributionPage::getSectionInfo([$contribPageId]); foreach ($contriPageInfo[$contribPageId] as $section => $info) { if (!$info) { diff --git a/CRM/Contribute/Form/ContributionPage/ThankYou.php b/CRM/Contribute/Form/ContributionPage/ThankYou.php index ccda3707643f..d8a53f151a12 100644 --- a/CRM/Contribute/Form/ContributionPage/ThankYou.php +++ b/CRM/Contribute/Form/ContributionPage/ThankYou.php @@ -1,9 +1,9 @@ add('text', 'thankyou_title', ts('Thank-you Page Title'), CRM_Core_DAO::getAttribute('CRM_Contribute_DAO_ContributionPage', 'thankyou_title'), TRUE); - $attributes = CRM_Core_DAO::getAttribute('CRM_Contribute_DAO_ContributionPage', 'thankyou_text') + array('class' => 'collapsed'); + $attributes = CRM_Core_DAO::getAttribute('CRM_Contribute_DAO_ContributionPage', 'thankyou_text') + ['class' => 'collapsed']; $this->add('wysiwyg', 'thankyou_text', ts('Thank-you Message'), $attributes); $this->add('wysiwyg', 'thankyou_footer', ts('Thank-you Footer'), $attributes); - $this->addElement('checkbox', 'is_email_receipt', ts('Email Receipt to Contributor?'), NULL, array('onclick' => "showReceipt()")); + $this->addElement('checkbox', 'is_email_receipt', ts('Email Receipt to Contributor?'), NULL, ['onclick' => "showReceipt()"]); $this->add('text', 'receipt_from_name', ts('Receipt From Name'), CRM_Core_DAO::getAttribute('CRM_Contribute_DAO_ContributionPage', 'receipt_from_name')); $this->add('text', 'receipt_from_email', ts('Receipt From Email'), CRM_Core_DAO::getAttribute('CRM_Contribute_DAO_ContributionPage', 'receipt_from_email')); $this->add('textarea', 'receipt_text', ts('Receipt Message'), CRM_Core_DAO::getAttribute('CRM_Contribute_DAO_ContributionPage', 'receipt_text')); @@ -70,7 +70,7 @@ public function buildQuickForm() { $this->addRule('bcc_receipt', ts('Please enter a valid list of comma delimited email addresses'), 'emailList'); parent::buildQuickForm(); - $this->addFormRule(array('CRM_Contribute_Form_ContributionPage_ThankYou', 'formRule'), $this); + $this->addFormRule(['CRM_Contribute_Form_ContributionPage_ThankYou', 'formRule'], $this); } /** @@ -87,7 +87,7 @@ public function buildQuickForm() { * true if no errors, else array of errors */ public static function formRule($fields, $files, $options) { - $errors = array(); + $errors = []; // if is_email_receipt is set, the receipt message must be non-empty if (!empty($fields['is_email_receipt'])) { diff --git a/CRM/Contribute/Form/ContributionPage/Widget.php b/CRM/Contribute/Form/ContributionPage/Widget.php index 9bc5355475b3..2a88bc06b5df 100644 --- a/CRM/Contribute/Form/ContributionPage/Widget.php +++ b/CRM/Contribute/Form/ContributionPage/Widget.php @@ -1,9 +1,9 @@ _fields = array( - 'title' => array( + $this->_fields = [ + 'title' => [ ts('Title'), 'text', FALSE, $title, - ), - 'url_logo' => array( + ], + 'url_logo' => [ ts('URL to Logo Image'), 'text', FALSE, NULL, - ), - 'button_title' => array( + ], + 'button_title' => [ ts('Button Title'), 'text', FALSE, ts('Contribute!'), - ), - ); + ], + ]; - $this->_colorFields = array( - 'color_title' => array( + $this->_colorFields = [ + 'color_title' => [ ts('Title Text Color'), - 'text', + 'color', FALSE, '#2786C2', - ), - 'color_bar' => array( + ], + 'color_bar' => [ ts('Progress Bar Color'), - 'text', + 'color', FALSE, '#2786C2', - ), - 'color_main_text' => array( + ], + 'color_main_text' => [ ts('Additional Text Color'), - 'text', + 'color', FALSE, '#FFFFFF', - ), - 'color_main' => array( + ], + 'color_main' => [ ts('Background Color'), - 'text', + 'color', FALSE, '#96C0E7', - ), - 'color_main_bg' => array( + ], + 'color_main_bg' => [ ts('Background Color Top Area'), - 'text', + 'color', FALSE, '#B7E2FF', - ), - 'color_bg' => array( + ], + 'color_bg' => [ ts('Border Color'), - 'text', + 'color', FALSE, '#96C0E7', - ), - 'color_about_link' => array( + ], + 'color_about_link' => [ ts('Button Text Color'), - 'text', + 'color', FALSE, '#556C82', - ), - 'color_button' => array( + ], + 'color_button' => [ ts('Button Background Color'), - 'text', + 'color', FALSE, '#FFFFFF', - ), - 'color_homepage_link' => array( + ], + 'color_homepage_link' => [ ts('Homepage Link Color'), - 'text', + 'color', FALSE, '#FFFFFF', - ), - ); + ], + ]; } /** * Set default values for the form. */ public function setDefaultValues() { - $defaults = array(); + $defaults = []; // check if there is a widget already created if ($this->_widget) { CRM_Core_DAO::storeValues($this->_widget, $defaults); @@ -175,7 +175,7 @@ public function buildQuickForm() { 'is_active', ts('Enable Widget?'), NULL, - array('onclick' => "widgetBlock(this)") + ['onclick' => "widgetBlock(this)"] ); $this->add('wysiwyg', 'about', ts('About'), $attributes['about']); @@ -206,7 +206,7 @@ public function buildQuickForm() { ts('Save and Preview') ); parent::buildQuickForm(); - $this->addFormRule(array('CRM_Contribute_Form_ContributionPage_Widget', 'formRule'), $this); + $this->addFormRule(['CRM_Contribute_Form_ContributionPage_Widget', 'formRule'], $this); } /** @@ -222,7 +222,7 @@ public function buildQuickForm() { * mixed true or array of errors */ public static function formRule($params, $files, $self) { - $errors = array(); + $errors = []; if (!empty($params['is_active'])) { if (empty($params['title'])) { $errors['title'] = ts('Title is a required field.'); @@ -233,7 +233,7 @@ public static function formRule($params, $files, $self) { foreach ($params as $key => $val) { if (substr($key, 0, 6) == 'color_' && empty($params[$key])) { - $errors[$key] = ts('%1 is a required field.', array(1 => $self->_colorFields[$key][0])); + $errors[$key] = ts('%1 is a required field.', [1 => $self->_colorFields[$key][0]]); } } } diff --git a/CRM/Contribute/Form/ContributionRecur.php b/CRM/Contribute/Form/ContributionRecur.php new file mode 100644 index 000000000000..d0b1ee2873ad --- /dev/null +++ b/CRM/Contribute/Form/ContributionRecur.php @@ -0,0 +1,88 @@ +_mid = CRM_Utils_Request::retrieve('mid', 'Integer', $this, FALSE); + $this->_crid = CRM_Utils_Request::retrieve('crid', 'Integer', $this, FALSE); + $this->contributionRecurID = $this->_crid; + $this->_coid = CRM_Utils_Request::retrieve('coid', 'Integer', $this, FALSE); + } + +} diff --git a/CRM/Contribute/Form/ContributionView.php b/CRM/Contribute/Form/ContributionView.php index 2f001debd808..a9eb615488e0 100644 --- a/CRM/Contribute/Form/ContributionView.php +++ b/CRM/Contribute/Form/ContributionView.php @@ -1,9 +1,9 @@ get('id'); - $params = array('id' => $id); - $context = CRM_Utils_Request::retrieve('context', 'String', $this); + $params = ['id' => $id]; + $context = CRM_Utils_Request::retrieve('context', 'Alphanumeric', $this); $this->assign('context', $context); $values = CRM_Contribute_BAO_Contribution::getValuesWithMappings($params); @@ -94,7 +94,7 @@ public function preProcess() { if (!empty($values['contribution_recur_id'])) { $sql = "SELECT installments, frequency_interval, frequency_unit FROM civicrm_contribution_recur WHERE id = %1"; - $params = array(1 => array($values['contribution_recur_id'], 'Integer')); + $params = [1 => [$values['contribution_recur_id'], 'Integer']]; $dao = CRM_Core_DAO::executeQuery($sql, $params); if ($dao->fetch()) { $values['recur_installments'] = $dao->installments; @@ -103,7 +103,7 @@ public function preProcess() { } } - $groupTree = CRM_Core_BAO_CustomGroup::getTree('Contribution', $this, $id, 0, CRM_Utils_Array::value('financial_type_id', $values)); + $groupTree = CRM_Core_BAO_CustomGroup::getTree('Contribution', NULL, $id, 0, CRM_Utils_Array::value('financial_type_id', $values)); CRM_Core_BAO_CustomGroup::buildCustomDataView($this, $groupTree, FALSE, NULL, NULL, NULL, $id); $premiumId = NULL; @@ -132,7 +132,7 @@ public function preProcess() { // show billing address location details, if exists if (!empty($values['address_id'])) { - $addressParams = array('id' => CRM_Utils_Array::value('address_id', $values)); + $addressParams = ['id' => CRM_Utils_Array::value('address_id', $values)]; $addressDetails = CRM_Core_BAO_Address::getValues($addressParams, FALSE, 'id'); $addressDetails = array_values($addressDetails); $values['billing_address'] = $addressDetails[0]['display']; @@ -150,16 +150,33 @@ public function preProcess() { $this->assign($name, $value); } - $lineItems = array(); + $lineItems = []; + $displayLineItems = FALSE; if ($id) { - $lineItem = CRM_Price_BAO_LineItem::getLineItems($id, 'contribution', 1, TRUE, TRUE); - if (!empty($lineItem)) { - $lineItems[] = $lineItem; + $lineItems = [CRM_Price_BAO_LineItem::getLineItemsByContributionID(($id))]; + $firstLineItem = reset($lineItems[0]); + if (empty($firstLineItem['price_set_id'])) { + // CRM-20297 All we care is that it's not QuickConfig, so no price set + // is no problem. + $displayLineItems = TRUE; + } + else { + try { + $priceSet = civicrm_api3('PriceSet', 'getsingle', [ + 'id' => $firstLineItem['price_set_id'], + 'return' => 'is_quick_config, id', + ]); + $displayLineItems = !$priceSet['is_quick_config']; + } + catch (CiviCRM_API3_Exception $e) { + throw new CRM_Core_Exception('Cannot find price set by ID'); + } } - } - $this->assign('lineItem', empty($lineItems) ? FALSE : $lineItems); + $this->assign('lineItem', $lineItems); + $this->assign('displayLineItems', $displayLineItems); $values['totalAmount'] = $values['total_amount']; + $this->assign('displayLineItemFinancialType', TRUE); //do check for campaigns if ($campaignId = CRM_Utils_Array::value('campaign_id', $values)) { @@ -173,7 +190,7 @@ public function preProcess() { // assign values to the template $this->assign($values); $invoiceSettings = Civi::settings()->get('contribution_invoice_settings'); - $invoicing = CRM_Utils_Array::value('invoicing', $invoiceSettings); + $invoicing = CRM_Invoicing_Utils::isInvoicingEnabled(); $this->assign('invoicing', $invoicing); $this->assign('isDeferred', CRM_Utils_Array::value('deferred_revenue_enabled', $invoiceSettings)); if ($invoicing && isset($values['tax_amount'])) { @@ -196,9 +213,9 @@ public function preProcess() { "action=view&reset=1&id={$values['id']}&cid={$values['contact_id']}&context=home" ); - $title = $displayName . ' - (' . CRM_Utils_Money::format($values['total_amount']) . ' ' . ' - ' . $values['financial_type'] . ')'; + $title = $displayName . ' - (' . CRM_Utils_Money::format($values['total_amount'], $values['currency']) . ' ' . ' - ' . $values['financial_type'] . ')'; - $recentOther = array(); + $recentOther = []; if (CRM_Core_Permission::checkActionPermission('CiviContribute', CRM_Core_Action::UPDATE)) { $recentOther['editUrl'] = CRM_Utils_System::url('civicrm/contact/view/contribution', "action=update&reset=1&id={$values['id']}&cid={$values['contact_id']}&context=home" @@ -217,22 +234,59 @@ public function preProcess() { NULL, $recentOther ); + $contributionStatus = $status[$values['contribution_status_id']]; + if (in_array($contributionStatus, ['Partially paid', 'Pending refund']) + || ($contributionStatus == 'Pending' && $values['is_pay_later']) + ) { + if ($contributionStatus == 'Pending refund') { + $this->assign('paymentButtonName', ts('Record Refund')); + } + else { + $this->assign('paymentButtonName', ts('Record Payment')); + } + $this->assign('addRecordPayment', TRUE); + $this->assign('contactId', $values['contact_id']); + $this->assign('componentId', $id); + $this->assign('component', 'contribution'); + } + $this->assignPaymentInfoBlock($id); } /** * Build the form object. */ public function buildQuickForm() { + $this->addButtons([ + [ + 'type' => 'cancel', + 'name' => ts('Done'), + 'spacing' => '         ', + 'isDefault' => TRUE, + ], + ]); + } - $this->addButtons(array( - array( - 'type' => 'cancel', - 'name' => ts('Done'), - 'spacing' => '         ', - 'isDefault' => TRUE, - ), - ) - ); + /** + * Assign the values to build the payment info block. + * + * @todo - this is a bit too much copy & paste from AbstractEditPayment + * (justifying on the basis it's 'pretty short' and in a different inheritance + * tree. I feel like traits are probably the longer term answer). + * + * @param int $id + * + * @return string + * Block title. + */ + protected function assignPaymentInfoBlock($id) { + // component is used in getPaymentInfo primarily to retrieve the contribution id, we + // already have that. + $paymentInfo = CRM_Contribute_BAO_Contribution::getPaymentInfo($id, 'contribution', TRUE); + $title = ts('View Payment'); + $this->assign('transaction', TRUE); + $this->assign('payments', $paymentInfo['transaction']); + $this->assign('paymentLinks', $paymentInfo['payment_links']); + return $title; } } diff --git a/CRM/Contribute/Form/ManagePremiums.php b/CRM/Contribute/Form/ManagePremiums.php index 72a7d8ff9a68..bbb5543e20f0 100644 --- a/CRM/Contribute/Form/ManagePremiums.php +++ b/CRM/Contribute/Form/ManagePremiums.php @@ -1,9 +1,9 @@ _id) { - $params = array('id' => $this->_id); - CRM_Contribute_BAO_ManagePremiums::retrieve($params, $tempDefaults); - $imageUrl = (isset($tempDefaults['image'])) ? $tempDefaults['image'] : ""; + $params = ['id' => $this->_id]; + CRM_Contribute_BAO_Product::retrieve($params, $tempDefaults); if (isset($tempDefaults['image']) && isset($tempDefaults['thumbnail'])) { $defaults['imageUrl'] = $tempDefaults['image']; $defaults['thumbnailUrl'] = $tempDefaults['thumbnail']; @@ -92,10 +91,10 @@ public function buildQuickForm() { $this->applyFilter('__ALL__', 'trim'); $this->add('text', 'name', ts('Name'), CRM_Core_DAO::getAttribute('CRM_Contribute_DAO_Product', 'name'), TRUE); - $this->addRule('name', ts('A product with this name already exists. Please select another name.'), 'objectExists', array( - 'CRM_Contribute_DAO_Product', - $this->_id, - )); + $this->addRule('name', ts('A product with this name already exists. Please select another name.'), 'objectExists', [ + 'CRM_Contribute_DAO_Product', + $this->_id, + ]); $this->add('text', 'sku', ts('SKU'), CRM_Core_DAO::getAttribute('CRM_Contribute_DAO_Product', 'sku')); $this->add('textarea', 'description', ts('Description'), 'rows=3, cols=60'); @@ -124,25 +123,25 @@ public function buildQuickForm() { $this->add('textarea', 'options', ts('Options'), 'rows=3, cols=60'); - $this->add('select', 'period_type', ts('Period Type'), array( - '' => '- select -', - 'rolling' => 'Rolling', - 'fixed' => 'Fixed', - )); + $this->add('select', 'period_type', ts('Period Type'), [ + '' => '- select -', + 'rolling' => 'Rolling', + 'fixed' => 'Fixed', + ]); $this->add('text', 'fixed_period_start_day', ts('Fixed Period Start Day'), CRM_Core_DAO::getAttribute('CRM_Contribute_DAO_Product', 'fixed_period_start_day')); - $this->add('Select', 'duration_unit', ts('Duration Unit'), array('' => '- select period -') + CRM_Core_SelectValues::getPremiumUnits()); + $this->add('Select', 'duration_unit', ts('Duration Unit'), ['' => '- select period -'] + CRM_Core_SelectValues::getPremiumUnits()); $this->add('text', 'duration_interval', ts('Duration'), CRM_Core_DAO::getAttribute('CRM_Contribute_DAO_Product', 'duration_interval')); - $this->add('Select', 'frequency_unit', ts('Frequency Unit'), array('' => '- select period -') + CRM_Core_SelectValues::getPremiumUnits()); + $this->add('Select', 'frequency_unit', ts('Frequency Unit'), ['' => '- select period -'] + CRM_Core_SelectValues::getPremiumUnits()); $this->add('text', 'frequency_interval', ts('Frequency'), CRM_Core_DAO::getAttribute('CRM_Contribute_DAO_Product', 'frequency_interval')); //Financial Type CRM-11106 $financialType = CRM_Contribute_PseudoConstant::financialType(); - $premiumFinancialType = array(); + $premiumFinancialType = []; CRM_Core_PseudoConstant::populate( $premiumFinancialType, 'CRM_Financial_DAO_EntityFinancialAccount', @@ -152,7 +151,7 @@ public function buildQuickForm() { 'account_relationship = 8' ); - $costFinancialType = array(); + $costFinancialType = []; CRM_Core_PseudoConstant::populate( $costFinancialType, 'CRM_Financial_DAO_EntityFinancialAccount', @@ -174,25 +173,24 @@ public function buildQuickForm() { 'select', 'financial_type_id', ts('Financial Type'), - array('' => ts('- select -')) + $financialType + ['' => ts('- select -')] + $financialType ); $this->add('checkbox', 'is_active', ts('Enabled?')); - $this->addFormRule(array('CRM_Contribute_Form_ManagePremiums', 'formRule')); - - $this->addButtons(array( - array( - 'type' => 'upload', - 'name' => ts('Save'), - 'isDefault' => TRUE, - ), - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - ), - ) - ); + $this->addFormRule(['CRM_Contribute_Form_ManagePremiums', 'formRule']); + + $this->addButtons([ + [ + 'type' => 'upload', + 'name' => ts('Save'), + 'isDefault' => TRUE, + ], + [ + 'type' => 'cancel', + 'name' => ts('Cancel'), + ], + ]); $this->assign('productId', $this->_id); } @@ -201,35 +199,34 @@ public function buildQuickForm() { * * @param array $params * (ref.) an assoc array of name/value pairs. - * * @param $files * * @return bool|array * mixed true or array of errors */ public static function formRule($params, $files) { - if (isset($params['imageOption'])) { - if ($params['imageOption'] == 'thumbnail') { - if (!$params['imageUrl']) { - $errors['imageUrl'] = ts('Image URL is Required'); - } - if (!$params['thumbnailUrl']) { - $errors['thumbnailUrl'] = ts('Thumbnail URL is Required'); - } + + // If choosing to upload an image, then an image must be provided + if (CRM_Utils_Array::value('imageOption', $params) == 'image' + && empty($files['uploadFile']['name']) + ) { + $errors['uploadFile'] = ts('A file must be selected'); + } + + // If choosing to use image URLs, then both URLs must be present + if (CRM_Utils_Array::value('imageOption', $params) == 'thumbnail') { + if (!$params['imageUrl']) { + $errors['imageUrl'] = ts('Image URL is Required'); + } + if (!$params['thumbnailUrl']) { + $errors['thumbnailUrl'] = ts('Thumbnail URL is Required'); } } + // CRM-13231 financial type required if product has cost if (!empty($params['cost']) && empty($params['financial_type_id'])) { $errors['financial_type_id'] = ts('Financial Type is required for product having cost.'); } - $fileLocation = $files['uploadFile']['tmp_name']; - if ($fileLocation != "") { - list($width, $height) = getimagesize($fileLocation); - - if (($width < 80 || $width > 500) || ($height < 80 || $height > 500)) { - //$errors ['uploadFile'] = "Please Enter files with dimensions between 80 x 80 and 500 x 500," . " Dimensions of this file is ".$width."X".$height; - } - } if (!$params['period_type']) { if ($params['fixed_period_start_day'] || $params['duration_unit'] || $params['duration_interval'] || @@ -266,133 +263,112 @@ public static function formRule($params, $files) { * Process the form submission. */ public function postProcess() { - + // If previewing, don't do any post-processing if ($this->_action & CRM_Core_Action::PREVIEW) { return; } + // If deleting, then only delete and skip the rest of the post-processing if ($this->_action & CRM_Core_Action::DELETE) { - CRM_Contribute_BAO_ManagePremiums::del($this->_id); - CRM_Core_Session::setStatus(ts('Selected Premium Product type has been deleted.'), ts('Deleted'), 'info'); - } - else { - $params = $this->controller->exportValues($this->_name); - $imageFile = CRM_Utils_Array::value('uploadFile', $params); - $imageFile = $imageFile['name']; - - $config = CRM_Core_Config::singleton(); - - $ids = array(); - $error = FALSE; - // store the submitted values in an array - - // FIX ME - if (CRM_Utils_Array::value('imageOption', $params, FALSE)) { - $value = CRM_Utils_Array::value('imageOption', $params, FALSE); - if ($value == 'image') { - - // to check wether GD is installed or not - $gdSupport = CRM_Utils_System::getModuleSetting('gd', 'GD Support'); - if ($gdSupport) { - if ($imageFile) { - $error = FALSE; - $params['image'] = $this->_resizeImage($imageFile, "_full", 200, 200); - $params['thumbnail'] = $this->_resizeImage($imageFile, "_thumb", 50, 50); - } - } - else { - $error = TRUE; - $params['image'] = $config->resourceBase . 'i/contribute/default_premium.jpg'; - $params['thumbnail'] = $config->resourceBase . 'i/contribute/default_premium_thumb.jpg'; - } - } - elseif ($value == 'thumbnail') { - $params['image'] = $params['imageUrl']; - $params['thumbnail'] = $params['thumbnailUrl']; - } - elseif ($value == 'default_image') { - $url = parse_url($config->userFrameworkBaseURL); - $params['image'] = $config->resourceBase . 'i/contribute/default_premium.jpg'; - $params['thumbnail'] = $config->resourceBase . 'i/contribute/default_premium_thumb.jpg'; - } - else { - $params['image'] = ""; - $params['thumbnail'] = ""; - } + try { + CRM_Contribute_BAO_Product::del($this->_id); } - - if ($this->_action & CRM_Core_Action::UPDATE) { - $ids['premium'] = $this->_id; + catch (CRM_Core_Exception $e) { + $message = ts("This Premium is linked to an Online Contribution page. Please remove it before deleting this Premium.", [1 => CRM_Utils_System::url('civicrm/admin/contribute', 'reset=1')]); + CRM_Core_Session::setStatus($message, ts('Cannot delete Premium'), 'error'); + CRM_Core_Session::singleton()->pushUserContext(CRM_Utils_System::url('civicrm/admin/contribute/managePremiums', 'reset=1&action=browse')); + return; } + CRM_Core_Session::setStatus( + ts('Selected Premium Product type has been deleted.'), + ts('Deleted'), 'info'); + return; + } - // fix the money fields - foreach (array( - 'cost', - 'price', - 'min_contribution', - ) as $f) { - $params[$f] = CRM_Utils_Rule::cleanMoney($params[$f]); - } + $params = $this->controller->exportValues($this->_name); - $premium = CRM_Contribute_BAO_ManagePremiums::add($params, $ids); - if ($error) { - CRM_Core_Session::setStatus(ts('No thumbnail of your image was created because the GD image library is not currently compiled in your PHP installation. Product is currently configured to use default thumbnail image. If you have a local thumbnail image you can upload it separately and input the thumbnail URL by editing this premium.'), ts('Notice'), 'alert'); - } - else { - CRM_Core_Session::setStatus(ts("The Premium '%1' has been saved.", array(1 => $premium->name)), ts('Saved'), 'success'); - } + // Clean the the money fields + $moneyFields = ['cost', 'price', 'min_contribution']; + foreach ($moneyFields as $field) { + $params[$field] = CRM_Utils_Rule::cleanMoney($params[$field]); + } + + // If we're updating, we need to pass in the premium product Id + if ($this->_action & CRM_Core_Action::UPDATE) { + $params['id'] = $this->_id; } + + $this->_processImages($params); + + // Save the premium product to database + $premium = CRM_Contribute_BAO_Product::create($params); + + CRM_Core_Session::setStatus( + ts("The Premium '%1' has been saved.", [1 => $premium->name]), + ts('Saved'), 'success'); } /** - * Resize a premium image to a different size. + * Look at $params to find form info about images. Manipulate images if + * necessary. Then alter $params to point to the newly manipulated images. * - * - * @param string $filename - * @param string $resizedName - * @param $width - * @param $height - * - * @return string - * Path to image + * @param array $params */ - private function _resizeImage($filename, $resizedName, $width, $height) { - // figure out the new filename - $pathParts = pathinfo($filename); - $newFilename = $pathParts['dirname'] . "/" . $pathParts['filename'] . $resizedName . "." . $pathParts['extension']; - - // get image about original image - $imageInfo = getimagesize($filename); - $widthOrig = $imageInfo[0]; - $heightOrig = $imageInfo[1]; - $image = imagecreatetruecolor($width, $height); - if ($imageInfo['mime'] == 'image/gif') { - $source = imagecreatefromgif($filename); + protected function _processImages(&$params) { + $defaults = [ + 'imageOption' => 'noImage', + 'uploadFile' => ['name' => ''], + 'image' => '', + 'thumbnail' => '', + 'imageUrl' => '', + 'thumbnailUrl' => '', + ]; + $params = array_merge($defaults, $params); + + // User is uploading an image + if ($params['imageOption'] == 'image') { + $imageFile = $params['uploadFile']['name']; + try { + $params['image'] = CRM_Utils_File::resizeImage($imageFile, 200, 200, "_full"); + $params['thumbnail'] = CRM_Utils_File::resizeImage($imageFile, 50, 50, "_thumb"); + } + catch (CRM_Core_Exception $e) { + $params['image'] = self::_defaultImage(); + $params['thumbnail'] = self::_defaultThumbnail(); + $msg = ts('The product has been configured to use a default image.'); + CRM_Core_Session::setStatus($e->getMessage() . " $msg", ts('Notice'), 'alert'); + } } - elseif ($imageInfo['mime'] == 'image/png') { - $source = imagecreatefrompng($filename); + + // User is specifying existing URLs for the images + elseif ($params['imageOption'] == 'thumbnail') { + $params['image'] = $params['imageUrl']; + $params['thumbnail'] = $params['thumbnailUrl']; } - else { - $source = imagecreatefromjpeg($filename); + + // User wants a default image + elseif ($params['imageOption'] == 'default_image') { + $params['image'] = self::_defaultImage(); + $params['thumbnail'] = self::_defaultThumbnail(); } + } + + /** + * Returns the path to the default premium image + * @return string + */ + protected static function _defaultImage() { + $config = CRM_Core_Config::singleton(); + return $config->resourceBase . 'i/contribute/default_premium.jpg'; + } - // resize - imagecopyresized($image, $source, 0, 0, 0, 0, $width, $height, $widthOrig, $heightOrig); - - // save the resized image - $fp = fopen($newFilename, 'w+'); - ob_start(); - imagejpeg($image); - $image_buffer = ob_get_contents(); - ob_end_clean(); - imagedestroy($image); - fwrite($fp, $image_buffer); - rewind($fp); - fclose($fp); - - // return the URL to link to + /** + * Returns the path to the default premium thumbnail + * @return string + */ + protected static function _defaultThumbnail() { $config = CRM_Core_Config::singleton(); - return $config->imageUploadURL . basename($newFilename); + return $config->resourceBase . 'i/contribute/default_premium_thumb.jpg'; } } diff --git a/CRM/Contribute/Form/Search.php b/CRM/Contribute/Form/Search.php index 866c736fd9ff..8351b6d9ad8b 100644 --- a/CRM/Contribute/Form/Search.php +++ b/CRM/Contribute/Form/Search.php @@ -1,9 +1,9 @@ _actionButtonName = $this->getButtonName('next', 'action'); $this->_done = FALSE; - // @todo - is this an error - $this->_defaults is used. - $this->defaults = array(); - - /* - * we allow the controller to set force/reset externally, useful when we are being - * driven by the wizard framework - */ - - $this->_reset = CRM_Utils_Request::retrieve('reset', 'Boolean', CRM_Core_DAO::$_nullObject); - $this->_force = CRM_Utils_Request::retrieve('force', 'Boolean', $this, FALSE); - $this->_limit = CRM_Utils_Request::retrieve('limit', 'Positive', $this); - $this->_context = CRM_Utils_Request::retrieve('context', 'String', $this, FALSE, 'search'); - $this->assign("context", $this->_context); + $this->loadStandardSearchOptionsFromUrl(); // get user submitted values // get it from controller only if form has been submitted, else preProcess has set this @@ -165,29 +161,13 @@ public function setDefaultValues() { * Build the form object. */ public function buildQuickForm() { - parent::buildQuickForm(); - $this->addSortNameField(); - - $this->_group = CRM_Core_PseudoConstant::nestedGroup(); - - // multiselect for groups - if ($this->_group) { - $this->add('select', 'group', ts('Groups'), $this->_group, FALSE, - array('id' => 'group', 'multiple' => 'multiple', 'class' => 'crm-select2') - ); - } - - // multiselect for tags - $contactTags = CRM_Core_BAO_Tag::getTags(); + if ($this->isFormInViewOrEditMode()) { + parent::buildQuickForm(); + $this->addContactSearchFields(); - if ($contactTags) { - $this->add('select', 'contact_tags', ts('Tags'), $contactTags, FALSE, - array('id' => 'contact_tags', 'multiple' => 'multiple', 'class' => 'crm-select2') - ); + CRM_Contribute_BAO_Query::buildSearchForm($this); } - CRM_Contribute_BAO_Query::buildSearchForm($this); - $rows = $this->get('rows'); if (is_array($rows)) { if (!$this->_single) { @@ -197,11 +177,11 @@ public function buildQuickForm() { $permission = CRM_Core_Permission::getPermission(); $queryParams = $this->get('queryParams'); - $softCreditFiltering = FALSE; + $taskParams['softCreditFiltering'] = FALSE; if (!empty($queryParams)) { - $softCreditFiltering = CRM_Contribute_BAO_Query::isSoftCreditOptionEnabled($queryParams); + $taskParams['softCreditFiltering'] = CRM_Contribute_BAO_Query::isSoftCreditOptionEnabled($queryParams); } - $tasks = CRM_Contribute_Task::permissionedTaskTitles($permission, $softCreditFiltering); + $tasks = CRM_Contribute_Task::permissionedTaskTitles($permission, $taskParams); $this->addTaskMenu($tasks); } @@ -229,6 +209,36 @@ protected function getSortNameLabelWithOutEmail() { return ts('Contributor Name'); } + /** + * Get the label for the tag field. + * + * We do this in a function so the 'ts' wraps the whole string to allow + * better translation. + * + * @return string + */ + protected function getTagLabel() { + return ts('Contributor Tag(s)'); + } + + /** + * Get the label for the group field. + * + * @return string + */ + protected function getGroupLabel() { + return ts('Contributor Group(s)'); + } + + /** + * Get the label for the group field. + * + * @return string + */ + protected function getContactTypeLabel() { + return ts('Contributor Contact Type'); + } + /** * The post processing of the form gets done here. * @@ -248,7 +258,7 @@ public function postProcess() { $this->_done = TRUE; - if (!empty($_POST)) { + if (!empty($_POST) && !$this->_force) { $this->_formValues = $this->controller->exportValues($this->_name); } @@ -259,10 +269,10 @@ public function postProcess() { $this->_formValues["contribution_test"] = 0; } - foreach (array( - 'contribution_amount_low', - 'contribution_amount_high', - ) as $f) { + foreach ([ + 'contribution_amount_low', + 'contribution_amount_high', + ] as $f) { if (isset($this->_formValues[$f])) { $this->_formValues[$f] = CRM_Utils_Rule::cleanMoney($this->_formValues[$f]); } @@ -270,7 +280,7 @@ public function postProcess() { $config = CRM_Core_Config::singleton(); if (!empty($_POST)) { - $specialParams = array( + $specialParams = [ 'financial_type_id', 'contribution_soft_credit_type_id', 'contribution_status_id', @@ -280,7 +290,8 @@ public function postProcess() { 'contribution_product_id', 'invoice_id', 'payment_instrument_id', - ); + 'contribution_batch_id', + ]; CRM_Contact_BAO_Query::processSpecialFormValue($this->_formValues, $specialParams); $tags = CRM_Utils_Array::value('contact_tags', $this->_formValues); @@ -296,18 +307,16 @@ public function postProcess() { } } - if (!defined('CIVICRM_GROUPTREE')) { - $group = CRM_Utils_Array::value('group', $this->_formValues); - if ($group && !is_array($group)) { - unset($this->_formValues['group']); - $this->_formValues['group'][$group] = 1; - } + $group = CRM_Utils_Array::value('group', $this->_formValues); + if ($group && !is_array($group)) { + unset($this->_formValues['group']); + $this->_formValues['group'][$group] = 1; + } - if ($group && is_array($group)) { - unset($this->_formValues['group']); - foreach ($group as $notImportant => $groupID) { - $this->_formValues['group'][$groupID] = 1; - } + if ($group && is_array($group)) { + unset($this->_formValues['group']); + foreach ($group as $groupID) { + $this->_formValues['group'][$groupID] = 1; } } } @@ -366,9 +375,7 @@ public function postProcess() { if ($this->_context == 'user') { $query->setSkipPermission(TRUE); } - $summary = &$query->summaryContribution($this->_context); - $this->set('summary', $summary); - $this->assign('contributionSummary', $summary); + $controller->run(); } @@ -382,12 +389,20 @@ public function fixFormValues() { return; } - $status = CRM_Utils_Request::retrieve('status', 'String', - CRM_Core_DAO::$_nullObject - ); + $status = CRM_Utils_Request::retrieve('status', 'String'); if ($status) { - $this->_formValues['contribution_status_id'] = array($status => 1); - $this->_defaults['contribution_status_id'] = array($status => 1); + $this->_formValues['contribution_status_id'] = [$status => 1]; + $this->_defaults['contribution_status_id'] = [$status => 1]; + } + + $pcpid = (array) CRM_Utils_Request::retrieve('pcpid', 'String', $this); + if ($pcpid) { + // Add new pcpid to the tail of the array... + foreach ($pcpid as $pcpIdList) { + $this->_formValues['contribution_pcp_made_through_id'][] = $pcpIdList; + } + // and avoid any duplicate + $this->_formValues['contribution_pcp_made_through_id'] = array_unique($this->_formValues['contribution_pcp_made_through_id']); } $cid = CRM_Utils_Request::retrieve('cid', 'Positive', $this); @@ -406,18 +421,14 @@ public function fixFormValues() { } } - $lowDate = CRM_Utils_Request::retrieve('start', 'Timestamp', - CRM_Core_DAO::$_nullObject - ); + $lowDate = CRM_Utils_Request::retrieve('start', 'Timestamp'); if ($lowDate) { $lowDate = CRM_Utils_Type::escape($lowDate, 'Timestamp'); $date = CRM_Utils_Date::setDateDefaults($lowDate); $this->_formValues['contribution_date_low'] = $this->_defaults['contribution_date_low'] = $date[0]; } - $highDate = CRM_Utils_Request::retrieve('end', 'Timestamp', - CRM_Core_DAO::$_nullObject - ); + $highDate = CRM_Utils_Request::retrieve('end', 'Timestamp'); if ($highDate) { $highDate = CRM_Utils_Type::escape($highDate, 'Timestamp'); $date = CRM_Utils_Date::setDateDefaults($highDate); @@ -433,9 +444,7 @@ public function fixFormValues() { $this ); - $test = CRM_Utils_Request::retrieve('test', 'Boolean', - CRM_Core_DAO::$_nullObject - ); + $test = CRM_Utils_Request::retrieve('test', 'Boolean'); if (isset($test)) { $test = CRM_Utils_Type::escape($test, 'Boolean'); $this->_formValues['contribution_test'] = $test; diff --git a/CRM/Contribute/Form/SearchContribution.php b/CRM/Contribute/Form/SearchContribution.php index b52b5a6f5a8c..1b3130469550 100644 --- a/CRM/Contribute/Form/SearchContribution.php +++ b/CRM/Contribute/Form/SearchContribution.php @@ -1,9 +1,9 @@ addButtons(array( - array( + $this->addButtons([ + [ 'type' => 'refresh', 'name' => ts('Search'), 'isDefault' => TRUE, - ), - )); + ], + ]); } public function postProcess() { @@ -62,7 +62,7 @@ public function postProcess() { $parent = $this->controller->getParent(); $parent->set('searchResult', 1); if (!empty($params)) { - $fields = array('title', 'financial_type_id', 'campaign_id'); + $fields = ['title', 'financial_type_id', 'campaign_id']; foreach ($fields as $field) { if (isset($params[$field]) && !CRM_Utils_System::isNull($params[$field]) diff --git a/CRM/Contribute/Form/SoftCredit.php b/CRM/Contribute/Form/SoftCredit.php index a739ae7e02c7..431cb24dce16 100644 --- a/CRM/Contribute/Form/SoftCredit.php +++ b/CRM/Contribute/Form/SoftCredit.php @@ -1,9 +1,9 @@ id = $form->_id; - $contriDAO->find(TRUE); - if ($contriDAO->contribution_page_id) { - $ufJoinParams = array( - 'module' => 'soft_credit', - 'entity_table' => 'civicrm_contribution_page', - 'entity_id' => $contriDAO->contribution_page_id, - ); - $profileId = CRM_Core_BAO_UFJoin::getUFGroupIds($ufJoinParams); - - //check if any honree profile is enabled if yes then assign its profile type to $_honoreeProfileType - // which will be used to constraint soft-credit contact type in formRule, CRM-13981 - if (!empty($profileId[0]) && !empty($profileId[2])) { - $form->_honoreeProfileType = CRM_Core_BAO_UFGroup::getContactType($profileId[0]); - } - } - } - /** * Function used to build form element for soft credit block. * @@ -76,7 +51,7 @@ public static function buildQuickForm(&$form) { if ($ufJoinDAO->find(TRUE)) { $jsonData = CRM_Contribute_BAO_ContributionPage::formatModuleData($ufJoinDAO->module_data, TRUE, 'soft_credit'); if ($jsonData) { - foreach (array('honor_block_title', 'honor_block_text') as $name) { + foreach (['honor_block_title', 'honor_block_text'] as $name) { $form->assign($name, $jsonData[$name]); } @@ -93,7 +68,7 @@ public static function buildQuickForm(&$form) { } // by default generate 10 blocks - $item_count = 11; + $item_count = $form->_softCreditItemCount; $showSoftCreditRow = 2; if ($form->getAction() & CRM_Core_Action::UPDATE) { @@ -121,15 +96,15 @@ public static function buildQuickForm(&$form) { } for ($rowNumber = 1; $rowNumber <= $item_count; $rowNumber++) { - $form->addEntityRef("soft_credit_contact_id[{$rowNumber}]", ts('Contact'), array('create' => TRUE)); + $form->addEntityRef("soft_credit_contact_id[{$rowNumber}]", ts('Contact'), ['create' => TRUE]); $form->addMoney("soft_credit_amount[{$rowNumber}]", ts('Amount'), FALSE, NULL, FALSE); - $form->addSelect("soft_credit_type[{$rowNumber}]", array( - 'entity' => 'contribution_soft', - 'field' => 'soft_credit_type_id', - 'label' => ts('Type'), - )); + $form->addSelect("soft_credit_type[{$rowNumber}]", [ + 'entity' => 'contribution_soft', + 'field' => 'soft_credit_type_id', + 'label' => ts('Type'), + ]); if (!empty($form->_softCreditInfo['soft_credit'][$rowNumber]['soft_credit_id'])) { $form->add('hidden', "soft_credit_id[{$rowNumber}]", $form->_softCreditInfo['soft_credit'][$rowNumber]['soft_credit_id']); @@ -142,7 +117,7 @@ public static function buildQuickForm(&$form) { $form->assign('rowCount', $item_count); $form->addElement('hidden', 'sct_default_id', CRM_Core_OptionGroup::getDefaultValue("soft_credit_type"), - array('id' => 'sct_default_id') + ['id' => 'sct_default_id'] ); } @@ -160,7 +135,7 @@ public static function addPCPFields(&$form, $suffix = '') { if (!CRM_Utils_Array::crmIsEmptyArray($siteHasPCPs)) { $form->assign('siteHasPCPs', 1); // Fixme: Not a true entityRef field. Relies on PCP.js.tpl - $form->add('text', "pcp_made_through_id$suffix", ts('Credit to a Personal Campaign Page'), array('class' => 'twenty', 'placeholder' => ts('- select -'))); + $form->add('text', "pcp_made_through_id$suffix", ts('Credit to a Personal Campaign Page'), ['class' => 'twenty', 'placeholder' => ts('- select -')]); // stores the label $form->add('hidden', "pcp_made_through$suffix"); $form->addElement('checkbox', "pcp_display_in_roll$suffix", ts('Display in Honor Roll?'), NULL); @@ -216,7 +191,7 @@ public static function setDefaultValues(&$defaults, &$form) { * Array of errors */ public static function formRule($fields, $errors, $self) { - $errors = array(); + $errors = []; // if honor roll fields are populated but no PCP is selected if (empty($fields['pcp_made_through_id'])) { @@ -232,7 +207,7 @@ public static function formRule($fields, $errors, $self) { foreach ($fields['soft_credit_amount'] as $key => $val) { if (!empty($fields['soft_credit_contact_id'][$key])) { if ($repeat[$fields['soft_credit_contact_id'][$key]] > 1) { - $errors["soft_credit_contact[$key]"] = ts('You cannot enter multiple soft credits for the same contact.'); + $errors["soft_credit_contact_id[$key]"] = ts('You cannot enter multiple soft credits for the same contact.'); } if ($self->_action == CRM_Core_Action::ADD && $fields['soft_credit_amount'][$key] && (CRM_Utils_Rule::cleanMoney($fields['soft_credit_amount'][$key]) > CRM_Utils_Rule::cleanMoney($fields['total_amount'])) @@ -242,10 +217,6 @@ public static function formRule($fields, $errors, $self) { if (empty($fields['soft_credit_amount'][$key])) { $errors["soft_credit_amount[$key]"] = ts('Please enter the soft credit amount.'); } - $contactType = CRM_Contact_BAO_Contact::getContactType($fields['soft_credit_contact_id'][$key]); - if ($self->_honoreeProfileType && $self->_honoreeProfileType != $contactType) { - $errors["soft_credit_contact[$key]"] = ts('Please choose a contact of type %1', array(1 => $self->_honoreeProfileType)); - } } } } diff --git a/CRM/Contribute/Form/Task.php b/CRM/Contribute/Form/Task.php index 9e943e35a5e3..8e6eb1d3ad3c 100644 --- a/CRM/Contribute/Form/Task.php +++ b/CRM/Contribute/Form/Task.php @@ -1,9 +1,9 @@ _contributionIds = array(); + public static function preProcessCommon(&$form) { + $form->_contributionIds = []; $values = $form->controller->exportValues($form->get('searchFormName')); - $form->_task = $values['task']; + $form->_task = CRM_Utils_Array::value('task', $values); $contributeTasks = CRM_Contribute_Task::tasks(); - $form->assign('taskName', $contributeTasks[$form->_task]); + $form->assign('taskName', CRM_Utils_Array::value($form->_task, $contributeTasks)); - $ids = array(); - if ($values['radio_ts'] == 'ts_sel') { + $ids = []; + if (isset($values['radio_ts']) && $values['radio_ts'] == 'ts_sel') { foreach ($values as $name => $value) { if (substr($name, 0, CRM_Core_Form::CB_PREFIX_LEN) == CRM_Core_Form::CB_PREFIX) { $ids[] = substr($name, CRM_Core_Form::CB_PREFIX_LEN); @@ -115,19 +87,41 @@ public static function preProcessCommon(&$form, $useTable = FALSE) { } else { $queryParams = $form->get('queryParams'); - $sortOrder = NULL; + $isTest = FALSE; + if (is_array($queryParams)) { + foreach ($queryParams as $fields) { + if ($fields[0] == 'contribution_test') { + $isTest = TRUE; + break; + } + } + } + if (!$isTest) { + $queryParams[] = [ + 'contribution_test', + '=', + 0, + 0, + 0, + ]; + } + $returnProperties = ['contribution_id' => 1]; + $sortOrder = $sortCol = NULL; if ($form->get(CRM_Utils_Sort::SORT_ORDER)) { $sortOrder = $form->get(CRM_Utils_Sort::SORT_ORDER); + //Include sort column in select clause. + $sortCol = trim(str_replace(['`', 'asc', 'desc'], '', $sortOrder)); + $returnProperties[$sortCol] = 1; } $form->_includesSoftCredits = CRM_Contribute_BAO_Query::isSoftCreditOptionEnabled($queryParams); - $query = new CRM_Contact_BAO_Query($queryParams, array('contribution_id'), NULL, FALSE, FALSE, + $query = new CRM_Contact_BAO_Query($queryParams, $returnProperties, NULL, FALSE, FALSE, CRM_Contact_BAO_Query::MODE_CONTRIBUTE ); // @todo the function CRM_Contribute_BAO_Query::isSoftCreditOptionEnabled should handle this // can we remove? if not why not? if ($form->_includesSoftCredits) { - $contactIds = $contributionContactIds = array(); + $contactIds = $contributionContactIds = []; $query->_rowCountClause = " count(civicrm_contribution.id)"; $query->_groupByComponentClause = " GROUP BY contribution_search_scredit_combined.id, contribution_search_scredit_combined.contact_id, contribution_search_scredit_combined.scredit_id "; } @@ -158,6 +152,7 @@ public static function preProcessCommon(&$form, $useTable = FALSE) { } $form->_contributionIds = $form->_componentIds = $ids; + $form->set('contributionIds', $form->_contributionIds); //set the context for redirection for any task actions $session = CRM_Core_Session::singleton(); @@ -179,13 +174,22 @@ public static function preProcessCommon(&$form, $useTable = FALSE) { } } + /** + * Sets contribution Ids for unit test. + * + * @param array $contributionIds + */ + public function setContributionIds($contributionIds) { + $this->_contributionIds = $contributionIds; + } + /** * Given the contribution id, compute the contact id * since its used for things like send email */ public function setContactIDs() { if (!$this->_includesSoftCredits) { - $this->_contactIds = &CRM_Core_DAO::getContactIDsFromComponent( + $this->_contactIds = CRM_Core_DAO::getContactIDsFromComponent( $this->_contributionIds, 'civicrm_contribution' ); @@ -204,18 +208,17 @@ public function setContactIDs() { * @param bool $submitOnce */ public function addDefaultButtons($title, $nextType = 'next', $backType = 'back', $submitOnce = FALSE) { - $this->addButtons(array( - array( - 'type' => $nextType, - 'name' => $title, - 'isDefault' => TRUE, - ), - array( - 'type' => $backType, - 'name' => ts('Cancel'), - ), - ) - ); + $this->addButtons([ + [ + 'type' => $nextType, + 'name' => $title, + 'isDefault' => TRUE, + ], + [ + 'type' => $backType, + 'name' => ts('Cancel'), + ], + ]); } } diff --git a/CRM/Contribute/Form/Task/Batch.php b/CRM/Contribute/Form/Task/Batch.php index 2f3a4c4b5cae..b2f32ee2549c 100644 --- a/CRM/Contribute/Form/Task/Batch.php +++ b/CRM/Contribute/Form/Task/Batch.php @@ -1,9 +1,9 @@ ts('Name')), + $readOnlyFields = array_merge(['sort_name' => ts('Name')], CRM_Core_BAO_Setting::valueOptions(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'contact_autocomplete_options', TRUE, NULL, FALSE, 'name', TRUE @@ -89,12 +91,12 @@ public function buildQuickForm() { CRM_Utils_System::setTitle($this->_title); $this->addDefaultButtons(ts('Save')); - $this->_fields = array(); + $this->_fields = []; $this->_fields = CRM_Core_BAO_UFGroup::getFields($ufGroupId, FALSE, CRM_Core_Action::VIEW); // remove file type field and then limit fields $suppressFields = FALSE; - $removehtmlTypes = array('File', 'Autocomplete-Select'); + $removehtmlTypes = ['File']; foreach ($this->_fields as $name => $field) { if ($cfID = CRM_Core_BAO_CustomField::getKeyID($name) && in_array($this->_fields[$name]['html_type'], $removehtmlTypes) @@ -112,25 +114,24 @@ public function buildQuickForm() { $this->_fields = array_slice($this->_fields, 0, $this->_maxFields); - $this->addButtons(array( - array( - 'type' => 'submit', - 'name' => ts('Update Contribution(s)'), - 'isDefault' => TRUE, - ), - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'submit', + 'name' => ts('Update Contribution(s)'), + 'isDefault' => TRUE, + ], + [ + 'type' => 'cancel', + 'name' => ts('Cancel'), + ], + ]); $this->assign('profileTitle', $this->_title); $this->assign('componentIds', $this->_contributionIds); //load all campaigns. if (array_key_exists('contribution_campaign_id', $this->_fields)) { - $this->_componentCampaigns = array(); + $this->_componentCampaigns = []; CRM_Core_PseudoConstant::populate($this->_componentCampaigns, 'CRM_Contribute_DAO_Contribution', TRUE, 'campaign_id', 'id', @@ -138,11 +139,17 @@ public function buildQuickForm() { ); } + // It is possible to have fields that are required in CiviCRM not be required in the + // profile. Overriding that here. Perhaps a better approach would be to + // make them required in the schema & read that up through getFields functionality. + $requiredFields = ['receive_date']; + //fix for CRM-2752 $customFields = CRM_Core_BAO_CustomField::getFields('Contribution'); foreach ($this->_contributionIds as $contributionId) { $typeId = CRM_Core_DAO::getFieldValue("CRM_Contribute_DAO_Contribution", $contributionId, 'financial_type_id'); foreach ($this->_fields as $name => $field) { + $entityColumnValue = []; if ($customFieldID = CRM_Core_BAO_CustomField::getKeyID($name)) { $customValue = CRM_Utils_Array::value($customFieldID, $customFields); if (!empty($customValue['extends_entity_column_value'])) { @@ -152,13 +159,16 @@ public function buildQuickForm() { } if (!empty($entityColumnValue[$typeId]) || - CRM_Utils_System::isNull($entityColumnValue[$typeId]) + CRM_Utils_System::isNull(CRM_Utils_Array::value($typeId, $entityColumnValue)) ) { CRM_Core_BAO_UFGroup::buildProfile($this, $field, NULL, $contributionId); } } else { // handle non custom fields + if (in_array($field['name'], $requiredFields)) { + $field['is_required'] = TRUE; + } CRM_Core_BAO_UFGroup::buildProfile($this, $field, NULL, $contributionId); } } @@ -170,7 +180,7 @@ public function buildQuickForm() { $buttonName = $this->controller->getButtonName('submit'); if ($suppressFields && $buttonName != '_qf_Batch_next') { - CRM_Core_Session::setStatus(ts("File or Autocomplete-Select type field(s) in the selected profile are not supported for Update multiple contributions."), ts('Unsupported Field Type'), 'error'); + CRM_Core_Session::setStatus(ts("File type field(s) in the selected profile are not supported for Update multiple contributions."), ts('Unsupported Field Type'), 'error'); } $this->addDefaultButtons(ts('Update Contributions')); @@ -184,9 +194,8 @@ public function setDefaultValues() { return; } - $defaults = array(); + $defaults = []; foreach ($this->_contributionIds as $contributionId) { - $details[$contributionId] = array(); CRM_Core_BAO_UFGroup::setProfileDefaults(NULL, $this->_fields, $defaults, FALSE, $contributionId, 'Contribute'); } @@ -198,58 +207,32 @@ public function setDefaultValues() { */ public function postProcess() { $params = $this->exportValues(); - $dates = array( - 'receive_date', - 'receipt_date', - 'thankyou_date', - 'cancel_date', - ); + // @todo extract submit functions & + // extend CRM_Event_Form_Task_BatchTest::testSubmit with a data provider to test + // handling of custom data, specifically checkbox fields. if (isset($params['field'])) { - foreach ($params['field'] as $key => $value) { + foreach ($params['field'] as $contributionID => $value) { - $value['custom'] = CRM_Core_BAO_CustomField::postProcess($value, - $key, - 'Contribution' - ); - - $ids['contribution'] = $key; - foreach ($dates as $val) { - if (isset($value[$val])) { - $value[$val] = CRM_Utils_Date::processDate($value[$val]); - } - } + $value['id'] = $contributionID; if (!empty($value['financial_type'])) { $value['financial_type_id'] = $value['financial_type']; } - if (!empty($value['payment_instrument'])) { - $value['payment_instrument_id'] = $value['payment_instrument']; - } - - if (!empty($value['contribution_source'])) { - $value['source'] = $value['contribution_source']; - } - - unset($value['financial_type']); - unset($value['contribution_source']); - $contribution = CRM_Contribute_BAO_Contribution::add($value, $ids); + $value['options'] = [ + 'reload' => 1, + ]; + $contribution = civicrm_api3('Contribution', 'create', $value); + $contribution = $contribution['values'][$contributionID]; + // @todo add check as to whether the status is updated. if (!empty($value['contribution_status_id'])) { - CRM_Contribute_BAO_Contribution::transitionComponentWithReturnMessage($contribution->id, + // @todo - use completeorder api or make api call do this. + CRM_Contribute_BAO_Contribution::transitionComponentWithReturnMessage($contribution['id'], $value['contribution_status_id'], - CRM_Utils_Array::value("field[{$key}][contribution_status_id]", - $this->_defaultValues - ), - $contribution->receive_date + CRM_Utils_Array::value("field[{$contributionID}][contribution_status_id]", $this->_defaultValues), + $contribution['receive_date'] ); } - - // add custom field values - if (!empty($value['custom']) && - is_array($value['custom']) - ) { - CRM_Core_BAO_CustomValueTable::store($value['custom'], 'civicrm_contribution', $contribution->id); - } } CRM_Core_Session::setStatus(ts("Your updates have been saved."), ts('Saved'), 'success'); } diff --git a/CRM/Contribute/Form/Task/Delete.php b/CRM/Contribute/Form/Task/Delete.php index 842d04f0d109..8601ee564c11 100644 --- a/CRM/Contribute/Form/Task/Delete.php +++ b/CRM/Contribute/Form/Task/Delete.php @@ -1,9 +1,9 @@ _contributionIds)) { - CRM_Core_Session::setStatus(ts('1 contribution could not be deleted.', array('plural' => '%count contributions could not be deleted.', 'count' => $count)), ts('Error'), 'error'); - $this->addButtons(array( - array( + CRM_Core_Session::setStatus(ts('1 contribution could not be deleted.', ['plural' => '%count contributions could not be deleted.', 'count' => $count]), ts('Error'), 'error'); + $this->addButtons([ + [ 'type' => 'back', 'name' => ts('Cancel'), - ), - ) - ); + ], + ]); } elseif ($count && !empty($this->_contributionIds)) { - CRM_Core_Session::setStatus(ts('1 contribution will not be deleted.', array('plural' => '%count contributions will not be deleted.', 'count' => $count)), ts('Warning'), 'warning'); + CRM_Core_Session::setStatus(ts('1 contribution will not be deleted.', ['plural' => '%count contributions will not be deleted.', 'count' => $count]), ts('Warning'), 'warning'); $this->addDefaultButtons(ts('Delete Contributions'), 'done'); } else { @@ -115,12 +114,12 @@ public function postProcess() { } if ($deleted) { - $msg = ts('%count contribution deleted.', array('plural' => '%count contributions deleted.', 'count' => $deleted)); + $msg = ts('%count contribution deleted.', ['plural' => '%count contributions deleted.', 'count' => $deleted]); CRM_Core_Session::setStatus($msg, ts('Removed'), 'success'); } if ($failed) { - CRM_Core_Session::setStatus(ts('1 could not be deleted.', array('plural' => '%count could not be deleted.', 'count' => $failed)), ts('Error'), 'error'); + CRM_Core_Session::setStatus(ts('1 could not be deleted.', ['plural' => '%count could not be deleted.', 'count' => $failed]), ts('Error'), 'error'); } } diff --git a/CRM/Contribute/Form/Task/Email.php b/CRM/Contribute/Form/Task/Email.php index 8922985a9646..1300c85d5e7e 100644 --- a/CRM/Contribute/Form/Task/Email.php +++ b/CRM/Contribute/Form/Task/Email.php @@ -1,9 +1,9 @@ _contributionIds = array($id); + $this->_contributionIds = [$id]; $this->_componentClause = " civicrm_contribution.id IN ( $id ) "; $this->_single = TRUE; $this->assign('totalSelectedContributions', 1); @@ -92,8 +96,8 @@ public function preProcess() { // check that all the contribution ids have status Completed, Pending, Refunded. $this->_contributionStatusId = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name'); - $status = array('Completed', 'Pending', 'Refunded'); - $statusId = array(); + $status = ['Completed', 'Pending', 'Refunded']; + $statusId = []; foreach ($this->_contributionStatusId as $key => $value) { if (in_array($value, $status)) { $statusId[] = $key; @@ -117,18 +121,19 @@ public function preProcess() { } $url = CRM_Utils_System::url('civicrm/contribute/search', $urlParams); - $breadCrumb = array( - array( + $breadCrumb = [ + [ 'url' => $url, 'title' => ts('Search Results'), - ), - ); + ], + ]; CRM_Utils_System::appendBreadCrumb($breadCrumb); $this->_selectedOutput = CRM_Utils_Request::retrieve('select', 'String', $this); $this->assign('selectedOutput', $this->_selectedOutput); + CRM_Contact_Form_Task_EmailCommon::preProcessFromAddress($this); if ($this->_selectedOutput == 'email') { CRM_Utils_System::setTitle(ts('Email Invoice')); } @@ -141,63 +146,38 @@ public function preProcess() { * Build the form object. */ public function buildQuickForm() { - $session = CRM_Core_Session::singleton(); $this->preventAjaxSubmit(); if (CRM_Core_Permission::check('administer CiviCRM')) { $this->assign('isAdmin', 1); } - $contactID = $session->get('userID'); - $contactEmails = CRM_Core_BAO_Email::allEmails($contactID); - $emails = array(); - $fromDisplayName = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', - $contactID, 'display_name' - ); - foreach ($contactEmails as $emailId => $item) { - $email = $item['email']; - if ($email) { - $emails[$emailId] = '"' . $fromDisplayName . '" <' . $email . '> '; - } - if (isset($emails[$emailId])) { - $emails[$emailId] .= $item['locationType']; - if ($item['is_primary']) { - $emails[$emailId] .= ' ' . ts('(preferred)'); - } - $emails[$emailId] = htmlspecialchars($emails[$emailId]); - } - } - $fromEmailAddress = CRM_Core_OptionGroup::values('from_email_address'); - foreach ($fromEmailAddress as $key => $email) { - $fromEmailAddress[$key] = htmlspecialchars($fromEmailAddress[$key]); - } - $fromEmail = CRM_Utils_Array::crmArrayMerge($emails, $fromEmailAddress); - $this->add('select', 'from_email_address', ts('From Email Address'), array('' => '- select -') + $fromEmail); + + $this->add('select', 'from_email_address', ts('From'), $this->_fromEmails, TRUE); if ($this->_selectedOutput != 'email') { $this->addElement('radio', 'output', NULL, ts('Email Invoice'), 'email_invoice'); $this->addElement('radio', 'output', NULL, ts('PDF Invoice'), 'pdf_invoice'); $this->addRule('output', ts('Selection required'), 'required'); - $this->addFormRule(array('CRM_Contribute_Form_Task_Invoice', 'formRule')); + $this->addFormRule(['CRM_Contribute_Form_Task_Invoice', 'formRule']); } else { $this->addRule('from_email_address', ts('From Email Address is required'), 'required'); } - $this->add('wysiwyg', 'email_comment', ts('If you would like to add personal message to email please add it here. (If sending to more then one receipient the same message will be sent to each contact.)'), array( + $this->add('wysiwyg', 'email_comment', ts('If you would like to add personal message to email please add it here. (If sending to more then one receipient the same message will be sent to each contact.)'), [ 'rows' => 2, 'cols' => 40, - )); - - $this->addButtons(array( - array( - 'type' => 'upload', - 'name' => $this->_selectedOutput == 'email' ? ts('Send Email') : ts('Process Invoice(s)'), - 'isDefault' => TRUE, - ), - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - ), - ) - ); + ]); + + $this->addButtons([ + [ + 'type' => 'upload', + 'name' => $this->_selectedOutput == 'email' ? ts('Send Email') : ts('Process Invoice(s)'), + 'isDefault' => TRUE, + ], + [ + 'type' => 'cancel', + 'name' => ts('Cancel'), + ], + ]); } /** @@ -209,7 +189,7 @@ public function buildQuickForm() { * list of errors to be posted back to the form */ public static function formRule($values) { - $errors = array(); + $errors = []; if ($values['output'] == 'email_invoice' && empty($values['from_email_address'])) { $errors['from_email_address'] = ts("From Email Address is required"); @@ -223,7 +203,7 @@ public static function formRule($values) { */ public function postProcess() { $params = $this->controller->exportValues($this->_name); - $this->printPDF($this->_contributionIds, $params, $this->_contactIds, $this); + self::printPDF($this->_contributionIds, $params, $this->_contactIds); } /** @@ -235,12 +215,10 @@ public function postProcess() { * Associated array of submitted values. * @param array $contactIds * Contact Id. - * @param CRM_Core_Form $form - * Form object. */ - public static function printPDF($contribIDs, &$params, $contactIds, &$form) { + public static function printPDF($contribIDs, &$params, $contactIds) { // get all the details needed to generate a invoice - $messageInvoice = array(); + $messageInvoice = []; $invoiceTemplate = CRM_Core_Smarty::singleton(); $invoiceElements = CRM_Contribute_Form_Task_PDF::getElements($contribIDs, $params, $contactIds); @@ -254,7 +232,7 @@ public static function printPDF($contribIDs, &$params, $contactIds, &$form) { $prefixValue = Civi::settings()->get('contribution_invoice_settings'); foreach ($invoiceElements['details'] as $contribID => $detail) { - $input = $ids = $objects = array(); + $input = $ids = $objects = []; if (in_array($detail['contact'], $invoiceElements['excludeContactIds'])) { continue; } @@ -283,17 +261,17 @@ public static function printPDF($contribIDs, &$params, $contactIds, &$form) { $objects['contribution']->receive_date = CRM_Utils_Date::isoToMysql($objects['contribution']->receive_date); - $addressParams = array('contact_id' => $contribution->contact_id); + $addressParams = ['contact_id' => $contribution->contact_id]; $addressDetails = CRM_Core_BAO_Address::getValues($addressParams); // to get billing address if present - $billingAddress = array(); - foreach ($addressDetails as $key => $address) { - if ((isset($address['is_billing']) && $address['is_billing'] == 1) && (isset($address['is_primary']) && $address['is_primary'] == 1) && $address['contact_id'] == $contribution->contact_id) { + $billingAddress = []; + foreach ($addressDetails as $address) { + if (($address['is_billing'] == 1) && ($address['is_primary'] == 1) && ($address['contact_id'] == $contribution->contact_id)) { $billingAddress[$address['contact_id']] = $address; break; } - elseif (($address['is_billing'] == 0 && $address['is_primary'] == 1) || (isset($address['is_billing']) && $address['is_billing'] == 1) && $address['contact_id'] == $contribution->contact_id) { + elseif (($address['is_billing'] == 0 && $address['is_primary'] == 1) || ($address['is_billing'] == 1) && ($address['contact_id'] == $contribution->contact_id)) { $billingAddress[$address['contact_id']] = $address; } } @@ -314,31 +292,40 @@ public static function printPDF($contribIDs, &$params, $contactIds, &$form) { $creditNoteId = $contribution->creditnote_id; } } - $invoiceId = CRM_Utils_Array::value('invoice_prefix', $prefixValue) . "" . $contribution->id; + if (!$contribution->invoice_number) { + $contribution->invoice_number = CRM_Contribute_BAO_Contribution::getInvoiceNumber($contribution->id); + } //to obtain due date for PDF invoice $contributionReceiveDate = date('F j,Y', strtotime(date($input['receive_date']))); $invoiceDate = date("F j, Y"); - $dueDate = date('F j ,Y', strtotime($contributionReceiveDate . "+" . $prefixValue['due_date'] . "" . $prefixValue['due_date_period'])); + $dueDate = date('F j, Y', strtotime($contributionReceiveDate . "+" . $prefixValue['due_date'] . "" . $prefixValue['due_date_period'])); if ($input['component'] == 'contribute') { - $eid = $contribID; - $etable = 'contribution'; - $lineItem = CRM_Price_BAO_LineItem::getLineItems($eid, $etable, NULL, TRUE, TRUE); + $lineItem = CRM_Price_BAO_LineItem::getLineItemsByContributionID($contribID); } else { $eid = $contribution->_relatedObjects['participant']->id; - $etable = 'participant'; - $lineItem = CRM_Price_BAO_LineItem::getLineItems($eid, $etable, NULL, TRUE, FALSE, '', TRUE); + $lineItem = CRM_Price_BAO_LineItem::getLineItems($eid, 'participant', NULL, TRUE, FALSE, TRUE); } - //TO DO: Need to do changes for partially paid to display amount due on PDF invoice - $amountDue = ($input['amount'] - $input['amount']); + $resultPayments = civicrm_api3('Payment', 'get', [ + 'sequential' => 1, + 'contribution_id' => $contribID, + ]); + $amountPaid = 0; + foreach ($resultPayments['values'] as $singlePayment) { + // Only count payments that have been (status =) completed. + if ($singlePayment['status_id'] == 1) { + $amountPaid += $singlePayment['total_amount']; + } + } + $amountDue = ($input['amount'] - $amountPaid); - // retreiving the subtotal and sum of same tax_rate - $dataArray = array(); + // retrieving the subtotal and sum of same tax_rate + $dataArray = []; $subTotal = 0; - foreach ($lineItem as $entity_id => $taxRate) { + foreach ($lineItem as $taxRate) { if (isset($dataArray[(string) $taxRate['tax_rate']])) { $dataArray[(string) $taxRate['tax_rate']] = $dataArray[(string) $taxRate['tax_rate']] + CRM_Utils_Array::value('tax_amount', $taxRate); } @@ -349,18 +336,18 @@ public static function printPDF($contribIDs, &$params, $contactIds, &$form) { } // to email the invoice - $mailDetails = array(); - $values = array(); + $mailDetails = []; + $values = []; if ($contribution->_component == 'event') { $daoName = 'CRM_Event_DAO_Event'; $pageId = $contribution->_relatedObjects['event']->id; - $mailElements = array( + $mailElements = [ 'title', 'confirm_from_name', 'confirm_from_email', 'cc_confirm', 'bcc_confirm', - ); + ]; CRM_Core_DAO::commonRetrieveAll($daoName, 'id', $pageId, $mailDetails, $mailElements); $values['title'] = CRM_Utils_Array::value('title', $mailDetails[$contribution->_relatedObjects['event']->id]); $values['confirm_from_name'] = CRM_Utils_Array::value('confirm_from_name', $mailDetails[$contribution->_relatedObjects['event']->id]); @@ -373,13 +360,13 @@ public static function printPDF($contribIDs, &$params, $contactIds, &$form) { elseif ($contribution->_component == 'contribute') { $daoName = 'CRM_Contribute_DAO_ContributionPage'; $pageId = $contribution->contribution_page_id; - $mailElements = array( + $mailElements = [ 'title', 'receipt_from_name', 'receipt_from_email', 'cc_receipt', 'bcc_receipt', - ); + ]; CRM_Core_DAO::commonRetrieveAll($daoName, 'id', $pageId, $mailDetails, $mailElements); $values['title'] = CRM_Utils_Array::value('title', CRM_Utils_Array::value($contribution->contribution_page_id, $mailDetails)); @@ -399,7 +386,7 @@ public static function printPDF($contribIDs, &$params, $contactIds, &$form) { // get organization address $domain = CRM_Core_BAO_Domain::getDomain(); - $locParams = array('contact_id' => $domain->contact_id); + $locParams = ['contact_id' => $domain->contact_id]; $locationDefaults = CRM_Core_BAO_Location::getValues($locParams); if (isset($locationDefaults['address'][1]['state_province_id'])) { $stateProvinceAbbreviationDomain = CRM_Core_PseudoConstant::stateProvinceAbbreviation($locationDefaults['address'][1]['state_province_id']); @@ -415,16 +402,18 @@ public static function printPDF($contribIDs, &$params, $contactIds, &$form) { } // parameters to be assign for template - $tplParams = array( + $tplParams = [ 'title' => $title, 'component' => $input['component'], 'id' => $contribution->id, 'source' => $source, - 'invoice_id' => $invoiceId, + 'invoice_number' => $contribution->invoice_number, + 'invoice_id' => $contribution->invoice_id, 'resourceBase' => $config->userFrameworkResourceURL, 'defaultCurrency' => $config->defaultCurrency, 'amount' => $contribution->total_amount, 'amountDue' => $amountDue, + 'amountPaid' => $amountPaid, 'invoice_date' => $invoiceDate, 'dueDate' => $dueDate, 'notes' => CRM_Utils_Array::value('notes', $prefixValue), @@ -435,10 +424,12 @@ public static function printPDF($contribIDs, &$params, $contactIds, &$form) { 'pendingStatusId' => $pendingStatusId, 'cancelledStatusId' => $cancelledStatusId, 'contribution_status_id' => $contribution->contribution_status_id, + 'contributionStatusName' => CRM_Core_PseudoConstant::getName('CRM_Contribute_BAO_Contribution', 'contribution_status_id', $contribution->contribution_status_id), 'subTotal' => $subTotal, 'street_address' => CRM_Utils_Array::value('street_address', CRM_Utils_Array::value($contribution->contact_id, $billingAddress)), 'supplemental_address_1' => CRM_Utils_Array::value('supplemental_address_1', CRM_Utils_Array::value($contribution->contact_id, $billingAddress)), 'supplemental_address_2' => CRM_Utils_Array::value('supplemental_address_2', CRM_Utils_Array::value($contribution->contact_id, $billingAddress)), + 'supplemental_address_3' => CRM_Utils_Array::value('supplemental_address_3', CRM_Utils_Array::value($contribution->contact_id, $billingAddress)), 'city' => CRM_Utils_Array::value('city', CRM_Utils_Array::value($contribution->contact_id, $billingAddress)), 'stateProvinceAbbreviation' => $stateProvinceAbbreviation, 'postal_code' => CRM_Utils_Array::value('postal_code', CRM_Utils_Array::value($contribution->contact_id, $billingAddress)), @@ -448,55 +439,31 @@ public static function printPDF($contribIDs, &$params, $contactIds, &$form) { 'domain_street_address' => CRM_Utils_Array::value('street_address', CRM_Utils_Array::value('1', $locationDefaults['address'])), 'domain_supplemental_address_1' => CRM_Utils_Array::value('supplemental_address_1', CRM_Utils_Array::value('1', $locationDefaults['address'])), 'domain_supplemental_address_2' => CRM_Utils_Array::value('supplemental_address_2', CRM_Utils_Array::value('1', $locationDefaults['address'])), + 'domain_supplemental_address_3' => CRM_Utils_Array::value('supplemental_address_3', CRM_Utils_Array::value('1', $locationDefaults['address'])), 'domain_city' => CRM_Utils_Array::value('city', CRM_Utils_Array::value('1', $locationDefaults['address'])), 'domain_postal_code' => CRM_Utils_Array::value('postal_code', CRM_Utils_Array::value('1', $locationDefaults['address'])), 'domain_state' => $stateProvinceAbbreviationDomain, 'domain_country' => $countryDomain, 'domain_email' => CRM_Utils_Array::value('email', CRM_Utils_Array::value('1', $locationDefaults['email'])), 'domain_phone' => CRM_Utils_Array::value('phone', CRM_Utils_Array::value('1', $locationDefaults['phone'])), - ); + ]; if (isset($creditNoteId)) { $tplParams['creditnote_id'] = $creditNoteId; } - $pdfFileName = "{$invoiceId}.pdf"; - $sendTemplateParams = array( + $pdfFileName = $contribution->invoice_number . ".pdf"; + $sendTemplateParams = [ 'groupName' => 'msg_tpl_workflow_contribution', 'valueName' => 'contribution_invoice_receipt', 'contactId' => $contribution->contact_id, 'tplParams' => $tplParams, 'PDFFilename' => $pdfFileName, - ); - $session = CRM_Core_Session::singleton(); - $contactID = $session->get('userID'); - //CRM-16319 - we dont store in userID in case the user is doing multiple - //transactions etc - if (empty($contactID)) { - $contactID = $session->get('transaction.userID'); - } - // Fix Invoice email doesnot send out when completed payment using Paypal - if (empty($contactID)) { - $contactID = current($contactIds); - } - $contactEmails = CRM_Core_BAO_Email::allEmails($contactID); - $emails = array(); - $fromDisplayName = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', - $contactID, 'display_name' - ); - - foreach ($contactEmails as $emailId => $item) { - $email = $item['email']; - if ($email) { - $emails[$emailId] = '"' . $fromDisplayName . '" <' . $email . '> '; - } - } - $fromEmail = CRM_Utils_Array::crmArrayMerge($emails, CRM_Core_OptionGroup::values('from_email_address')); + ]; // from email address - if (isset($params['from_email_address'])) { - $fromEmailAddress = CRM_Utils_Array::value($params['from_email_address'], $fromEmail); - } + $fromEmailAddress = CRM_Utils_Array::value('from_email_address', $params); + // condition to check for download PDF Invoice or email Invoice if ($invoiceElements['createPdf']) { list($sent, $subject, $message, $html) = CRM_Core_BAO_MessageTemplate::sendTemplate($sendTemplateParams); @@ -504,11 +471,11 @@ public static function printPDF($contribIDs, &$params, $contactIds, &$form) { return $html; } else { - $mail = array( + $mail = [ 'subject' => $subject, 'body' => $message, 'html' => $html, - ); + ]; if ($mail['html']) { $messageInvoice[] = $mail['html']; } @@ -520,7 +487,7 @@ public static function printPDF($contribIDs, &$params, $contactIds, &$form) { elseif ($contribution->_component == 'contribute') { $email = CRM_Contact_BAO_Contact::getPrimaryEmail($contribution->contact_id); - $sendTemplateParams['tplParams'] = array_merge($tplParams, array('email_comment' => $invoiceElements['params']['email_comment'])); + $sendTemplateParams['tplParams'] = array_merge($tplParams, ['email_comment' => $invoiceElements['params']['email_comment']]); $sendTemplateParams['from'] = $fromEmailAddress; $sendTemplateParams['toEmail'] = $email; $sendTemplateParams['cc'] = CRM_Utils_Array::value('cc_receipt', $values); @@ -528,14 +495,13 @@ public static function printPDF($contribIDs, &$params, $contactIds, &$form) { list($sent, $subject, $message, $html) = CRM_Core_BAO_MessageTemplate::sendTemplate($sendTemplateParams); // functions call for adding activity with attachment - $pdfFileName = "{$invoiceId}.pdf"; $fileName = self::putFile($html, $pdfFileName); self::addActivities($subject, $contribution->contact_id, $fileName, $params); } elseif ($contribution->_component == 'event') { $email = CRM_Contact_BAO_Contact::getPrimaryEmail($contribution->contact_id); - $sendTemplateParams['tplParams'] = array_merge($tplParams, array('email_comment' => $invoiceElements['params']['email_comment'])); + $sendTemplateParams['tplParams'] = array_merge($tplParams, ['email_comment' => $invoiceElements['params']['email_comment']]); $sendTemplateParams['from'] = $fromEmailAddress; $sendTemplateParams['toEmail'] = $email; $sendTemplateParams['cc'] = CRM_Utils_Array::value('cc_confirm', $values); @@ -543,12 +509,9 @@ public static function printPDF($contribIDs, &$params, $contactIds, &$form) { list($sent, $subject, $message, $html) = CRM_Core_BAO_MessageTemplate::sendTemplate($sendTemplateParams); // functions call for adding activity with attachment - $pdfFileName = "{$invoiceId}.pdf"; $fileName = self::putFile($html, $pdfFileName); self::addActivities($subject, $contribution->contact_id, $fileName, $params); } - - CRM_Core_DAO::setFieldValue('CRM_Contribute_DAO_Contribution', $contribution->id, 'invoice_id', $invoiceId); $invoiceTemplate->clearTemplateVars(); } @@ -557,12 +520,11 @@ public static function printPDF($contribIDs, &$params, $contactIds, &$form) { return $html; } else { - $pdfFileName = "{$invoiceId}.pdf"; - CRM_Utils_PDF_Utils::html2pdf($messageInvoice, $pdfFileName, FALSE, array( + CRM_Utils_PDF_Utils::html2pdf($messageInvoice, $pdfFileName, FALSE, [ 'margin_top' => 10, 'margin_left' => 65, 'metric' => 'px', - )); + ]); // functions call for adding activity with attachment $fileName = self::putFile($html, $pdfFileName); self::addActivities($subject, $contactIds, $fileName, $params); @@ -572,7 +534,7 @@ public static function printPDF($contribIDs, &$params, $contactIds, &$form) { } else { if ($invoiceElements['suppressedEmails']) { - $status = ts('Email was NOT sent to %1 contacts (no email address on file, or communication preferences specify DO NOT EMAIL, or contact is deceased).', array(1 => $invoiceElements['suppressedEmails'])); + $status = ts('Email was NOT sent to %1 contacts (no email address on file, or communication preferences specify DO NOT EMAIL, or contact is deceased).', [1 => $invoiceElements['suppressedEmails']]); $msgTitle = ts('Email Error'); $msgType = 'error'; } @@ -598,38 +560,40 @@ public static function printPDF($contribIDs, &$params, $contactIds, &$form) { * For invoices. * */ - static public function addActivities($subject, $contactIds, $fileName, $params) { + public static function addActivities($subject, $contactIds, $fileName, $params) { $session = CRM_Core_Session::singleton(); $userID = $session->get('userID'); $config = CRM_Core_Config::singleton(); $config->doNotAttachPDFReceipt = 1; if (!empty($params['output']) && $params['output'] == 'pdf_invoice') { - $activityTypeID = CRM_Core_OptionGroup::getValue('activity_type', - 'Downloaded Invoice', - 'name' + $activityTypeID = CRM_Core_PseudoConstant::getKey( + 'CRM_Activity_DAO_Activity', + 'activity_type_id', + 'Downloaded Invoice' ); } else { - $activityTypeID = CRM_Core_OptionGroup::getValue('activity_type', - 'Emailed Invoice', - 'name' + $activityTypeID = CRM_Core_PseudoConstant::getKey( + 'CRM_Activity_DAO_Activity', + 'activity_type_id', + 'Emailed Invoice' ); } - $activityParams = array( + $activityParams = [ 'subject' => $subject, 'source_contact_id' => $userID, 'target_contact_id' => $contactIds, 'activity_type_id' => $activityTypeID, 'activity_date_time' => date('YmdHis'), - 'attachFile_1' => array( + 'attachFile_1' => [ 'uri' => $fileName, 'type' => 'application/pdf', 'location' => $fileName, 'upload_date' => date('YmdHis'), - ), - ); + ], + ]; CRM_Activity_BAO_Activity::create($activityParams); } @@ -644,7 +608,7 @@ static public function addActivities($subject, $contactIds, $fileName, $params) * @return string * Name of file which is in pdf format */ - static public function putFile($html, $name = 'Invoice.pdf') { + public static function putFile($html, $name = 'Invoice.pdf') { $options = new Options(); $options->set('isRemoteEnabled', TRUE); @@ -663,10 +627,10 @@ static public function putFile($html, $name = 'Invoice.pdf') { */ public static function getPrintPDF() { $contributionId = CRM_Utils_Request::retrieve('id', 'Positive', CRM_Core_DAO::$_nullObject, FALSE); - $contributionIDs = array($contributionId); + $contributionIDs = [$contributionId]; $contactId = CRM_Utils_Request::retrieve('cid', 'Positive', CRM_Core_DAO::$_nullObject, FALSE); - $params = array('output' => 'pdf_invoice'); - CRM_Contribute_Form_Task_Invoice::printPDF($contributionIDs, $params, $contactId, CRM_Core_DAO::$_nullObject); + $params = ['output' => 'pdf_invoice']; + CRM_Contribute_Form_Task_Invoice::printPDF($contributionIDs, $params, $contactId); } } diff --git a/CRM/Contribute/Form/Task/PDF.php b/CRM/Contribute/Form/Task/PDF.php index 29444008fcee..c27b4fd176ac 100644 --- a/CRM/Contribute/Form/Task/PDF.php +++ b/CRM/Contribute/Form/Task/PDF.php @@ -1,9 +1,9 @@ _contributionIds = array($id); + $this->_contributionIds = [$id]; $this->_componentClause = " civicrm_contribution.id IN ( $id ) "; $this->_single = TRUE; $this->assign('totalSelectedContributions', 1); @@ -85,12 +85,12 @@ public function preProcess() { } $url = CRM_Utils_System::url('civicrm/contribute/search', $urlParams); - $breadCrumb = array( - array( + $breadCrumb = [ + [ 'url' => $url, 'title' => ts('Search Results'), - ), - ); + ], + ]; CRM_Contact_Form_Task_EmailCommon ::preProcessFromAddress($this, FALSE); // we have all the contribution ids, so now we get the contact ids parent::setContactIDs(); @@ -104,35 +104,38 @@ public function preProcess() { public function buildQuickForm() { $this->addElement('radio', 'output', NULL, ts('Email Receipts'), 'email_receipt', - array( + [ 'onClick' => "document.getElementById('selectPdfFormat').style.display = 'none'; - document.getElementById('selectEmailFrom').style.display = 'block';") + document.getElementById('selectEmailFrom').style.display = 'block';", + ] ); $this->addElement('radio', 'output', NULL, ts('PDF Receipts'), 'pdf_receipt', - array('onClick' => "document.getElementById('selectPdfFormat').style.display = 'block';") + [ + 'onClick' => "document.getElementById('selectPdfFormat').style.display = 'block'; + document.getElementById('selectEmailFrom').style.display = 'none';", + ] ); $this->addRule('output', ts('Selection required'), 'required'); $this->add('select', 'pdf_format_id', ts('Page Format'), - array(0 => ts('- default -')) + CRM_Core_BAO_PdfFormat::getList(TRUE) + [0 => ts('- default -')] + CRM_Core_BAO_PdfFormat::getList(TRUE) ); $this->add('checkbox', 'receipt_update', ts('Update receipt dates for these contributions'), FALSE); $this->add('checkbox', 'override_privacy', ts('Override privacy setting? (Do not email / Do not mail)'), FALSE); - $this->add('select', 'fromEmailAddress', ts('From Email'), $this->_fromEmails, FALSE, array('class' => 'crm-select2 huge')); - - $this->addButtons(array( - array( - 'type' => 'next', - 'name' => ts('Process Receipt(s)'), - 'isDefault' => TRUE, - ), - array( - 'type' => 'back', - 'name' => ts('Cancel'), - ), - ) - ); + $this->add('select', 'from_email_address', ts('From Email'), $this->_fromEmails, FALSE); + + $this->addButtons([ + [ + 'type' => 'next', + 'name' => ts('Process Receipt(s)'), + 'isDefault' => TRUE, + ], + [ + 'type' => 'back', + 'name' => ts('Cancel'), + ], + ]); } /** @@ -140,7 +143,7 @@ public function buildQuickForm() { */ public function setDefaultValues() { $defaultFormat = CRM_Core_BAO_PdfFormat::getDefaultValues(); - return array('pdf_format_id' => $defaultFormat['id'], 'receipt_update' => 1, 'override_privacy' => 0); + return ['pdf_format_id' => $defaultFormat['id'], 'receipt_update' => 1, 'override_privacy' => 0]; } /** @@ -148,14 +151,14 @@ public function setDefaultValues() { */ public function postProcess() { // get all the details needed to generate a receipt - $message = array(); + $message = []; $template = CRM_Core_Smarty::singleton(); $params = $this->controller->exportValues($this->_name); $elements = self::getElements($this->_contributionIds, $params, $this->_contactIds); foreach ($elements['details'] as $contribID => $detail) { - $input = $ids = $objects = array(); + $input = $ids = $objects = []; if (in_array($detail['contact'], $elements['excludeContactIds'])) { continue; @@ -190,23 +193,26 @@ public function postProcess() { CRM_Core_DAO::singleValueQuery("SELECT payment_processor_id FROM civicrm_financial_trxn WHERE trxn_id = %1 - LIMIT 1", array( - 1 => array($contribution->trxn_id, 'String'))); + LIMIT 1", [ + 1 => [$contribution->trxn_id, 'String'], + ]); // CRM_Contribute_BAO_Contribution::composeMessageArray expects mysql formatted date $objects['contribution']->receive_date = CRM_Utils_Date::isoToMysql($objects['contribution']->receive_date); - $values = array(); - if (isset($params['fromEmailAddress']) && !$elements['createPdf']) { + $values = []; + if (isset($params['from_email_address']) && !$elements['createPdf']) { + // If a logged in user from email is used rather than a domain wide from email address + // the from_email_address params key will be numerical and we need to convert it to be + // in normal from email format + $from = CRM_Utils_Mail::formatFromAddress($params['from_email_address']); // CRM-19129 Allow useres the choice of From Email to send the receipt from. - $fromEmail = $params['fromEmailAddress']; - $from = CRM_Utils_Array::value($fromEmail, $this->_emails); $fromDetails = explode(' <', $from); $input['receipt_from_email'] = substr(trim($fromDetails[1]), 0, -1); $input['receipt_from_name'] = str_replace('"', '', $fromDetails[0]); } - $mail = CRM_Contribute_BAO_Contribution::sendMail($input, $ids, $objects['contribution']->id, $values, FALSE, + $mail = CRM_Contribute_BAO_Contribution::sendMail($input, $ids, $objects['contribution']->id, $values, $elements['createPdf']); if ($mail['html']) { @@ -230,7 +236,7 @@ public function postProcess() { } else { if ($elements['suppressedEmails']) { - $status = ts('Email was NOT sent to %1 contacts (no email address on file, or communication preferences specify DO NOT EMAIL, or contact is deceased).', array(1 => $elements['suppressedEmails'])); + $status = ts('Email was NOT sent to %1 contacts (no email address on file, or communication preferences specify DO NOT EMAIL, or contact is deceased).', [1 => $elements['suppressedEmails']]); $msgTitle = ts('Email Error'); $msgType = 'error'; } @@ -258,8 +264,8 @@ public function postProcess() { * array of common elements * */ - static public function getElements($contribIds, $params, $contactIds) { - $pdfElements = array(); + public static function getElements($contribIds, $params, $contactIds) { + $pdfElements = []; $pdfElements['contribIDs'] = implode(',', $contribIds); @@ -276,14 +282,14 @@ static public function getElements($contribIds, $params, $contactIds) { $pdfElements['createPdf'] = TRUE; } - $excludeContactIds = array(); + $excludeContactIds = []; if (!$pdfElements['createPdf']) { - $returnProperties = array( + $returnProperties = [ 'email' => 1, 'do_not_email' => 1, 'is_deceased' => 1, 'on_hold' => 1, - ); + ]; list($contactDetails) = CRM_Utils_Token::getTokenDetails($contactIds, $returnProperties, FALSE, FALSE); $pdfElements['suppressedEmails'] = 0; diff --git a/CRM/Contribute/Form/Task/PDFLetter.php b/CRM/Contribute/Form/Task/PDFLetter.php index 49c52dbc1e21..03620ec4dd2c 100644 --- a/CRM/Contribute/Form/Task/PDFLetter.php +++ b/CRM/Contribute/Form/Task/PDFLetter.php @@ -1,9 +1,9 @@ skipOnHold = $this->skipDeceased = FALSE; CRM_Contact_Form_Task_PDFLetterCommon::preProcess($this); // store case id if present - $this->_caseId = CRM_Utils_Request::retrieve('caseid', 'Positive', $this, FALSE); + $this->_caseId = CRM_Utils_Request::retrieve('caseid', 'CommaSeparatedIntegers', $this, FALSE); + if (!empty($this->_caseId) && strpos($this->_caseId, ',')) { + $this->_caseIds = explode(',', $this->_caseId); + unset($this->_caseId); + } // retrieve contact ID if this is 'single' mode - $cid = CRM_Utils_Request::retrieve('cid', 'Positive', $this, FALSE); + $cid = CRM_Utils_Request::retrieve('cid', 'CommaSeparatedIntegers', $this, FALSE); $this->_activityId = CRM_Utils_Request::retrieve('id', 'Positive', $this, FALSE); if ($cid) { CRM_Contact_Form_Task_PDFLetterCommon::preProcessSingle($this, $cid); $this->_single = TRUE; - $this->_cid = $cid; } else { parent::preProcess(); @@ -81,13 +84,14 @@ public function preProcess() { * @return array * reference to the array of default values */ + /** * @return array */ public function setDefaultValues() { - $defaults = array(); + $defaults = []; if (isset($this->_activityId)) { - $params = array('id' => $this->_activityId); + $params = ['id' => $this->_activityId]; CRM_Activity_BAO_Activity::retrieve($params, $defaults); $defaults['html_message'] = CRM_Utils_Array::value('details', $defaults); } @@ -105,8 +109,8 @@ public function buildQuickForm() { //enable form element $this->assign('suppressForm', FALSE); - // use contact form as a base - CRM_Contact_Form_Task_PDFLetterCommon::buildQuickForm($this); + // Build common form elements + CRM_Contribute_Form_Task_PDFLetterCommon::buildQuickForm($this); // specific need for contributions $this->add('static', 'more_options_header', NULL, ts('Thank-you Letter Options')); @@ -114,42 +118,41 @@ public function buildQuickForm() { $this->add('checkbox', 'thankyou_update', ts('Update thank-you dates for these contributions'), FALSE); // Group options for tokens are not yet implemented. dgg - $options = array( + $options = [ '' => ts('- no grouping -'), 'contact_id' => ts('Contact'), 'contribution_recur_id' => ts('Contact and Recurring'), 'financial_type_id' => ts('Contact and Financial Type'), 'campaign_id' => ts('Contact and Campaign'), 'payment_instrument_id' => ts('Contact and Payment Method'), - ); - $this->addElement('select', 'group_by', ts('Group contributions by'), $options, array(), "
    ", FALSE); + ]; + $this->addElement('select', 'group_by', ts('Group contributions by'), $options, [], "
    ", FALSE); // this was going to be free-text but I opted for radio options in case there was a script injection risk - $separatorOptions = array('comma' => 'Comma', 'td' => 'Horizontal Table Cell', 'tr' => 'Vertical Table Cell', 'br' => 'Line Break'); + $separatorOptions = ['comma' => 'Comma', 'td' => 'Horizontal Table Cell', 'tr' => 'Vertical Table Cell', 'br' => 'Line Break']; $this->addElement('select', 'group_by_separator', ts('Separator (grouped contributions)'), $separatorOptions); - $emailOptions = array( + $emailOptions = [ '' => ts('Generate PDFs for printing (only)'), 'email' => ts('Send emails where possible. Generate printable PDFs for contacts who cannot receive email.'), 'both' => ts('Send emails where possible. Generate printable PDFs for all contacts.'), - ); + ]; if (CRM_Core_Config::singleton()->doNotAttachPDFReceipt) { $emailOptions['pdfemail'] = ts('Send emails with an attached PDF where possible. Generate printable PDFs for contacts who cannot receive email.'); $emailOptions['pdfemail_both'] = ts('Send emails with an attached PDF where possible. Generate printable PDFs for all contacts.'); } - $this->addElement('select', 'email_options', ts('Print and email options'), $emailOptions, array(), "
    ", FALSE); - - $this->addButtons(array( - array( - 'type' => 'submit', - 'name' => ts('Make Thank-you Letters'), - 'isDefault' => TRUE, - ), - array( - 'type' => 'cancel', - 'name' => ts('Done'), - ), - ) - ); + $this->addElement('select', 'email_options', ts('Print and email options'), $emailOptions, [], "
    ", FALSE); + + $this->addButtons([ + [ + 'type' => 'upload', + 'name' => ts('Make Thank-you Letters'), + 'isDefault' => TRUE, + ], + [ + 'type' => 'cancel', + 'name' => ts('Done'), + ], + ]); } diff --git a/CRM/Contribute/Form/Task/PDFLetterCommon.php b/CRM/Contribute/Form/Task/PDFLetterCommon.php index b93bd533a030..88a870f7169d 100644 --- a/CRM/Contribute/Form/Task/PDFLetterCommon.php +++ b/CRM/Contribute/Form/Task/PDFLetterCommon.php @@ -6,21 +6,42 @@ */ class CRM_Contribute_Form_Task_PDFLetterCommon extends CRM_Contact_Form_Task_PDFLetterCommon { + /** + * Build the form object. + * + * @var CRM_Core_Form $form + */ + public static function buildQuickForm(&$form) { + // use contact form as a base + CRM_Contact_Form_Task_PDFLetterCommon::buildQuickForm($form); + + // Contribute PDF tasks allow you to email as well, so we need to add email address to those forms + $form->add('select', 'from_email_address', ts('From Email Address'), $form->_fromEmails, TRUE); + parent::buildQuickForm($form); + } + /** * Process the form after the input has been submitted and validated. * * @param CRM_Contribute_Form_Task $form + * @param array $formValues */ - public static function postProcess(&$form) { - $formValues = $form->controller->exportValues($form->getName()); + public static function postProcess(&$form, $formValues = NULL) { + if (empty($formValues)) { + $formValues = $form->controller->exportValues($form->getName()); + } list($formValues, $categories, $html_message, $messageToken, $returnProperties) = self::processMessageTemplate($formValues); $isPDF = FALSE; - $emailParams = array(); + $emailParams = []; if (!empty($formValues['email_options'])) { $returnProperties['email'] = $returnProperties['on_hold'] = $returnProperties['is_deceased'] = $returnProperties['do_not_email'] = 1; - $emailParams = array( - 'subject' => $formValues['subject'], - ); + $emailParams = [ + 'subject' => CRM_Utils_Array::value('subject', $formValues), + 'from' => CRM_Utils_Array::value('from_email_address', $formValues), + ]; + + $emailParams['from'] = CRM_Utils_Mail::formatFromAddress($emailParams['from']); + // We need display_name for emailLetter() so add to returnProperties here $returnProperties['display_name'] = 1; if (stristr($formValues['email_options'], 'pdfemail')) { @@ -35,23 +56,22 @@ public static function postProcess(&$form) { $updateStatus = ''; $task = 'CRM_Contribution_Form_Task_PDFLetterCommon'; $realSeparator = ', '; - $tableSeparators = array( + $tableSeparators = [ 'td' => '', 'tr' => '', - ); + ]; //the original thinking was mutliple options - but we are going with only 2 (comma & td) for now in case // there are security (& UI) issues we need to think through if (isset($formValues['group_by_separator'])) { - if (in_array($formValues['group_by_separator'], array('td', 'tr'))) { + if (in_array($formValues['group_by_separator'], ['td', 'tr'])) { $realSeparator = $tableSeparators[$formValues['group_by_separator']]; } elseif ($formValues['group_by_separator'] == 'br') { $realSeparator = "
    "; } } - $separator = '****~~~~';// a placeholder in case the separator is common in the string - e.g ', ' - $validated = FALSE; - + // a placeholder in case the separator is common in the string - e.g ', ' + $separator = '****~~~~'; $groupBy = $formValues['group_by']; // skip some contacts ? @@ -63,69 +83,76 @@ public static function postProcess(&$form) { $contributionIDs = $form->getVar('_contributionContactIds'); } list($contributions, $contacts) = self::buildContributionArray($groupBy, $contributionIDs, $returnProperties, $skipOnHold, $skipDeceased, $messageToken, $task, $separator, $form->_includesSoftCredits); - $html = array(); + $html = []; + $contactHtml = $emailedHtml = []; foreach ($contributions as $contributionId => $contribution) { $contact = &$contacts[$contribution['contact_id']]; - $grouped = $groupByID = 0; + $grouped = FALSE; + $groupByID = 0; if ($groupBy) { $groupByID = empty($contribution[$groupBy]) ? 0 : $contribution[$groupBy]; $contribution = $contact['combined'][$groupBy][$groupByID]; $grouped = TRUE; } - self::assignCombinedContributionValues($contact, $contributions, $groupBy, $groupByID); - if (empty($groupBy) || empty($contact['is_sent'][$groupBy][$groupByID])) { - if (!$validated && in_array($realSeparator, $tableSeparators) && !self::isValidHTMLWithTableSeparator($messageToken, $html_message)) { - $realSeparator = ', '; - CRM_Core_Session::setStatus(ts('You have selected the table cell separator, but one or more token fields are not placed inside a table cell. This would result in invalid HTML, so comma separators have been used instead.')); - } - $validated = TRUE; - $html[$contributionId] = str_replace($separator, $realSeparator, self::resolveTokens($html_message, $contact, $contribution, $messageToken, $categories, $grouped, $separator)); - $contact['is_sent'][$groupBy][$groupByID] = TRUE; + $html[$contributionId] = self::generateHtml($contact, $contribution, $groupBy, $contributions, $realSeparator, $tableSeparators, $messageToken, $html_message, $separator, $grouped, $groupByID); + $contactHtml[$contact['contact_id']][] = $html[$contributionId]; if (!empty($formValues['email_options'])) { if (self::emailLetter($contact, $html[$contributionId], $isPDF, $formValues, $emailParams)) { $emailed++; if (!stristr($formValues['email_options'], 'both')) { - unset($html[$contributionId]); + $emailedHtml[$contributionId] = TRUE; } } } + $contact['is_sent'][$groupBy][$groupByID] = TRUE; } - - // update dates (do it for each contribution including grouped recurring contribution) - //@todo - the 2 calls below bypass all hooks. Using the api would possibly be slower than one call but not than 2 + // Update receipt/thankyou dates + $contributionParams = ['id' => $contributionId]; if ($receipt_update) { - $result = CRM_Core_DAO::setFieldValue('CRM_Contribute_DAO_Contribution', $contributionId, 'receipt_date', $nowDate); - if ($result) { - $receipts++; - } + $contributionParams['receipt_date'] = $nowDate; } if ($thankyou_update) { - $result = CRM_Core_DAO::setFieldValue('CRM_Contribute_DAO_Contribution', $contributionId, 'thankyou_date', $nowDate); - if ($result) { - $thanks++; - } + $contributionParams['thankyou_date'] = $nowDate; + } + if ($receipt_update || $thankyou_update) { + civicrm_api3('Contribution', 'create', $contributionParams); + $receipts = ($receipt_update ? $receipts + 1 : $receipts); + $thanks = ($thankyou_update ? $thanks + 1 : $thanks); } } - //createActivities requires both $form->_contactIds and $contacts - - //@todo - figure out why - $form->_contactIds = array_keys($contacts); - self::createActivities($form, $html_message, $form->_contactIds); + + $contactIds = array_keys($contacts); + self::createActivities($form, $html_message, $contactIds, CRM_Utils_Array::value('subject', $formValues, ts('Thank you letter')), CRM_Utils_Array::value('campaign_id', $formValues), $contactHtml); + $html = array_diff_key($html, $emailedHtml); + + if (!empty($formValues['is_unit_test'])) { + return $html; + } + + //CRM-19761 if (!empty($html)) { - CRM_Utils_PDF_Utils::html2pdf($html, "CiviLetter.pdf", FALSE, $formValues); + $type = $formValues['document_type']; + + if ($type == 'pdf') { + CRM_Utils_PDF_Utils::html2pdf($html, "CiviLetter.pdf", FALSE, $formValues); + } + else { + CRM_Utils_PDF_Document::html2doc($html, "CiviLetter.$type", $formValues); + } } $form->postProcessHook(); if ($emailed) { - $updateStatus = ts('Receipts have been emailed to %1 contributions.', array(1 => $emailed)); + $updateStatus = ts('Receipts have been emailed to %1 contributions.', [1 => $emailed]); } if ($receipts) { - $updateStatus = ts('Receipt date has been updated for %1 contributions.', array(1 => $receipts)); + $updateStatus = ts('Receipt date has been updated for %1 contributions.', [1 => $receipts]); } if ($thanks) { - $updateStatus .= ' ' . ts('Thank-you date has been updated for %1 contributions.', array(1 => $thanks)); + $updateStatus .= ' ' . ts('Thank-you date has been updated for %1 contributions.', [1 => $thanks]); } if ($updateStatus) { @@ -133,7 +160,7 @@ public static function postProcess(&$form) { } if (!empty($html)) { // ie. we have only sent emails - lets no show a white screen - CRM_Utils_System::civiExit(1); + CRM_Utils_System::civiExit(); } } @@ -148,7 +175,7 @@ public static function postProcess(&$form) { * @return bool */ public static function isValidHTMLWithTableSeparator($tokens, $html) { - $relevantEntities = array('contribution'); + $relevantEntities = ['contribution']; foreach ($relevantEntities as $entity) { if (isset($tokens[$entity]) && is_array($tokens[$entity])) { foreach ($tokens[$entity] as $token) { @@ -165,17 +192,17 @@ public static function isValidHTMLWithTableSeparator($tokens, $html) { * Check that the token only appears in a table cell. The '' separator cannot otherwise work * Calculate the number of times it appears IN the cell & the number of times it appears - should be the same! * - * @param $token - * @param $entity - * @param $textToSearch + * @param string $token + * @param string $entity + * @param string $textToSearch * * @return bool */ public static function isHtmlTokenInTableCell($token, $entity, $textToSearch) { - $tokenToMatch = $entity . '.' . $token; - $dontCare = array(); - $within = preg_match_all("||si'; + $within = preg_match_all($pattern, $textToSearch); + $total = preg_match_all("|{" . $tokenToMatch . "}|", $textToSearch); return ($within == $total); } @@ -185,14 +212,14 @@ public static function isHtmlTokenInTableCell($token, $entity, $textToSearch) { * @param array $contact * @param array $contribution * @param array $messageToken - * @param array $categories * @param bool $grouped * Does this letter represent more than one contribution. * @param string $separator * What is the preferred letter separator. * @return string */ - private static function resolveTokens($html_message, $contact, $contribution, $messageToken, $categories, $grouped, $separator) { + private static function resolveTokens($html_message, $contact, $contribution, $messageToken, $grouped, $separator) { + $categories = self::getTokenCategories(); $tokenHtml = CRM_Utils_Token::replaceContactTokens($html_message, $contact, TRUE, $messageToken); if ($grouped) { $tokenHtml = CRM_Utils_Token::replaceMultipleContributionTokens($separator, $tokenHtml, $contribution, TRUE, $messageToken); @@ -228,16 +255,20 @@ private static function resolveTokens($html_message, $contact, $contribution, $m * @return array */ public static function buildContributionArray($groupBy, $contributionIDs, $returnProperties, $skipOnHold, $skipDeceased, $messageToken, $task, $separator, $isIncludeSoftCredits) { - $contributions = $contacts = $notSent = array(); + $contributions = $contacts = []; foreach ($contributionIDs as $item => $contributionId) { - // get contribution information - $contribution = CRM_Utils_Token::getContributionTokenDetails(array('contribution_id' => $contributionId), - $returnProperties, - NULL, - $messageToken, - $task - ); - $contribution = $contributions[$contributionId] = $contribution[$contributionId]; + // Basic return attributes available to the template. + $returnValues = ['contact_id', 'total_amount', 'financial_type', 'receive_date', 'contribution_campaign_title']; + if (!empty($messageToken['contribution'])) { + $returnValues = array_merge($messageToken['contribution'], $returnValues); + } + // retrieve contribution tokens listed in $returnProperties using Contribution.Get API + $contribution = civicrm_api3('Contribution', 'getsingle', [ + 'id' => $contributionId, + 'return' => $returnValues, + ]); + $contribution['campaign'] = CRM_Utils_Array::value('contribution_campaign_title', $contribution); + $contributions[$contributionId] = $contribution; if ($isIncludeSoftCredits) { //@todo find out why this happens & add comments @@ -248,9 +279,9 @@ public static function buildContributionArray($groupBy, $contributionIDs, $retur $contactID = $contribution['contact_id']; } if (!isset($contacts[$contactID])) { - $contacts[$contactID] = array(); + $contacts[$contactID] = []; $contacts[$contactID]['contact_aggregate'] = 0; - $contacts[$contactID]['combined'] = $contacts[$contactID]['contribution_ids'] = array(); + $contacts[$contactID]['combined'] = $contacts[$contactID]['contribution_ids'] = []; } $contacts[$contactID]['contact_aggregate'] += $contribution['total_amount']; @@ -272,7 +303,7 @@ public static function buildContributionArray($groupBy, $contributionIDs, $retur // Hooks allow more nuanced smarty usage here. CRM_Core_Smarty::singleton()->assign('contributions', $contributions); foreach ($contacts as $contactID => $contact) { - $tokenResolvedContacts = CRM_Utils_Token::getTokenDetails(array('contact_id' => $contactID), + $tokenResolvedContacts = CRM_Utils_Token::getTokenDetails(['contact_id' => $contactID], $returnProperties, $skipOnHold, $skipDeceased, @@ -282,7 +313,7 @@ public static function buildContributionArray($groupBy, $contributionIDs, $retur ); $contacts[$contactID] = array_merge($tokenResolvedContacts[0][$contactID], $contact); } - return array($contributions, $contacts); + return [$contributions, $contacts]; } /** @@ -314,9 +345,6 @@ public static function combineContributions($existing, $contribution, $separator * @param int $groupByID */ public static function assignCombinedContributionValues($contact, $contributions, $groupBy, $groupByID) { - if (!defined('CIVICRM_MAIL_SMARTY') || !CIVICRM_MAIL_SMARTY) { - return; - } CRM_Core_Smarty::singleton()->assign('contact_aggregate', $contact['contact_aggregate']); CRM_Core_Smarty::singleton() ->assign('contributions', array_intersect_key($contributions, $contact['contribution_ids'][$groupBy][$groupByID])); @@ -336,29 +364,32 @@ public static function assignCombinedContributionValues($contact, $contributions * * @return bool */ - public static function emailLetter($contact, $html, $is_pdf, $format = array(), $params = array()) { + public static function emailLetter($contact, $html, $is_pdf, $format = [], $params = []) { try { if (empty($contact['email'])) { return FALSE; } - $mustBeEmpty = array('do_not_email', 'is_deceased', 'on_hold'); + $mustBeEmpty = ['do_not_email', 'is_deceased', 'on_hold']; foreach ($mustBeEmpty as $emptyField) { if (!empty($contact[$emptyField])) { return FALSE; } } - $defaults = array( + $defaults = [ 'toName' => $contact['display_name'], 'toEmail' => $contact['email'], 'text' => '', 'html' => $html, - ); + ]; if (empty($params['from'])) { $emails = CRM_Core_BAO_Email::getFromEmail(); $emails = array_keys($emails); $defaults['from'] = array_pop($emails); } + else { + $defaults['from'] = $params['from']; + } if (!empty($params['subject'])) { $defaults['subject'] = $params['subject']; } @@ -367,7 +398,7 @@ public static function emailLetter($contact, $html, $is_pdf, $format = array(), } if ($is_pdf) { $defaults['html'] = ts('Please see attached'); - $defaults['attachments'] = array(CRM_Utils_Mail::appendPDF('ThankYou.pdf', $html, $format)); + $defaults['attachments'] = [CRM_Utils_Mail::appendPDF('ThankYou.pdf', $html, $format)]; } $params = array_merge($defaults); return CRM_Utils_Mail::send($params); @@ -377,4 +408,39 @@ public static function emailLetter($contact, $html, $is_pdf, $format = array(), } } + /** + * @param $contact + * @param $formValues + * @param $contribution + * @param $groupBy + * @param $contributions + * @param $realSeparator + * @param $tableSeparators + * @param $messageToken + * @param $html_message + * @param $separator + * @param $categories + * @param bool $grouped + * @param int $groupByID + * + * @return string + */ + protected static function generateHtml(&$contact, $contribution, $groupBy, $contributions, $realSeparator, $tableSeparators, $messageToken, $html_message, $separator, $grouped, $groupByID) { + static $validated = FALSE; + $html = NULL; + + self::assignCombinedContributionValues($contact, $contributions, $groupBy, $groupByID); + + if (empty($groupBy) || empty($contact['is_sent'][$groupBy][$groupByID])) { + if (!$validated && in_array($realSeparator, $tableSeparators) && !self::isValidHTMLWithTableSeparator($messageToken, $html_message)) { + $realSeparator = ', '; + CRM_Core_Session::setStatus(ts('You have selected the table cell separator, but one or more token fields are not placed inside a table cell. This would result in invalid HTML, so comma separators have been used instead.')); + } + $validated = TRUE; + $html = str_replace($separator, $realSeparator, self::resolveTokens($html_message, $contact, $contribution, $messageToken, $grouped, $separator)); + } + + return $html; + } + } diff --git a/CRM/Contribute/Form/Task/PickProfile.php b/CRM/Contribute/Form/Task/PickProfile.php index 4d7583584c66..9d45ddafcd21 100644 --- a/CRM/Contribute/Form/Task/PickProfile.php +++ b/CRM/Contribute/Form/Task/PickProfile.php @@ -1,9 +1,9 @@ _contributionIds) > $this->_maxContributions) { - CRM_Core_Session::setStatus(ts("The maximum number of contributions you can select for Update multiple contributions is %1. You have selected %2. Please select fewer contributions from your search results and try again.", array( - 1 => $this->_maxContributions, - 2 => count($this->_contributionIds), - )), ts('Update multiple records error'), 'error'); + CRM_Core_Session::setStatus(ts("The maximum number of contributions you can select for Update multiple contributions is %1. You have selected %2. Please select fewer contributions from your search results and try again.", [ + 1 => $this->_maxContributions, + 2 => count($this->_contributionIds), + ]), ts('Update multiple records error'), 'error'); $validate = TRUE; } @@ -85,18 +87,18 @@ public function preProcess() { */ public function buildQuickForm() { - $types = array('Contribution'); + $types = ['Contribution']; $profiles = CRM_Core_BAO_UFGroup::getProfiles($types, TRUE); if (empty($profiles)) { - CRM_Core_Session::setStatus(ts("You will need to create a Profile containing the %1 fields you want to edit before you can use Update multiple contributions. Navigate to Administer CiviCRM > Customize Data and Screens > CiviCRM Profile to configure a Profile. Consult the online Administrator documentation for more information.", array(1 => $types[0])), ts('Profile Required'), 'error'); + CRM_Core_Session::setStatus(ts("You will need to create a Profile containing the %1 fields you want to edit before you can use Update multiple contributions. Navigate to Administer CiviCRM > Customize Data and Screens > CiviCRM Profile to configure a Profile. Consult the online Administrator documentation for more information.", [1 => $types[0]]), ts('Profile Required'), 'error'); CRM_Utils_System::redirect($this->_userContext); } $ufGroupElement = $this->add('select', 'uf_group_id', ts('Select Profile'), - array( + [ '' => ts('- select profile -'), - ) + $profiles, TRUE + ] + $profiles, TRUE ); $this->addDefaultButtons(ts('Continue')); } @@ -105,7 +107,7 @@ public function buildQuickForm() { * Add local and global form rules. */ public function addRules() { - $this->addFormRule(array('CRM_Contribute_Form_Task_PickProfile', 'formRule')); + $this->addFormRule(['CRM_Contribute_Form_Task_PickProfile', 'formRule']); } /** diff --git a/CRM/Contribute/Form/Task/Print.php b/CRM/Contribute/Form/Task/Print.php index 2fd9f38e87f3..58055ce580e5 100644 --- a/CRM/Contribute/Form/Task/Print.php +++ b/CRM/Contribute/Form/Task/Print.php @@ -1,9 +1,9 @@ addButtons(array( - array( - 'type' => 'next', - 'name' => ts('Print Contributions'), - 'js' => array('onclick' => 'window.print()'), - 'isDefault' => TRUE, - ), - array( - 'type' => 'back', - 'name' => ts('Done'), - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'next', + 'name' => ts('Print Contributions'), + 'js' => ['onclick' => 'window.print()'], + 'isDefault' => TRUE, + ], + [ + 'type' => 'back', + 'name' => ts('Done'), + ], + ]); } /** diff --git a/CRM/Contribute/Form/Task/Result.php b/CRM/Contribute/Form/Task/Result.php index 99ff986a53eb..11ff679294d5 100644 --- a/CRM/Contribute/Form/Task/Result.php +++ b/CRM/Contribute/Form/Task/Result.php @@ -1,9 +1,9 @@ addButtons(array( - array( - 'type' => 'done', - 'name' => ts('Done'), - 'isDefault' => TRUE, - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'done', + 'name' => ts('Done'), + 'isDefault' => TRUE, + ], + ]); } } diff --git a/CRM/Contribute/Form/Task/SearchTaskHookSample.php b/CRM/Contribute/Form/Task/SearchTaskHookSample.php index 5ee7ddc01878..6c84b5315d19 100644 --- a/CRM/Contribute/Form/Task/SearchTaskHookSample.php +++ b/CRM/Contribute/Form/Task/SearchTaskHookSample.php @@ -1,9 +1,9 @@ _contributionIds); @@ -60,12 +60,12 @@ public function preProcess() { ); while ($dao->fetch()) { - $rows[] = array( + $rows[] = [ 'display_name' => $dao->display_name, 'amount' => $dao->amount, 'source' => $dao->source, 'receive_date' => $dao->receive_date, - ); + ]; } $this->assign('rows', $rows); } @@ -74,14 +74,13 @@ public function preProcess() { * Build the form object. */ public function buildQuickForm() { - $this->addButtons(array( - array( - 'type' => 'done', - 'name' => ts('Done'), - 'isDefault' => TRUE, - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'done', + 'name' => ts('Done'), + 'isDefault' => TRUE, + ], + ]); } } diff --git a/CRM/Contribute/Form/Task/Status.php b/CRM/Contribute/Form/Task/Status.php index 26aefacf3c60..348ed223870d 100644 --- a/CRM/Contribute/Form/Task/Status.php +++ b/CRM/Contribute/Form/Task/Status.php @@ -1,9 +1,9 @@ _contributionIds = array($id); + $this->_contributionIds = [$id]; $this->_componentClause = " civicrm_contribution.id IN ( $id ) "; $this->_single = TRUE; $this->assign('totalSelectedContributions', 1); @@ -82,13 +82,6 @@ public function preProcess() { $this->assign('single', $this->_single); } - /** - * Sets contribution Ids for unit test. - */ - public function setContributionIds($contributionIds) { - $this->_contributionIds = $contributionIds; - } - /** * Build the form object. */ @@ -122,11 +115,11 @@ public function buildQuickForm() { ); // build a row for each contribution id - $this->_rows = array(); + $this->_rows = []; $attributes = CRM_Core_DAO::getAttribute('CRM_Contribute_DAO_Contribution'); - $defaults = array(); - $now = date("m/d/Y"); - $paidByOptions = array('' => ts('- select -')) + CRM_Contribute_PseudoConstant::paymentInstrument(); + $defaults = []; + $now = date("Y-m-d"); + $paidByOptions = ['' => ts('- select -')] + CRM_Contribute_PseudoConstant::paymentInstrument(); while ($dao->fetch()) { $row['contact_id'] = $dao->contact_id; @@ -138,7 +131,7 @@ public function buildQuickForm() { $this->addRule("trxn_id_{$row['contribution_id']}", ts('This Transaction ID already exists in the database. Include the account number for checks.'), 'objectExists', - array('CRM_Contribute_DAO_Contribution', $dao->contribution_id, 'trxn_id') + ['CRM_Contribute_DAO_Contribution', $dao->contribution_id, 'trxn_id'] ); $row['fee_amount'] = &$this->add('text', "fee_amount_{$row['contribution_id']}", ts('Fee Amount'), @@ -147,9 +140,7 @@ public function buildQuickForm() { $this->addRule("fee_amount_{$row['contribution_id']}", ts('Please enter a valid amount.'), 'money'); $defaults["fee_amount_{$row['contribution_id']}"] = 0.0; - $row['trxn_date'] = $this->addDate("trxn_date_{$row['contribution_id']}", FALSE, - ts('Receipt Date'), array('formatType' => 'activityDate') - ); + $row['trxn_date'] = $this->add('datepicker', "trxn_date_{$row['contribution_id']}", ts('Transaction Date'), [], FALSE, ['time' => FALSE]); $defaults["trxn_date_{$row['contribution_id']}"] = $now; $this->add("text", "check_number_{$row['contribution_id']}", ts('Check Number')); @@ -163,20 +154,19 @@ public function buildQuickForm() { $this->assign_by_ref('rows', $this->_rows); $this->setDefaults($defaults); - $this->addButtons(array( - array( - 'type' => 'next', - 'name' => ts('Update Pending Status'), - 'isDefault' => TRUE, - ), - array( - 'type' => 'back', - 'name' => ts('Cancel'), - ), - ) - ); - - $this->addFormRule(array('CRM_Contribute_Form_Task_Status', 'formRule')); + $this->addButtons([ + [ + 'type' => 'next', + 'name' => ts('Update Pending Status'), + 'isDefault' => TRUE, + ], + [ + 'type' => 'back', + 'name' => ts('Cancel'), + ], + ]); + + $this->addFormRule(['CRM_Contribute_Form_Task_Status', 'formRule']); } /** @@ -189,7 +179,7 @@ public function buildQuickForm() { * list of errors to be posted back to the form */ public static function formRule($fields) { - $seen = $errors = array(); + $seen = $errors = []; foreach ($fields as $name => $value) { if (strpos($name, 'trxn_id_') !== FALSE) { if ($fields[$name]) { @@ -203,7 +193,7 @@ public static function formRule($fields) { if ((strpos($name, 'check_number_') !== FALSE) && $value) { $contribID = substr($name, 13); - if ($fields["payment_instrument_id_{$contribID}"] != CRM_Core_OptionGroup::getValue('payment_instrument', 'Check', 'name')) { + if ($fields["payment_instrument_id_{$contribID}"] != CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', 'Check')) { $errors["payment_instrument_id_{$contribID}"] = ts("Payment Method should be Check when a check number is entered for a contribution."); } } @@ -225,7 +215,13 @@ public function postProcess() { /** * Process the form with submitted params. + * * Also supports unit test. + * + * @param CRM_Core_Form $form + * @param array $params + * + * @throws \Exception */ public static function processForm($form, $params) { $statusID = CRM_Utils_Array::value('contribution_status_id', $params); @@ -240,7 +236,7 @@ public static function processForm($form, $params) { // for each contribution id, we just call the baseIPN stuff foreach ($form->_rows as $row) { - $input = $ids = $objects = array(); + $input = $ids = $objects = []; $input['component'] = $details[$row['contribution_id']]['component']; $ids['contact'] = $row['contact_id']; @@ -295,7 +291,7 @@ public static function processForm($form, $params) { else { $input['trxn_id'] = $contribution->invoice_id; } - $input['trxn_date'] = CRM_Utils_Date::processDate($params["trxn_date_{$row['contribution_id']}"], date('H:i:s')); + $input['trxn_date'] = $params["trxn_date_{$row['contribution_id']}"] . ' ' . date('H:i:s'); // @todo calling baseIPN like this is a pattern in it's last gasps. Call contribute.completetransaction api. $baseIPN->completeTransaction($input, $ids, $objects, $transaction, FALSE); @@ -306,11 +302,14 @@ public static function processForm($form, $params) { } /** - * @param $contributionIDs + * @param string $contributionIDs * * @return array */ public static function &getDetails($contributionIDs) { + if (empty($contributionIDs)) { + return []; + } $query = " SELECT c.id as contribution_id, c.contact_id as contact_id , @@ -323,18 +322,17 @@ public static function &getDetails($contributionIDs) { LEFT JOIN civicrm_participant p ON pp.participant_id = p.id WHERE c.id IN ( $contributionIDs )"; - $rows = array(); + $rows = []; $dao = CRM_Core_DAO::executeQuery($query, CRM_Core_DAO::$_nullArray ); - $rows = array(); while ($dao->fetch()) { $rows[$dao->contribution_id]['component'] = $dao->participant_id ? 'event' : 'contribute'; $rows[$dao->contribution_id]['contact'] = $dao->contact_id; if ($dao->membership_id) { if (!array_key_exists('membership', $rows[$dao->contribution_id])) { - $rows[$dao->contribution_id]['membership'] = array(); + $rows[$dao->contribution_id]['membership'] = []; } $rows[$dao->contribution_id]['membership'][] = $dao->membership_id; } diff --git a/CRM/Contribute/Form/UpdateBilling.php b/CRM/Contribute/Form/UpdateBilling.php index 683f94c0e15c..8d58d978e0ce 100644 --- a/CRM/Contribute/Form/UpdateBilling.php +++ b/CRM/Contribute/Form/UpdateBilling.php @@ -1,9 +1,9 @@ _mid = CRM_Utils_Request::retrieve('mid', 'Integer', $this, FALSE); - $this->_crid = CRM_Utils_Request::retrieve('crid', 'Integer', $this, FALSE); + parent::preProcess(); if ($this->_crid) { $this->_paymentProcessor = CRM_Financial_BAO_PaymentProcessor::getProcessorForEntity($this->_crid, 'recur', 'info'); - $this->_paymentProcessorObj = CRM_Financial_BAO_PaymentProcessor::getProcessorForEntity($this->_crid, 'recur', 'obj'); + $this->_paymentProcessor['object'] = CRM_Financial_BAO_PaymentProcessor::getProcessorForEntity($this->_crid, 'recur', 'obj'); $this->_subscriptionDetails = CRM_Contribute_BAO_ContributionRecur::getSubscriptionDetails($this->_crid); // Are we cancelling a recurring contribution that is linked to an auto-renew membership? @@ -69,7 +64,6 @@ public function preProcess() { } } - $this->_coid = CRM_Utils_Request::retrieve('coid', 'Integer', $this, FALSE); if ($this->_coid) { $this->_paymentProcessor = CRM_Financial_BAO_PaymentProcessor::getProcessorForEntity($this->_coid, 'contribute', 'info'); $this->_paymentProcessor['object'] = CRM_Financial_BAO_PaymentProcessor::getProcessorForEntity($this->_coid, 'contribute', 'obj'); @@ -86,20 +80,17 @@ public function preProcess() { $this->_mode = 'auto_renew'; } - if ((!$this->_crid && !$this->_coid && !$this->_mid) || - ($this->_subscriptionDetails == CRM_Core_DAO::$_nullObject) - ) { + if ((!$this->_crid && !$this->_coid && !$this->_mid) || (!$this->_subscriptionDetails)) { CRM_Core_Error::fatal('Required information missing.'); } if (!CRM_Core_Permission::check('edit contributions')) { - $userChecksum = CRM_Utils_Request::retrieve('cs', 'String', $this, FALSE); - if (!CRM_Contact_BAO_Contact_Utils::validChecksum($this->_subscriptionDetails->contact_id, $userChecksum)) { - CRM_Core_Error::fatal(ts('You do not have permission to cancel subscription.')); + if ($this->_subscriptionDetails->contact_id != $this->getContactID()) { + CRM_Core_Error::statusBounce(ts('You do not have permission to cancel subscription.')); } $this->_selfService = TRUE; } - if (!$this->_paymentProcessor['object']->isSupported('updateSubscriptionBillingInfo')) { + if (!$this->_paymentProcessor['object']->supports('updateSubscriptionBillingInfo')) { CRM_Core_Error::fatal(ts("%1 processor doesn't support updating subscription billing details.", array(1 => $this->_paymentProcessor['object']->_processorName) )); @@ -182,17 +173,16 @@ public function buildQuickForm() { } $this->addButtons(array( - array( - 'type' => $type, - 'name' => ts('Save'), - 'isDefault' => TRUE, - ), - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - ), - ) - ); + array( + 'type' => $type, + 'name' => ts('Save'), + 'isDefault' => TRUE, + ), + array( + 'type' => 'cancel', + 'name' => ts('Cancel'), + ), + )); CRM_Core_Payment_Form::buildPaymentForm($this, $this->_paymentProcessor, TRUE, TRUE); $this->addFormRule(array('CRM_Contribute_Form_UpdateBilling', 'formRule'), $this); @@ -246,9 +236,7 @@ public function postProcess() { $processorParams['year'] = $processorParams['credit_card_exp_date']['Y']; $processorParams['subscriptionId'] = $this->_subscriptionDetails->subscription_id; $processorParams['amount'] = $this->_subscriptionDetails->amount; - - $updateSubscription = $this->_paymentProcessorObj->updateSubscriptionBillingInfo($message, $processorParams); - + $updateSubscription = $this->_paymentProcessor['object']->updateSubscriptionBillingInfo($message, $processorParams); if (is_a($updateSubscription, 'CRM_Core_Error')) { CRM_Core_Error::displaySessionError($updateSubscription); } @@ -341,17 +329,15 @@ public function postProcess() { $activityParams = array( 'source_contact_id' => $this->_subscriptionDetails->contact_id, - 'activity_type_id' => CRM_Core_OptionGroup::getValue('activity_type', - 'Update Recurring Contribution Billing Details', - 'name' + 'activity_type_id' => CRM_Core_PseudoConstant::getKey( + 'CRM_Activity_BAO_Activity', + 'activity_type_id', + 'Update Recurring Contribution Billing Details' ), 'subject' => ts('Recurring Contribution Billing Details Updated'), 'details' => $message, 'activity_date_time' => date('YmdHis'), - 'status_id' => CRM_Core_OptionGroup::getValue('activity_status', - 'Completed', - 'name' - ), + 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'status_id', 'Completed'), ); $session = CRM_Core_Session::singleton(); $cid = $session->get('userID'); diff --git a/CRM/Contribute/Form/UpdateSubscription.php b/CRM/Contribute/Form/UpdateSubscription.php index c3ff12ae30aa..7b4be7a22c46 100644 --- a/CRM/Contribute/Form/UpdateSubscription.php +++ b/CRM/Contribute/Form/UpdateSubscription.php @@ -1,9 +1,9 @@ setAction(CRM_Core_Action::UPDATE); - $this->contributionRecurID = CRM_Utils_Request::retrieve('crid', 'Integer', $this, FALSE); if ($this->contributionRecurID) { - $this->_paymentProcessor = CRM_Contribute_BAO_ContributionRecur::getPaymentProcessor($this->contributionRecurID); - if (!$this->_paymentProcessor) { + try { + $this->_paymentProcessorObj = CRM_Financial_BAO_PaymentProcessor::getPaymentProcessorForRecurringContribution($this->contributionRecurID); + } + catch (CRM_Core_Exception $e) { + CRM_Core_Error::statusBounce(ts('There is no valid processor for this subscription so it cannot be edited.')); + } + catch (CiviCRM_API3_Exception $e) { CRM_Core_Error::statusBounce(ts('There is no valid processor for this subscription so it cannot be edited.')); } - $this->_paymentProcessorObj = $this->_paymentProcessor['object']; $this->_subscriptionDetails = CRM_Contribute_BAO_ContributionRecur::getSubscriptionDetails($this->contributionRecurID); } - $this->_coid = CRM_Utils_Request::retrieve('coid', 'Integer', $this, FALSE); if ($this->_coid) { $this->_paymentProcessor = CRM_Financial_BAO_PaymentProcessor::getProcessorForEntity($this->_coid, 'contribute', 'info'); + // @todo test & replace with $this->_paymentProcessorObj = Civi\Payment\System::singleton()->getById($this->_paymentProcessor['id']); $this->_paymentProcessorObj = CRM_Financial_BAO_PaymentProcessor::getProcessorForEntity($this->_coid, 'contribute', 'obj'); $this->_subscriptionDetails = CRM_Contribute_BAO_ContributionRecur::getSubscriptionDetails($this->_coid, 'contribution'); $this->contributionRecurID = $this->_subscriptionDetails->recur_id; @@ -100,20 +95,27 @@ public function preProcess() { $this->_coid = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_Contribution', $this->contributionRecurID, 'id', 'contribution_recur_id'); } - if ((!$this->contributionRecurID) || - ($this->_subscriptionDetails == CRM_Core_DAO::$_nullObject) - ) { - CRM_Core_Error::fatal('Required information missing.'); + if (!$this->contributionRecurID || !$this->_subscriptionDetails) { + CRM_Core_Error::statusBounce(ts('Required information missing.')); } if ($this->_subscriptionDetails->membership_id && $this->_subscriptionDetails->auto_renew) { - CRM_Core_Error::fatal(ts('You cannot update the subscription.')); + // Add Membership details to form + $membership = civicrm_api3('Membership', 'get', [ + 'contribution_recur_id' => $this->contributionRecurID, + ]); + if (!empty($membership['count'])) { + $membershipDetails = reset($membership['values']); + $values['membership_id'] = $membershipDetails['id']; + $values['membership_name'] = $membershipDetails['membership_name']; + } + $this->assign('recurMembership', $values); + $this->assign('contactId', $this->_subscriptionDetails->contact_id); } if (!CRM_Core_Permission::check('edit contributions')) { - $userChecksum = CRM_Utils_Request::retrieve('cs', 'String', $this, FALSE); - if (!CRM_Contact_BAO_Contact_Utils::validChecksum($this->_subscriptionDetails->contact_id, $userChecksum)) { - CRM_Core_Error::fatal(ts('You do not have permission to update subscription.')); + if ($this->_subscriptionDetails->contact_id != $this->getContactID()) { + CRM_Core_Error::statusBounce(ts('You do not have permission to update subscription.')); } $this->_selfService = TRUE; } @@ -129,17 +131,21 @@ public function preProcess() { else { $this->assign('changeHelpText', $changeHelpText); } - $alreadyHardCodedFields = array('amount', 'installments'); + $alreadyHardCodedFields = ['amount', 'installments']; foreach ($this->editableScheduleFields as $editableScheduleField) { if (!in_array($editableScheduleField, $alreadyHardCodedFields)) { - $this->addField($editableScheduleField, array('entity' => 'ContributionRecur')); + $this->addField($editableScheduleField, ['entity' => 'ContributionRecur'], FALSE, FALSE); } } + // when custom data is included in this page + if (!empty($_POST['hidden_custom'])) { + CRM_Custom_Form_CustomData::preProcess($this, NULL, NULL, 1, 'ContributionRecur', $this->contributionRecurID); + CRM_Custom_Form_CustomData::buildQuickForm($this); + CRM_Custom_Form_CustomData::setDefaultValues($this); + } + $this->assign('editableScheduleFields', array_diff($this->editableScheduleFields, $alreadyHardCodedFields)); - $this->assign('paymentProcessor', $this->_paymentProcessor); - $this->assign('frequency_unit', $this->_subscriptionDetails->frequency_unit); - $this->assign('frequency_interval', $this->_subscriptionDetails->frequency_interval); if ($this->_subscriptionDetails->contact_id) { list($this->_donorDisplayName, $this->_donorEmail) = CRM_Contact_BAO_Contact::getContactDetails($this->_subscriptionDetails->contact_id); @@ -157,14 +163,14 @@ public function preProcess() { * Note that in edit/view mode the default values are retrieved from the database. */ public function setDefaultValues() { - $this->_defaults = array(); + $this->_defaults = []; $this->_defaults['amount'] = $this->_subscriptionDetails->amount; $this->_defaults['installments'] = $this->_subscriptionDetails->installments; $this->_defaults['campaign_id'] = $this->_subscriptionDetails->campaign_id; $this->_defaults['financial_type_id'] = $this->_subscriptionDetails->financial_type_id; $this->_defaults['is_notify'] = 1; foreach ($this->editableScheduleFields as $field) { - $this->_defaults[$field] = $this->_subscriptionDetails->$field; + $this->_defaults[$field] = isset($this->_subscriptionDetails->$field) ? $this->_subscriptionDetails->$field : NULL; } return $this->_defaults; @@ -175,16 +181,16 @@ public function setDefaultValues() { */ public function buildQuickForm() { // CRM-16398: If current recurring contribution got > 1 lineitems then make amount field readonly - $amtAttr = array('size' => 20); + $amtAttr = ['size' => 20]; $lineItems = CRM_Price_BAO_LineItem::getLineItemsByContributionID($this->_coid); if (count($lineItems) > 1) { - $amtAttr += array('readonly' => TRUE); + $amtAttr += ['readonly' => TRUE]; } $this->addMoney('amount', ts('Recurring Contribution Amount'), TRUE, $amtAttr, TRUE, 'currency', $this->_subscriptionDetails->currency, TRUE ); - $this->add('text', 'installments', ts('Number of Installments'), array('size' => 20), FALSE); + $this->add('text', 'installments', ts('Number of Installments'), ['size' => 20], FALSE); if ($this->_donorEmail) { $this->add('checkbox', 'is_notify', ts('Notify Contributor?')); @@ -195,27 +201,30 @@ public function buildQuickForm() { } if (CRM_Contribute_BAO_ContributionRecur::supportsFinancialTypeChange($this->contributionRecurID)) { - $this->addEntityRef('financial_type_id', ts('Financial Type'), array('entity' => 'FinancialType'), !$this->_selfService); + $this->addEntityRef('financial_type_id', ts('Financial Type'), ['entity' => 'FinancialType'], !$this->_selfService); } + // Add custom data + $this->assign('customDataType', 'ContributionRecur'); + $this->assign('entityID', $this->contributionRecurID); + $type = 'next'; if ($this->_selfService) { $type = 'submit'; } // define the buttons - $this->addButtons(array( - array( - 'type' => $type, - 'name' => ts('Save'), - 'isDefault' => TRUE, - ), - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - ), - ) - ); + $this->addButtons([ + [ + 'type' => $type, + 'name' => ts('Save'), + 'isDefault' => TRUE, + ], + [ + 'type' => 'cancel', + 'name' => ts('Cancel'), + ], + ]); } /** @@ -236,7 +245,7 @@ public function postProcess() { $params['subscriptionId'] = $this->_subscriptionDetails->subscription_id; $updateSubscription = TRUE; - if ($this->_paymentProcessorObj->isSupported('changeSubscriptionAmount')) { + if ($this->_paymentProcessorObj->supports('changeSubscriptionAmount')) { $updateSubscription = $this->_paymentProcessorObj->changeSubscriptionAmount($message, $params); } if (is_a($updateSubscription, 'CRM_Core_Error')) { @@ -246,15 +255,17 @@ public function postProcess() { $msgType = 'error'; } elseif ($updateSubscription) { + // Handle custom data + $params['custom'] = CRM_Core_BAO_CustomField::postProcess($params, $this->contributionRecurID, 'ContributionRecur'); // save the changes - $result = CRM_Contribute_BAO_ContributionRecur::add($params); + CRM_Contribute_BAO_ContributionRecur::add($params); $status = ts('Recurring contribution has been updated to: %1, every %2 %3(s) for %4 installments.', - array( + [ 1 => CRM_Utils_Money::format($params['amount'], $this->_subscriptionDetails->currency), 2 => $this->_subscriptionDetails->frequency_interval, 3 => $this->_subscriptionDetails->frequency_unit, 4 => $params['installments'], - ) + ] ); $msgTitle = ts('Update Success'); @@ -264,10 +275,10 @@ public function postProcess() { if ($this->_subscriptionDetails->amount != $params['amount']) { $message .= "
    " . ts("Recurring contribution amount has been updated from %1 to %2 for this subscription.", - array( + [ 1 => CRM_Utils_Money::format($this->_subscriptionDetails->amount, $this->_subscriptionDetails->currency), 2 => CRM_Utils_Money::format($params['amount'], $this->_subscriptionDetails->currency), - )) . ' '; + ]) . ' '; if ($this->_subscriptionDetails->amount < $params['amount']) { $msg = ts('Recurring Contribution Updated - increased installment amount'); } @@ -277,26 +288,21 @@ public function postProcess() { } if ($this->_subscriptionDetails->installments != $params['installments']) { - $message .= "
    " . ts("Recurring contribution installments have been updated from %1 to %2 for this subscription.", array( - 1 => $this->_subscriptionDetails->installments, - 2 => $params['installments'], - )) . ' '; + $message .= "
    " . ts("Recurring contribution installments have been updated from %1 to %2 for this subscription.", [ + 1 => $this->_subscriptionDetails->installments, + 2 => $params['installments'], + ]) . ' '; } - $activityParams = array( + $activityParams = [ 'source_contact_id' => $contactID, - 'activity_type_id' => CRM_Core_OptionGroup::getValue('activity_type', - 'Update Recurring Contribution', - 'name' - ), + 'activity_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Update Recurring Contribution'), 'subject' => $msg, 'details' => $message, 'activity_date_time' => date('YmdHis'), - 'status_id' => CRM_Core_OptionGroup::getValue('activity_status', - 'Completed', - 'name' - ), - ); + 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', 'Completed'), + ]; + $session = CRM_Core_Session::singleton(); $cid = $session->get('userID'); @@ -310,11 +316,11 @@ public function postProcess() { // send notification if ($this->_subscriptionDetails->contribution_page_id) { CRM_Core_DAO::commonRetrieveAll('CRM_Contribute_DAO_ContributionPage', 'id', - $this->_subscriptionDetails->contribution_page_id, $value, array( + $this->_subscriptionDetails->contribution_page_id, $value, [ 'title', 'receipt_from_name', 'receipt_from_email', - ) + ] ); $receiptFrom = '"' . CRM_Utils_Array::value('receipt_from_name', $value[$this->_subscriptionDetails->contribution_page_id]) . '" <' . $value[$this->_subscriptionDetails->contribution_page_id]['receipt_from_email'] . '>'; } @@ -325,17 +331,17 @@ public function postProcess() { list($donorDisplayName, $donorEmail) = CRM_Contact_BAO_Contact::getContactDetails($contactID); - $tplParams = array( + $tplParams = [ 'recur_frequency_interval' => $this->_subscriptionDetails->frequency_interval, 'recur_frequency_unit' => $this->_subscriptionDetails->frequency_unit, 'amount' => CRM_Utils_Money::format($params['amount']), 'installments' => $params['installments'], - ); + ]; - $tplParams['contact'] = array('display_name' => $donorDisplayName); + $tplParams['contact'] = ['display_name' => $donorDisplayName]; $tplParams['receipt_from_email'] = $receiptFrom; - $sendTemplateParams = array( + $sendTemplateParams = [ 'groupName' => 'msg_tpl_workflow_contribution', 'valueName' => 'contribution_recurring_edit', 'contactId' => $contactID, @@ -345,7 +351,7 @@ public function postProcess() { 'from' => $receiptFrom, 'toName' => $donorDisplayName, 'toEmail' => $donorEmail, - ); + ]; list($sent) = CRM_Core_BAO_MessageTemplate::sendTemplate($sendTemplateParams); } } @@ -365,11 +371,4 @@ public function postProcess() { } } - /** - * Explicitly declare the form context. - */ - public function getDefaultContext() { - return 'create'; - } - } diff --git a/CRM/Contribute/Import/Controller.php b/CRM/Contribute/Import/Controller.php index 0cb1b2a11e0a..7860644926cb 100644 --- a/CRM/Contribute/Import/Controller.php +++ b/CRM/Contribute/Import/Controller.php @@ -1,9 +1,9 @@ addActions($config->uploadDir, array('uploadFile')); + $this->addActions($config->uploadDir, ['uploadFile']); } } diff --git a/CRM/Contribute/Import/Field.php b/CRM/Contribute/Import/Field.php index 4f4080046a8c..969441ef83e9 100644 --- a/CRM/Contribute/Import/Field.php +++ b/CRM/Contribute/Import/Field.php @@ -1,9 +1,9 @@ _value); case 'trxn_id': - static $seenTrxnIds = array(); + static $seenTrxnIds = []; if (in_array($this->_value, $seenTrxnIds)) { return FALSE; } diff --git a/CRM/Contribute/Import/Form/DataSource.php b/CRM/Contribute/Import/Form/DataSource.php index ec0c6d1e3587..ff0ee51e5479 100644 --- a/CRM/Contribute/Import/Form/DataSource.php +++ b/CRM/Contribute/Import/Form/DataSource.php @@ -1,9 +1,9 @@ createElement('radio', NULL, NULL, ts('Insert new contributions'), CRM_Import_Parser::DUPLICATE_SKIP ); @@ -57,9 +57,9 @@ public function buildQuickForm() { ts('Import mode') ); - $this->setDefaults(array('onDuplicate' => CRM_Import_Parser::DUPLICATE_SKIP)); + $this->setDefaults(['onDuplicate' => CRM_Import_Parser::DUPLICATE_SKIP]); - $this->addElement('submit', 'loadMapping', ts('Load Mapping'), NULL, array('onclick' => 'checkSelect()')); + $this->addElement('submit', 'loadMapping', ts('Load Mapping'), NULL, ['onclick' => 'checkSelect()']); $this->addContactTypeSelector(); } @@ -68,12 +68,12 @@ public function buildQuickForm() { * Process the uploaded file. */ public function postProcess() { - $this->storeFormValues(array( + $this->storeFormValues([ 'onDuplicate', 'contactType', 'dateFormats', 'savedMapping', - )); + ]); $this->submitFileForMapping('CRM_Contribute_Import_Parser_Contribution'); } diff --git a/CRM/Contribute/Import/Form/MapField.php b/CRM/Contribute/Import/Form/MapField.php index 4c68992b6c28..e0f58c436e77 100644 --- a/CRM/Contribute/Import/Form/MapField.php +++ b/CRM/Contribute/Import/Form/MapField.php @@ -1,9 +1,9 @@ assign('skipColumnHeader', $skipColumnHeader); $this->assign('rowDisplayCount', 3); - /* if we had a column header to skip, stash it for later */ + // If we had a column header to skip, stash it for later $this->_columnHeaders = $this->_dataValues[0]; } else { $this->assign('rowDisplayCount', 2); } - $highlightedFields = array('financial_type', 'total_amount'); + $highlightedFields = ['financial_type', 'total_amount']; //CRM-2219 removing other required fields since for updation only //invoice id or trxn id or contribution id is required. if ($this->_onDuplicate == CRM_Import_Parser::DUPLICATE_UPDATE) { - $remove = array('contribution_contact_id', 'email', 'first_name', 'last_name', 'external_identifier'); + $remove = [ + 'contribution_contact_id', + 'email', + 'first_name', + 'last_name', + 'external_identifier', + ]; foreach ($remove as $value) { unset($this->_mapperFields[$value]); } //modify field title only for update mode. CRM-3245 - foreach (array( - 'contribution_id', - 'invoice_id', - 'trxn_id', - ) as $key) { + foreach ([ + 'contribution_id', + 'invoice_id', + 'trxn_id', + ] as $key) { $this->_mapperFields[$key] .= ' (match to contribution record)'; $highlightedFields[] = $key; } } elseif ($this->_onDuplicate == CRM_Import_Parser::DUPLICATE_SKIP) { unset($this->_mapperFields['contribution_id']); - $highlightedFieldsArray = array( + $highlightedFieldsArray = [ 'contribution_contact_id', 'email', 'first_name', 'last_name', 'external_identifier', - ); + ]; foreach ($highlightedFieldsArray as $name) { $highlightedFields[] = $name; } @@ -125,8 +130,8 @@ public function buildQuickForm() { //mapping is to be loaded from database - $params = array('id' => $savedMapping); - $temp = array(); + $params = ['id' => $savedMapping]; + $temp = []; $mappingDetails = CRM_Core_BAO_Mapping::retrieve($params, $temp); $this->assign('loadedMapping', $mappingDetails->name); @@ -150,22 +155,23 @@ public function buildQuickForm() { $this->add('text', 'saveMappingDesc', ts('Description')); } - $this->addElement('checkbox', 'saveMapping', $saveDetailsName, NULL, array('onclick' => "showSaveDetails(this)")); + $this->addElement('checkbox', 'saveMapping', $saveDetailsName, NULL, ['onclick' => "showSaveDetails(this)"]); - $this->addFormRule(array('CRM_Contribute_Import_Form_MapField', 'formRule'), $this); + $this->addFormRule([ + 'CRM_Contribute_Import_Form_MapField', + 'formRule', + ], $this); //-------- end of saved mapping stuff --------- - $defaults = array(); + $defaults = []; $mapperKeys = array_keys($this->_mapperFields); $hasHeaders = !empty($this->_columnHeaders); $headerPatterns = $this->get('headerPatterns'); $dataPatterns = $this->get('dataPatterns'); - $hasLocationTypes = $this->get('fieldTypes'); $mapperKeysValues = $this->controller->exportValue($this->_name, 'mapper'); /* Initialize all field usages to false */ - foreach ($mapperKeys as $key) { $this->_fieldUsed[$key] = FALSE; } @@ -177,27 +183,6 @@ public function buildQuickForm() { unset($sel1['contribution_id']); } - // start of soft credit section - // get contact type for this import - $contactTypeId = $this->get('contactType'); - $contactTypes = array( - CRM_Import_Parser::CONTACT_INDIVIDUAL => 'Individual', - CRM_Import_Parser::CONTACT_HOUSEHOLD => 'Household', - CRM_Import_Parser::CONTACT_ORGANIZATION => 'Organization', - ); - - $contactType = isset($contactTypes[$contactTypeId]) ? $contactTypes[$contactTypeId] : ''; - - // get importable fields for contact type - $contactFields = CRM_Contact_BAO_Contact::importableFields($contactType, NULL); - - // get the Dedupe rule for this contact type and build soft credit array - $ruleParams = array( - 'contact_type' => $contactType, - 'used' => 'Unsupervised', - ); - $fieldsArray = CRM_Dedupe_BAO_Rule::dedupeRuleFields($ruleParams); - $softCreditFields['contact_id'] = ts('Contact ID'); $softCreditFields['external_identifier'] = ts('External ID'); $softCreditFields['email'] = ts('Email'); @@ -207,7 +192,6 @@ public function buildQuickForm() { $sel4 = NULL; // end of soft credit section - $js = "\n"; $this->assign('initHideBoxes', $js); @@ -316,23 +296,22 @@ public function buildQuickForm() { $this->setDefaults($defaults); - $this->addButtons(array( - array( - 'type' => 'back', - 'name' => ts('Previous'), - ), - array( - 'type' => 'next', - 'name' => ts('Continue'), - 'spacing' => '          ', - 'isDefault' => TRUE, - ), - array( - 'type' => 'cancel', - 'name' => ts('Cancel'), - ), - ) - ); + $this->addButtons([ + [ + 'type' => 'back', + 'name' => ts('Previous'), + ], + [ + 'type' => 'next', + 'name' => ts('Continue'), + 'spacing' => '          ', + 'isDefault' => TRUE, + ], + [ + 'type' => 'cancel', + 'name' => ts('Cancel'), + ], + ]); } /** @@ -348,25 +327,25 @@ public function buildQuickForm() { * list of errors to be posted back to the form */ public static function formRule($fields, $files, $self) { - $errors = array(); + $errors = []; $fieldMessage = NULL; $contactORContributionId = $self->_onDuplicate == CRM_Import_Parser::DUPLICATE_UPDATE ? 'contribution_id' : 'contribution_contact_id'; if (!array_key_exists('savedMapping', $fields)) { - $importKeys = array(); + $importKeys = []; foreach ($fields['mapper'] as $mapperPart) { $importKeys[] = $mapperPart[0]; } $contactTypeId = $self->get('contactType'); - $contactTypes = array( + $contactTypes = [ CRM_Import_Parser::CONTACT_INDIVIDUAL => 'Individual', CRM_Import_Parser::CONTACT_HOUSEHOLD => 'Household', CRM_Import_Parser::CONTACT_ORGANIZATION => 'Organization', - ); - $params = array( + ]; + $params = [ 'used' => 'Unsupervised', 'contact_type' => isset($contactTypes[$contactTypeId]) ? $contactTypes[$contactTypeId] : '', - ); + ]; list($ruleFields, $threshold) = CRM_Dedupe_BAO_RuleGroup::dedupeRuleFieldsWeight($params); $weightSum = 0; foreach ($importKeys as $key => $val) { @@ -388,11 +367,11 @@ public static function formRule($fields, $files, $self) { } // FIXME: should use the schema titles, not redeclare them - $requiredFields = array( + $requiredFields = [ $contactORContributionId == 'contribution_id' ? 'contribution_id' : 'contribution_contact_id' => $contactORContributionId == 'contribution_id' ? ts('Contribution ID') : ts('Contact ID'), 'total_amount' => ts('Total Amount'), 'financial_type' => ts('Financial Type'), - ); + ]; foreach ($requiredFields as $field => $title) { if (!in_array($field, $importKeys)) { @@ -403,9 +382,9 @@ public static function formRule($fields, $files, $self) { if (!($weightSum >= $threshold || in_array('external_identifier', $importKeys)) && $self->_onDuplicate != CRM_Import_Parser::DUPLICATE_UPDATE ) { - $errors['_qf_default'] .= ts('Missing required contact matching fields.') . " $fieldMessage " . ts('(Sum of all weights should be greater than or equal to threshold: %1).', array( - 1 => $threshold, - )) . '
    '; + $errors['_qf_default'] .= ts('Missing required contact matching fields.') . " $fieldMessage " . ts('(Sum of all weights should be greater than or equal to threshold: %1).', [ + 1 => $threshold, + ]) . '
    '; } elseif ($self->_onDuplicate == CRM_Import_Parser::DUPLICATE_UPDATE && !(in_array('invoice_id', $importKeys) || in_array('trxn_id', $importKeys) || @@ -416,9 +395,9 @@ public static function formRule($fields, $files, $self) { } } else { - $errors['_qf_default'] .= ts('Missing required field: %1', array( - 1 => $title, - )) . '
    '; + $errors['_qf_default'] .= ts('Missing required field: %1', [ + 1 => $title, + ]) . '
    '; } } } @@ -428,7 +407,12 @@ public static function formRule($fields, $files, $self) { $atleastOne = FALSE; foreach ($self->_mapperFields as $key => $field) { if (in_array($key, $importKeys) && - !in_array($key, array('doNotImport', 'contribution_id', 'invoice_id', 'trxn_id')) + !in_array($key, [ + 'doNotImport', + 'contribution_id', + 'invoice_id', + 'trxn_id', + ]) ) { $atleastOne = TRUE; break; @@ -446,8 +430,7 @@ public static function formRule($fields, $files, $self) { $errors['saveMappingName'] = ts('Name is required to save Import Mapping'); } else { - $mappingTypeId = CRM_Core_OptionGroup::getValue('mapping_type', 'Import Contribution', 'name'); - if (CRM_Core_BAO_Mapping::checkMapping($nameField, $mappingTypeId)) { + if (CRM_Core_BAO_Mapping::checkMapping($nameField, CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_Mapping', 'mapping_type_id', 'Import Contribution'))) { $errors['saveMappingName'] = ts('Duplicate Import Contribution Mapping Name'); } } @@ -482,12 +465,10 @@ public function postProcess() { } $fileName = $this->controller->exportValue('DataSource', 'uploadFile'); + $seperator = $this->controller->exportValue('DataSource', 'fieldSeparator'); $skipColumnHeader = $this->controller->exportValue('DataSource', 'skipColumnHeader'); - $config = CRM_Core_Config::singleton(); - $seperator = $config->fieldSeparator; - - $mapper = $mapperKeys = $mapperKeysMain = $mapperSoftCredit = $softCreditFields = $mapperPhoneType = $mapperSoftCreditType = array(); + $mapper = $mapperKeys = $mapperKeysMain = $mapperSoftCredit = $softCreditFields = $mapperPhoneType = $mapperSoftCreditType = []; $mapperKeys = $this->controller->exportValue($this->_name, 'mapper'); $softCreditTypes = CRM_Core_OptionGroup::values('soft_credit_type'); @@ -505,10 +486,10 @@ public function postProcess() { else { $softCreditFields[$i] = $mapperSoftCredit[$i]; } - $mapperSoftCreditType[$i] = array( + $mapperSoftCreditType[$i] = [ 'value' => isset($mapperKeys[$i][2]) ? $mapperKeys[$i][2] : '', 'label' => isset($softCreditTypes[$mapperKeys[$i][2]]) ? $softCreditTypes[$mapperKeys[$i][2]] : '', - ); + ]; } else { $mapperSoftCredit[$i] = $softCreditFields[$i] = $mapperSoftCreditType[$i] = NULL; @@ -528,7 +509,7 @@ public function postProcess() { $mappingFields->mapping_id = $params['mappingId']; $mappingFields->find(); - $mappingFieldsId = array(); + $mappingFieldsId = []; while ($mappingFields->fetch()) { if ($mappingFields->id) { $mappingFieldsId[$mappingFields->column_number] = $mappingFields->id; @@ -550,14 +531,11 @@ public function postProcess() { //Saving Mapping Details and Records if (!empty($params['saveMapping'])) { - $mappingParams = array( + $mappingParams = [ 'name' => $params['saveMappingName'], 'description' => $params['saveMappingDesc'], - 'mapping_type_id' => CRM_Core_OptionGroup::getValue('mapping_type', - 'Import Contribution', - 'name' - ), - ); + 'mapping_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_Mapping', 'mapping_type_id', 'Import Contribution'), + ]; $saveMapping = CRM_Core_BAO_Mapping::add($mappingParams); for ($i = 0; $i < $this->_columnCount; $i++) { diff --git a/CRM/Contribute/Import/Form/Preview.php b/CRM/Contribute/Import/Form/Preview.php index 28c078add2de..074c06b6d5f6 100644 --- a/CRM/Contribute/Import/Form/Preview.php +++ b/CRM/Contribute/Import/Form/Preview.php @@ -1,9 +1,9 @@ set('downloadMismatchRecordsUrl', CRM_Utils_System::url('civicrm/export', $urlParams)); } - $properties = array( + $properties = [ 'mapper', 'softCreditFields', 'mapperSoftCreditType', @@ -97,7 +97,8 @@ public function preProcess() { 'downloadErrorRecordsUrl', 'downloadConflictRecordsUrl', 'downloadMismatchRecordsUrl', - ); + ]; + $this->setStatusUrl(); foreach ($properties as $property) { $this->assign($property, $this->get($property)); @@ -109,19 +110,17 @@ public function preProcess() { */ public function postProcess() { $fileName = $this->controller->exportValue('DataSource', 'uploadFile'); + $seperator = $this->controller->exportValue('DataSource', 'fieldSeparator'); $skipColumnHeader = $this->controller->exportValue('DataSource', 'skipColumnHeader'); $invalidRowCount = $this->get('invalidRowCount'); $conflictRowCount = $this->get('conflictRowCount'); $onDuplicate = $this->get('onDuplicate'); $mapperSoftCreditType = $this->get('mapperSoftCreditType'); - $config = CRM_Core_Config::singleton(); - $seperator = $config->fieldSeparator; - $mapper = $this->controller->exportValue('MapField', 'mapper'); - $mapperKeys = array(); - $mapperSoftCredit = array(); - $mapperPhoneType = array(); + $mapperKeys = []; + $mapperSoftCredit = []; + $mapperPhoneType = []; foreach ($mapper as $key => $value) { $mapperKeys[$key] = $mapper[$key][0]; @@ -139,7 +138,7 @@ public function postProcess() { $mapFields = $this->get('fields'); foreach ($mapper as $key => $value) { - $header = array(); + $header = []; if (isset($mapFields[$mapper[$key][0]])) { $header[] = $mapFields[$mapper[$key][0]]; } @@ -150,7 +149,9 @@ public function postProcess() { $skipColumnHeader, CRM_Import_Parser::MODE_IMPORT, $this->get('contactType'), - $onDuplicate + $onDuplicate, + $this->get('statusID'), + $this->get('totalRowCount') ); // Add all the necessary variables to the form. @@ -160,7 +161,7 @@ public function postProcess() { $errorStack = CRM_Core_Error::singleton(); $errors = $errorStack->getErrors(); - $errorMessage = array(); + $errorMessage = []; if (is_array($errors)) { foreach ($errors as $key => $value) { diff --git a/CRM/Contribute/Import/Form/Summary.php b/CRM/Contribute/Import/Form/Summary.php index ee580e7f4f94..ddd0222305d4 100644 --- a/CRM/Contribute/Import/Form/Summary.php +++ b/CRM/Contribute/Import/Form/Summary.php @@ -1,9 +1,9 @@ assign('dupeActionString', $dupeActionString); - $properties = array( + $properties = [ 'totalRowCount', 'validRowCount', 'invalidRowCount', @@ -119,7 +119,7 @@ public function preProcess() { 'invalidPledgePaymentRowCount', 'downloadPledgePaymentErrorRecordsUrl', 'downloadSoftCreditErrorRecordsUrl', - ); + ]; foreach ($properties as $property) { $this->assign($property, $this->get($property)); } diff --git a/CRM/Contribute/Import/Parser.php b/CRM/Contribute/Import/Parser.php index ea3ee32695eb..939eef3d8018 100644 --- a/CRM/Contribute/Import/Parser.php +++ b/CRM/Contribute/Import/Parser.php @@ -1,9 +1,9 @@ _invalidRowCount = $this->_validCount = $this->_invalidSoftCreditRowCount = $this->_invalidPledgePaymentRowCount = 0; $this->_totalCount = $this->_conflictCount = 0; - $this->_errors = array(); - $this->_warnings = array(); - $this->_conflicts = array(); - $this->_pledgePaymentErrors = array(); - $this->_softCreditErrors = array(); + $this->_errors = []; + $this->_warnings = []; + $this->_conflicts = []; + $this->_pledgePaymentErrors = []; + $this->_softCreditErrors = []; + if ($statusID) { + $this->progressImport($statusID); + $startTimestamp = $currTimestamp = $prevTimestamp = time(); + } $this->_fileSize = number_format(filesize($fileName) / 1024.0, 2); if ($mode == self::MODE_MAPFIELD) { - $this->_rows = array(); + $this->_rows = []; } else { $this->_activeFieldCount = count($this->_activeFields); @@ -215,6 +235,9 @@ public function run( } elseif ($mode == self::MODE_IMPORT) { $returnCode = $this->import($onDuplicate, $values); + if ($statusID && (($this->_lineCount % 50) == 0)) { + $prevTimestamp = $this->progressImport($statusID, FALSE, $startTimestamp, $prevTimestamp, $totalRowCount); + } } else { $returnCode = self::ERROR; @@ -256,38 +279,32 @@ public function run( if ($returnCode == self::ERROR) { $this->_invalidRowCount++; - if ($this->_invalidRowCount < $this->_maxErrorCount) { - $recordNumber = $this->_lineCount; - if ($this->_haveColumnHeader) { - $recordNumber--; - } - array_unshift($values, $recordNumber); - $this->_errors[] = $values; + $recordNumber = $this->_lineCount; + if ($this->_haveColumnHeader) { + $recordNumber--; } + array_unshift($values, $recordNumber); + $this->_errors[] = $values; } if ($returnCode == self::PLEDGE_PAYMENT_ERROR) { $this->_invalidPledgePaymentRowCount++; - if ($this->_invalidPledgePaymentRowCount < $this->_maxErrorCount) { - $recordNumber = $this->_lineCount; - if ($this->_haveColumnHeader) { - $recordNumber--; - } - array_unshift($values, $recordNumber); - $this->_pledgePaymentErrors[] = $values; + $recordNumber = $this->_lineCount; + if ($this->_haveColumnHeader) { + $recordNumber--; } + array_unshift($values, $recordNumber); + $this->_pledgePaymentErrors[] = $values; } if ($returnCode == self::SOFT_CREDIT_ERROR) { $this->_invalidSoftCreditRowCount++; - if ($this->_invalidSoftCreditRowCount < $this->_maxErrorCount) { - $recordNumber = $this->_lineCount; - if ($this->_haveColumnHeader) { - $recordNumber--; - } - array_unshift($values, $recordNumber); - $this->_softCreditErrors[] = $values; + $recordNumber = $this->_lineCount; + if ($this->_haveColumnHeader) { + $recordNumber--; } + array_unshift($values, $recordNumber); + $this->_softCreditErrors[] = $values; } if ($returnCode == self::CONFLICT) { @@ -342,57 +359,47 @@ public function run( } if ($this->_invalidRowCount) { // removed view url for invlaid contacts - $headers = array_merge(array( - ts('Line Number'), - ts('Reason'), - ), - $customHeaders - ); + $headers = array_merge([ + ts('Line Number'), + ts('Reason'), + ], $customHeaders); $this->_errorFileName = self::errorFileName(self::ERROR); self::exportCSV($this->_errorFileName, $headers, $this->_errors); } if ($this->_invalidPledgePaymentRowCount) { // removed view url for invlaid contacts - $headers = array_merge(array( - ts('Line Number'), - ts('Reason'), - ), - $customHeaders - ); + $headers = array_merge([ + ts('Line Number'), + ts('Reason'), + ], $customHeaders); $this->_pledgePaymentErrorsFileName = self::errorFileName(self::PLEDGE_PAYMENT_ERROR); self::exportCSV($this->_pledgePaymentErrorsFileName, $headers, $this->_pledgePaymentErrors); } if ($this->_invalidSoftCreditRowCount) { // removed view url for invlaid contacts - $headers = array_merge(array( - ts('Line Number'), - ts('Reason'), - ), - $customHeaders - ); + $headers = array_merge([ + ts('Line Number'), + ts('Reason'), + ], $customHeaders); $this->_softCreditErrorsFileName = self::errorFileName(self::SOFT_CREDIT_ERROR); self::exportCSV($this->_softCreditErrorsFileName, $headers, $this->_softCreditErrors); } if ($this->_conflictCount) { - $headers = array_merge(array( - ts('Line Number'), - ts('Reason'), - ), - $customHeaders - ); + $headers = array_merge([ + ts('Line Number'), + ts('Reason'), + ], $customHeaders); $this->_conflictFileName = self::errorFileName(self::CONFLICT); self::exportCSV($this->_conflictFileName, $headers, $this->_conflicts); } if ($this->_duplicateCount) { - $headers = array_merge(array( - ts('Line Number'), - ts('View Contribution URL'), - ), - $customHeaders - ); + $headers = array_merge([ + ts('Line Number'), + ts('View Contribution URL'), + ], $customHeaders); $this->_duplicateFileName = self::errorFileName(self::DUPLICATE); self::exportCSV($this->_duplicateFileName, $headers, $this->_duplicates); @@ -444,12 +451,12 @@ public function setActiveFieldSoftCreditType($elements) { * (reference ) associative array of name/value pairs */ public function &getActiveFieldParams() { - $params = array(); + $params = []; for ($i = 0; $i < $this->_activeFieldCount; $i++) { if (isset($this->_activeFields[$i]->_value)) { if (isset($this->_activeFields[$i]->_softCreditField)) { if (!isset($params[$this->_activeFields[$i]->_name])) { - $params[$this->_activeFields[$i]->_name] = array(); + $params[$this->_activeFields[$i]->_name] = []; } $params[$this->_activeFields[$i]->_name][$i][$this->_activeFields[$i]->_softCreditField] = $this->_activeFields[$i]->_value; if (isset($this->_activeFields[$i]->_softCreditType)) { @@ -565,7 +572,7 @@ public function set($store, $mode = self::MODE_SUMMARY) { * @param array $data */ public static function exportCSV($fileName, $header, $data) { - $output = array(); + $output = []; $fd = fopen($fileName, 'w'); foreach ($header as $key => $value) { diff --git a/CRM/Contribute/Import/Parser/Contribution.php b/CRM/Contribute/Import/Parser/Contribution.php index de86b842c315..6ee91c40ef6d 100644 --- a/CRM/Contribute/Import/Parser/Contribution.php +++ b/CRM/Contribute/Import/Parser/Contribution.php @@ -1,9 +1,9 @@ _contactType, FALSE); $fields = array_merge($fields, - array( - 'soft_credit' => array( + [ + 'soft_credit' => [ 'title' => ts('Soft Credit'), 'softCredit' => TRUE, 'headerPattern' => '/Soft Credit/i', - ), - ) + ], + ] ); // add pledge fields only if its is enabled if (CRM_Core_Permission::access('CiviPledge')) { - $pledgeFields = array( - 'pledge_payment' => array( + $pledgeFields = [ + 'pledge_payment' => [ 'title' => ts('Pledge Payment'), 'headerPattern' => '/Pledge Payment/i', - ), - 'pledge_id' => array( + ], + 'pledge_id' => [ 'title' => ts('Pledge ID'), 'headerPattern' => '/Pledge ID/i', - ), - ); + ], + ]; $fields = array_merge($fields, $pledgeFields); } @@ -105,7 +105,7 @@ public function init() { $this->addField($name, $field['title'], $field['type'], $field['headerPattern'], $field['dataPattern']); } - $this->_newContributions = array(); + $this->_newContributions = []; $this->setActiveFields($this->_mapperKeys); $this->setActiveFieldSoftCredit($this->_mapperSoftCredit); @@ -177,49 +177,7 @@ public function summary(&$values) { $errorMessage = NULL; //for date-Formats - $session = CRM_Core_Session::singleton(); - $dateType = $session->get('dateTypes'); - foreach ($params as $key => $val) { - if ($val) { - switch ($key) { - case 'receive_date': - if ($dateValue = CRM_Utils_Date::formatDate($params[$key], $dateType)) { - $params[$key] = $dateValue; - } - else { - CRM_Contact_Import_Parser_Contact::addToErrorMsg('Receive Date', $errorMessage); - } - break; - - case 'cancel_date': - if ($dateValue = CRM_Utils_Date::formatDate($params[$key], $dateType)) { - $params[$key] = $dateValue; - } - else { - CRM_Contact_Import_Parser_Contact::addToErrorMsg('Cancel Date', $errorMessage); - } - break; - - case 'receipt_date': - if ($dateValue = CRM_Utils_Date::formatDate($params[$key], $dateType)) { - $params[$key] = $dateValue; - } - else { - CRM_Contact_Import_Parser_Contact::addToErrorMsg('Receipt date', $errorMessage); - } - break; - - case 'thankyou_date': - if ($dateValue = CRM_Utils_Date::formatDate($params[$key], $dateType)) { - $params[$key] = $dateValue; - } - else { - CRM_Contact_Import_Parser_Contact::addToErrorMsg('Thankyou Date', $errorMessage); - } - break; - } - } - } + $errorMessage = implode('; ', $this->formatDateFields($params)); //date-Format part ends $params['contact_type'] = 'Contribution'; @@ -256,49 +214,13 @@ public function import($onDuplicate, &$values) { } $params = &$this->getActiveFieldParams(); - $formatted = array('version' => 3); - - // don't add to recent items, CRM-4399 - $formatted['skipRecentView'] = TRUE; - - //for date-Formats - $session = CRM_Core_Session::singleton(); - $dateType = $session->get('dateTypes'); - - $customDataType = !empty($params['contact_type']) ? $params['contact_type'] : 'Contribution'; - $customFields = CRM_Core_BAO_CustomField::getFields($customDataType); + $formatted = ['version' => 3, 'skipRecentView' => TRUE, 'skipCleanMoney' => FALSE]; //CRM-10994 if (isset($params['total_amount']) && $params['total_amount'] == 0) { $params['total_amount'] = '0.00'; } - foreach ($params as $key => $val) { - if ($val) { - switch ($key) { - case 'receive_date': - case 'cancel_date': - case 'receipt_date': - case 'thankyou_date': - $params[$key] = CRM_Utils_Date::formatDate($params[$key], $dateType); - break; - - case 'pledge_payment': - $params[$key] = CRM_Utils_String::strtobool($val); - break; - - } - if ($customFieldID = CRM_Core_BAO_CustomField::getKeyID($key)) { - if ($customFields[$customFieldID]['data_type'] == 'Date') { - CRM_Contact_Import_Parser_Contact::formatCustomDate($params, $formatted, $dateType, $key); - unset($params[$key]); - } - elseif ($customFields[$customFieldID]['data_type'] == 'Boolean') { - $params[$key] = CRM_Utils_String::strtoboolstr($val); - } - } - } - } - //date-Format part ends + $this->formatInput($params); static $indieFields = NULL; if ($indieFields == NULL) { @@ -306,7 +228,7 @@ public function import($onDuplicate, &$values) { $indieFields = $tempIndieFields; } - $paramValues = array(); + $paramValues = []; foreach ($params as $key => $field) { if ($field == NULL || $field === '') { continue; @@ -360,11 +282,11 @@ public function import($onDuplicate, &$values) { //fix for CRM-2219 - Update Contribution // onDuplicate == CRM_Import_Parser::DUPLICATE_UPDATE if (!empty($paramValues['invoice_id']) || !empty($paramValues['trxn_id']) || !empty($paramValues['contribution_id'])) { - $dupeIds = array( + $dupeIds = [ 'id' => CRM_Utils_Array::value('contribution_id', $paramValues), 'trxn_id' => CRM_Utils_Array::value('trxn_id', $paramValues), 'invoice_id' => CRM_Utils_Array::value('invoice_id', $paramValues), - ); + ]; $ids['contribution'] = CRM_Contribute_BAO_Contribution::checkDuplicateIds($dupeIds); @@ -376,7 +298,7 @@ public function import($onDuplicate, &$values) { ); //process note if (!empty($paramValues['note'])) { - $noteID = array(); + $noteID = []; $contactID = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_Contribution', $ids['contribution'], 'contact_id'); $daoNote = new CRM_Core_BAO_Note(); $daoNote->entity_table = 'civicrm_contribution'; @@ -385,33 +307,32 @@ public function import($onDuplicate, &$values) { $noteID['id'] = $daoNote->id; } - $noteParams = array( + $noteParams = [ 'entity_table' => 'civicrm_contribution', 'note' => $paramValues['note'], 'entity_id' => $ids['contribution'], 'contact_id' => $contactID, - ); + ]; CRM_Core_BAO_Note::add($noteParams, $noteID); unset($formatted['note']); } //need to check existing soft credit contribution, CRM-3968 if (!empty($formatted['soft_credit'])) { - $dupeSoftCredit = array( + $dupeSoftCredit = [ 'contact_id' => $formatted['soft_credit'], 'contribution_id' => $ids['contribution'], - ); + ]; //Delete all existing soft Contribution from contribution_soft table for pcp_id is_null $existingSoftCredit = CRM_Contribute_BAO_ContributionSoft::getSoftContribution($dupeSoftCredit['contribution_id']); if (isset($existingSoftCredit['soft_credit']) && !empty($existingSoftCredit['soft_credit'])) { foreach ($existingSoftCredit['soft_credit'] as $key => $existingSoftCreditValues) { if (!empty($existingSoftCreditValues['soft_credit_id'])) { - $deleteParams = array( + civicrm_api3('ContributionSoft', 'delete', [ 'id' => $existingSoftCreditValues['soft_credit_id'], 'pcp_id' => NULL, - ); - CRM_Contribute_BAO_ContributionSoft::del($deleteParams); + ]); } } } @@ -431,11 +352,11 @@ public function import($onDuplicate, &$values) { return CRM_Import_Parser::VALID; } else { - $labels = array( + $labels = [ 'id' => 'Contribution ID', 'trxn_id' => 'Transaction ID', 'invoice_id' => 'Invoice ID', - ); + ]; foreach ($dupeIds as $k => $v) { if ($v) { $errorMsg[] = "$labels[$k] $v"; @@ -454,10 +375,7 @@ public function import($onDuplicate, &$values) { $paramValues['contact_type'] = $this->_contactType; } - $paramValues['version'] = 3; - //retrieve contact id using contact dedupe rule - require_once 'CRM/Utils/DeprecatedUtils.php'; - $error = _civicrm_api3_deprecated_check_contact_dedupe($paramValues); + $error = $this->checkContactDuplicate($paramValues); if (CRM_Core_Error::isAPIError($error, CRM_Core_ERROR::DUPLICATE_CONTACT)) { $matchedIDs = explode(',', $error['error_message']['params'][0]); @@ -499,10 +417,10 @@ public function import($onDuplicate, &$values) { } else { // Using new Dedupe rule. - $ruleParams = array( + $ruleParams = [ 'contact_type' => $this->_contactType, 'used' => 'Unsupervised', - ); + ]; $fieldsArray = CRM_Dedupe_BAO_Rule::dedupeRuleFields($ruleParams); $disp = NULL; foreach ($fieldsArray as $value) { @@ -579,7 +497,7 @@ public function import($onDuplicate, &$values) { public function processPledgePayments(&$formatted) { if (!empty($formatted['pledge_payment_id']) && !empty($formatted['pledge_id'])) { //get completed status - $completeStatusID = CRM_Core_OptionGroup::getValue('contribution_status', 'Completed', 'name'); + $completeStatusID = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'); //need to update payment record to map contribution_id CRM_Core_DAO::setFieldValue('CRM_Pledge_DAO_PledgePayment', $formatted['pledge_payment_id'], @@ -587,7 +505,7 @@ public function processPledgePayments(&$formatted) { ); CRM_Pledge_BAO_PledgePayment::updatePledgePaymentStatus($formatted['pledge_id'], - array($formatted['pledge_payment_id']), + [$formatted['pledge_payment_id']], $completeStatusID, NULL, $formatted['total_amount'] @@ -612,4 +530,105 @@ public function &getImportedContributions() { public function fini() { } + /** + * Format date fields from input to mysql. + * + * @param array $params + * + * @return array + * Error messages, if any. + */ + public function formatDateFields(&$params) { + $errorMessage = []; + $dateType = CRM_Core_Session::singleton()->get('dateTypes'); + foreach ($params as $key => $val) { + if ($val) { + switch ($key) { + case 'receive_date': + if ($dateValue = CRM_Utils_Date::formatDate($params[$key], $dateType)) { + $params[$key] = $dateValue; + } + else { + $errorMessage[] = ts('Receive Date'); + } + break; + + case 'cancel_date': + if ($dateValue = CRM_Utils_Date::formatDate($params[$key], $dateType)) { + $params[$key] = $dateValue; + } + else { + $errorMessage[] = ts('Cancel Date'); + } + break; + + case 'receipt_date': + if ($dateValue = CRM_Utils_Date::formatDate($params[$key], $dateType)) { + $params[$key] = $dateValue; + } + else { + $errorMessage[] = ts('Receipt date'); + } + break; + + case 'thankyou_date': + if ($dateValue = CRM_Utils_Date::formatDate($params[$key], $dateType)) { + $params[$key] = $dateValue; + } + else { + $errorMessage[] = ts('Thankyou Date'); + } + break; + } + } + } + return $errorMessage; + } + + /** + * Format input params to suit api handling. + * + * Over time all the parts of _civicrm_api3_deprecated_formatted_param + * and all the parts of the import function on this class that relate to + * reformatting input should be moved here and tests should be added in + * CRM_Contribute_Import_Parser_ContributionTest. + * + * @param array $params + */ + public function formatInput(&$params) { + $dateType = CRM_Core_Session::singleton()->get('dateTypes'); + $customDataType = !empty($params['contact_type']) ? $params['contact_type'] : 'Contribution'; + $customFields = CRM_Core_BAO_CustomField::getFields($customDataType); + // @todo call formatDateFields & move custom data handling there. + // Also note error handling for dates is currently in _civicrm_api3_deprecated_formatted_param + // we should use the error handling in formatDateFields. + foreach ($params as $key => $val) { + // @todo - call formatDateFields instead. + if ($val) { + switch ($key) { + case 'receive_date': + case 'cancel_date': + case 'receipt_date': + case 'thankyou_date': + $params[$key] = CRM_Utils_Date::formatDate($params[$key], $dateType); + break; + + case 'pledge_payment': + $params[$key] = CRM_Utils_String::strtobool($val); + break; + + } + if ($customFieldID = CRM_Core_BAO_CustomField::getKeyID($key)) { + if ($customFields[$customFieldID]['data_type'] == 'Date') { + CRM_Contact_Import_Parser_Contact::formatCustomDate($params, $params, $dateType, $key); + unset($params[$key]); + } + elseif ($customFields[$customFieldID]['data_type'] == 'Boolean') { + $params[$key] = CRM_Utils_String::strtoboolstr($val); + } + } + } + } + } + } diff --git a/CRM/Contribute/Info.php b/CRM/Contribute/Info.php index 29fa1b5ab440..6fd1c9c51c39 100644 --- a/CRM/Contribute/Info.php +++ b/CRM/Contribute/Info.php @@ -1,9 +1,9 @@ 'CiviContribute', 'translatedName' => ts('CiviContribute'), 'title' => ts('CiviCRM Contribution Engine'), 'search' => 1, 'showActivitiesInCore' => 1, - ); + ]; } /** @@ -80,23 +82,23 @@ public function getInfo() { * collection of permissions, null if none */ public function getPermissions($getAllUnconditionally = FALSE, $descriptions = FALSE) { - $permissions = array( - 'access CiviContribute' => array( + $permissions = [ + 'access CiviContribute' => [ ts('access CiviContribute'), ts('Record backend contributions (with edit contributions) and view all contributions (for visible contacts)'), - ), - 'edit contributions' => array( + ], + 'edit contributions' => [ ts('edit contributions'), ts('Record and update contributions'), - ), - 'make online contributions' => array( + ], + 'make online contributions' => [ ts('make online contributions'), - ), - 'delete in CiviContribute' => array( + ], + 'delete in CiviContribute' => [ ts('delete in CiviContribute'), ts('Delete contributions'), - ), - ); + ], + ]; if (!$descriptions) { foreach ($permissions as $name => $attr) { @@ -114,13 +116,14 @@ public function getPermissions($getAllUnconditionally = FALSE, $descriptions = F * list of permissions * @see CRM_Component_Info::getPermissions */ + /** * @return array */ public function getAnonymousPermissionWarnings() { - return array( + return [ 'access CiviContribute', - ); + ]; } /** @@ -132,16 +135,17 @@ public function getAnonymousPermissionWarnings() { * collection of required dashboard settings, * null if no element offered */ + /** * @return array|null */ public function getUserDashboardElement() { - return array( + return [ 'name' => ts('Contributions'), 'title' => ts('Your Contribution(s)'), - 'perm' => array('make online contributions'), + 'perm' => ['make online contributions'], 'weight' => 10, - ); + ]; } /** @@ -153,15 +157,24 @@ public function getUserDashboardElement() { * collection of required dashboard settings, * null if no element offered */ + /** * @return array|null */ public function registerTab() { - return array( + return [ 'title' => ts('Contributions'), 'url' => 'contribution', 'weight' => 20, - ); + ]; + } + + /** + * @inheritDoc + * @return string + */ + public function getIcon() { + return 'crm-i fa-credit-card'; } /** @@ -173,14 +186,15 @@ public function registerTab() { * collection of required pane settings, * null if no element offered */ + /** * @return array|null */ public function registerAdvancedSearchPane() { - return array( + return [ 'title' => ts('Contributions'), 'weight' => 20, - ); + ]; } /** @@ -193,6 +207,7 @@ public function registerAdvancedSearchPane() { * @return array|null * collection of activity types */ + /** * @return array|null */ @@ -209,20 +224,20 @@ public function creatNewShortcut(&$shortCuts, $newCredit) { if (CRM_Core_Permission::check('access CiviContribute') && CRM_Core_Permission::check('edit contributions') ) { - $shortCut[] = array( + $shortCut[] = [ 'path' => 'civicrm/contribute/add', 'query' => "reset=1&action=add&context=standalone", 'ref' => 'new-contribution', 'title' => ts('Contribution'), - ); + ]; if ($newCredit) { $title = ts('Contribution') . '
      (' . ts('credit card') . ')'; - $shortCut[0]['shortCuts'][] = array( + $shortCut[0]['shortCuts'][] = [ 'path' => 'civicrm/contribute/add', 'query' => "reset=1&action=add&context=standalone&mode=live", 'ref' => 'new-contribution-cc', 'title' => $title, - ); + ]; } $shortCuts = array_merge($shortCuts, $shortCut); } diff --git a/CRM/Contribute/Page/AJAX.php b/CRM/Contribute/Page/AJAX.php index 9f670969a8e3..9cbfacdd6af3 100644 --- a/CRM/Contribute/Page/AJAX.php +++ b/CRM/Contribute/Page/AJAX.php @@ -1,9 +1,9 @@ 'Integer', 'context' => 'String', - ); - $optionalParameters = array( + ]; + $optionalParameters = [ 'entityID' => 'Integer', 'isTest' => 'Integer', - ); + ]; $params = CRM_Core_Page_AJAX::defaultSortAndPagerParams(); $params += CRM_Core_Page_AJAX::validateParams($requiredParameters, $optionalParameters); diff --git a/CRM/Contribute/Page/ContributionPage.php b/CRM/Contribute/Page/ContributionPage.php index b219846ceda3..0f9f8df41a68 100644 --- a/CRM/Contribute/Page/ContributionPage.php +++ b/CRM/Contribute/Page/ContributionPage.php @@ -1,9 +1,9 @@ ts('Test-drive'), 'url' => $urlString, 'qs' => $urlParams . '&action=preview', + // Addresses https://lab.civicrm.org/dev/core/issues/658 + 'fe' => TRUE, 'uniqueName' => 'test_drive', ), ); @@ -419,8 +421,10 @@ public function browse($action = NULL) { $params = array(); $whereClause = $this->whereClause($params, FALSE); - $this->pagerAToZ($whereClause, $params); - + $config = CRM_Core_Config::singleton(); + if ($config->includeAlphabeticalPager) { + $this->pagerAToZ($whereClause, $params); + } $params = array(); $whereClause = $this->whereClause($params, TRUE); $this->pager($whereClause, $params); diff --git a/CRM/Contribute/Page/ContributionRecur.php b/CRM/Contribute/Page/ContributionRecur.php index c8313f0a039f..7aa68b264e3d 100644 --- a/CRM/Contribute/Page/ContributionRecur.php +++ b/CRM/Contribute/Page/ContributionRecur.php @@ -1,9 +1,9 @@ id = $this->_id; - if ($recur->find(TRUE)) { - $values = array(); - CRM_Core_DAO::storeValues($recur, $values); - // if there is a payment processor ID, get the name of the payment processor - if (!empty($values['payment_processor_id'])) { - $values['payment_processor'] = CRM_Core_DAO::getFieldValue( - 'CRM_Financial_DAO_PaymentProcessor', - $values['payment_processor_id'], - 'name' - ); - } - $idFields = array('contribution_status_id', 'campaign_id'); - if (CRM_Contribute_BAO_ContributionRecur::supportsFinancialTypeChange($values['id'])) { - $idFields[] = 'financial_type_id'; - } - foreach ($idFields as $idField) { - if (!empty($values[$idField])) { - $values[substr($idField, 0, -3)] = CRM_Core_PseudoConstant::getLabel('CRM_Contribute_BAO_ContributionRecur', $idField, $values[$idField]); - } + if (empty($this->_id)) { + CRM_Core_Error::statusBounce('Recurring contribution not found'); + } + + try { + $contributionRecur = civicrm_api3('ContributionRecur', 'getsingle', [ + 'id' => $this->_id, + ]); + } + catch (Exception $e) { + CRM_Core_Error::statusBounce('Recurring contribution not found (ID: ' . $this->_id); + } + + $contributionRecur['payment_processor'] = CRM_Financial_BAO_PaymentProcessor::getPaymentProcessorName( + CRM_Utils_Array::value('payment_processor_id', $contributionRecur) + ); + $idFields = ['contribution_status_id', 'campaign_id', 'financial_type_id']; + foreach ($idFields as $idField) { + if (!empty($contributionRecur[$idField])) { + $contributionRecur[substr($idField, 0, -3)] = CRM_Core_PseudoConstant::getLabel('CRM_Contribute_BAO_ContributionRecur', $idField, $contributionRecur[$idField]); } + } - $this->assign('recur', $values); + // Add linked membership + $membership = civicrm_api3('Membership', 'get', [ + 'contribution_recur_id' => $contributionRecur['id'], + ]); + if (!empty($membership['count'])) { + $membershipDetails = reset($membership['values']); + $contributionRecur['membership_id'] = $membershipDetails['id']; + $contributionRecur['membership_name'] = $membershipDetails['membership_name']; } + + $groupTree = CRM_Core_BAO_CustomGroup::getTree('ContributionRecur', NULL, $contributionRecur['id']); + CRM_Core_BAO_CustomGroup::buildCustomDataView($this, $groupTree, FALSE, NULL, NULL, NULL, $contributionRecur['id']); + + $this->assign('recur', $contributionRecur); + + $displayName = CRM_Contact_BAO_Contact::displayName($contributionRecur['contact_id']); + $this->assign('displayName', $displayName); + + // Check if this is default domain contact CRM-10482 + if (CRM_Contact_BAO_Contact::checkDomainContact($contributionRecur['contact_id'])) { + $displayName .= ' (' . ts('default organization') . ')'; + } + + // omitting contactImage from title for now since the summary overlay css doesn't work outside of our crm-container + CRM_Utils_System::setTitle(ts('View Recurring Contribution from') . ' ' . $displayName); } public function preProcess() { - $context = CRM_Utils_Request::retrieve('context', 'String', $this); $this->_action = CRM_Utils_Request::retrieve('action', 'String', $this, FALSE, 'view'); $this->_id = CRM_Utils_Request::retrieve('id', 'Positive', $this); $this->_contactId = CRM_Utils_Request::retrieve('cid', 'Positive', $this, TRUE); diff --git a/CRM/Contribute/Page/ContributionRecurPayments.php b/CRM/Contribute/Page/ContributionRecurPayments.php new file mode 100644 index 000000000000..91419ff36428 --- /dev/null +++ b/CRM/Contribute/Page/ContributionRecurPayments.php @@ -0,0 +1,225 @@ +id = CRM_Utils_Request::retrieve('id', 'Positive', $this, TRUE); + $this->contactId = CRM_Utils_Request::retrieve('cid', 'Positive', $this, TRUE); + + $this->loadRelatedContributions(); + + return parent::run(); + } + + /** + * Loads contributions associated to the current recurring contribution being + * viewed. + */ + private function loadRelatedContributions() { + $relatedContributions = []; + + $relatedContributionsResult = civicrm_api3('Contribution', 'get', [ + 'sequential' => 1, + 'contribution_recur_id' => $this->id, + 'contact_id' => $this->contactId, + 'options' => ['limit' => 0], + 'contribution_test' => '', + ]); + + foreach ($relatedContributionsResult['values'] as $contribution) { + $this->insertAmountExpandingPaymentsControl($contribution); + $this->fixDateFormats($contribution); + $this->insertStatusLabels($contribution); + $this->insertContributionActions($contribution); + + if ($contribution['is_test']) { + $contribution['financial_type'] = CRM_Core_TestEntity::appendTestText($contribution['financial_type']); + } + $relatedContributions[] = $contribution; + } + + if (count($relatedContributions) > 0) { + $this->assign('contributionsCount', count($relatedContributions)); + $this->assign('relatedContributions', json_encode($relatedContributions)); + } + } + + /** + * Inserts a string into the array with the html used to show the expanding + * payments control, which loads when user clicks on the amount. + * + * @param array $contribution + * Reference to the array holding the contribution's data and where the + * control will be inserted into + */ + private function insertAmountExpandingPaymentsControl(&$contribution) { + $amount = CRM_Utils_Money::format($contribution['total_amount'], $contribution['currency']); + + $expandPaymentsUrl = CRM_Utils_System::url('civicrm/payment', + [ + 'view' => 'transaction', + 'component' => 'contribution', + 'action' => 'browse', + 'cid' => $this->contactId, + 'id' => $contribution['contribution_id'], + 'selector' => 1, + ], + FALSE, NULL, TRUE + ); + + $contribution['amount_control'] = ' + +   ' . $amount . ' + + '; + } + + /** + * Fixes date fields present in the given contribution. + * + * @param array $contribution + * Reference to the array holding the contribution's data + */ + private function fixDateFormats(&$contribution) { + $config = CRM_Core_Config::singleton(); + + $contribution['formatted_receive_date'] = CRM_Utils_Date::customFormat($contribution['receive_date'], $config->dateformatDatetime); + $contribution['formatted_thankyou_date'] = CRM_Utils_Date::customFormat($contribution['thankyou_date'], $config->dateformatDatetime); + } + + /** + * Inserts a contribution_status_label key into the array, with the value + * showing the current status plus observations on the current status. + * + * @param array $contribution + * Reference to the array holding the contribution's data and where the new + * position will be inserted + */ + private function insertStatusLabels(&$contribution) { + $contribution['contribution_status_label'] = $contribution['contribution_status']; + + if ($contribution['is_pay_later'] && CRM_Utils_Array::value('contribution_status', $contribution) == 'Pending') { + $contribution['contribution_status_label'] .= ' (' . ts('Pay Later') . ')'; + } + elseif (CRM_Utils_Array::value('contribution_status', $contribution) == 'Pending') { + $contribution['contribution_status_label'] .= ' (' . ts('Incomplete Transaction') . ')'; + } + } + + /** + * Inserts into the given array a string with the 'action' key, holding the + * html to be used to show available actions for the contribution. + * + * @param $contribution + * Reference to the array holding the contribution's data. It is also the + * array where the new 'action' key will be inserted. + */ + private function insertContributionActions(&$contribution) { + $contribution['action'] = CRM_Core_Action::formLink( + $this->buildContributionLinks($contribution), + $this->getContributionPermissionsMask(), + [ + 'id' => $contribution['contribution_id'], + 'cid' => $contribution['contact_id'], + 'cxt' => 'contribution', + ], + ts('more'), + FALSE, + 'contribution.selector.row', + 'Contribution', + $contribution['contribution_id'] + ); + } + + /** + * Builds list of links for authorized actions that can be done on given + * contribution. + * + * @param array $contribution + * + * @return array + */ + private function buildContributionLinks($contribution) { + $links = CRM_Contribute_Selector_Search::links($contribution['contribution_id'], + CRM_Utils_Request::retrieve('action', 'String'), + NULL, + NULL + ); + + $isPayLater = FALSE; + if ($contribution['is_pay_later'] && CRM_Utils_Array::value('contribution_status', $contribution) == 'Pending') { + $isPayLater = TRUE; + + $links[CRM_Core_Action::ADD] = [ + 'name' => ts('Pay with Credit Card'), + 'url' => 'civicrm/contact/view/contribution', + 'qs' => 'reset=1&action=update&id=%%id%%&cid=%%cid%%&context=%%cxt%%&mode=live', + 'title' => ts('Pay with Credit Card'), + ]; + } + + if (in_array($contribution['contribution_status'], ['Partially paid', 'Pending refund']) || $isPayLater) { + $buttonName = ts('Record Payment'); + + if ($contribution['contribution_status'] == 'Pending refund') { + $buttonName = ts('Record Refund'); + } + elseif (CRM_Core_Config::isEnabledBackOfficeCreditCardPayments()) { + $links[CRM_Core_Action::BASIC] = [ + 'name' => ts('Submit Credit Card payment'), + 'url' => 'civicrm/payment/add', + 'qs' => 'reset=1&id=%%id%%&cid=%%cid%%&action=add&component=contribution&mode=live', + 'title' => ts('Submit Credit Card payment'), + ]; + } + $links[CRM_Core_Action::ADD] = [ + 'name' => $buttonName, + 'url' => 'civicrm/payment', + 'qs' => 'reset=1&id=%%id%%&cid=%%cid%%&action=add&component=contribution', + 'title' => $buttonName, + ]; + } + + return $links; + } + + /** + * Builds a mask with allowed contribution related permissions. + * + * @return int + */ + private function getContributionPermissionsMask() { + $permissions = [CRM_Core_Permission::VIEW]; + if (CRM_Core_Permission::check('edit contributions')) { + $permissions[] = CRM_Core_Permission::EDIT; + } + if (CRM_Core_Permission::check('delete in CiviContribute')) { + $permissions[] = CRM_Core_Permission::DELETE; + } + + return CRM_Core_Action::mask($permissions); + } + +} diff --git a/CRM/Contribute/Page/DashBoard.php b/CRM/Contribute/Page/DashBoard.php index 60b6e8b7de33..59b07ffda409 100644 --- a/CRM/Contribute/Page/DashBoard.php +++ b/CRM/Contribute/Page/DashBoard.php @@ -1,9 +1,9 @@ array( + self::$_links = [ + CRM_Core_Action::UPDATE => [ 'name' => ts('Edit'), 'url' => 'civicrm/admin/contribute/managePremiums', 'qs' => 'action=update&id=%%id%%&reset=1', 'title' => ts('Edit Premium'), - ), - CRM_Core_Action::PREVIEW => array( + ], + CRM_Core_Action::PREVIEW => [ 'name' => ts('Preview'), 'url' => 'civicrm/admin/contribute/managePremiums', 'qs' => 'action=preview&id=%%id%%', 'title' => ts('Preview Premium'), - ), - CRM_Core_Action::DISABLE => array( + ], + CRM_Core_Action::DISABLE => [ 'name' => ts('Disable'), 'ref' => 'crm-enable-disable', 'title' => ts('Disable Premium'), - ), - CRM_Core_Action::ENABLE => array( + ], + CRM_Core_Action::ENABLE => [ 'name' => ts('Enable'), 'ref' => 'crm-enable-disable', 'title' => ts('Enable Premium'), - ), - CRM_Core_Action::DELETE => array( + ], + CRM_Core_Action::DELETE => [ 'name' => ts('Delete'), 'url' => 'civicrm/admin/contribute/managePremiums', 'qs' => 'action=delete&id=%%id%%', 'title' => ts('Delete Premium'), - ), - ); + ], + ]; } return self::$_links; } @@ -105,28 +105,17 @@ public function &links() { * Finally it calls the parent's run method. */ public function run() { - - // get the requested action - $action = CRM_Utils_Request::retrieve('action', 'String', - // default to 'browse' - $this, FALSE, 'browse' - ); - - // assign vars to templates - $this->assign('action', $action); - $id = CRM_Utils_Request::retrieve('id', 'Positive', - $this, FALSE, 0 - ); + $id = $this->getIdAndAction(); // what action to take ? - if ($action & (CRM_Core_Action::UPDATE | CRM_Core_Action::ADD | CRM_Core_Action::PREVIEW)) { - $this->edit($action, $id, TRUE); + if (!($this->_action & CRM_Core_Action::BROWSE)) { + $this->edit($this->_action, $id, TRUE); } // finally browse the custom groups $this->browse(); // parent run - return parent::run(); + return CRM_Core_Page::run(); } /** @@ -134,13 +123,13 @@ public function run() { */ public function browse() { // get all custom groups sorted by weight - $premiums = array(); + $premiums = []; $dao = new CRM_Contribute_DAO_Product(); $dao->orderBy('name'); $dao->find(); while ($dao->fetch()) { - $premiums[$dao->id] = array(); + $premiums[$dao->id] = []; CRM_Core_DAO::storeValues($dao, $premiums[$dao->id]); // form all action links $action = array_sum(array_keys($this->links())); @@ -154,17 +143,16 @@ public function browse() { $premiums[$dao->id]['action'] = CRM_Core_Action::formLink(self::links(), $action, - array('id' => $dao->id), + ['id' => $dao->id], ts('more'), FALSE, 'premium.manage.row', 'Premium', $dao->id ); - //Financial Type + // Financial Type if (!empty($dao->financial_type_id)) { - require_once 'CRM/Core/DAO.php'; - $premiums[$dao->id]['financial_type_id'] = CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_FinancialType', $dao->financial_type_id, 'name'); + $premiums[$dao->id]['financial_type'] = CRM_Core_PseudoConstant::getLabel('CRM_Contribute_BAO_Product', 'financial_type_id', $dao->financial_type_id); } } $this->assign('rows', $premiums); diff --git a/CRM/Contribute/Page/PaymentInfo.php b/CRM/Contribute/Page/PaymentInfo.php index 9975748dc87e..bfb9e8436180 100644 --- a/CRM/Contribute/Page/PaymentInfo.php +++ b/CRM/Contribute/Page/PaymentInfo.php @@ -1,9 +1,9 @@ _component = CRM_Utils_Request::retrieve('component', 'String', $this, TRUE); $this->_action = CRM_Utils_Request::retrieve('action', 'String', $this, FALSE, 'browse'); $this->_id = CRM_Utils_Request::retrieve('id', 'Positive', $this, TRUE); - $this->_context = CRM_Utils_Request::retrieve('context', 'String', $this, TRUE); + $this->_context = CRM_Utils_Request::retrieve('context', 'Alphanumeric', $this, TRUE); $this->_cid = CRM_Utils_Request::retrieve('cid', 'String', $this, TRUE); $this->assign('cid', $this->_cid); $this->assign('id', $this->_id); $this->assign('context', $this->_context); $this->assign('component', $this->_component); + if ($this->_component != 'event') { + $this->assign('hideButtonLinks', TRUE); + } } public function browse() { diff --git a/CRM/Contribute/Page/Premium.php b/CRM/Contribute/Page/Premium.php index 525f3e623c80..25189668ad14 100644 --- a/CRM/Contribute/Page/Premium.php +++ b/CRM/Contribute/Page/Premium.php @@ -1,9 +1,9 @@ array( + self::$_links = [ + CRM_Core_Action::UPDATE => [ 'name' => ts('Edit'), 'url' => 'civicrm/admin/contribute/addProductToPage', 'qs' => 'action=update&id=%%id%%&pid=%%pid%%&reset=1', 'title' => ts('Edit Premium'), - ), - CRM_Core_Action::PREVIEW => array( + ], + CRM_Core_Action::PREVIEW => [ 'name' => ts('Preview'), 'url' => 'civicrm/admin/contribute/addProductToPage', 'qs' => 'action=preview&id=%%id%%&pid=%%pid%%', 'title' => ts('Preview Premium'), - ), - CRM_Core_Action::DELETE => array( + ], + CRM_Core_Action::DELETE => [ 'name' => ts('Remove'), 'url' => 'civicrm/admin/contribute/addProductToPage', 'qs' => 'action=delete&id=%%id%%&pid=%%pid%%', 'extra' => 'onclick = "if (confirm(\'' . $deleteExtra . '\') ) this.href+=\'&confirmed=1\'; else return false;"', 'title' => ts('Disable Premium'), - ), - ); + ], + ]; } return self::$_links; } @@ -126,49 +126,49 @@ public function run() { */ public function browse() { // get all custom groups sorted by weight - $premiums = array(); + $premiums = []; $pageID = CRM_Utils_Request::retrieve('id', 'Positive', $this, FALSE, 0 ); - $dao = new CRM_Contribute_DAO_Premium(); - $dao->entity_table = 'civicrm_contribution_page'; - $dao->entity_id = $pageID; - $dao->find(TRUE); - $premiumID = $dao->id; + $premiumDao = new CRM_Contribute_DAO_Premium(); + $premiumDao->entity_table = 'civicrm_contribution_page'; + $premiumDao->entity_id = $pageID; + $premiumDao->find(TRUE); + $premiumID = $premiumDao->id; $this->assign('products', FALSE); $this->assign('id', $pageID); if (!$premiumID) { return; } - $dao = new CRM_Contribute_DAO_PremiumsProduct(); - $dao->premiums_id = $premiumID; - $dao->orderBy('weight'); - $dao->find(); + $premiumsProductDao = new CRM_Contribute_DAO_PremiumsProduct(); + $premiumsProductDao->premiums_id = $premiumID; + $premiumsProductDao->orderBy('weight'); + $premiumsProductDao->find(); - while ($dao->fetch()) { + while ($premiumsProductDao->fetch()) { $productDAO = new CRM_Contribute_DAO_Product(); - $productDAO->id = $dao->product_id; + $productDAO->id = $premiumsProductDao->product_id; $productDAO->is_active = 1; if ($productDAO->find(TRUE)) { - $premiums[$productDAO->id] = array(); - $premiums[$productDAO->id]['weight'] = $dao->weight; + $premiums[$productDAO->id] = []; + $premiums[$productDAO->id]['weight'] = $premiumsProductDao->weight; CRM_Core_DAO::storeValues($productDAO, $premiums[$productDAO->id]); $action = array_sum(array_keys($this->links())); - $premiums[$dao->product_id]['action'] = CRM_Core_Action::formLink(self::links(), $action, - array('id' => $pageID, 'pid' => $dao->id), + $premiums[$premiumsProductDao->product_id]['action'] = CRM_Core_Action::formLink(self::links(), $action, + ['id' => $pageID, 'pid' => $premiumsProductDao->id], ts('more'), FALSE, 'premium.contributionpage.row', 'Premium', - $dao->id + $premiumsProductDao->id ); - //Financial Type - if (!empty($dao->financial_type_id)) { - $premiums[$productDAO->id]['financial_type_id'] = CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_FinancialType', $dao->financial_type_id, 'name'); + // Financial Type + if (!empty($premiumsProductDao->financial_type_id)) { + $premiums[$productDAO->id]['financial_type'] = CRM_Core_PseudoConstant::getLabel('CRM_Contribute_BAO_Product', 'financial_type_id', $premiumsProductDao->financial_type_id); } } } diff --git a/CRM/Contribute/Page/SubscriptionStatus.php b/CRM/Contribute/Page/SubscriptionStatus.php index 873ec6928f0a..96e7f0786f92 100644 --- a/CRM/Contribute/Page/SubscriptionStatus.php +++ b/CRM/Contribute/Page/SubscriptionStatus.php @@ -1,9 +1,9 @@ assign('task', $task); $this->assign('result', $result); diff --git a/CRM/Contribute/Page/Tab.php b/CRM/Contribute/Page/Tab.php index a5d0ad071f59..b93544e30703 100644 --- a/CRM/Contribute/Page/Tab.php +++ b/CRM/Contribute/Page/Tab.php @@ -1,9 +1,9 @@ array( + self::$_links = [ + CRM_Core_Action::VIEW => [ 'name' => ts('View'), 'title' => ts('View Recurring Payment'), 'url' => 'civicrm/contact/view/contributionrecur', 'qs' => "reset=1&id=%%crid%%&cid=%%cid%%&context={$context}", - ), - CRM_Core_Action::UPDATE => array( + ], + CRM_Core_Action::UPDATE => [ 'name' => ts('Edit'), 'title' => ts('Edit Recurring Payment'), 'url' => 'civicrm/contribute/updaterecur', 'qs' => "reset=1&action=update&crid=%%crid%%&cid=%%cid%%&context={$context}", - ), - CRM_Core_Action::DISABLE => array( + ], + CRM_Core_Action::DISABLE => [ 'name' => ts('Cancel'), 'title' => ts('Cancel'), 'ref' => 'crm-enable-disable', - ), - ); + ], + ]; } if ($recurID) { - $paymentProcessorObj = CRM_Financial_BAO_PaymentProcessor::getProcessorForEntity($recurID, 'recur', 'obj'); - if (is_object($paymentProcessorObj) && $paymentProcessorObj->supports('cancelRecurring')) { - unset(self::$_links[CRM_Core_Action::DISABLE]['extra'], self::$_links[CRM_Core_Action::DISABLE]['ref']); - self::$_links[CRM_Core_Action::DISABLE]['url'] = "civicrm/contribute/unsubscribe"; - self::$_links[CRM_Core_Action::DISABLE]['qs'] = "reset=1&crid=%%crid%%&cid=%%cid%%&context={$context}"; + $links = self::$_links; + $paymentProcessorObj = CRM_Contribute_BAO_ContributionRecur::getPaymentProcessorObject($recurID); + if (!$paymentProcessorObj) { + unset($links[CRM_Core_Action::DISABLE]); + unset($links[CRM_Core_Action::UPDATE]); + return $links; + } + if ($paymentProcessorObj->supports('cancelRecurring')) { + unset($links[CRM_Core_Action::DISABLE]['extra'], $links[CRM_Core_Action::DISABLE]['ref']); + $links[CRM_Core_Action::DISABLE]['url'] = "civicrm/contribute/unsubscribe"; + $links[CRM_Core_Action::DISABLE]['qs'] = "reset=1&crid=%%crid%%&cid=%%cid%%&context={$context}"; } - if (is_object($paymentProcessorObj) && $paymentProcessorObj->isSupported('updateSubscriptionBillingInfo')) { - self::$_links[CRM_Core_Action::RENEW] = array( + if ($paymentProcessorObj->supports('UpdateSubscriptionBillingInfo')) { + $links[CRM_Core_Action::RENEW] = [ 'name' => ts('Change Billing Details'), 'title' => ts('Change Billing Details'), 'url' => 'civicrm/contribute/updatebilling', 'qs' => "reset=1&crid=%%crid%%&cid=%%cid%%&context={$context}", - ); + ]; + } + + if (!$paymentProcessorObj->supports('ChangeSubscriptionAmount') && !$paymentProcessorObj->supports('EditRecurringContribution')) { + unset($links[CRM_Core_Action::UPDATE]); } + return $links; } return self::$_links; } - // end function /** * called when action is browse. @@ -106,7 +116,7 @@ public static function &recurLinks($recurID = FALSE, $context = 'contribution') */ public function browse() { // add annual contribution - $annual = array(); + $annual = []; list($annual['count'], $annual['amount'], $annual['avg'] @@ -129,44 +139,9 @@ public function browse() { $controller->run(); // add recurring block - $action = array_sum(array_keys($this->recurLinks())); - $params = CRM_Contribute_BAO_ContributionRecur::getRecurContributions($this->_contactId); - - if (!empty($params)) { - foreach ($params as $ids => $recur) { - $action = array_sum(array_keys($this->recurLinks($ids))); - // no action allowed if it's not active - $params[$ids]['is_active'] = ($recur['contribution_status_id'] != 3); - - if ($params[$ids]['is_active']) { - $details = CRM_Contribute_BAO_ContributionRecur::getSubscriptionDetails($params[$ids]['id'], 'recur'); - $hideUpdate = $details->membership_id & $details->auto_renew; - - if ($hideUpdate) { - $action -= CRM_Core_Action::UPDATE; - } - - $params[$ids]['action'] = CRM_Core_Action::formLink(self::recurLinks($ids), $action, - array( - 'cid' => $this->_contactId, - 'crid' => $ids, - 'cxt' => 'contribution', - ), - ts('more'), - FALSE, - 'contribution.selector.recurring', - 'Contribution', - $ids - ); - } - } - // assign vars to templates - $this->assign('action', $this->_action); - $this->assign('recurRows', $params); - $this->assign('recur', TRUE); - } + $this->addRecurringContributionsBlock(); - //enable/disable soft credit records for test contribution + // enable/disable soft credit records for test contribution $isTest = 0; if (CRM_Utils_Request::retrieve('isTest', 'Positive', $this)) { $isTest = 1; @@ -176,12 +151,14 @@ public function browse() { $softCreditList = CRM_Contribute_BAO_ContributionSoft::getSoftContributionList($this->_contactId, NULL, $isTest); if (!empty($softCreditList)) { - $softCreditTotals = array(); + $softCreditTotals = []; - list($softCreditTotals['amount'], + list($softCreditTotals['count'], + $softCreditTotals['cancel']['count'], + $softCreditTotals['amount'], $softCreditTotals['avg'], - $softCreditTotals['currency'], - $softCreditTotals['cancelAmount'] //to get cancel amount + // to get cancel amount + $softCreditTotals['cancel']['amount'] ) = CRM_Contribute_BAO_ContributionSoft::getSoftContributionTotals($this->_contactId, $isTest); $this->assign('softCredit', TRUE); @@ -192,14 +169,124 @@ public function browse() { if ($this->_contactId) { $displayName = CRM_Contact_BAO_Contact::displayName($this->_contactId); $this->assign('displayName', $displayName); - $this->ajaxResponse['tabCount'] = CRM_Contact_BAO_Contact::getCountComponent('contribution', $this->_contactId); + $tabCount = CRM_Contact_BAO_Contact::getCountComponent('contribution', $this->_contactId); + $this->assign('tabCount', $tabCount); + $this->ajaxResponse['tabCount'] = $tabCount; } } + /** + * Get all the recurring contribution information and assign to the template + */ + private function addRecurringContributionsBlock() { + list($activeContributions, $activeContributionsCount) = $this->getActiveRecurringContributions(); + list($inactiveRecurringContributions, $inactiveContributionsCount) = $this->getInactiveRecurringContributions(); + + if (!empty($activeContributions) || !empty($inactiveRecurringContributions)) { + // assign vars to templates + $this->assign('action', $this->_action); + $this->assign('activeRecurRows', $activeContributions); + $this->assign('contributionRecurCount', $activeContributionsCount + $inactiveContributionsCount); + $this->assign('inactiveRecurRows', $inactiveRecurringContributions); + $this->assign('recur', TRUE); + } + } + + /** + * Loads active recurring contributions for the current contact and formats + * them to be used on the form. + * + * @return array; + */ + private function getActiveRecurringContributions() { + try { + $contributionRecurResult = civicrm_api3('ContributionRecur', 'get', [ + 'contact_id' => $this->_contactId, + 'contribution_status_id' => ['NOT IN' => CRM_Contribute_BAO_ContributionRecur::getInactiveStatuses()], + 'options' => ['limit' => 0, 'sort' => 'is_test, start_date DESC'], + ]); + $recurContributions = CRM_Utils_Array::value('values', $contributionRecurResult); + } + catch (Exception $e) { + $recurContributions = []; + } + + return $this->buildRecurringContributionsArray($recurContributions); + } + + /** + * Loads inactive recurring contributions for the current contact and formats + * them to be used on the form. + * + * @return array; + */ + private function getInactiveRecurringContributions() { + try { + $contributionRecurResult = civicrm_api3('ContributionRecur', 'get', [ + 'contact_id' => $this->_contactId, + 'contribution_status_id' => ['IN' => CRM_Contribute_BAO_ContributionRecur::getInactiveStatuses()], + 'options' => ['limit' => 0, 'sort' => 'is_test, start_date DESC'], + ]); + $recurContributions = CRM_Utils_Array::value('values', $contributionRecurResult); + } + catch (Exception $e) { + $recurContributions = NULL; + } + + return $this->buildRecurringContributionsArray($recurContributions); + } + + /** + * @param $recurContributions + * + * @return mixed + */ + private function buildRecurringContributionsArray($recurContributions) { + $liveRecurringContributionCount = 0; + foreach ($recurContributions as $recurId => $recurDetail) { + // Is recurring contribution active? + $recurContributions[$recurId]['is_active'] = !in_array(CRM_Contribute_PseudoConstant::contributionStatus($recurDetail['contribution_status_id'], 'name'), CRM_Contribute_BAO_ContributionRecur::getInactiveStatuses()); + if ($recurContributions[$recurId]['is_active']) { + $actionMask = array_sum(array_keys(self::recurLinks($recurId))); + } + else { + $actionMask = CRM_Core_Action::mask([CRM_Core_Permission::VIEW]); + } + + if (empty($recurDetail['is_test'])) { + $liveRecurringContributionCount++; + } + + // Get the name of the payment processor + if (!empty($recurDetail['payment_processor_id'])) { + $recurContributions[$recurId]['payment_processor'] = CRM_Financial_BAO_PaymentProcessor::getPaymentProcessorName($recurDetail['payment_processor_id']); + } + // Get the label for the contribution status + if (!empty($recurDetail['contribution_status_id'])) { + $recurContributions[$recurId]['contribution_status'] = CRM_Core_PseudoConstant::getLabel('CRM_Contribute_BAO_ContributionRecur', 'contribution_status_id', $recurDetail['contribution_status_id']); + } + + $recurContributions[$recurId]['action'] = CRM_Core_Action::formLink(self::recurLinks($recurId), $actionMask, + [ + 'cid' => $this->_contactId, + 'crid' => $recurId, + 'cxt' => 'contribution', + ], + ts('more'), + FALSE, + 'contribution.selector.recurring', + 'Contribution', + $recurId + ); + } + + return [$recurContributions, $liveRecurringContributionCount]; + } + /** * called when action is view. * - * @return null + * @return mixed */ public function view() { $controller = new CRM_Core_Controller_Simple( @@ -217,11 +304,13 @@ public function view() { /** * called when action is update or new. * - * @return null + * @return mixed + * @throws \CRM_Core_Exception + * @throws \Exception */ public function edit() { // set https for offline cc transaction - $mode = CRM_Utils_Request::retrieve('mode', 'String', $this); + $mode = CRM_Utils_Request::retrieve('mode', 'Alphanumeric', $this); if ($mode == 'test' || $mode == 'live') { CRM_Utils_System::redirectToSSL(); } @@ -239,7 +328,7 @@ public function edit() { } public function preProcess() { - $context = CRM_Utils_Request::retrieve('context', 'String', $this); + $context = CRM_Utils_Request::retrieve('context', 'Alphanumeric', $this); $this->_action = CRM_Utils_Request::retrieve('action', 'String', $this, FALSE, 'browse'); $this->_id = CRM_Utils_Request::retrieve('id', 'Positive', $this); @@ -249,10 +338,10 @@ public function preProcess() { else { $this->_contactId = CRM_Utils_Request::retrieve('cid', 'Positive', $this, empty($this->_id)); if (empty($this->_contactId)) { - $this->_contactId = civicrm_api3('contribution', 'getvalue', array( - 'id' => $this->_id, - 'return' => 'contact_id', - )); + $this->_contactId = civicrm_api3('contribution', 'getvalue', [ + 'id' => $this->_id, + 'return' => 'contact_id', + ]); } $this->assign('contactId', $this->_contactId); @@ -297,7 +386,7 @@ public function run() { public function setContext() { $qfKey = CRM_Utils_Request::retrieve('key', 'String', $this); - $context = CRM_Utils_Request::retrieve('context', 'String', + $context = CRM_Utils_Request::retrieve('context', 'Alphanumeric', $this, FALSE, 'search' ); $compContext = CRM_Utils_Request::retrieve('compContext', 'String', $this); @@ -318,8 +407,6 @@ public function setContext() { $qfKey = NULL; } - $session = CRM_Core_Session::singleton(); - switch ($context) { case 'user': $url = CRM_Utils_System::url('civicrm/user', 'reset=1'); diff --git a/CRM/Contribute/Page/UserDashboard.php b/CRM/Contribute/Page/UserDashboard.php index bf4fb7387ee2..32c231782f0c 100644 --- a/CRM/Contribute/Page/UserDashboard.php +++ b/CRM/Contribute/Page/UserDashboard.php @@ -1,9 +1,9 @@ setEmbedded(TRUE); - $controller->reset(); - $controller->set('limit', 12); - $controller->set('cid', $this->_contactId); - $controller->set('context', 'user'); - $controller->set('force', 1); - $controller->process(); - $controller->run(); + $rows = civicrm_api3('Contribution', 'get', [ + 'options' => [ + 'limit' => 12, + 'sort' => 'receive_date DESC', + ], + 'sequential' => 1, + 'contact_id' => $this->_contactId, + 'return' => [ + 'total_amount', + 'contribution_recur_id', + 'financial_type', + 'receive_date', + 'receipt_date', + 'contribution_status', + 'currency', + 'amount_level', + 'contact_id,', + 'contribution_source', + ], + ])['values']; + + // We want oldest first, just among the most recent contributions + $rows = array_reverse($rows); + + foreach ($rows as $index => &$row) { + // This is required for tpl logic. We should move away from hard-code this to adding an array of actions to the row + // which the tpl can iterate through - this should allow us to cope with competing attempts to add new buttons + // and allow extensions to assign new ones through the pageRun hook + if ('Pending' === CRM_Core_PseudoConstant::getName('CRM_Contribute_BAO_Contribution', 'contribution_status_id', $row['contribution_status_id'])) { + $row['buttons']['pay'] = [ + 'class' => 'button', + 'label' => ts('Pay Now'), + 'url' => CRM_Utils_System::url('civicrm/contribute/transact', [ + 'reset' => 1, + 'id' => CRM_Invoicing_Utils::getDefaultPaymentPage(), + 'ccid' => $row['contribution_id'], + 'cs' => $this->getUserChecksum(), + 'cid' => $row['contact_id'], + ]) + ]; + } + } + + $this->assign('contribute_rows', $rows); + $this->assign('contributionSummary', ['total_amount' => civicrm_api3('Contribution', 'getcount', ['contact_id' => $this->_contactId])]); //add honor block $params = CRM_Contribute_BAO_Contribution::getHonorContacts($this->_contactId); @@ -65,18 +96,14 @@ public function listContribution() { $recur->is_test = 0; $recur->find(); - $config = CRM_Core_Config::singleton(); - $recurStatus = CRM_Contribute_PseudoConstant::contributionStatus(); - $recurRow = array(); - $recurIDs = array(); + $recurRow = []; + $recurIDs = []; while ($recur->fetch()) { - $mode = $recur->is_test ? 'test' : 'live'; - $paymentProcessor = CRM_Contribute_BAO_ContributionRecur::getPaymentProcessor($recur->id, - $mode - ); - if (!$paymentProcessor) { + if (empty($recur->payment_processor_id)) { + // it's not clear why we continue here as any without a processor id would likely + // be imported from another system & still seem valid. continue; } @@ -97,11 +124,11 @@ public function listContribution() { } $recurRow[$values['id']]['action'] = CRM_Core_Action::formLink(CRM_Contribute_Page_Tab::recurLinks($recur->id, 'dashboard'), - $action, array( + $action, [ 'cid' => $this->_contactId, 'crid' => $values['id'], 'cxt' => 'contribution', - ), + ], ts('more'), FALSE, 'contribution.dashboard.recurring', @@ -110,10 +137,6 @@ public function listContribution() { ); $recurIDs[] = $values['id']; - - //reset $paymentObject for checking other paymenet processor - //recurring url - $paymentObject = NULL; } if (is_array($recurIDs) && !empty($recurIDs)) { $getCount = CRM_Contribute_BAO_ContributionRecur::getCount($recurIDs); @@ -134,16 +157,28 @@ public function listContribution() { } } + /** + * Should invoice links be displayed on the template. + * + * @todo This should be moved to a hook-like structure on the invoicing class + * (currently CRM_Utils_Invoicing) with a view to possible removal from core. + */ + public function isIncludeInvoiceLinks() { + if (!CRM_Invoicing_Utils::isInvoicingEnabled()) { + return FALSE; + } + $dashboardOptions = CRM_Core_BAO_Setting::valueOptions(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, + 'user_dashboard_options' + ); + return $dashboardOptions['Invoices / Credit Notes']; + } + /** * the main function that is called when the page * loads, it decides the which action has to be taken for the page. */ public function run() { - $invoiceSettings = Civi::settings()->get('contribution_invoice_settings'); - $invoicing = CRM_Utils_Array::value('invoicing', $invoiceSettings); - $defaultInvoicePage = CRM_Utils_Array::value('default_invoice_page', $invoiceSettings); - $this->assign('invoicing', $invoicing); - $this->assign('defaultInvoicePage', $defaultInvoicePage); + $this->assign('isIncludeInvoiceLinks', $this->isIncludeInvoiceLinks()); parent::preProcess(); $this->listContribution(); } diff --git a/CRM/Contribute/PseudoConstant.php b/CRM/Contribute/PseudoConstant.php index f5f79750b506..37433be2e309 100644 --- a/CRM/Contribute/PseudoConstant.php +++ b/CRM/Contribute/PseudoConstant.php @@ -1,9 +1,9 @@ is_active = 1; $dao->orderBy('id'); @@ -277,7 +276,7 @@ public static function products($pageID = NULL) { $dao->find(TRUE); $premiumID = $dao->id; - $productID = array(); + $productID = []; $dao = new CRM_Contribute_DAO_PremiumsProduct(); $dao->premiums_id = $premiumID; @@ -286,7 +285,7 @@ public static function products($pageID = NULL) { $productID[$dao->product_id] = $dao->product_id; } - $tempProduct = array(); + $tempProduct = []; foreach ($products as $key => $value) { if (!array_key_exists($key, $productID)) { $tempProduct[$key] = $value; @@ -366,10 +365,10 @@ public static function &pcPage($pageType = NULL, $id = NULL) { */ public static function &pcpStatus($column = 'label') { if (NULL === self::$pcpStatus) { - self::$pcpStatus = array(); + self::$pcpStatus = []; } if (!array_key_exists($column, self::$pcpStatus)) { - self::$pcpStatus[$column] = array(); + self::$pcpStatus[$column] = []; self::$pcpStatus[$column] = CRM_Core_OptionGroup::values('pcp_status', FALSE, FALSE, FALSE, NULL, $column @@ -379,36 +378,31 @@ public static function &pcpStatus($column = 'label') { } /** - * Get all financial accounts for a Financial type. + * Get financial account for a Financial type. * - * The static array $financialTypeAccount is returned + * @deprecated use the alternative with caching + * CRM_Financial_BAO_FinancialAccount::getFinancialAccountForFinancialTypeByRelationship * - * - * @param int $financialTypeId - * @param int $relationTypeId - * @return array - * array reference of all financial accounts for a Financial type + * @param int $entityId + * @param string $accountRelationType + * @param string $entityTable + * @param string $returnField + * @return int */ - public static function financialAccountType($financialTypeId, $relationTypeId = NULL) { - if (!CRM_Utils_Array::value($financialTypeId, self::$financialTypeAccount)) { - $condition = " entity_id = $financialTypeId "; - CRM_Core_PseudoConstant::populate( - self::$financialTypeAccount[$financialTypeId], - 'CRM_Financial_DAO_EntityFinancialAccount', - $all = TRUE, - $retrieve = 'financial_account_id', - $filter = NULL, - $condition, - NULL, - 'account_relationship' - ); + public static function getRelationalFinancialAccount($entityId, $accountRelationType, $entityTable = 'civicrm_financial_type', $returnField = 'financial_account_id') { + $params = [ + 'return' => [$returnField], + 'entity_table' => $entityTable, + 'entity_id' => $entityId, + ]; + if ($accountRelationType) { + $params['account_relationship.name'] = $accountRelationType; } - - if ($relationTypeId) { - return CRM_Utils_Array::value($relationTypeId, self::$financialTypeAccount[$financialTypeId]); + $result = civicrm_api3('EntityFinancialAccount', 'get', $params); + if (!$result['count']) { + return NULL; } - - return self::$financialTypeAccount[$financialTypeId]; + return $result['values'][$result['id']][$returnField]; } /** diff --git a/CRM/Contribute/Selector/Search.php b/CRM/Contribute/Selector/Search.php index c799ad09d835..f2250641c625 100644 --- a/CRM/Contribute/Selector/Search.php +++ b/CRM/Contribute/Selector/Search.php @@ -1,9 +1,9 @@ _action = $action; - + $returnProperties = CRM_Contribute_BAO_Query::selectorReturnProperties($this->_queryParams); $this->_includeSoftCredits = CRM_Contribute_BAO_Query::isSoftCreditOptionEnabled($this->_queryParams); + $this->_queryParams[] = ['contribution_id', '!=', 0, 0, 0]; $this->_query = new CRM_Contact_BAO_Query( $this->_queryParams, - CRM_Contribute_BAO_Query::selectorReturnProperties(), + $returnProperties, NULL, FALSE, FALSE, CRM_Contact_BAO_Query::MODE_CONTRIBUTE ); @@ -230,26 +227,26 @@ public static function &links($componentId = NULL, $componentAction = NULL, $key } if (!(self::$_links)) { - self::$_links = array( - CRM_Core_Action::VIEW => array( + self::$_links = [ + CRM_Core_Action::VIEW => [ 'name' => ts('View'), 'url' => 'civicrm/contact/view/contribution', 'qs' => "reset=1&id=%%id%%&cid=%%cid%%&action=view&context=%%cxt%%&selectedChild=contribute{$extraParams}", 'title' => ts('View Contribution'), - ), - CRM_Core_Action::UPDATE => array( + ], + CRM_Core_Action::UPDATE => [ 'name' => ts('Edit'), 'url' => 'civicrm/contact/view/contribution', 'qs' => "reset=1&action=update&id=%%id%%&cid=%%cid%%&context=%%cxt%%{$extraParams}", 'title' => ts('Edit Contribution'), - ), - CRM_Core_Action::DELETE => array( + ], + CRM_Core_Action::DELETE => [ 'name' => ts('Delete'), 'url' => 'civicrm/contact/view/contribution', 'qs' => "reset=1&action=delete&id=%%id%%&cid=%%cid%%&context=%%cxt%%{$extraParams}", 'title' => ts('Delete Contribution'), - ), - ); + ], + ]; } return self::$_links; } @@ -320,10 +317,10 @@ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) { $this->_contributionClause ); // process the result of the query - $rows = array(); + $rows = []; //CRM-4418 check for view/edit/delete - $permissions = array(CRM_Core_Permission::VIEW); + $permissions = [CRM_Core_Permission::VIEW]; if (CRM_Core_Permission::check('edit contributions')) { $permissions[] = CRM_Core_Permission::EDIT; } @@ -336,10 +333,10 @@ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) { $componentId = $componentContext = NULL; if ($this->_context != 'contribute') { // @todo explain the significance of context & why we do not get these i that context. - $qfKey = CRM_Utils_Request::retrieve('key', 'String', CRM_Core_DAO::$_nullObject); - $componentId = CRM_Utils_Request::retrieve('id', 'Positive', CRM_Core_DAO::$_nullObject); - $componentAction = CRM_Utils_Request::retrieve('action', 'String', CRM_Core_DAO::$_nullObject); - $componentContext = CRM_Utils_Request::retrieve('compContext', 'String', CRM_Core_DAO::$_nullObject); + $qfKey = CRM_Utils_Request::retrieve('key', 'String'); + $componentId = CRM_Utils_Request::retrieve('id', 'Positive'); + $componentAction = CRM_Utils_Request::retrieve('action', 'String'); + $componentContext = CRM_Utils_Request::retrieve('compContext', 'String'); if (!$componentContext && $this->_compContext @@ -366,13 +363,14 @@ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) { $allCampaigns = CRM_Campaign_BAO_Campaign::getCampaigns(NULL, NULL, FALSE, FALSE, FALSE, TRUE); while ($result->fetch()) { + $this->_query->convertToPseudoNames($result); $links = self::links($componentId, $componentAction, $qfKey, $componentContext ); $checkLineItem = FALSE; - $row = array(); + $row = []; // Now check for lineItems if (CRM_Financial_BAO_FinancialType::isACLFinancialTypeStatus()) { $lineItems = CRM_Price_BAO_LineItem::getLineItemsByContributionID($result->id); @@ -416,14 +414,16 @@ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) { $contributionStatuses ); + $isPayLater = FALSE; if ($result->is_pay_later && CRM_Utils_Array::value('contribution_status_name', $row) == 'Pending') { + $isPayLater = TRUE; $row['contribution_status'] .= ' (' . ts('Pay Later') . ')'; - $links[CRM_Core_Action::ADD] = array( + $links[CRM_Core_Action::ADD] = [ 'name' => ts('Pay with Credit Card'), 'url' => 'civicrm/contact/view/contribution', 'qs' => 'reset=1&action=update&id=%%id%%&cid=%%cid%%&context=%%cxt%%&mode=live', 'title' => ts('Pay with Credit Card'), - ); + ]; } elseif (CRM_Utils_Array::value('contribution_status_name', $row) == 'Pending') { $row['contribution_status'] .= ' (' . ts('Incomplete Transaction') . ')'; @@ -435,11 +435,32 @@ public function &getRows($action, $offset, $rowCount, $sort, $output = NULL) { $row['checkbox'] = CRM_Core_Form::CB_PREFIX . $result->contribution_id; - $actions = array( + $actions = [ 'id' => $result->contribution_id, 'cid' => $result->contact_id, 'cxt' => $this->_context, - ); + ]; + + if (in_array($row['contribution_status_name'], ['Partially paid', 'Pending refund']) || $isPayLater) { + $buttonName = ts('Record Payment'); + if ($row['contribution_status_name'] == 'Pending refund') { + $buttonName = ts('Record Refund'); + } + elseif (CRM_Core_Config::isEnabledBackOfficeCreditCardPayments()) { + $links[CRM_Core_Action::BASIC] = [ + 'name' => ts('Submit Credit Card payment'), + 'url' => 'civicrm/payment/add', + 'qs' => 'reset=1&id=%%id%%&cid=%%cid%%&action=add&component=contribution&mode=live', + 'title' => ts('Submit Credit Card payment'), + ]; + } + $links[CRM_Core_Action::ADD] = [ + 'name' => $buttonName, + 'url' => 'civicrm/payment', + 'qs' => 'reset=1&id=%%id%%&cid=%%cid%%&action=add&component=contribution', + 'title' => $buttonName, + ]; + } $row['action'] = CRM_Core_Action::formLink( $links, @@ -484,96 +505,113 @@ public function getQILL() { * the column headers that need to be displayed */ public function &getColumnHeaders($action = NULL, $output = NULL) { - $pre = array(); - self::$_columnHeaders = array( - array( + $pre = []; + self::$_columnHeaders = [ + [ 'name' => $this->_includeSoftCredits ? ts('Contribution Amount') : ts('Amount'), 'sort' => 'total_amount', 'direction' => CRM_Utils_Sort::DONTCARE, - ), - ); + 'field_name' => 'total_amount', + ], + ]; if ($this->_includeSoftCredits) { self::$_columnHeaders = array_merge( self::$_columnHeaders, - array( - array( + [ + [ 'name' => ts('Soft Credit Amount'), 'sort' => 'contribution_soft_credit_amount', + 'field_name' => 'contribution_soft_credit_amount', 'direction' => CRM_Utils_Sort::DONTCARE, - ), - ) + ], + ] ); } self::$_columnHeaders = array_merge( self::$_columnHeaders, - array( - array( + [ + [ 'name' => ts('Type'), 'sort' => 'financial_type', + 'field_name' => 'financial_type', 'direction' => CRM_Utils_Sort::DONTCARE, - ), - array( + ], + [ 'name' => ts('Source'), 'sort' => 'contribution_source', + 'field_name' => 'contribution_source', 'direction' => CRM_Utils_Sort::DONTCARE, - ), - array( + ], + [ 'name' => ts('Received'), 'sort' => 'receive_date', + 'field_name' => 'receive_date', + 'type' => 'date', 'direction' => CRM_Utils_Sort::DESCENDING, - ), - array( + ], + [ 'name' => ts('Thank-you Sent'), 'sort' => 'thankyou_date', + 'field_name' => 'thankyou_date', + 'type' => 'date', 'direction' => CRM_Utils_Sort::DONTCARE, - ), - array( + ], + [ 'name' => ts('Status'), 'sort' => 'contribution_status', + 'field_name' => 'contribution_status', 'direction' => CRM_Utils_Sort::DONTCARE, - ), - array( - 'name' => ts('Premium'), - 'sort' => 'product_name', - 'direction' => CRM_Utils_Sort::DONTCARE, - ), - ) + ], + ] ); + if (CRM_Contribute_BAO_Query::isSiteHasProducts()) { + self::$_columnHeaders[] = [ + 'name' => ts('Premium'), + 'sort' => 'product_name', + 'field_name' => 'product_name', + 'direction' => CRM_Utils_Sort::DONTCARE, + ]; + } if (!$this->_single) { - $pre = array( - array( + $pre = [ + [ 'name' => ts('Name'), 'sort' => 'sort_name', 'direction' => CRM_Utils_Sort::DONTCARE, - ), - ); + ], + ]; } self::$_columnHeaders = array_merge($pre, self::$_columnHeaders); if ($this->_includeSoftCredits) { self::$_columnHeaders = array_merge( self::$_columnHeaders, - array( - array( + [ + [ 'name' => ts('Soft Credit For'), 'sort' => 'contribution_soft_credit_name', 'direction' => CRM_Utils_Sort::DONTCARE, - ), - array( + ], + [ 'name' => ts('Soft Credit Type'), 'sort' => 'contribution_soft_credit_type', 'direction' => CRM_Utils_Sort::ASCENDING, - ), - ) + ], + ] ); } self::$_columnHeaders = array_merge( - self::$_columnHeaders, array( - array('desc' => ts('Actions')), - ) + self::$_columnHeaders, [ + ['desc' => ts('Actions'), 'type' => 'actions'], + ] ); + foreach (array_keys(self::$_columnHeaders) as $index) { + // Add weight & space it out a bit to allow headers to be inserted. + self::$_columnHeaders[$index]['weight'] = $index * 10; + } + CRM_Core_Smarty::singleton()->assign('softCreditColumns', $this->_includeSoftCredits); return self::$_columnHeaders; } @@ -582,7 +620,7 @@ public function &getColumnHeaders($action = NULL, $output = NULL) { * @return mixed */ public function alphabetQuery() { - return $this->_query->searchQuery(NULL, NULL, NULL, FALSE, FALSE, TRUE); + return $this->_query->alphabetQuery(); } /** diff --git a/CRM/Contribute/StateMachine/Contribution.php b/CRM/Contribute/StateMachine/Contribution.php index b363c25d05c7..f7c598805c53 100644 --- a/CRM/Contribute/StateMachine/Contribution.php +++ b/CRM/Contribute/StateMachine/Contribution.php @@ -1,9 +1,9 @@ _pages = array( + $this->_pages = [ 'CRM_Contribute_Form_Contribution_Main' => NULL, 'CRM_Contribute_Form_Contribution_Confirm' => NULL, 'CRM_Contribute_Form_Contribution_ThankYou' => NULL, - ); + ]; $this->addSequentialPages($this->_pages, $action); } diff --git a/CRM/Contribute/StateMachine/ContributionPage.php b/CRM/Contribute/StateMachine/ContributionPage.php index 25a0b889e9cc..47ccc247b88b 100644 --- a/CRM/Contribute/StateMachine/ContributionPage.php +++ b/CRM/Contribute/StateMachine/ContributionPage.php @@ -1,9 +1,9 @@ _pages = array( + $this->_pages = [ 'CRM_Contribute_Form_ContributionPage_Settings' => NULL, 'CRM_Contribute_Form_ContributionPage_Amount' => NULL, 'CRM_Member_Form_MembershipBlock' => NULL, @@ -62,7 +62,7 @@ public function __construct($controller, $action = CRM_Core_Action::NONE) { 'CRM_Contribute_Form_ContributionPage_Custom' => NULL, 'CRM_Contribute_Form_ContributionPage_Premium' => NULL, 'CRM_Contribute_Form_ContributionPage_Widget' => NULL, - ); + ]; if (!in_array("CiviMember", $config->enableComponents)) { unset($this->_pages['CRM_Member_Form_MembershipBlock']); diff --git a/CRM/Contribute/StateMachine/Search.php b/CRM/Contribute/StateMachine/Search.php index d0951b69b1de..62c0d6c07885 100644 --- a/CRM/Contribute/StateMachine/Search.php +++ b/CRM/Contribute/StateMachine/Search.php @@ -1,9 +1,9 @@ _pages = array(); + $this->_pages = []; $this->_pages['CRM_Contribute_Form_Search'] = NULL; list($task, $result) = $this->taskName($controller, 'Search'); @@ -78,7 +78,8 @@ public function __construct($controller, $action = CRM_Core_Action::NONE) { * * @param string $formName * - * @return string + * @return array + * [ 'class' => task classname, 'result' => TRUE ] * the name of the form that will handle the task */ public function taskName($controller, $formName = 'Search') { diff --git a/CRM/Contribute/Task.php b/CRM/Contribute/Task.php index 25edbc0c9a99..c7679aab1b5b 100644 --- a/CRM/Contribute/Task.php +++ b/CRM/Contribute/Task.php @@ -1,9 +1,9 @@ array( + self::$_tasks = [ + self::TASK_DELETE => [ 'title' => ts('Delete contributions'), 'class' => 'CRM_Contribute_Form_Task_Delete', 'result' => FALSE, - ), - 2 => array( + ], + self::TASK_PRINT => [ 'title' => ts('Print selected rows'), 'class' => 'CRM_Contribute_Form_Task_Print', 'result' => FALSE, - ), - 3 => array( + ], + self::TASK_EXPORT => [ 'title' => ts('Export contributions'), - 'class' => array( + 'class' => [ 'CRM_Export_Form_Select', 'CRM_Export_Form_Map', - ), + ], 'result' => FALSE, - ), - 4 => array( + ], + self::BATCH_UPDATE => [ 'title' => ts('Update multiple contributions'), - 'class' => array( + 'class' => [ 'CRM_Contribute_Form_Task_PickProfile', 'CRM_Contribute_Form_Task_Batch', - ), + ], 'result' => TRUE, - ), - 5 => array( - 'title' => ts('Email - send now'), + ], + self::TASK_EMAIL => [ + 'title' => ts('Email - send now (to %1 or less)', [ + 1 => Civi::settings() + ->get('simple_mail_limit'), + ]), 'class' => 'CRM_Contribute_Form_Task_Email', 'result' => TRUE, - ), - 6 => array( + ], + self::UPDATE_STATUS => [ 'title' => ts('Update pending contribution status'), 'class' => 'CRM_Contribute_Form_Task_Status', 'result' => TRUE, - ), - 7 => array( + ], + self::PDF_RECEIPT => [ 'title' => ts('Receipts - print or email'), 'class' => 'CRM_Contribute_Form_Task_PDF', 'result' => FALSE, - ), - 8 => array( + ], + self::PDF_THANKYOU => [ 'title' => ts('Thank-you letters - print or email'), 'class' => 'CRM_Contribute_Form_Task_PDFLetter', 'result' => FALSE, - ), - 9 => array( + ], + self::PDF_INVOICE => [ 'title' => ts('Invoices - print or email'), 'class' => 'CRM_Contribute_Form_Task_Invoice', 'result' => FALSE, - ), - ); + ], + ]; //CRM-4418, check for delete if (!CRM_Core_Permission::check('delete in CiviContribute')) { - unset(self::$_tasks[1]); + unset(self::$_tasks[self::TASK_DELETE]); } //CRM-12920 - check for edit permission if (!CRM_Core_Permission::check('edit contributions')) { - unset(self::$_tasks[4], self::$_tasks[6]); + unset(self::$_tasks[self::BATCH_UPDATE], self::$_tasks[self::UPDATE_STATUS]); } // remove action "Invoices - print or email" $invoiceSettings = Civi::settings()->get('contribution_invoice_settings'); $invoicing = CRM_Utils_Array::value('invoicing', $invoiceSettings); if (!$invoicing) { - unset(self::$_tasks[9]); + unset(self::$_tasks[self::PDF_INVOICE]); } - CRM_Utils_Hook::searchTasks('contribution', self::$_tasks); - asort(self::$_tasks); + + parent::tasks(); } return self::$_tasks; } - /** - * These tasks are the core set of task titles - * on contributors - * - * @return array - * the set of task titles - */ - public static function &taskTitles() { - self::tasks(); - $titles = array(); - foreach (self::$_tasks as $id => $value) { - $titles[$id] = $value['title']; - } - return $titles; - } - /** * Show tasks selectively based on the permission level * of the user * * @param int $permission * - * @param bool $softCreditFiltering + * @param array $params + * bool softCreditFiltering: derived from CRM_Contribute_BAO_Query::isSoftCreditOptionEnabled * * @return array * set of tasks that are valid for the user */ - public static function &permissionedTaskTitles($permission, $softCreditFiltering = FALSE) { - $tasks = array(); + public static function permissionedTaskTitles($permission, $params = []) { + if (!isset($params['softCreditFiltering'])) { + $params['softCreditFiltering'] = FALSE; + } if (($permission == CRM_Core_Permission::EDIT) || CRM_Core_Permission::check('edit contributions') ) { $tasks = self::taskTitles(); } else { - $tasks = array( - 3 => self::$_tasks[3]['title'], - 5 => self::$_tasks[5]['title'], - 7 => self::$_tasks[7]['title'], - ); + $tasks = [ + self::TASK_EXPORT => self::$_tasks[self::TASK_EXPORT]['title'], + self::TASK_EMAIL => self::$_tasks[self::TASK_EMAIL]['title'], + self::PDF_RECEIPT => self::$_tasks[self::PDF_RECEIPT]['title'], + ]; //CRM-4418, if (CRM_Core_Permission::check('delete in CiviContribute')) { - $tasks[1] = self::$_tasks[1]['title']; + $tasks[self::TASK_DELETE] = self::$_tasks[self::TASK_DELETE]['title']; } } - if ($softCreditFiltering) { - unset($tasks[4], $tasks[7]); + if ($params['softCreditFiltering']) { + unset($tasks[self::BATCH_UPDATE], $tasks[self::PDF_RECEIPT]); } + + $tasks = parent::corePermissionedTaskTitles($tasks, $permission, $params); return $tasks; } @@ -203,17 +194,9 @@ public static function getTask($value) { self::tasks(); if (!$value || !CRM_Utils_Array::value($value, self::$_tasks)) { // make the print task by default - $value = 2; - } - // this is possible since hooks can inject a task - // CRM-13697 - if (!isset(self::$_tasks[$value]['result'])) { - self::$_tasks[$value]['result'] = NULL; + $value = self::TASK_PRINT; } - return array( - self::$_tasks[$value]['class'], - self::$_tasks[$value]['result'], - ); + return parent::getTask($value); } } diff --git a/CRM/Contribute/Tokens.php b/CRM/Contribute/Tokens.php index d199af8a3700..25d247c51f59 100644 --- a/CRM/Contribute/Tokens.php +++ b/CRM/Contribute/Tokens.php @@ -2,9 +2,9 @@ /* +--------------------------------------------------------------------+ - | CiviCRM version 4.7 | + | CiviCRM version 5 | +--------------------------------------------------------------------+ - | Copyright CiviCRM LLC (c) 2004-2016 | + | Copyright CiviCRM LLC (c) 2004-2019 | +--------------------------------------------------------------------+ | This file is a part of CiviCRM. | | | @@ -41,7 +41,7 @@ class CRM_Contribute_Tokens extends \Civi\Token\AbstractTokenSubscriber { * @return array */ protected function getPassthruTokens() { - return array( + return [ 'contribution_page_id', 'receive_date', 'total_amount', @@ -54,7 +54,7 @@ protected function getPassthruTokens() { 'receipt_date', 'thankyou_date', 'tax_amount', - ); + ]; } /** @@ -63,13 +63,13 @@ protected function getPassthruTokens() { * @return array */ protected function getAliasTokens() { - return array( + return [ 'id' => 'contribution_id', 'payment_instrument' => 'payment_instrument_id', 'source' => 'contribution_source', 'status' => 'contribution_status_id', 'type' => 'financial_type_id', - ); + ]; } /** @@ -85,15 +85,27 @@ public function __construct() { $tokens['source'] = ts('Contribution Source'); $tokens['status'] = ts('Contribution Status'); $tokens['type'] = ts('Financial Type'); + $tokens = array_merge($tokens, CRM_Utils_Token::getCustomFieldTokens('Contribution')); parent::__construct('contribution', $tokens); } + /** + * Check if the token processor is active. + * + * @param \Civi\Token\TokenProcessor $processor + * + * @return bool + */ public function checkActive(\Civi\Token\TokenProcessor $processor) { - return - !empty($processor->context['actionMapping']) + return !empty($processor->context['actionMapping']) && $processor->context['actionMapping']->getEntity() === 'civicrm_contribution'; } + /** + * Alter action schedule query. + * + * @param \Civi\ActionSchedule\Event\MailingQueryEvent $e + */ public function alterActionScheduleQuery(\Civi\ActionSchedule\Event\MailingQueryEvent $e) { if ($e->mapping->getEntity() !== 'civicrm_contribution') { return; @@ -109,31 +121,23 @@ public function alterActionScheduleQuery(\Civi\ActionSchedule\Event\MailingQuery } /** - * Evaluate the content of a single token. - * - * @param \Civi\Token\TokenRow $row - * The record for which we want token values. - * @param string $entity - * @param string $field - * The name of the token field. - * @param mixed $prefetch - * Any data that was returned by the prefetch(). - * - * @return mixed - * @throws \CRM_Core_Exception + * @inheritDoc */ public function evaluateToken(\Civi\Token\TokenRow $row, $entity, $field, $prefetch = NULL) { $actionSearchResult = $row->context['actionSearchResult']; $fieldValue = isset($actionSearchResult->{"contrib_$field"}) ? $actionSearchResult->{"contrib_$field"} : NULL; $aliasTokens = $this->getAliasTokens(); - if (in_array($field, array('total_amount', 'fee_amount', 'net_amount'))) { + if (in_array($field, ['total_amount', 'fee_amount', 'net_amount'])) { return $row->format('text/plain')->tokens($entity, $field, \CRM_Utils_Money::format($fieldValue, $actionSearchResult->contrib_currency)); } elseif (isset($aliasTokens[$field])) { $row->dbToken($entity, $field, 'CRM_Contribute_BAO_Contribution', $aliasTokens[$field], $fieldValue); } + elseif ($cfID = \CRM_Core_BAO_CustomField::getKeyID($field)) { + $row->customToken($entity, $cfID, $actionSearchResult->entity_id); + } else { $row->dbToken($entity, $field, 'CRM_Contribute_BAO_Contribution', $field, $fieldValue); } diff --git a/CRM/Contribute/xml/Menu/Contribute.xml b/CRM/Contribute/xml/Menu/Contribute.xml index c303bcf26f28..548902dbc07c 100644 --- a/CRM/Contribute/xml/Menu/Contribute.xml +++ b/CRM/Contribute/xml/Menu/Contribute.xml @@ -318,18 +318,21 @@ 630 CiviContribute - - civicrm/admin/contribute/closeaccperiod - Close Accounting Period - CRM_Contribute_Form_CloseAccPeriod - access CiviContribute,administer CiviCRM,administer Accounting - 1 - 640 - CiviContribute - civicrm/ajax/softcontributionlist CRM_Contribute_Page_AJAX::getSoftContributionRows access CiviCRM + + civicrm/contribute/contributionrecur-payments + Recurring Contribution's Payments + CRM_Contribute_Page_ContributionRecurPayments + access CiviContribute + + + civicrm/membership/recurring-contributions + Membership Recurring Contributions + CRM_Member_Page_RecurringContributions + access CiviContribute + diff --git a/CRM/Core/Action.php b/CRM/Core/Action.php index 3ef5dc89bf8c..638976ea3283 100644 --- a/CRM/Core/Action.php +++ b/CRM/Core/Action.php @@ -1,9 +1,9 @@ self::ADD, 'update' => self::UPDATE, 'view' => self::VIEW, @@ -95,14 +95,14 @@ class CRM_Core_Action { 'revert' => self::REVERT, 'close' => self::CLOSE, 'reopen' => self::REOPEN, - ); + ]; /** * The flipped version of the names array, initialized when used * * @var array */ - static $_description; + public static $_description; /** * Called by the request object to translate a string into a mask. @@ -216,7 +216,7 @@ public static function formLink( // make links indexed sequentially instead of by bitmask // otherwise it's next to impossible to reliably add new ones - $seqLinks = array(); + $seqLinks = []; foreach ($links as $bit => $link) { $link['bit'] = $bit; $seqLinks[] = $link; @@ -226,7 +226,7 @@ public static function formLink( CRM_Utils_Hook::links($op, $objectName, $objectId, $seqLinks, $mask, $values); } - $url = array(); + $url = []; foreach ($seqLinks as $i => $link) { if (!$mask || !array_key_exists('bit', $link) || ($mask & $link['bit'])) { @@ -236,7 +236,7 @@ public static function formLink( if (isset($link['qs']) && !CRM_Utils_System::isNull($link['qs'])) { $urlPath = CRM_Utils_System::url(self::replace($link['url'], $values), - self::replace($link['qs'], $values), TRUE, NULL, TRUE, $frontend + self::replace($link['qs'], $values), FALSE, NULL, TRUE, $frontend ); } else { diff --git a/CRM/Core/BAO/ActionLog.php b/CRM/Core/BAO/ActionLog.php index 4033e482cf07..2d8532e1f9e6 100644 --- a/CRM/Core/BAO/ActionLog.php +++ b/CRM/Core/BAO/ActionLog.php @@ -1,9 +1,9 @@ $_action_mapping[$filters['id']], - ); + ]; } else { throw new CRM_Core_Exception("getMappings() called with unsupported filter: " . implode(', ', array_keys($filters))); @@ -84,11 +84,11 @@ public static function getMapping($id) { * @throws CRM_Core_Exception */ public static function getAllEntityValueLabels() { - $entityValueLabels = array(); + $entityValueLabels = []; foreach (CRM_Core_BAO_ActionSchedule::getMappings() as $mapping) { /** @var \Civi\ActionSchedule\Mapping $mapping */ $entityValueLabels[$mapping->getId()] = $mapping->getValueLabels(); - $valueLabel = array('- ' . strtolower($mapping->getValueHeader()) . ' -'); + $valueLabel = ['- ' . strtolower($mapping->getValueHeader()) . ' -']; $entityValueLabels[$mapping->getId()] = $valueLabel + $entityValueLabels[$mapping->getId()]; } return $entityValueLabels; @@ -102,10 +102,10 @@ public static function getAllEntityValueLabels() { */ public static function getAllEntityStatusLabels() { $entityValueLabels = self::getAllEntityValueLabels(); - $entityStatusLabels = array(); + $entityStatusLabels = []; foreach (CRM_Core_BAO_ActionSchedule::getMappings() as $mapping) { /** @var \Civi\ActionSchedule\Mapping $mapping */ - $statusLabel = array('- ' . strtolower($mapping->getStatusHeader()) . ' -'); + $statusLabel = ['- ' . strtolower($mapping->getStatusHeader()) . ' -']; $entityStatusLabels[$mapping->getId()] = $entityValueLabels[$mapping->getId()]; foreach ($entityStatusLabels[$mapping->getId()] as $kkey => & $vval) { $vval = $statusLabel + $mapping->getStatusLabels($kkey); @@ -146,21 +146,21 @@ public static function &getList($namesOnly = FALSE, $filterMapping = NULL, $filt FROM civicrm_action_schedule cas "; - $queryParams = array(); + $queryParams = []; $where = " WHERE 1 "; if ($filterMapping and $filterValue) { $where .= " AND cas.entity_value = %1 AND cas.mapping_id = %2"; - $queryParams[1] = array($filterValue, 'Integer'); - $queryParams[2] = array($filterMapping->getId(), 'String'); + $queryParams[1] = [$filterValue, 'Integer']; + $queryParams[2] = [$filterMapping->getId(), 'String']; } $where .= " AND cas.used_for IS NULL"; $query .= $where; $dao = CRM_Core_DAO::executeQuery($query, $queryParams); while ($dao->fetch()) { /** @var Civi\ActionSchedule\Mapping $filterMapping */ - $filterMapping = CRM_Utils_Array::first(self::getMappings(array( + $filterMapping = CRM_Utils_Array::first(self::getMappings([ 'id' => $dao->mapping_id, - ))); + ])); $list[$dao->id]['id'] = $dao->id; $list[$dao->id]['title'] = $dao->title; $list[$dao->id]['start_action_offset'] = $dao->start_action_offset; @@ -194,7 +194,7 @@ public static function &getList($namesOnly = FALSE, $filterMapping = NULL, $filt * * @return CRM_Core_DAO_ActionSchedule */ - public static function add(&$params, $ids = array()) { + public static function add(&$params, $ids = []) { $actionSchedule = new CRM_Core_DAO_ActionSchedule(); $actionSchedule->copyValues($params); @@ -259,8 +259,8 @@ public static function del($id) { * @param bool $is_active * Value we want to set the is_active field. * - * @return Object - * DAO object on success, null otherwise + * @return bool + * true if we found and updated the object, else false */ public static function setIsActive($id, $is_active) { return CRM_Core_DAO::setFieldValue('CRM_Core_DAO_ActionSchedule', $id, 'is_active', $is_active); @@ -273,9 +273,9 @@ public static function setIsActive($id, $is_active) { * @throws CRM_Core_Exception */ public static function sendMailings($mappingID, $now) { - $mapping = CRM_Utils_Array::first(self::getMappings(array( + $mapping = CRM_Utils_Array::first(self::getMappings([ 'id' => $mappingID, - ))); + ])); $actionSchedule = new CRM_Core_DAO_ActionSchedule(); $actionSchedule->mapping_id = $mappingID; @@ -285,7 +285,7 @@ public static function sendMailings($mappingID, $now) { while ($actionSchedule->fetch()) { $query = CRM_Core_BAO_ActionSchedule::prepareMailingQuery($mapping, $actionSchedule); $dao = CRM_Core_DAO::executeQuery($query, - array(1 => array($actionSchedule->id, 'Integer')) + [1 => [$actionSchedule->id, 'Integer']] ); $multilingual = CRM_Core_I18n::isMultilingual(); @@ -296,7 +296,7 @@ public static function sendMailings($mappingID, $now) { CRM_Core_BAO_ActionSchedule::setCommunicationLanguage($actionSchedule->communication_language, $preferred_language); } - $errors = array(); + $errors = []; try { $tokenProcessor = self::createTokenProcessor($actionSchedule, $mapping); $tokenProcessor->addRow() @@ -310,6 +310,11 @@ public static function sendMailings($mappingID, $now) { if ($actionSchedule->mode == 'Email' or $actionSchedule->mode == 'User_Preference') { CRM_Utils_Array::extend($errors, self::sendReminderEmail($tokenRow, $actionSchedule, $dao->contactID)); } + // insert activity log record if needed + if ($actionSchedule->record_activity && empty($errors)) { + $caseID = empty($dao->case_id) ? NULL : $dao->case_id; + CRM_Core_BAO_ActionSchedule::createMailingActivity($tokenRow, $mapping, $dao->contactID, $dao->entityID, $caseID); + } } } catch (\Civi\Token\TokenException $e) { @@ -317,19 +322,13 @@ public static function sendMailings($mappingID, $now) { } // update action log record - $logParams = array( + $logParams = [ 'id' => $dao->reminderID, 'is_error' => !empty($errors), 'message' => empty($errors) ? "null" : implode(' ', $errors), 'action_date_time' => $now, - ); + ]; CRM_Core_BAO_ActionLog::create($logParams); - - // insert activity log record if needed - if ($actionSchedule->record_activity && empty($errors)) { - $caseID = empty($dao->case_id) ? NULL : $dao->case_id; - CRM_Core_BAO_ActionSchedule::createMailingActivity($actionSchedule, $mapping, $dao->contactID, $dao->entityID, $caseID); - } } $dao->free(); @@ -343,7 +342,7 @@ public static function sendMailings($mappingID, $now) { * * @throws API_Exception */ - public static function buildRecipientContacts($mappingID, $now, $params = array()) { + public static function buildRecipientContacts($mappingID, $now, $params = []) { $actionSchedule = new CRM_Core_DAO_ActionSchedule(); $actionSchedule->mapping_id = $mappingID; $actionSchedule->is_active = 1; @@ -354,9 +353,9 @@ public static function buildRecipientContacts($mappingID, $now, $params = array( while ($actionSchedule->fetch()) { /** @var \Civi\ActionSchedule\Mapping $mapping */ - $mapping = CRM_Utils_Array::first(self::getMappings(array( + $mapping = CRM_Utils_Array::first(self::getMappings([ 'id' => $mappingID, - ))); + ])); $builder = new \Civi\ActionSchedule\RecipientBuilder($now, $actionSchedule, $mapping); $builder->build(); } @@ -368,7 +367,7 @@ public static function buildRecipientContacts($mappingID, $now, $params = array( * * @return array */ - public static function processQueue($now = NULL, $params = array()) { + public static function processQueue($now = NULL, $params = []) { $now = $now ? CRM_Utils_Time::setTime($now) : CRM_Utils_Time::getTime(); $mappings = CRM_Core_BAO_ActionSchedule::getMappings(); @@ -377,10 +376,10 @@ public static function processQueue($now = NULL, $params = array()) { CRM_Core_BAO_ActionSchedule::sendMailings($mappingID, $now); } - $result = array( + $result = [ 'is_error' => 0, 'messages' => ts('Sent all scheduled reminders successfully'), - ); + ]; return $result; } @@ -395,10 +394,10 @@ public static function isConfigured($id, $mappingID) { WHERE mapping_id = %1 AND entity_value = %2"; - $params = array( - 1 => array($mappingID, 'String'), - 2 => array($id, 'Integer'), - ); + $params = [ + 1 => [$mappingID, 'String'], + 2 => [$id, 'Integer'], + ]; return CRM_Core_DAO::singleValueQuery($queryString, $params); } @@ -410,13 +409,13 @@ public static function isConfigured($id, $mappingID) { */ public static function getRecipientListing($mappingID, $recipientType) { if (!$mappingID) { - return array(); + return []; } /** @var \Civi\ActionSchedule\Mapping $mapping */ - $mapping = CRM_Utils_Array::first(CRM_Core_BAO_ActionSchedule::getMappings(array( + $mapping = CRM_Utils_Array::first(CRM_Core_BAO_ActionSchedule::getMappings([ 'id' => $mappingID, - ))); + ])); return $mapping->getRecipientListing($recipientType); } @@ -425,8 +424,8 @@ public static function getRecipientListing($mappingID, $recipientType) { * @param $preferred_language */ public static function setCommunicationLanguage($communication_language, $preferred_language) { - $config = CRM_Core_Config::singleton(); - $language = $config->lcMessages; + $currentLocale = CRM_Core_I18n::getLocale(); + $language = $currentLocale; // prepare the language for the email if ($communication_language == CRM_Core_I18n::AUTO) { @@ -440,13 +439,13 @@ public static function setCommunicationLanguage($communication_language, $prefer // language not in the existing language, use default $languages = CRM_Core_I18n::languages(TRUE); - if (!in_array($language, $languages)) { - $language = $config->lcMessages; + if (!array_key_exists($language, $languages)) { + $language = $currentLocale; } // change the language $i18n = CRM_Core_I18n::singleton(); - $i18n->setLanguage($language); + $i18n->setLocale($language); } /** @@ -455,40 +454,45 @@ public static function setCommunicationLanguage($communication_language, $prefer * WISHLIST: Instead of saving $actionSchedule->body_html, call this immediately after * sending the message and pass in the fully rendered text of the message. * - * @param CRM_Core_DAO_ActionSchedule $actionSchedule + * @param object $tokenRow * @param Civi\ActionSchedule\Mapping $mapping * @param int $contactID * @param int $entityID * @param int|NULL $caseID * @throws CRM_Core_Exception */ - protected static function createMailingActivity($actionSchedule, $mapping, $contactID, $entityID, $caseID) { + protected static function createMailingActivity($tokenRow, $mapping, $contactID, $entityID, $caseID) { $session = CRM_Core_Session::singleton(); if ($mapping->getEntity() == 'civicrm_membership') { + // @todo - not required with api $activityTypeID - = CRM_Core_OptionGroup::getValue('activity_type', 'Membership Renewal Reminder', 'name'); + = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Membership Renewal Reminder'); } else { + // @todo - not required with api $activityTypeID - = CRM_Core_OptionGroup::getValue('activity_type', 'Reminder Sent', 'name'); + = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Reminder Sent'); } - $activityParams = array( - 'subject' => $actionSchedule->title, - 'details' => $actionSchedule->body_html, + $activityParams = [ + 'subject' => $tokenRow->render('subject'), + 'details' => $tokenRow->render('body_html'), 'source_contact_id' => $session->get('userID') ? $session->get('userID') : $contactID, 'target_contact_id' => $contactID, + // @todo - not required with api 'activity_date_time' => CRM_Utils_Time::getTime('YmdHis'), - 'status_id' => CRM_Core_OptionGroup::getValue('activity_status', 'Completed', 'name'), + // @todo - not required with api + 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'status_id', 'Completed'), 'activity_type_id' => $activityTypeID, 'source_record_id' => $entityID, - ); + ]; + // @todo use api, remove all the above wrangling $activity = CRM_Activity_BAO_Activity::create($activityParams); //file reminder on case if source activity is a case activity if (!empty($caseID)) { - $caseActivityParams = array(); + $caseActivityParams = []; $caseActivityParams['case_id'] = $caseID; $caseActivityParams['activity_id'] = $activity->id; CRM_Case_BAO_Case::processCaseActivity($caseActivityParams); @@ -507,13 +511,13 @@ protected static function prepareMailingQuery($mapping, $actionSchedule) { ->select("e.id as entityID, e.*") ->where("reminder.action_schedule_id = #casActionScheduleId") ->where("reminder.action_date_time IS NULL") - ->param(array( + ->param([ 'casActionScheduleId' => $actionSchedule->id, 'casMailingJoinType' => ($actionSchedule->limit_to == 0) ? 'LEFT JOIN' : 'INNER JOIN', 'casMappingId' => $mapping->getId(), 'casMappingEntity' => $mapping->getEntity(), 'casEntityJoinExpr' => 'e.id = reminder.entity_id', - )); + ]); if ($actionSchedule->limit_to == 0) { $select->where("e.id = reminder.entity_id OR reminder.entity_table = 'civicrm_contact'"); @@ -539,7 +543,7 @@ protected static function prepareMailingQuery($mapping, $actionSchedule) { protected static function sendReminderSms($tokenRow, $schedule, $toContactID) { $toPhoneNumber = self::pickSmsPhoneNumber($toContactID); if (!$toPhoneNumber) { - return array("sms_phone_missing" => "Couldn't find recipient's phone number."); + return ["sms_phone_missing" => "Couldn't find recipient's phone number."]; } $messageSubject = $tokenRow->render('subject'); @@ -547,23 +551,20 @@ protected static function sendReminderSms($tokenRow, $schedule, $toContactID) { $session = CRM_Core_Session::singleton(); $userID = $session->get('userID') ? $session->get('userID') : $tokenRow->context['contactId']; - $smsParams = array( + $smsParams = [ 'To' => $toPhoneNumber, 'provider_id' => $schedule->sms_provider_id, 'activity_subject' => $messageSubject, - ); - $activityTypeID = CRM_Core_OptionGroup::getValue('activity_type', - 'SMS', - 'name' - ); - $activityParams = array( + ]; + $activityTypeID = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'SMS'); + $activityParams = [ 'source_contact_id' => $userID, 'activity_type_id' => $activityTypeID, 'activity_date_time' => date('YmdHis'), 'subject' => $messageSubject, 'details' => $sms_body_text, - 'status_id' => CRM_Core_OptionGroup::getValue('activity_status', 'Completed', 'name'), - ); + 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'status_id', 'Completed'), + ]; $activity = CRM_Activity_BAO_Activity::create($activityParams); @@ -574,7 +575,7 @@ protected static function sendReminderSms($tokenRow, $schedule, $toContactID) { $userID ); - return array(); + return []; } /** @@ -602,7 +603,7 @@ protected static function pickFromEmail($actionSchedule) { protected static function sendReminderEmail($tokenRow, $schedule, $toContactID) { $toEmail = CRM_Contact_BAO_Contact::getPrimaryEmail($toContactID); if (!$toEmail) { - return array("email_missing" => "Couldn't find recipient's email address."); + return ["email_missing" => "Couldn't find recipient's email address."]; } $body_text = $tokenRow->render('body_text'); @@ -612,7 +613,7 @@ protected static function sendReminderEmail($tokenRow, $schedule, $toContactID) } // set up the parameters for CRM_Utils_Mail::send - $mailParams = array( + $mailParams = [ 'groupName' => 'Scheduled Reminder Sender', 'from' => self::pickFromEmail($schedule), 'toName' => $tokenRow->context['contact']['display_name'], @@ -620,7 +621,7 @@ protected static function sendReminderEmail($tokenRow, $schedule, $toContactID) 'subject' => $tokenRow->render('subject'), 'entity' => 'action_schedule', 'entity_id' => $schedule->id, - ); + ]; if (!$body_html || $tokenRow->context['contact']['preferred_mail_format'] == 'Text' || $tokenRow->context['contact']['preferred_mail_format'] == 'Both' @@ -636,10 +637,10 @@ protected static function sendReminderEmail($tokenRow, $schedule, $toContactID) } $result = CRM_Utils_Mail::send($mailParams); if (!$result || is_a($result, 'PEAR_Error')) { - return array('email_fail' => 'Failed to send message'); + return ['email_fail' => 'Failed to send message']; } - return array(); + return []; } /** @@ -648,12 +649,12 @@ protected static function sendReminderEmail($tokenRow, $schedule, $toContactID) * @return \Civi\Token\TokenProcessor */ protected static function createTokenProcessor($schedule, $mapping) { - $tp = new \Civi\Token\TokenProcessor(\Civi\Core\Container::singleton()->get('dispatcher'), array( + $tp = new \Civi\Token\TokenProcessor(\Civi\Core\Container::singleton()->get('dispatcher'), [ 'controller' => __CLASS__, 'actionSchedule' => $schedule, 'actionMapping' => $mapping, 'smarty' => TRUE, - )); + ]); $tp->addMessage('body_text', $schedule->body_text, 'text/plain'); $tp->addMessage('body_html', $schedule->body_html, 'text/html'); $tp->addMessage('sms_body_text', $schedule->sms_body_text, 'text/plain'); @@ -669,11 +670,11 @@ protected static function createTokenProcessor($schedule, $mapping) { * @return NULL|string */ protected static function pickSmsPhoneNumber($smsToContactId) { - $toPhoneNumbers = CRM_Core_BAO_Phone::allPhones($smsToContactId, FALSE, 'Mobile', array( + $toPhoneNumbers = CRM_Core_BAO_Phone::allPhones($smsToContactId, FALSE, 'Mobile', [ 'is_deceased' => 0, 'is_deleted' => 0, 'do_not_sms' => 0, - )); + ]); //to get primary mobile ph,if not get a first mobile phONE if (!empty($toPhoneNumbers)) { $toPhoneNumberDetails = reset($toPhoneNumbers); @@ -690,10 +691,10 @@ protected static function pickSmsPhoneNumber($smsToContactId) { * array(mixed $value => string $label). */ public static function getAdditionalRecipients() { - return array( + return [ 'manual' => ts('Choose Recipient(s)'), 'group' => ts('Select Group'), - ); + ]; } } diff --git a/CRM/Core/BAO/Address.php b/CRM/Core/BAO/Address.php index dbc3d489385b..d94ecf2800dd 100644 --- a/CRM/Core/BAO/Address.php +++ b/CRM/Core/BAO/Address.php @@ -1,9 +1,9 @@ $params['entity_table'], 'entity_id' => $params['entity_id'], - ); + ]; $addresses = self::allEntityAddress($entityElements); } $isPrimary = $isBilling = TRUE; - $blocks = array(); + $blocks = []; foreach ($params['address'] as $key => $value) { if (!is_array($value)) { continue; @@ -92,7 +92,7 @@ public static function create(&$params, $fixAddress = TRUE, $entity = NULL) { // $updateBlankLocInfo will help take appropriate decision. CRM-5969 if (isset($value['id']) && !$addressExists && $updateBlankLocInfo) { //delete the existing record - CRM_Core_BAO_Block::blockDelete('Address', array('id' => $value['id'])); + CRM_Core_BAO_Block::blockDelete('Address', ['id' => $value['id']]); continue; } elseif (!$addressExists) { @@ -134,7 +134,7 @@ public static function create(&$params, $fixAddress = TRUE, $entity = NULL) { * * @return CRM_Core_BAO_Address|null */ - public static function add(&$params, $fixAddress) { + public static function add(&$params, $fixAddress = FALSE) { $address = new CRM_Core_DAO_Address(); $checkPermissions = isset($params['check_permissions']) ? $params['check_permissions'] : TRUE; @@ -152,6 +152,11 @@ public static function add(&$params, $fixAddress) { CRM_Core_BAO_Block::handlePrimary($params, get_class()); } + // (prevent chaining 1 and 3) CRM-21214 + if (isset($params['master_id']) && !CRM_Utils_System::isNull($params['master_id'])) { + self::fixSharedAddress($params); + } + $address->copyValues($params); $address->save(); @@ -171,15 +176,11 @@ public static function add(&$params, $fixAddress) { CRM_Core_BAO_CustomValueTable::store($addressCustom, 'civicrm_address', $address->id); } - //call the function to sync shared address + // call the function to sync shared address and create relationships + // if address is already shared, share master_id with all children and update relationships accordingly + // (prevent chaining 2) CRM-21214 self::processSharedAddress($address->id, $params); - // call the function to create shared relationships - // we only create create relationship if address is shared by Individual - if (!CRM_Utils_System::isNull($address->master_id)) { - self::processSharedAddressRelationship($address->master_id, $params); - } - // lets call the post hook only after we've done all the follow on processing CRM_Utils_Hook::post($hook, 'Address', $address->id, $address); } @@ -197,7 +198,7 @@ public static function fixAddress(&$params) { if (!empty($params['billing_street_address'])) { //Check address is coming from online contribution / registration page //Fixed :CRM-5076 - $billing = array( + $billing = [ 'street_address' => 'billing_street_address', 'city' => 'billing_city', 'postal_code' => 'billing_postal_code', @@ -205,7 +206,7 @@ public static function fixAddress(&$params) { 'state_province_id' => 'billing_state_province_id', 'country' => 'billing_country', 'country_id' => 'billing_country_id', - ); + ]; foreach ($billing as $key => $val) { if ($value = CRM_Utils_Array::value($val, $params)) { @@ -341,51 +342,41 @@ public static function fixAddress(&$params) { $params['country'] = CRM_Core_PseudoConstant::country($params['country_id']); } - $config = CRM_Core_Config::singleton(); - $asp = Civi::settings()->get('address_standardization_provider'); // clean up the address via USPS web services if enabled if ($asp === 'USPS' && $params['country_id'] == 1228 ) { CRM_Utils_Address_USPS::checkAddress($params); + } + // do street parsing again if enabled, since street address might have changed + $parseStreetAddress = CRM_Utils_Array::value( + 'street_address_parsing', + CRM_Core_BAO_Setting::valueOptions( + CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, + 'address_options' + ), + FALSE + ); - // do street parsing again if enabled, since street address might have changed - $parseStreetAddress = CRM_Utils_Array::value( - 'street_address_parsing', - CRM_Core_BAO_Setting::valueOptions( - CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, - 'address_options' - ), - FALSE - ); - - if ($parseStreetAddress && !empty($params['street_address'])) { - foreach (array( - 'street_number', - 'street_name', - 'street_unit', - 'street_number_suffix', - ) as $fld) { - unset($params[$fld]); - } - // main parse string. - $parseString = CRM_Utils_Array::value('street_address', $params); - $parsedFields = CRM_Core_BAO_Address::parseStreetAddress($parseString); - - // merge parse address in to main address block. - $params = array_merge($params, $parsedFields); + if ($parseStreetAddress && !empty($params['street_address'])) { + foreach (['street_number', 'street_name', 'street_unit', 'street_number_suffix'] as $fld) { + unset($params[$fld]); } - } + // main parse string. + $parseString = CRM_Utils_Array::value('street_address', $params); + $parsedFields = CRM_Core_BAO_Address::parseStreetAddress($parseString); - // check if geocode should be skipped (can be forced with an optional parameter through the api) - $skip_geocode = (isset($params['skip_geocode']) && $params['skip_geocode']) ? TRUE : FALSE; + // merge parse address in to main address block. + $params = array_merge($params, $parsedFields); + } - // add latitude and longitude and format address if needed - if (!$skip_geocode && !empty($config->geocodeMethod) && ($config->geocodeMethod != 'CRM_Utils_Geocode_OpenStreetMaps') && empty($params['manual_geo_code'])) { - $class = $config->geocodeMethod; - $class::format($params); + // skip_geocode is an optional parameter through the api. + // manual_geo_code is on the contact edit form. They do the same thing.... + if (empty($params['skip_geocode']) && empty($params['manual_geo_code'])) { + self::addGeocoderData($params); } + } /** @@ -404,7 +395,7 @@ public static function dataExists(&$params) { $config = CRM_Core_Config::singleton(); foreach ($params as $name => $value) { - if (in_array($name, array( + if (in_array($name, [ 'is_primary', 'location_type_id', 'id', @@ -412,7 +403,7 @@ public static function dataExists(&$params) { 'is_billing', 'display', 'master_id', - ))) { + ])) { continue; } elseif (!CRM_Utils_System::isNull($value)) { @@ -466,14 +457,14 @@ public static function &getValues($entityBlock, $microformat = FALSE, $fieldName if (empty($entityBlock)) { return NULL; } - $addresses = array(); + $addresses = []; $address = new CRM_Core_BAO_Address(); if (empty($entityBlock['entity_table'])) { $address->$fieldName = CRM_Utils_Array::value($fieldName, $entityBlock); } else { - $addressIds = array(); + $addressIds = []; $addressIds = self::allEntityAddress($entityBlock); if (!empty($addressIds[1])) { @@ -498,19 +489,14 @@ public static function &getValues($entityBlock, $microformat = FALSE, $fieldName while ($address->fetch()) { // deprecate reference. if ($count > 1) { - foreach (array( - 'state', - 'state_name', - 'country', - 'world_region', - ) as $fld) { + foreach (['state', 'state_name', 'country', 'world_region'] as $fld) { if (isset($address->$fld)) { unset($address->$fld); } } } $stree = $address->street_address; - $values = array(); + $values = []; CRM_Core_DAO::storeValues($address, $values); // add state and country information: CRM-369 @@ -536,16 +522,16 @@ public static function &getValues($entityBlock, $microformat = FALSE, $fieldName $values['display'] = $address->display; $values['display_text'] = $address->display_text; - if (is_numeric($address->master_id)) { + if (isset($address->master_id) && !CRM_Utils_System::isNull($address->master_id)) { $values['use_shared_address'] = 1; } $addresses[$count] = $values; - //unset is_primary after first block. Due to some bug in earlier version - //there might be more than one primary blocks, hence unset is_primary other than first + //There should never be more than one primary blocks, hence set is_primary = 0 other than first + // Calling functions expect the key is_primary to be set, so do not unset it here! if ($count > 1) { - unset($addresses[$count]['is_primary']); + $addresses[$count]['is_primary'] = 0; } $count++; @@ -561,7 +547,7 @@ public static function &getValues($entityBlock, $microformat = FALSE, $fieldName * Unexplained parameter that I've always wondered about. */ public function addDisplay($microformat = FALSE) { - $fields = array( + $fields = [ // added this for CRM 1200 'address_id' => $this->id, // CRM-4003 @@ -569,6 +555,7 @@ public function addDisplay($microformat = FALSE) { 'street_address' => $this->street_address, 'supplemental_address_1' => $this->supplemental_address_1, 'supplemental_address_2' => $this->supplemental_address_2, + 'supplemental_address_3' => $this->supplemental_address_3, 'city' => $this->city, 'state_province_name' => isset($this->state_name) ? $this->state_name : "", 'state_province' => isset($this->state) ? $this->state : "", @@ -576,7 +563,7 @@ public function addDisplay($microformat = FALSE) { 'postal_code_suffix' => isset($this->postal_code_suffix) ? $this->postal_code_suffix : "", 'country' => isset($this->country) ? $this->country : "", 'world_region' => isset($this->world_region) ? $this->world_region : "", - ); + ]; if (isset($this->county_id) && $this->county_id) { $fields['county'] = CRM_Core_PseudoConstant::county($this->county_id); @@ -610,9 +597,9 @@ public static function allAddress($id, $updateBlankLocInfo = FALSE) { FROM civicrm_contact, civicrm_address WHERE civicrm_address.contact_id = civicrm_contact.id AND civicrm_contact.id = %1 ORDER BY civicrm_address.is_primary DESC, address_id ASC"; - $params = array(1 => array($id, 'Integer')); + $params = [1 => [$id, 'Integer']]; - $addresses = array(); + $addresses = []; $dao = CRM_Core_DAO::executeQuery($query, $params); $count = 1; while ($dao->fetch()) { @@ -637,7 +624,7 @@ public static function allAddress($id, $updateBlankLocInfo = FALSE) { * the array of adrress data */ public static function allEntityAddress(&$entityElements) { - $addresses = array(); + $addresses = []; if (empty($entityElements)) { return $addresses; } @@ -654,7 +641,7 @@ public static function allEntityAddress(&$entityElements) { AND ltype.id = civicrm_address.location_type_id ORDER BY civicrm_address.is_primary DESC, civicrm_address.location_type_id DESC, address_id ASC "; - $params = array(1 => array($entityId, 'Integer')); + $params = [1 => [$entityId, 'Integer']]; $dao = CRM_Core_DAO::executeQuery($sql, $params); $locationCount = 1; while ($dao->fetch()) { @@ -677,21 +664,21 @@ public static function addressSequence() { $countryState = $cityPostal = FALSE; foreach ($addressSequence as $key => $field) { if ( - in_array($field, array('country', 'state_province')) && + in_array($field, ['country', 'state_province']) && !$countryState ) { $countryState = TRUE; $addressSequence[$key] = 'country_state_province'; } elseif ( - in_array($field, array('city', 'postal_code')) && + in_array($field, ['city', 'postal_code']) && !$cityPostal ) { $cityPostal = TRUE; $addressSequence[$key] = 'city_postal_code'; } elseif ( - in_array($field, array('country', 'state_province', 'city', 'postal_code')) + in_array($field, ['country', 'state_province', 'city', 'postal_code']) ) { unset($addressSequence[$key]); } @@ -717,31 +704,17 @@ public static function addressSequence() { * parsed fields values. */ public static function parseStreetAddress($streetAddress, $locale = NULL) { - $config = CRM_Core_Config::singleton(); - - /* locales supported include: - * en_US - http://pe.usps.com/cpim/ftp/pubs/pub28/pub28.pdf - * en_CA - http://www.canadapost.ca/tools/pg/manual/PGaddress-e.asp - * fr_CA - http://www.canadapost.ca/tools/pg/manual/PGaddress-f.asp - * NB: common use of comma after street number also supported - * default is en_US - */ - - $supportedLocalesForParsing = array('en_US', 'en_CA', 'fr_CA'); - if (!$locale) { - $locale = $config->lcMessages; - } - // as different locale explicitly requested but is not available, display warning message and set $locale = 'en_US' - if (!in_array($locale, $supportedLocalesForParsing)) { - CRM_Core_Session::setStatus(ts('Unsupported locale specified to parseStreetAddress: %1. Proceeding with en_US locale.', array(1 => $locale)), ts('Unsupported Locale'), 'alert'); + // use 'en_US' for address parsing if the requested locale is not supported. + if (!self::isSupportedParsingLocale($locale)) { $locale = 'en_US'; } - $emptyParseFields = $parseFields = array( + + $emptyParseFields = $parseFields = [ 'street_name' => '', 'street_unit' => '', 'street_number' => '', 'street_number_suffix' => '', - ); + ]; if (empty($streetAddress)) { return $parseFields; @@ -749,11 +722,9 @@ public static function parseStreetAddress($streetAddress, $locale = NULL) { $streetAddress = trim($streetAddress); - $matches = array(); - if (in_array($locale, array( - 'en_CA', - 'fr_CA', - )) && preg_match('/^([A-Za-z0-9]+)[ ]*\-[ ]*/', $streetAddress, $matches) + $matches = []; + if (in_array($locale, ['en_CA', 'fr_CA']) + && preg_match('/^([A-Za-z0-9]+)[ ]*\-[ ]*/', $streetAddress, $matches) ) { $parseFields['street_unit'] = $matches[1]; // unset from rest of street address @@ -761,7 +732,7 @@ public static function parseStreetAddress($streetAddress, $locale = NULL) { } // get street number and suffix. - $matches = array(); + $matches = []; //alter street number/suffix handling so that we accept -digit if (preg_match('/^[A-Za-z0-9]+([\S]+)/', $streetAddress, $matches)) { // check that $matches[0] is numeric, else assume no street number @@ -769,7 +740,7 @@ public static function parseStreetAddress($streetAddress, $locale = NULL) { $streetNumAndSuffix = $matches[0]; // get street number. - $matches = array(); + $matches = []; if (preg_match('/^(\d+)/', $streetNumAndSuffix, $matches)) { $parseFields['street_number'] = $matches[0]; $suffix = preg_replace('/^(\d+)/', '', $streetNumAndSuffix); @@ -788,8 +759,13 @@ public static function parseStreetAddress($streetAddress, $locale = NULL) { $streetAddress = trim($streetAddress); } + // If street number is too large, we cannot store it. + if ($parseFields['street_number'] > CRM_Utils_Type::INT_MAX) { + return $emptyParseFields; + } + // suffix might be like 1/2 - $matches = array(); + $matches = []; if (preg_match('/^\d\/\d/', $streetAddress, $matches)) { $parseFields['street_number_suffix'] .= $matches[0]; @@ -800,7 +776,7 @@ public static function parseStreetAddress($streetAddress, $locale = NULL) { // now get the street unit. // supportable street unit formats. - $streetUnitFormats = array( + $streetUnitFormats = [ 'APT', 'APARTMENT', 'BSMT', @@ -841,19 +817,19 @@ public static function parseStreetAddress($streetAddress, $locale = NULL) { 'SUITE', 'UNIT', '#', - ); + ]; // overwriting $streetUnitFormats for 'en_CA' and 'fr_CA' locale - if (in_array($locale, array( + if (in_array($locale, [ 'en_CA', 'fr_CA', - ))) { - $streetUnitFormats = array('APT', 'APP', 'SUITE', 'BUREAU', 'UNIT'); + ])) { + $streetUnitFormats = ['APT', 'APP', 'SUITE', 'BUREAU', 'UNIT']; } //@todo per CRM-14459 this regex picks up words with the string in them - e.g APT picks up //Captain - presuming fixing regex (& adding test) to ensure a-z does not preced string will fix $streetUnitPreg = '/(' . implode('|\s', $streetUnitFormats) . ')(.+)?/i'; - $matches = array(); + $matches = []; if (preg_match($streetUnitPreg, $streetAddress, $matches)) { $parseFields['street_unit'] = trim($matches[0]); $streetAddress = str_replace($matches[0], '', $streetAddress); @@ -879,6 +855,38 @@ public static function parseStreetAddress($streetAddress, $locale = NULL) { return $parseFields; } + /** + * Determines if the specified locale is + * supported by address parsing. + * If no locale is specified then it + * will check the default configured locale. + * + * locales supported include: + * en_US - http://pe.usps.com/cpim/ftp/pubs/pub28/pub28.pdf + * en_CA - http://www.canadapost.ca/tools/pg/manual/PGaddress-e.asp + * fr_CA - http://www.canadapost.ca/tools/pg/manual/PGaddress-f.asp + * NB: common use of comma after street number also supported + * + * @param string $locale + * The locale to be checked + * + * @return bool + */ + public static function isSupportedParsingLocale($locale = NULL) { + if (!$locale) { + $config = CRM_Core_Config::singleton(); + $locale = $config->lcMessages; + } + + $parsingSupportedLocales = ['en_US', 'en_CA', 'fr_CA']; + + if (in_array($locale, $parsingSupportedLocales)) { + return TRUE; + } + + return FALSE; + } + /** * Validate the address fields based on the address options enabled. * in the Address Settings @@ -919,7 +927,7 @@ public static function validateAddressOptions($fields) { */ public static function checkContactSharedAddress($addressId) { $query = 'SELECT count(id) FROM civicrm_address WHERE master_id = %1'; - return CRM_Core_DAO::singleValueQuery($query, array(1 => array($addressId, 'Integer'))); + return CRM_Core_DAO::singleValueQuery($query, [1 => [$addressId, 'Integer']]); } /** @@ -936,7 +944,7 @@ public static function checkContactSharedAddressFields(&$fields, $contactId) { return; } - $sharedLocations = array(); + $sharedLocations = []; $query = " SELECT is_primary, @@ -945,7 +953,7 @@ public static function checkContactSharedAddressFields(&$fields, $contactId) { WHERE contact_id = %1 AND master_id IS NOT NULL"; - $dao = CRM_Core_DAO::executeQuery($query, array(1 => array($contactId, 'Positive'))); + $dao = CRM_Core_DAO::executeQuery($query, [1 => [$contactId, 'Positive']]); while ($dao->fetch()) { $sharedLocations[$dao->location_type_id] = $dao->location_type_id; if ($dao->is_primary) { @@ -958,7 +966,7 @@ public static function checkContactSharedAddressFields(&$fields, $contactId) { return; } - $addressFields = array( + $addressFields = [ 'city', 'county', 'country', @@ -971,7 +979,8 @@ public static function checkContactSharedAddressFields(&$fields, $contactId) { 'postal_code_suffix', 'supplemental_address_1', 'supplemental_address_2', - ); + 'supplemental_address_3', + ]; foreach ($fields as $name => & $values) { if (!is_array($values) || empty($values)) { @@ -993,6 +1002,27 @@ public static function checkContactSharedAddressFields(&$fields, $contactId) { } } + /** + * Fix the shared address if address is already shared + * or if address will be shared with itself. + * + * @param array $params + * Associated array of address params. + */ + public static function fixSharedAddress(&$params) { + // if address master address is shared, use its master (prevent chaining 1) CRM-21214 + $masterMasterId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_Address', $params['master_id'], 'master_id'); + if ($masterMasterId > 0) { + $params['master_id'] = $masterMasterId; + } + + // prevent an endless chain between two shared addresses (prevent chaining 3) CRM-21214 + if (CRM_Utils_Array::value('id', $params) == $params['master_id']) { + $params['master_id'] = NULL; + CRM_Core_Session::setStatus(ts("You can't connect an address to itself"), '', 'warning'); + } + } + /** * Update the shared addresses if master address is modified. * @@ -1002,17 +1032,34 @@ public static function checkContactSharedAddressFields(&$fields, $contactId) { * Associated array of address params. */ public static function processSharedAddress($addressId, $params) { - $query = 'SELECT id FROM civicrm_address WHERE master_id = %1'; - $dao = CRM_Core_DAO::executeQuery($query, array(1 => array($addressId, 'Integer'))); + $query = 'SELECT id, contact_id FROM civicrm_address WHERE master_id = %1'; + $dao = CRM_Core_DAO::executeQuery($query, [1 => [$addressId, 'Integer']]); + + // Default to TRUE if not set to maintain api backward compatibility. + $createRelationship = isset($params['update_current_employer']) ? $params['update_current_employer'] : TRUE; // unset contact id - $skipFields = array('is_primary', 'location_type_id', 'is_billing', 'master_id', 'contact_id'); + $skipFields = ['is_primary', 'location_type_id', 'is_billing', 'contact_id']; + if (isset($params['master_id']) && !CRM_Utils_System::isNull($params['master_id'])) { + if ($createRelationship) { + // call the function to create a relationship for the new shared address + self::processSharedAddressRelationship($params['master_id'], $params['contact_id']); + } + } + else { + // else no new shares will be created, only update shared addresses + $skipFields[] = 'master_id'; + } foreach ($skipFields as $value) { unset($params[$value]); } $addressDAO = new CRM_Core_DAO_Address(); while ($dao->fetch()) { + // call the function to update the relationship + if ($createRelationship && isset($params['master_id']) && !CRM_Utils_System::isNull($params['master_id'])) { + self::processSharedAddressRelationship($params['master_id'], $dao->contact_id); + } $addressDAO->copyValues($params); $addressDAO->id = $dao->id; $addressDAO->save(); @@ -1026,7 +1073,7 @@ public static function processSharedAddress($addressId, $params) { * Array[contact_id][contactDetails]. */ public static function mergeSameAddress(&$rows) { - $uniqueAddress = array(); + $uniqueAddress = []; foreach (array_keys($rows) as $rowID) { // load complete address as array key $address = trim($rows[$rowID]['street_address']) @@ -1042,12 +1089,12 @@ public static function mergeSameAddress(&$rows) { } // CRM-15120 - $formatted = array( + $formatted = [ 'first_name' => $rows[$rowID]['first_name'], 'individual_prefix' => $rows[$rowID]['individual_prefix'], - ); + ]; $format = Civi::settings()->get('display_name_format'); - $firstNameWithPrefix = CRM_Utils_Address::format($formatted, $format, FALSE, FALSE, TRUE); + $firstNameWithPrefix = CRM_Utils_Address::format($formatted, $format, FALSE, FALSE); $firstNameWithPrefix = trim($firstNameWithPrefix); // fill uniqueAddress array with last/first name tree @@ -1096,18 +1143,17 @@ public static function mergeSameAddress(&$rows) { /** * Create relationship between contacts who share an address. * - * Note that currently we create relationship only for Individual contacts - * Individual + Household and Individual + Orgnization + * Note that currently we create relationship between + * Individual + Household and Individual + Organization * * @param int $masterAddressId * Master address id. - * @param array $params - * Associated array of submitted values. + * @param int $currentContactId + * Current contact id. */ - public static function processSharedAddressRelationship($masterAddressId, $params) { + public static function processSharedAddressRelationship($masterAddressId, $currentContactId) { // get the contact type of contact being edited / created - $currentContactType = CRM_Contact_BAO_Contact::getContactType($params['contact_id']); - $currentContactId = $params['contact_id']; + $currentContactType = CRM_Contact_BAO_Contact::getContactType($currentContactId); // if current contact is not of type individual return if ($currentContactType != 'Individual') { @@ -1120,11 +1166,10 @@ public static function processSharedAddressRelationship($masterAddressId, $param FROM civicrm_contact cc INNER JOIN civicrm_address ca ON cc.id = ca.contact_id WHERE ca.id = %1'; - $dao = CRM_Core_DAO::executeQuery($query, array(1 => array($masterAddressId, 'Integer'))); + $dao = CRM_Core_DAO::executeQuery($query, [1 => [$masterAddressId, 'Integer']]); $dao->fetch(); - // if current contact is not of type individual return, since we don't create relationship between - // 2 individuals + // master address contact needs to be Household or Organization, otherwise return if ($dao->contact_type == 'Individual') { return; } @@ -1143,12 +1188,12 @@ public static function processSharedAddressRelationship($masterAddressId, $param CRM_Core_Error::fatal(ts("You seem to have deleted the relationship type 'Household Member of'")); } - $relParam = array( + $relParam = [ 'is_active' => TRUE, 'relationship_type_id' => $relTypeId, 'contact_id_a' => $currentContactId, 'contact_id_b' => $sharedContactId, - ); + ]; // If already there is a relationship record of $relParam criteria, avoid creating relationship again or else // it will casue CRM-16588 as the Duplicate Relationship Exception will revert other contact field values on update @@ -1195,10 +1240,10 @@ public static function setSharedAddressDeleteStatus($addressId = NULL, $contactI WHERE ca1.contact_id = %1'; } - $dao = CRM_Core_DAO::executeQuery($query, array(1 => array($entityId, 'Integer'))); + $dao = CRM_Core_DAO::executeQuery($query, [1 => [$entityId, 'Integer']]); - $deleteStatus = array(); - $sharedContactList = array(); + $deleteStatus = []; + $sharedContactList = []; $statusMessage = NULL; $addressCount = 0; while ($dao->fetch()) { @@ -1221,10 +1266,10 @@ public static function setSharedAddressDeleteStatus($addressId = NULL, $contactI CRM_Core_Session::setStatus($statusMessage, '', 'info'); } else { - return array( + return [ 'contactList' => $sharedContactList, 'count' => $addressCount, - ); + ]; } } @@ -1254,8 +1299,8 @@ public static function del($id) { * * @return array|bool */ - public static function buildOptions($fieldName, $context = NULL, $props = array()) { - $params = array(); + public static function buildOptions($fieldName, $context = NULL, $props = []) { + $params = []; // Special logic for fields whose options depend on context or properties switch ($fieldName) { // Filter state_province list based on chosen country or site defaults @@ -1264,7 +1309,7 @@ public static function buildOptions($fieldName, $context = NULL, $props = array( case 'state_province': // change $fieldName to DB specific names. $fieldName = 'state_province_id'; - if (empty($props['country_id'])) { + if (empty($props['country_id']) && $context !== 'validate') { $config = CRM_Core_Config::singleton(); if (!empty($config->provinceLimit)) { $props['country_id'] = $config->provinceLimit; @@ -1273,7 +1318,10 @@ public static function buildOptions($fieldName, $context = NULL, $props = array( $props['country_id'] = $config->defaultContactCountry; } } - if (!empty($props['country_id']) && $context !== 'validate') { + if (!empty($props['country_id'])) { + if (!CRM_Utils_Rule::commaSeparatedIntegers(implode(',', (array) $props['country_id']))) { + throw new CRM_Core_Exception(ts('Province limit or default country setting is incorrect')); + } $params['condition'] = 'country_id IN (' . implode(',', (array) $props['country_id']) . ')'; } break; @@ -1286,6 +1334,9 @@ public static function buildOptions($fieldName, $context = NULL, $props = array( if ($context != 'get' && $context != 'validate') { $config = CRM_Core_Config::singleton(); if (!empty($config->countryLimit) && is_array($config->countryLimit)) { + if (!CRM_Utils_Rule::commaSeparatedIntegers(implode(',', $config->countryLimit))) { + throw new CRM_Core_Exception(ts('Available Country setting is incorrect')); + } $params['condition'] = 'id IN (' . implode(',', $config->countryLimit) . ')'; } } @@ -1294,6 +1345,9 @@ public static function buildOptions($fieldName, $context = NULL, $props = array( // Filter county list based on chosen state case 'county_id': if (!empty($props['state_province_id'])) { + if (!CRM_Utils_Rule::commaSeparatedIntegers(implode(',', (array) $props['state_province_id']))) { + throw new CRM_Core_Exception(ts('Can only accept Integers for state_province_id filtering')); + } $params['condition'] = 'state_province_id IN (' . implode(',', (array) $props['state_province_id']) . ')'; } break; @@ -1302,9 +1356,29 @@ public static function buildOptions($fieldName, $context = NULL, $props = array( case 'world_region': case 'worldregion': case 'worldregion_id': - return CRM_Core_PseudoConstant::worldRegion(); + return CRM_Core_BAO_Country::buildOptions('region_id', $context, $props); } return CRM_Core_PseudoConstant::get(__CLASS__, $fieldName, $params, $context); } + /** + * Add data from the configured geocoding provider. + * + * Generally this means latitude & longitude data. + * + * @param array $params + * @return bool + * TRUE if params could be passed to a provider, else FALSE. + */ + public static function addGeocoderData(&$params) { + try { + $provider = CRM_Utils_GeocodeProvider::getConfiguredProvider(); + } + catch (CRM_Core_Exception $e) { + return FALSE; + } + $provider::format($params); + return TRUE; + } + } diff --git a/CRM/Core/BAO/Block.php b/CRM/Core/BAO/Block.php index ca29380af25e..a9ffa34e2f44 100644 --- a/CRM/Core/BAO/Block.php +++ b/CRM/Core/BAO/Block.php @@ -1,9 +1,9 @@ array('email'), - 'phone' => array('phone'), - 'im' => array('name'), - 'openid' => array('openid'), - ); + public static $requiredBlockFields = [ + 'email' => ['email'], + 'phone' => ['phone'], + 'im' => ['name'], + 'openid' => ['openid'], + ]; /** * Given the list of params in the params array, fetch the object @@ -63,7 +64,7 @@ public static function &getValues($blockName, $params) { $BAOString = 'CRM_Core_BAO_' . $blockName; $block = new $BAOString(); - $blocks = array(); + $blocks = []; if (!isset($params['entity_table'])) { $block->contact_id = $params['contact_id']; if (!$block->contact_id) { @@ -108,7 +109,7 @@ public static function retrieveBlock(&$block, $blockName) { $block->find(); $count = 1; - $blocks = array(); + $blocks = []; while ($block->fetch()) { CRM_Core_DAO::storeValues($block, $blocks[$count]); //unset is_primary after first block. Due to some bug in earlier version @@ -178,7 +179,7 @@ public static function blockExists($blockName, &$params) { * */ public static function getBlockIds($blockName, $contactId = NULL, $entityElements = NULL, $updateBlankLocInfo = FALSE) { - $allBlocks = array(); + $allBlocks = []; $name = ucfirst($blockName); if ($blockName == 'im') { @@ -226,15 +227,15 @@ public static function create($blockName, &$params, $entity = NULL, $contactId = $name = ucfirst($blockName); $isPrimary = $isBilling = TRUE; - $entityElements = $blocks = array(); + $entityElements = $blocks = []; $resetPrimaryId = NULL; $primaryId = FALSE; if ($entity) { - $entityElements = array( + $entityElements = [ 'entity_table' => $params['entity_table'], 'entity_id' => $params['entity_id'], - ); + ]; } else { $contactId = $params['contact_id']; @@ -320,16 +321,16 @@ public static function create($blockName, &$params, $entity = NULL, $contactId = // $updateBlankLocInfo will help take appropriate decision. CRM-5969 if (!empty($value['id']) && !$dataExists && $updateBlankLocInfo) { //delete the existing record - self::blockDelete($blockName, array('id' => $value['id'])); + self::blockDelete($blockName, ['id' => $value['id']]); continue; } elseif (!$dataExists) { continue; } - $contactFields = array( + $contactFields = [ 'contact_id' => $contactId, 'location_type_id' => CRM_Utils_Array::value('location_type_id', $value), - ); + ]; $contactFields['is_primary'] = 0; if ($isPrimary && !empty($value['is_primary'])) { @@ -422,11 +423,11 @@ public static function handlePrimary(&$params, $class) { // if is_primary = 1 if (!empty($params['is_primary'])) { $sql = "UPDATE $table SET is_primary = 0 WHERE contact_id = %1"; - $sqlParams = array(1 => array($contactId, 'Integer')); + $sqlParams = [1 => [$contactId, 'Integer']]; // we don't want to create unnecessary entries in the log_ tables so exclude the one we are working on if (!empty($params['id'])) { $sql .= " AND id <> %2"; - $sqlParams[2] = array($params['id'], 'Integer'); + $sqlParams[2] = [$params['id'], 'Integer']; } CRM_Core_DAO::executeQuery($sql, $sqlParams); return; diff --git a/CRM/Core/BAO/CMSUser.php b/CRM/Core/BAO/CMSUser.php index 88e9b40c8a8c..36bb8bdb6f0c 100644 --- a/CRM/Core/BAO/CMSUser.php +++ b/CRM/Core/BAO/CMSUser.php @@ -1,9 +1,9 @@ userFramework) == 'Joomla' ? TRUE : FALSE; $isWordPress = $config->userFramework == 'WordPress' ? TRUE : FALSE; - //if CMS is configured for not to allow creating new CMS user, - //don't build the form,Fixed for CRM-4036 - if ($isJoomla) { - $userParams = JComponentHelper::getParams('com_users'); - if (!$userParams->get('allowUserRegistration')) { - return FALSE; - } - } - elseif ($isDrupal && !variable_get('user_register', TRUE)) { - return FALSE; - } - elseif ($isWordPress && !get_option('users_can_register')) { + if (!$config->userSystem->isUserRegistrationPermitted()) { + // Do not build form if CMS is not configured to allow creating users. return FALSE; } @@ -111,8 +101,7 @@ public static function buildForm(&$form, $gid, $emailPresent, $action = CRM_Core } // $cms is true when there is email(primary location) is set in the profile field. - $session = CRM_Core_Session::singleton(); - $userID = $session->get('userID'); + $userID = CRM_Core_Session::singleton()->get('userID'); $showUserRegistration = FALSE; if ($action) { $showUserRegistration = TRUE; @@ -124,9 +113,9 @@ public static function buildForm(&$form, $gid, $emailPresent, $action = CRM_Core if ($isCMSUser && $emailPresent) { if ($showUserRegistration) { if ($isCMSUser != 2) { - $extra = array( + $extra = [ 'onclick' => "return showHideByValue('cms_create_account','','details','block','radio',false );", - ); + ]; $form->addElement('checkbox', 'cms_create_account', ts('Create an account?'), NULL, $extra); $required = FALSE; } @@ -138,12 +127,12 @@ public static function buildForm(&$form, $gid, $emailPresent, $action = CRM_Core $form->assign('isCMS', $required); if (!$userID || $action & CRM_Core_Action::PREVIEW || $action & CRM_Core_Action::PROFILE) { $form->add('text', 'cms_name', ts('Username'), NULL, $required); - if (($isDrupal && !variable_get('user_email_verification', TRUE)) OR ($isJoomla) OR ($isWordPress)) { + if ($config->userSystem->isPasswordUserGenerated()) { $form->add('password', 'cms_pass', ts('Password')); $form->add('password', 'cms_confirm_pass', ts('Confirm Password')); } - $form->addFormRule(array('CRM_Core_BAO_CMSUser', 'formRule'), $form); + $form->addFormRule(['CRM_Core_BAO_CMSUser', 'formRule'], $form); } $showCMS = TRUE; } @@ -178,7 +167,7 @@ public static function formRule($fields, $files, $form) { $isJoomla = ucfirst($config->userFramework) == 'Joomla' ? TRUE : FALSE; $isWordPress = $config->userFramework == 'WordPress' ? TRUE : FALSE; - $errors = array(); + $errors = []; if ($isDrupal || $isJoomla || $isWordPress) { $emailName = NULL; if (!empty($form->_bltID) && array_key_exists("email-{$form->_bltID}", $fields)) { @@ -196,7 +185,7 @@ public static function formRule($fields, $files, $form) { } if ($emailName == NULL) { - $errors['_qf_default'] == ts('Could not find an email address.'); + $errors['_qf_default'] = ts('Could not find an email address.'); return $errors; } @@ -208,7 +197,7 @@ public static function formRule($fields, $files, $form) { $errors[$emailName] = ts('Please specify a valid email address.'); } - if (($isDrupal && !variable_get('user_email_verification', TRUE)) OR ($isJoomla) OR ($isWordPress)) { + if ($config->userSystem->isPasswordUserGenerated()) { if (empty($fields['cms_pass']) || empty($fields['cms_confirm_pass']) ) { @@ -224,11 +213,11 @@ public static function formRule($fields, $files, $form) { } // now check that the cms db does not have the user name and/or email - if ($isDrupal OR $isJoomla OR $isWordPress) { - $params = array( + if ($isDrupal or $isJoomla or $isWordPress) { + $params = [ 'name' => $fields['cms_name'], 'mail' => $fields[$emailName], - ); + ]; } $config->userSystem->checkUserNameEmailExists($params, $errors, $emailName); diff --git a/CRM/Core/BAO/Cache.php b/CRM/Core/BAO/Cache.php index 85a79a7afe8d..13d59a848bdb 100644 --- a/CRM/Core/BAO/Cache.php +++ b/CRM/Core/BAO/Cache.php @@ -1,9 +1,9 @@ $cacheValue) */ - static $_cache = NULL; + public static $_cache = NULL; /** * Retrieve an item from the DB cache. @@ -57,24 +65,34 @@ class CRM_Core_BAO_Cache extends CRM_Core_DAO_Cache { * * @return object * The data if present in cache, else null + * @deprecated */ public static function &getItem($group, $path, $componentID = NULL) { + if (($adapter = CRM_Utils_Constant::value('CIVICRM_BAO_CACHE_ADAPTER')) !== NULL) { + $value = $adapter::getItem($group, $path, $componentID); + return $value; + } + if (self::$_cache === NULL) { - self::$_cache = array(); + self::$_cache = []; } $argString = "CRM_CT_{$group}_{$path}_{$componentID}"; if (!array_key_exists($argString, self::$_cache)) { $cache = CRM_Utils_Cache::singleton(); - self::$_cache[$argString] = $cache->get($argString); - if (!self::$_cache[$argString]) { + $cleanKey = self::cleanKey($argString); + self::$_cache[$argString] = $cache->get($cleanKey); + if (self::$_cache[$argString] === NULL) { $table = self::getTableName(); $where = self::whereCache($group, $path, $componentID); $rawData = CRM_Core_DAO::singleValueQuery("SELECT data FROM $table WHERE $where"); - $data = $rawData ? unserialize($rawData) : NULL; + $data = $rawData ? self::decode($rawData) : NULL; self::$_cache[$argString] = $data; - $cache->set($argString, self::$_cache[$argString]); + if ($data !== NULL) { + // Do not cache 'null' as that is most likely a cache miss & we shouldn't then cache it. + $cache->set($cleanKey, self::$_cache[$argString]); + } } } return self::$_cache[$argString]; @@ -90,29 +108,34 @@ public static function &getItem($group, $path, $componentID = NULL) { * * @return object * The data if present in cache, else null + * @deprecated */ public static function &getItems($group, $componentID = NULL) { + if (($adapter = CRM_Utils_Constant::value('CIVICRM_BAO_CACHE_ADAPTER')) !== NULL) { + return $adapter::getItems($group, $componentID); + } + if (self::$_cache === NULL) { - self::$_cache = array(); + self::$_cache = []; } $argString = "CRM_CT_CI_{$group}_{$componentID}"; if (!array_key_exists($argString, self::$_cache)) { $cache = CRM_Utils_Cache::singleton(); - self::$_cache[$argString] = $cache->get($argString); + $cleanKey = self::cleanKey($argString); + self::$_cache[$argString] = $cache->get($cleanKey); if (!self::$_cache[$argString]) { $table = self::getTableName(); $where = self::whereCache($group, NULL, $componentID); $dao = CRM_Core_DAO::executeQuery("SELECT path, data FROM $table WHERE $where"); - $result = array(); + $result = []; while ($dao->fetch()) { - $result[$dao->path] = unserialize($dao->data); + $result[$dao->path] = self::decode($dao->data); } - $dao->free(); self::$_cache[$argString] = $result; - $cache->set($argString, self::$_cache[$argString]); + $cache->set($cleanKey, self::$_cache[$argString]); } } @@ -130,10 +153,15 @@ public static function &getItems($group, $componentID = NULL) { * (required) The path under which this item is stored. * @param int $componentID * The optional component ID (so componenets can share the same name space). + * @deprecated */ public static function setItem(&$data, $group, $path, $componentID = NULL) { + if (($adapter = CRM_Utils_Constant::value('CIVICRM_BAO_CACHE_ADAPTER')) !== NULL) { + return $adapter::setItem($data, $group, $path, $componentID); + } + if (self::$_cache === NULL) { - self::$_cache = array(); + self::$_cache = []; } // get a lock so that multiple ajax requests on the same page @@ -146,47 +174,46 @@ public static function setItem(&$data, $group, $path, $componentID = NULL) { $table = self::getTableName(); $where = self::whereCache($group, $path, $componentID); - $id = CRM_Core_DAO::singleValueQuery("SELECT id FROM $table WHERE $where"); - $now = date('Y-m-d H:i:s'); // FIXME - Use SQL NOW() or CRM_Utils_Time? - $dataSerialized = serialize($data); + $dataExists = CRM_Core_DAO::singleValueQuery("SELECT COUNT(*) FROM $table WHERE {$where}"); + // FIXME - Use SQL NOW() or CRM_Utils_Time? + $now = date('Y-m-d H:i:s'); + $dataSerialized = self::encode($data); // This table has a wonky index, so we cannot use REPLACE or // "INSERT ... ON DUPE". Instead, use SELECT+(INSERT|UPDATE). - if ($id) { - $sql = "UPDATE $table SET data = %1, created_date = %2 WHERE id = %3"; - $dao = CRM_Core_DAO::executeQuery($sql, array( - 1 => array($dataSerialized, 'String'), - 2 => array($now, 'String'), - 3 => array($id, 'Int'), - )); + if ($dataExists) { + $sql = "UPDATE $table SET data = %1, created_date = %2 WHERE {$where}"; + $args = [ + 1 => [$dataSerialized, 'String'], + 2 => [$now, 'String'], + ]; + $dao = CRM_Core_DAO::executeQuery($sql, $args, TRUE, NULL, FALSE, FALSE); } else { $insert = CRM_Utils_SQL_Insert::into($table) - ->row(array( + ->row([ 'group_name' => $group, 'path' => $path, 'component_id' => $componentID, 'data' => $dataSerialized, 'created_date' => $now, - )); - $dao = CRM_Core_DAO::executeQuery($insert->toSQL()); + ]); + $dao = CRM_Core_DAO::executeQuery($insert->toSQL(), [], TRUE, NULL, FALSE, FALSE); } $lock->release(); - $dao->free(); - // cache coherency - refresh or remove dependent caches $argString = "CRM_CT_{$group}_{$path}_{$componentID}"; $cache = CRM_Utils_Cache::singleton(); - $data = unserialize($dataSerialized); + $data = self::decode($dataSerialized); self::$_cache[$argString] = $data; - $cache->set($argString, $data); + $cache->set(self::cleanKey($argString), $data); $argString = "CRM_CT_CI_{$group}_{$componentID}"; unset(self::$_cache[$argString]); - $cache->delete($argString); + $cache->delete(self::cleanKey($argString)); } /** @@ -197,11 +224,17 @@ public static function setItem(&$data, $group, $path, $componentID = NULL) { * @param string $path * Path of the item that needs to be deleted. * @param bool $clearAll clear all caches + * @deprecated */ public static function deleteGroup($group = NULL, $path = NULL, $clearAll = TRUE) { - $table = self::getTableName(); - $where = self::whereCache($group, $path, NULL); - CRM_Core_DAO::executeQuery("DELETE FROM $table WHERE $where"); + if (($adapter = CRM_Utils_Constant::value('CIVICRM_BAO_CACHE_ADAPTER')) !== NULL) { + return $adapter::deleteGroup($group, $path); + } + else { + $table = self::getTableName(); + $where = self::whereCache($group, $path, NULL); + CRM_Core_DAO::executeQuery("DELETE FROM $table WHERE $where"); + } if ($clearAll) { // also reset ACL Cache @@ -237,7 +270,8 @@ public static function storeSessionToCache($names, $resetSession = TRUE) { if (!empty($_SESSION[$sessionName[0]][$sessionName[1]])) { $value = $_SESSION[$sessionName[0]][$sessionName[1]]; } - self::setItem($value, 'CiviCRM Session', "{$sessionName[0]}_{$sessionName[1]}"); + $key = "{$sessionName[0]}_{$sessionName[1]}"; + Civi::cache('session')->set($key, $value, self::pickSessionTtl($key)); if ($resetSession) { $_SESSION[$sessionName[0]][$sessionName[1]] = NULL; unset($_SESSION[$sessionName[0]][$sessionName[1]]); @@ -248,7 +282,7 @@ public static function storeSessionToCache($names, $resetSession = TRUE) { if (!empty($_SESSION[$sessionName])) { $value = $_SESSION[$sessionName]; } - self::setItem($value, 'CiviCRM Session', $sessionName); + Civi::cache('session')->set($sessionName, $value, self::pickSessionTtl($sessionName)); if ($resetSession) { $_SESSION[$sessionName] = NULL; unset($_SESSION[$sessionName]); @@ -275,17 +309,13 @@ public static function storeSessionToCache($names, $resetSession = TRUE) { public static function restoreSessionFromCache($names) { foreach ($names as $key => $sessionName) { if (is_array($sessionName)) { - $value = self::getItem('CiviCRM Session', - "{$sessionName[0]}_{$sessionName[1]}" - ); + $value = Civi::cache('session')->get("{$sessionName[0]}_{$sessionName[1]}"); if ($value) { $_SESSION[$sessionName[0]][$sessionName[1]] = $value; } } else { - $value = self::getItem('CiviCRM Session', - $sessionName - ); + $value = Civi::cache('session')->get($sessionName); if ($value) { $_SESSION[$sessionName] = $value; } @@ -293,6 +323,32 @@ public static function restoreSessionFromCache($names) { } } + /** + * Determine how long session-state should be retained. + * + * @param string $sessionKey + * Ex: '_CRM_Admin_Form_Preferences_Display_f1a5f232e3d850a29a7a4d4079d7c37b_4654_container' + * Ex: 'CiviCRM_CRM_Admin_Form_Preferences_Display_f1a5f232e3d850a29a7a4d4079d7c37b_4654' + * @return int + * Number of seconds. + */ + protected static function pickSessionTtl($sessionKey) { + $secureSessionTimeoutMinutes = (int) Civi::settings()->get('secure_cache_timeout_minutes'); + if ($secureSessionTimeoutMinutes) { + $transactionPages = [ + 'CRM_Contribute_Controller_Contribution', + 'CRM_Event_Controller_Registration', + ]; + foreach ($transactionPages as $transactionPage) { + if (strpos($sessionKey, $transactionPage) !== FALSE) { + return $secureSessionTimeoutMinutes * 60; + } + } + } + + return self::DEFAULT_SESSION_TTL; + } + /** * Do periodic cleanup of the CiviCRM session table. * @@ -303,34 +359,9 @@ public static function restoreSessionFromCache($names) { * @param bool $session * @param bool $table * @param bool $prevNext + * @param bool $expired */ - public static function cleanup($session = FALSE, $table = FALSE, $prevNext = FALSE) { - // first delete all sessions more than 20 minutes old which are related to any potential transaction - $timeIntervalMins = (int) Civi::settings()->get('secure_cache_timeout_minutes'); - if ($timeIntervalMins && $session) { - $transactionPages = array( - 'CRM_Contribute_Controller_Contribution', - 'CRM_Event_Controller_Registration', - ); - - $params = array( - 1 => array( - date('Y-m-d H:i:s', time() - $timeIntervalMins * 60), - 'String', - ), - ); - foreach ($transactionPages as $trPage) { - $params[] = array("%${trPage}%", 'String'); - $where[] = 'path LIKE %' . count($params); - } - - $sql = " -DELETE FROM civicrm_cache -WHERE group_name = 'CiviCRM Session' -AND created_date <= %1 -AND (" . implode(' OR ', $where) . ")"; - CRM_Core_DAO::executeQuery($sql, $params); - } + public static function cleanup($session = FALSE, $table = FALSE, $prevNext = FALSE, $expired = FALSE) { // clean up the session cache every $cacheCleanUpNumber probabilistically $cleanUpNumber = 757; @@ -338,10 +369,10 @@ public static function cleanup($session = FALSE, $table = FALSE, $prevNext = FAL $timeIntervalDays = 2; if (mt_rand(1, 100000) % $cleanUpNumber == 0) { - $session = $table = $prevNext = TRUE; + $expired = $session = $table = $prevNext = TRUE; } - if (!$session && !$table && !$prevNext) { + if (!$session && !$table && !$prevNext && !$expired) { return; } @@ -355,13 +386,43 @@ public static function cleanup($session = FALSE, $table = FALSE, $prevNext = FAL } if ($session) { + // Session caches are just regular caches, so they expire naturally per TTL. + $expired = TRUE; + } - $sql = " -DELETE FROM civicrm_cache -WHERE group_name = 'CiviCRM Session' -AND created_date < date_sub( NOW( ), INTERVAL $timeIntervalDays DAY ) -"; - CRM_Core_DAO::executeQuery($sql); + if ($expired) { + $sql = "DELETE FROM civicrm_cache WHERE expired_date < %1"; + $params = [ + 1 => [date(CRM_Utils_Cache_SqlGroup::TS_FMT, CRM_Utils_Time::getTimeRaw()), 'String'], + ]; + CRM_Core_DAO::executeQuery($sql, $params); + } + } + + /** + * (Quasi-private) Encode an object/array/string/int as a string. + * + * @param $mixed + * @return string + */ + public static function encode($mixed) { + return base64_encode(serialize($mixed)); + } + + /** + * (Quasi-private) Decode an object/array/string/int from a string. + * + * @param $string + * @return mixed + */ + public static function decode($string) { + // Upgrade support -- old records (serialize) always have this punctuation, + // and new records (base64) never do. + if (strpos($string, ':') !== FALSE || strpos($string, ';') !== FALSE) { + return unserialize($string); + } + else { + return unserialize(base64_decode($string)); } } @@ -379,7 +440,7 @@ public static function cleanup($session = FALSE, $table = FALSE, $prevNext = FAL * @return string */ protected static function whereCache($group, $path, $componentID) { - $clauses = array(); + $clauses = []; $clauses[] = ('group_name = "' . CRM_Core_DAO::escapeString($group) . '"'); if ($path) { $clauses[] = ('path = "' . CRM_Core_DAO::escapeString($path) . '"'); @@ -390,4 +451,35 @@ protected static function whereCache($group, $path, $componentID) { return $clauses ? implode(' AND ', $clauses) : '(1)'; } + /** + * Normalize a cache key. + * + * This bridges an impedance mismatch between our traditional caching + * and PSR-16 -- PSR-16 accepts a narrower range of cache keys. + * + * @param string $key + * Ex: 'ab/cd:ef' + * @return string + * Ex: '_abcd1234abcd1234' or 'ab_xx/cd_xxef'. + * A similar key, but suitable for use with PSR-16-compliant cache providers. + */ + public static function cleanKey($key) { + if (!is_string($key) && !is_int($key)) { + throw new \RuntimeException("Malformed cache key"); + } + + $maxLen = 64; + $escape = '-'; + + if (strlen($key) >= $maxLen) { + return $escape . md5($key); + } + + $r = preg_replace_callback(';[^A-Za-z0-9_\.];', function($m) use ($escape) { + return $escape . dechex(ord($m[0])); + }, $key); + + return strlen($r) >= $maxLen ? $escape . md5($key) : $r; + } + } diff --git a/CRM/Core/BAO/Cache/Psr16.php b/CRM/Core/BAO/Cache/Psr16.php new file mode 100644 index 000000000000..03463d473ee5 --- /dev/null +++ b/CRM/Core/BAO/Cache/Psr16.php @@ -0,0 +1,210 @@ +warning('Unrecognized BAO cache group ({group}). This should work generally, but data may not be flushed in some edge-cases. Consider migrating explicitly to PSR-16.', [ + 'group' => $group, + ]); + } + + $cache = CRM_Utils_Cache::create([ + 'name' => "bao_$group", + 'type' => ['*memory*', 'SqlGroup', 'ArrayCache'], + // We're replacing CRM_Core_BAO_Cache, which traditionally used a front-cache + // that was not aware of TTLs. So it seems more consistent/performant to + // use 'fast' here. + 'withArray' => 'fast', + ]); + Civi::$statics[__CLASS__][$group] = $cache; + } + return Civi::$statics[__CLASS__][$group]; + } + + /** + * Retrieve an item from the DB cache. + * + * @param string $group + * (required) The group name of the item. + * @param string $path + * (required) The path under which this item is stored. + * @param int $componentID + * The optional component ID (so componenets can share the same name space). + * + * @return object + * The data if present in cache, else null + */ + public static function getItem($group, $path, $componentID = NULL) { + // TODO: Generate a general deprecation notice. + if ($componentID) { + Civi::log() + ->warning('getItem({group},{path},...) uses unsupported componentID. Consider migrating explicitly to PSR-16.', [ + 'group' => $group, + 'path' => $path, + ]); + } + return self::getGroup($group)->get(CRM_Core_BAO_Cache::cleanKey($path)); + } + + /** + * Retrieve all items in a group. + * + * @param string $group + * (required) The group name of the item. + * @param int $componentID + * The optional component ID (so componenets can share the same name space). + * + * @throws CRM_Core_Exception + */ + public static function &getItems($group, $componentID = NULL) { + // Based on grepping universe, this function is not currently used. + // Moreover, it's hard to implement in PSR-16. (We'd have to extend the + // interface.) Let's wait and see if anyone actually needs this... + throw new \CRM_Core_Exception('Not implemented: CRM_Core_BAO_Cache_Psr16::getItems'); + } + + /** + * Store an item in the DB cache. + * + * @param object $data + * (required) A reference to the data that will be serialized and stored. + * @param string $group + * (required) The group name of the item. + * @param string $path + * (required) The path under which this item is stored. + * @param int $componentID + * The optional component ID (so componenets can share the same name space). + */ + public static function setItem(&$data, $group, $path, $componentID = NULL) { + // TODO: Generate a general deprecation notice. + + if ($componentID) { + Civi::log() + ->warning('setItem({group},{path},...) uses unsupported componentID. Consider migrating explicitly to PSR-16.', [ + 'group' => $group, + 'path' => $path, + ]); + } + self::getGroup($group) + ->set(CRM_Core_BAO_Cache::cleanKey($path), $data, self::TTL); + } + + /** + * Delete all the cache elements that belong to a group OR delete the entire cache if group is not specified. + * + * @param string $group + * The group name of the entries to be deleted. + * @param string $path + * Path of the item that needs to be deleted. + */ + public static function deleteGroup($group = NULL, $path = NULL) { + // FIXME: Generate a general deprecation notice. + + if ($path) { + self::getGroup($group)->delete(CRM_Core_BAO_Cache::cleanKey($path)); + } + else { + self::getGroup($group)->clear(); + } + } + + /** + * Cleanup any caches that we've mapped. + * + * Traditional SQL-backed caches are cleared as a matter of course during a + * system flush (by way of "TRUNCATE TABLE civicrm_cache"). This provides + * a spot where the adapter can + */ + public static function clearDBCache() { + foreach (self::getLegacyGroups() as $groupName) { + $group = self::getGroup($groupName); + $group->clear(); + } + } + + /** + * Get a list of known cache-groups + * + * @return array + */ + public static function getLegacyGroups() { + return [ + // Core + 'CiviCRM Search PrevNextCache', + 'contact fields', + 'navigation', + 'contact groups', + 'custom data', + + // Universe + + // be.chiro.civi.atomfeeds + 'dashboard', + + // biz.jmaconsulting.lineitemedit + 'lineitem-editor', + + // civihr/uk.co.compucorp.civicrm.hrcore + 'HRCore_Info', + + // nz.co.fuzion.entitysetting + 'CiviCRM setting Spec', + + // org.civicrm.multisite + 'descendant groups for an org', + ]; + } + +} diff --git a/CRM/Core/BAO/ConfigSetting.php b/CRM/Core/BAO/ConfigSetting.php index e2a708c3dc74..946890452edb 100644 --- a/CRM/Core/BAO/ConfigSetting.php +++ b/CRM/Core/BAO/ConfigSetting.php @@ -1,9 +1,9 @@ selectAdd('config_backend'); } else { @@ -108,7 +108,7 @@ public static function retrieve(&$defaults) { if ($domain->config_backend) { $defaults = unserialize($domain->config_backend); if ($defaults === FALSE || !is_array($defaults)) { - $defaults = array(); + $defaults = []; return FALSE; } @@ -141,20 +141,22 @@ public static function applyLocale($settings, $activatedLocales) { $session = CRM_Core_Session::singleton(); - // on multi-lang sites based on request and civicrm_uf_match - if ($multiLang) { - $languageLimit = array(); - if (is_array($settings->get('languageLimit'))) { - $languageLimit = $settings->get('languageLimit'); - } + $permittedLanguages = CRM_Core_I18n::uiLanguages(TRUE); + // The locale to be used can come from various places: + // - the request (url) + // - the session + // - civicrm_uf_match + // - inherited from the CMS + // Only look at this if there is actually a choice of permitted languages + if (count($permittedLanguages) >= 2) { $requestLocale = CRM_Utils_Request::retrieve('lcMessages', 'String'); - if (in_array($requestLocale, array_keys($languageLimit))) { + if (in_array($requestLocale, $permittedLanguages)) { $chosenLocale = $requestLocale; //CRM-8559, cache navigation do not respect locale if it is changed, so reseting cache. // Ed: This doesn't sound good. - CRM_Core_BAO_Cache::deleteGroup('navigation'); + // CRM_Core_BAO_Cache::deleteGroup('navigation'); } else { $requestLocale = NULL; @@ -162,7 +164,7 @@ public static function applyLocale($settings, $activatedLocales) { if (!$requestLocale) { $sessionLocale = $session->get('lcMessages'); - if (in_array($sessionLocale, array_keys($languageLimit))) { + if (in_array($sessionLocale, $permittedLanguages)) { $chosenLocale = $sessionLocale; } else { @@ -184,7 +186,7 @@ public static function applyLocale($settings, $activatedLocales) { $ufm = new CRM_Core_DAO_UFMatch(); $ufm->contact_id = $session->get('userID'); if ($ufm->find(TRUE) && - in_array($ufm->language, array_keys($languageLimit)) + in_array($ufm->language, $permittedLanguages) ) { $chosenLocale = $ufm->language; } @@ -196,7 +198,8 @@ public static function applyLocale($settings, $activatedLocales) { // try to inherit the language from the hosting CMS if ($settings->get('inheritLocale')) { // FIXME: On multilanguage installs, CRM_Utils_System::getUFLocale() in many cases returns nothing if $dbLocale is not set - $dbLocale = $multiLang ? ("_" . $settings->get('lcMessages')) : ''; + $lcMessages = $settings->get('lcMessages'); + $dbLocale = $multiLang && $lcMessages ? "_{$lcMessages}" : ''; $chosenLocale = CRM_Utils_System::getUFLocale(); if ($activatedLocales and !in_array($chosenLocale, explode(CRM_Core_DAO::VALUE_SEPARATOR, $activatedLocales))) { $chosenLocale = NULL; @@ -209,7 +212,7 @@ public static function applyLocale($settings, $activatedLocales) { } // set suffix for table names - use views if more than one language - $dbLocale = $multiLang ? "_{$chosenLocale}" : ''; + $dbLocale = $multiLang && $chosenLocale ? "_{$chosenLocale}" : ''; // FIXME: an ugly hack to fix CRM-4041 global $tsLocale; @@ -228,7 +231,7 @@ public static function applyLocale($settings, $activatedLocales) { * @return string * @throws Exception */ - public static function doSiteMove($defaultValues = array()) { + public static function doSiteMove($defaultValues = []) { $moveStatus = ts('Beginning site move process...') . '
    '; $settings = Civi::settings(); @@ -237,15 +240,15 @@ public static function doSiteMove($defaultValues = array()) { if ($value && $value != $settings->getDefault($key)) { if ($settings->getMandatory($key) === NULL) { $settings->revert($key); - $moveStatus .= ts("WARNING: The setting (%1) has been reverted.", array( + $moveStatus .= ts("WARNING: The setting (%1) has been reverted.", [ 1 => $key, - )); + ]); $moveStatus .= '
    '; } else { - $moveStatus .= ts("WARNING: The setting (%1) is overridden and could not be reverted.", array( + $moveStatus .= ts("WARNING: The setting (%1) is overridden and could not be reverted.", [ 1 => $key, - )); + ]); $moveStatus .= '
    '; } } @@ -259,6 +262,7 @@ public static function doSiteMove($defaultValues = array()) { // clear all caches CRM_Core_Config::clearDBCache(); + Civi::cache('session')->clear(); $moveStatus .= ts('Database cache tables cleared.') . '
    '; $resetSessionTable = CRM_Utils_Request::retrieve('resetSessionTable', @@ -332,7 +336,7 @@ public static function disableComponent($componentName) { // get enabled-components from DB and add to the list $enabledComponents = Civi::settings()->get('enable_components'); - $enabledComponents = array_diff($enabledComponents, array($componentName)); + $enabledComponents = array_diff($enabledComponents, [$componentName]); self::setEnabledComponents($enabledComponents); @@ -356,7 +360,7 @@ public static function setEnabledComponents($enabledComponents) { * @return array */ public static function skipVars() { - return array( + return [ 'dsn', 'templateCompileDir', 'userFrameworkDSN', @@ -382,7 +386,7 @@ public static function skipVars() { 'autocompleteContactReference', 'checksumTimeout', 'checksum_timeout', - ); + ]; } /** @@ -406,26 +410,26 @@ public static function filterSkipVars($params) { * @return array */ private static function getUrlSettings() { - return array( + return [ 'userFrameworkResourceURL', 'imageUploadURL', 'customCSSURL', 'extensionsURL', - ); + ]; } /** * @return array */ private static function getPathSettings() { - return array( + return [ 'uploadDir', 'imageUploadDir', 'customFileUploadDir', 'customTemplateDir', 'customPHPPathDir', 'extensionsDir', - ); + ]; } } diff --git a/CRM/Core/BAO/Country.php b/CRM/Core/BAO/Country.php index 9a311cd7e55f..df195660c008 100644 --- a/CRM/Core/BAO/Country.php +++ b/CRM/Core/BAO/Country.php @@ -1,9 +1,9 @@ get('provinceLimit'); - $country = array(); + $country = []; if (is_array($provinceLimit)) { foreach ($provinceLimit as $val) { // CRM-12007 @@ -74,7 +74,7 @@ public static function provinceLimit() { public static function countryLimit() { if (!isset(Civi::$statics[__CLASS__]['countryLimit'])) { $countryIsoCodes = CRM_Core_PseudoConstant::countryIsoCode(); - $country = array(); + $country = []; $countryLimit = Civi::settings()->get('countryLimit'); if (is_array($countryLimit)) { foreach ($countryLimit as $val) { @@ -139,10 +139,10 @@ public static function defaultCurrencySymbol($defaultCurrency = NULL) { if (!$cachedSymbol || $defaultCurrency) { $currency = $defaultCurrency ? $defaultCurrency : Civi::settings()->get('defaultCurrency'); if ($currency) { - $currencySymbols = CRM_Core_PseudoConstant::get('CRM_Contribute_DAO_Contribution', 'currency', array( + $currencySymbols = CRM_Core_PseudoConstant::get('CRM_Contribute_DAO_Contribution', 'currency', [ 'labelColumn' => 'symbol', 'orderColumn' => TRUE, - )); + ]); $cachedSymbol = CRM_Utils_Array::value($currency, $currencySymbols, ''); } else { @@ -152,6 +152,13 @@ public static function defaultCurrencySymbol($defaultCurrency = NULL) { return $cachedSymbol; } + /** + * Get the default currency symbol. + * + * @param string $k Unused variable + * + * @return string + */ public static function getDefaultCurrencySymbol($k = NULL) { $config = CRM_Core_Config::singleton(); return $config->defaultCurrencySymbol(Civi::settings()->get('defaultCurrency')); diff --git a/CRM/Core/BAO/CustomField.php b/CRM/Core/BAO/CustomField.php index 4bdcacc0fa80..02b9519e671a 100644 --- a/CRM/Core/BAO/CustomField.php +++ b/CRM/Core/BAO/CustomField.php @@ -1,9 +1,9 @@ CRM_Utils_Type + */ + public static function dataToType() { + return [ + 'String' => CRM_Utils_Type::T_STRING, + 'Int' => CRM_Utils_Type::T_INT, + 'Money' => CRM_Utils_Type::T_MONEY, + 'Memo' => CRM_Utils_Type::T_LONGTEXT, + 'Float' => CRM_Utils_Type::T_FLOAT, + 'Date' => CRM_Utils_Type::T_DATE, + 'DateTime' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME, + 'Boolean' => CRM_Utils_Type::T_BOOLEAN, + 'StateProvince' => CRM_Utils_Type::T_INT, + 'File' => CRM_Utils_Type::T_STRING, + 'Link' => CRM_Utils_Type::T_STRING, + 'ContactReference' => CRM_Utils_Type::T_INT, + 'Country' => CRM_Utils_Type::T_INT, + ]; + } + /** * Get data to html array. * @@ -99,7 +123,6 @@ public static function dataToHtml() { 'Radio' => 'Radio', 'CheckBox' => 'CheckBox', 'Multi-Select' => 'Multi-Select', - 'AdvMulti-Select' => 'AdvMulti-Select', 'Autocomplete-Select' => 'Autocomplete-Select', ), array('Text' => 'Text', 'Select' => 'Select', 'Radio' => 'Radio'), @@ -131,7 +154,11 @@ public static function dataToHtml() { public static function create(&$params) { $origParams = array_merge(array(), $params); - if (!isset($params['id'])) { + $op = empty($params['id']) ? 'create' : 'edit'; + + CRM_Utils_Hook::pre($op, 'CustomField', CRM_Utils_Array::value('id', $params), $params); + + if ($op == 'create') { if (!isset($params['column_name'])) { // if add mode & column_name not present, calculate it. $params['column_name'] = strtolower(CRM_Utils_String::munge($params['label'], '_', 32)); @@ -163,7 +190,6 @@ public static function create(&$params) { break; case 'CheckBox': - case 'AdvMulti-Select': case 'Multi-Select': if (isset($params['default_checkbox_option'])) { $tempArray = array_keys($params['default_checkbox_option']); @@ -189,21 +215,14 @@ public static function create(&$params) { } $transaction = new CRM_Core_Transaction(); - // create any option group & values if required - if ($params['html_type'] != 'Text' && - in_array($params['data_type'], array( - 'String', - 'Int', - 'Float', - 'Money', - )) - ) { - $tableName = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', - $params['custom_group_id'], - 'table_name' - ); + $htmlType = CRM_Utils_Array::value('html_type', $params); + $dataType = CRM_Utils_Array::value('data_type', $params); + $allowedOptionTypes = array('String', 'Int', 'Float', 'Money'); + // create any option group & values if required + if ($htmlType != 'Text' && in_array($dataType, $allowedOptionTypes) + ) { //CRM-16659: if option_value then create an option group for this custom field. if ($params['option_type'] == 1 && (empty($params['option_group_id']) || !empty($params['option_value']))) { // first create an option group for this custom group @@ -211,7 +230,9 @@ public static function create(&$params) { $optionGroup->name = "{$columnName}_" . date('YmdHis'); $optionGroup->title = $params['label']; $optionGroup->is_active = 1; - $optionGroup->data_type = $params['data_type']; + // Don't set reserved as it's not a built-in option group and may be useful for other custom fields. + $optionGroup->is_reserved = 0; + $optionGroup->data_type = $dataType; $optionGroup->save(); $params['option_group_id'] = $optionGroup->id; if (!empty($params['option_value']) && is_array($params['option_value'])) { @@ -221,7 +242,7 @@ public static function create(&$params) { $optionValue->option_group_id = $optionGroup->id; $optionValue->label = $params['option_label'][$k]; $optionValue->name = CRM_Utils_String::titleToVar($params['option_label'][$k]); - switch ($params['data_type']) { + switch ($dataType) { case 'Money': $optionValue->value = CRM_Utils_Rule::cleanMoney($v); break; @@ -258,7 +279,7 @@ public static function create(&$params) { if (empty($params['default_value'])) { //don't insert only value separator as default value, CRM-4579 $defaultValue = self::getOptionGroupDefault($params['option_group_id'], - $params['html_type'] + $htmlType ); if (!CRM_Utils_System::isNull(explode(CRM_Core_DAO::VALUE_SEPARATOR, @@ -271,19 +292,21 @@ public static function create(&$params) { } // since we need to save option group id :) - if (!isset($params['attributes']) && strtolower($params['html_type']) == 'textarea') { + if (!isset($params['attributes']) && strtolower($htmlType) == 'textarea') { $params['attributes'] = 'rows=4, cols=60'; } $customField = new CRM_Core_DAO_CustomField(); $customField->copyValues($params); - $customField->is_required = CRM_Utils_Array::value('is_required', $params, FALSE); - $customField->is_searchable = CRM_Utils_Array::value('is_searchable', $params, FALSE); - $customField->in_selector = CRM_Utils_Array::value('in_selector', $params, FALSE); - $customField->is_search_range = CRM_Utils_Array::value('is_search_range', $params, FALSE); - //CRM-15792 - Custom field gets disabled if is_active not set - $customField->is_active = CRM_Utils_Array::value('is_active', $params, TRUE); - $customField->is_view = CRM_Utils_Array::value('is_view', $params, FALSE); + if ($op == 'create') { + $customField->is_required = CRM_Utils_Array::value('is_required', $params, FALSE); + $customField->is_searchable = CRM_Utils_Array::value('is_searchable', $params, FALSE); + $customField->in_selector = CRM_Utils_Array::value('in_selector', $params, FALSE); + $customField->is_search_range = CRM_Utils_Array::value('is_search_range', $params, FALSE); + //CRM-15792 - Custom field gets disabled if is_active not set + $customField->is_active = CRM_Utils_Array::value('is_active', $params, TRUE); + $customField->is_view = CRM_Utils_Array::value('is_view', $params, FALSE); + } $customField->save(); // make sure all values are present in the object for further processing @@ -291,7 +314,7 @@ public static function create(&$params) { $triggerRebuild = CRM_Utils_Array::value('triggerRebuild', $params, TRUE); //create/drop the index when we toggle the is_searchable flag - if (!empty($params['id'])) { + if ($op == 'edit') { self::createField($customField, 'modify', $indexExist, $triggerRebuild); } else { @@ -311,6 +334,8 @@ public static function create(&$params) { // complete transaction $transaction->commit(); + CRM_Utils_Hook::post($op, 'CustomField', $customField->id, $customField); + CRM_Utils_System::flushCache(); return $customField; @@ -338,8 +363,8 @@ public static function retrieve(&$params, &$defaults) { * @param bool $is_active * Value we want to set the is_active field. * - * @return Object - * DAO object on success, null otherwise + * @return bool + * true if we found and updated the object, else false */ public static function setIsActive($id, $is_active) { @@ -377,6 +402,9 @@ public function getOptions($context = NULL) { $this->find(TRUE); } + // This will hold the list of options in format key => label + $options = []; + if (!empty($this->option_group_id)) { $options = CRM_Core_OptionGroup::valuesByID( $this->option_group_id, @@ -396,9 +424,6 @@ public function getOptions($context = NULL) { elseif ($this->data_type === 'Boolean') { $options = $context == 'validate' ? array(0, 1) : CRM_Core_SelectValues::boolean(); } - else { - return FALSE; - } CRM_Utils_Hook::customFieldOptions($this->id, $options, FALSE); CRM_Utils_Hook::fieldOptions($this->getEntity(), "custom_{$this->id}", $options, array('context' => $context)); return $options; @@ -574,7 +599,7 @@ public static function &getFields( if (!empty($customDataSubType)) { $subtypeClause = array(); foreach ($customDataSubType as $subtype) { - $subtype = CRM_Core_DAO::VALUE_SEPARATOR . $subtype . CRM_Core_DAO::VALUE_SEPARATOR; + $subtype = CRM_Core_DAO::VALUE_SEPARATOR . CRM_Utils_Type::escape($subtype, 'String') . CRM_Core_DAO::VALUE_SEPARATOR; $subtypeClause[] = "$cgTable.extends_entity_column_value LIKE '%{$subtype}%'"; } if (!$onlySubType) { @@ -692,6 +717,7 @@ public static function getFieldsForImport( $regexp = preg_replace('/[.,;:!?]/', '', CRM_Utils_Array::value(0, $values)); $importableFields[$key] = array( 'name' => $key, + 'type' => CRM_Utils_Array::value(CRM_Utils_Array::value('data_type', $values), self::dataToType()), 'title' => CRM_Utils_Array::value('label', $values), 'headerPattern' => '/' . preg_quote($regexp, '/') . '/', 'import' => 1, @@ -797,6 +823,7 @@ public static function addQuickFormElement( $field = self::getFieldObject($fieldId); $widget = $field->html_type; $element = NULL; + $customFieldAttributes = array(); // Custom field HTML should indicate group+field name $groupName = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $field->custom_group_id); @@ -819,7 +846,6 @@ public static function addQuickFormElement( 'Multi-Select State/Province', 'Select Country', 'Multi-Select Country', - 'AdvMulti-Select', 'CheckBox', 'Radio', ))); @@ -828,29 +854,35 @@ public static function addQuickFormElement( $options = $field->getOptions($search ? 'search' : 'create'); // Consolidate widget types to simplify the below switch statement - if ($search || ($widget !== 'AdvMulti-Select' && strpos($widget, 'Select') !== FALSE)) { + if ($search || (strpos($widget, 'Select') !== FALSE)) { $widget = 'Select'; } - $selectAttributes = array( - 'data-crm-custom' => $dataCrmCustomVal, - 'class' => 'crm-select2', - ); + + $customFieldAttributes['data-crm-custom'] = $dataCrmCustomVal; + $selectAttributes = array('class' => 'crm-select2'); + // Search field is always multi-select if ($search || strpos($field->html_type, 'Multi') !== FALSE) { $selectAttributes['class'] .= ' huge'; $selectAttributes['multiple'] = 'multiple'; $selectAttributes['placeholder'] = $placeholder; } + // Add data for popup link. Normally this is handled by CRM_Core_Form->addSelect - if ($field->option_group_id && !$search && $widget == 'Select' && CRM_Core_Permission::check('administer CiviCRM')) { - $selectAttributes += array( + $isSupportedWidget = in_array($widget, ['Select', 'Radio']); + $canEditOptions = CRM_Core_Permission::check('administer CiviCRM'); + if ($field->option_group_id && !$search && $isSelect && $canEditOptions) { + $customFieldAttributes += array( 'data-api-entity' => $field->getEntity(), 'data-api-field' => 'custom_' . $field->id, 'data-option-edit-path' => 'civicrm/admin/options/' . CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionGroup', $field->option_group_id), ); + $selectAttributes += $customFieldAttributes; } } + $rangeDataTypes = ['Int', 'Float', 'Money']; + if (!isset($label)) { $label = $field->label; } @@ -860,7 +892,7 @@ public static function addQuickFormElement( switch ($widget) { case 'Text': case 'Link': - if ($field->is_search_range && $search) { + if ($field->is_search_range && $search && in_array($field->data_type, $rangeDataTypes)) { $qf->add('text', $elementName . '_from', $label . ' ' . ts('From'), $field->attributes); $qf->add('text', $elementName . '_to', ts('To'), $field->attributes); } @@ -926,65 +958,70 @@ public static function addQuickFormElement( break; case 'Radio': - $choice = array(); - foreach ($options as $v => $l) { - $choice[] = $qf->createElement('radio', NULL, '', $l, (string) $v, $field->attributes); - } - $element = $qf->addGroup($choice, $elementName, $label); - if ($useRequired && !$search) { - $qf->addRule($elementName, ts('%1 is a required field.', array(1 => $label)), 'required'); + if ($field->is_search_range && $search && in_array($field->data_type, $rangeDataTypes)) { + $qf->add('text', $elementName . '_from', $label . ' ' . ts('From'), $field->attributes); + $qf->add('text', $elementName . '_to', ts('To'), $field->attributes); } else { - $element->setAttribute('allowClear', TRUE); + $choice = array(); + parse_str($field->attributes, $radioAttributes); + $radioAttributes = array_merge($radioAttributes, $customFieldAttributes); + + foreach ($options as $v => $l) { + $choice[] = $qf->createElement('radio', NULL, '', $l, (string) $v, $radioAttributes); + } + $element = $qf->addGroup($choice, $elementName, $label); + $optionEditKey = 'data-option-edit-path'; + if (isset($selectAttributes[$optionEditKey])) { + $element->setAttribute($optionEditKey, $selectAttributes[$optionEditKey]); + } + + if ($useRequired && !$search) { + $qf->addRule($elementName, ts('%1 is a required field.', array(1 => $label)), 'required'); + } + else { + $element->setAttribute('allowClear', TRUE); + } } break; // For all select elements case 'Select': - if (empty($selectAttributes['multiple'])) { - $options = array('' => $placeholder) + $options; - } - $element = $qf->add('select', $elementName, $label, $options, $useRequired && !$search, $selectAttributes); - - // Add and/or option for fields that store multiple values - if ($search && self::isSerialized($field)) { - - $operators = array( - $qf->createElement('radio', NULL, '', ts('Any'), 'or', array('title' => ts('Results may contain any of the selected options'))), - $qf->createElement('radio', NULL, '', ts('All'), 'and', array('title' => ts('Results must have all of the selected options'))), - ); - $qf->addGroup($operators, $elementName . '_operator'); - $qf->setDefaults(array($elementName . '_operator' => 'or')); + if ($field->is_search_range && $search && in_array($field->data_type, $rangeDataTypes)) { + $qf->add('text', $elementName . '_from', $label . ' ' . ts('From'), $field->attributes); + $qf->add('text', $elementName . '_to', ts('To'), $field->attributes); } - break; - - case 'AdvMulti-Select': - $element = $qf->addElement( - 'advmultiselect', - $elementName, - $label, $options, - array( - 'size' => 5, - 'style' => '', - 'class' => 'advmultiselect', - 'data-crm-custom' => $dataCrmCustomVal, - ) - ); + else { + if (empty($selectAttributes['multiple'])) { + $options = array('' => $placeholder) + $options; + } + $element = $qf->add('select', $elementName, $label, $options, $useRequired && !$search, $selectAttributes); - $element->setButtonAttributes('add', array('value' => ts('Add >>'))); - $element->setButtonAttributes('remove', array('value' => ts('<< Remove'))); + // Add and/or option for fields that store multiple values + if ($search && self::isSerialized($field)) { - if ($useRequired && !$search) { - $qf->addRule($elementName, ts('%1 is a required field.', array(1 => $label)), 'required'); + $operators = array( + $qf->createElement('radio', NULL, '', ts('Any'), 'or', array('title' => ts('Results may contain any of the selected options'))), + $qf->createElement('radio', NULL, '', ts('All'), 'and', array('title' => ts('Results must have all of the selected options'))), + ); + $qf->addGroup($operators, $elementName . '_operator'); + $qf->setDefaults(array($elementName . '_operator' => 'or')); + } } break; case 'CheckBox': $check = array(); foreach ($options as $v => $l) { - $check[] = &$qf->addElement('advcheckbox', $v, NULL, $l, array('data-crm-custom' => $dataCrmCustomVal)); + $check[] = &$qf->addElement('advcheckbox', $v, NULL, $l, $customFieldAttributes); } - $element = $qf->addGroup($check, $elementName, $label); + + $group = $element = $qf->addGroup($check, $elementName, $label); + $optionEditKey = 'data-option-edit-path'; + if (isset($customFieldAttributes[$optionEditKey])) { + $group->setAttribute($optionEditKey, $customFieldAttributes[$optionEditKey]); + } + if ($useRequired && !$search) { $qf->addRule($elementName, ts('%1 is a required field.', array(1 => $label)), 'required'); } @@ -1039,8 +1076,15 @@ public static function addQuickFormElement( $element = $qf->add('text', $elementName, $label, $attributes, $useRequired && !$search); $urlParams = "context=customfield&id={$field->id}"; - - $customUrls[$elementName] = CRM_Utils_System::url('civicrm/ajax/contactref', + $idOfelement = $elementName; + // dev/core#362 if in an onbehalf profile clean up the name to get rid of square brackets that break the select 2 js + // However this caused regression https://lab.civicrm.org/dev/core/issues/619 so it has been hacked back to + // only affecting on behalf - next time someone looks at this code it should be with a view to overhauling it + // rather than layering on more hacks. + if (substr($elementName, 0, 8) === 'onbehalf' && strpos($elementName, '[') && strpos($elementName, ']')) { + $idOfelement = substr(substr($elementName, (strpos($elementName, '[') + 1)), 0, -1); + } + $customUrls[$idOfelement] = CRM_Utils_System::url('civicrm/ajax/contactref', $urlParams, FALSE, NULL, FALSE ); @@ -1049,11 +1093,11 @@ public static function addQuickFormElement( else { // FIXME: This won't work with customFieldOptions hook $attributes += array( - 'entity' => 'option_value', + 'entity' => 'OptionValue', 'placeholder' => $placeholder, 'multiple' => $search, 'api' => array( - 'params' => array('option_group_id' => $field->option_group_id), + 'params' => array('option_group_id' => $field->option_group_id, 'is_active' => 1), ), ); $element = $qf->addEntityRef($elementName, $label, $attributes, $useRequired && !$search); @@ -1128,17 +1172,19 @@ public static function deleteField($field) { $field->delete(); CRM_Core_BAO_UFField::delUFField($field->id); CRM_Utils_Weight::correctDuplicateWeights('CRM_Core_DAO_CustomField'); + + CRM_Utils_Hook::post('delete', 'CustomField', $field->id, $field); } /** * @param string|int|array|null $value * @param CRM_Core_BAO_CustomField|int|array|string $field - * @param $contactId + * @param int $entityId * * @return string * @throws \Exception */ - public static function displayValue($value, $field, $contactId = NULL) { + public static function displayValue($value, $field, $entityId = NULL) { $field = is_array($field) ? $field['id'] : $field; $fieldId = is_object($field) ? $field->id : (int) str_replace('custom_', '', $field); @@ -1152,7 +1198,7 @@ public static function displayValue($value, $field, $contactId = NULL) { $fieldInfo = array('options' => $field->getOptions()) + (array) $field; - return self::formatDisplayValue($value, $fieldInfo, $contactId); + return self::formatDisplayValue($value, $fieldInfo, $entityId); } /** @@ -1170,8 +1216,8 @@ private static function formatDisplayValue($value, $field, $entityId = NULL) { $value = CRM_Utils_Array::explodePadded($value); } // CRM-12989 fix - if ($field['html_type'] == 'CheckBox') { - CRM_Utils_Array::formatArrayKeys($value); + if ($field['html_type'] == 'CheckBox' && $value) { + $value = CRM_Utils_Array::convertCheckboxFormatToArray($value); } $display = is_array($value) ? implode(', ', $value) : (string) $value; @@ -1184,12 +1230,16 @@ private static function formatDisplayValue($value, $field, $entityId = NULL) { case 'Select Country': case 'Select State/Province': case 'CheckBox': - case 'AdvMulti-Select': case 'Multi-Select': case 'Multi-Select State/Province': case 'Multi-Select Country': if ($field['data_type'] == 'ContactReference' && $value) { - $display = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $value, 'display_name'); + if (is_numeric($value)) { + $display = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $value, 'display_name'); + } + else { + $display = $value; + } } elseif (is_array($value)) { $v = array(); @@ -1210,7 +1260,7 @@ private static function formatDisplayValue($value, $field, $entityId = NULL) { // Or should we throw an exception here if it is? $value = is_array($value) ? CRM_Utils_Array::first($value) : $value; - $actualPHPFormats = CRM_Core_SelectValues::datePluginToPHPFormats(); + $actualPHPFormats = CRM_Utils_Date::datePluginToPHPFormats(); $format = CRM_Utils_Array::value('date_format', $field); if ($format) { @@ -1226,8 +1276,8 @@ private static function formatDisplayValue($value, $field, $entityId = NULL) { break; default: - // if time is not selected remove time from value - $value = substr($value, 0, 10); + //If time is not selected remove time from value. + $value = $value ? date('Y-m-d', strtotime($value)) : ''; } $customFormat = implode(" ", $customTimeFormat); } @@ -1239,7 +1289,13 @@ private static function formatDisplayValue($value, $field, $entityId = NULL) { // In the context of displaying a profile, show file/image if ($value) { if ($entityId) { - $url = self::getFileURL($entityId, $field['id']); + if (CRM_Utils_Rule::positiveInteger($value)) { + $fileId = $value; + } + else { + $fileId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_File', $value, 'id', 'uri'); + } + $url = self::getFileURL($entityId, $field['id'], $fileId); if ($url) { $display = $url['file_url']; } @@ -1258,13 +1314,17 @@ private static function formatDisplayValue($value, $field, $entityId = NULL) { } break; + case 'Link': + $display = $display ? "$display" : $display; + break; + case 'TextArea': $display = nl2br($display); break; case 'Text': if ($field['data_type'] == 'Money' && isset($value)) { - //$value can also be an array(while using IN operator from search builder or api). + // $value can also be an array(while using IN operator from search builder or api). foreach ((array) $value as $val) { $disp[] = CRM_Utils_Money::format($val, NULL, NULL, TRUE); } @@ -1343,7 +1403,6 @@ public static function setProfileDefaults( } switch ($customField->html_type) { case 'CheckBox': - case 'AdvMulti-Select': case 'Multi-Select': $customOption = CRM_Core_BAO_CustomOption::getCustomOption($customFieldId, FALSE); $defaults[$elementName] = array(); @@ -1355,9 +1414,7 @@ public static function setProfileDefaults( if ($customField->html_type == 'CheckBox') { $defaults[$elementName][$val['value']] = 1; } - elseif ($customField->html_type == 'Multi-Select' || - $customField->html_type == 'AdvMulti-Select' - ) { + elseif ($customField->html_type == 'Multi-Select') { $defaults[$elementName][$val['value']] = $val['value']; } } @@ -1435,31 +1492,28 @@ public static function getFileURL($contactID, $cfID, $fileID = NULL, $absolute = $entityId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_EntityFile', $fileID, 'entity_id', - 'id' + 'file_id' ); - list($path) = CRM_Core_BAO_File::path($fileID, $entityId, NULL, NULL); - list($imageWidth, $imageHeight) = getimagesize($path); - list($imageThumbWidth, $imageThumbHeight) = CRM_Contact_BAO_Contact::getThumbSize($imageWidth, $imageHeight); + list($path) = CRM_Core_BAO_File::path($fileID, $entityId); + $fileHash = CRM_Core_BAO_File::generateFileHash($entityId, $fileID); $url = CRM_Utils_System::url('civicrm/file', - "reset=1&id=$fileID&eid=$contactID", + "reset=1&id=$fileID&eid=$entityId&fcs=$fileHash", $absolute, NULL, TRUE, TRUE ); - $result['file_url'] = " - - - "; - // for non image files + $result['file_url'] = CRM_Utils_File::getFileURL($path, $fileType, $url); } + // for non image files else { $uri = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_File', $fileID, 'uri' ); + $fileHash = CRM_Core_BAO_File::generateFileHash($contactID, $fileID); $url = CRM_Utils_System::url('civicrm/file', - "reset=1&id=$fileID&eid=$contactID", + "reset=1&id=$fileID&eid=$contactID&fcs=$fileHash", $absolute, NULL, TRUE, TRUE ); - $result['file_url'] = "{$uri}"; + $result['file_url'] = CRM_Utils_File::getFileURL($uri, $fileType, $url); } } return $result; @@ -1572,9 +1626,7 @@ public static function formatCustomField( } } - if ($customFields[$customFieldId]['html_type'] == 'Multi-Select' || - $customFields[$customFieldId]['html_type'] == 'AdvMulti-Select' - ) { + if ($customFields[$customFieldId]['html_type'] == 'Multi-Select') { if ($value) { $value = CRM_Utils_Array::implodePadded($value); } @@ -1584,7 +1636,6 @@ public static function formatCustomField( } if (($customFields[$customFieldId]['html_type'] == 'Multi-Select' || - $customFields[$customFieldId]['html_type'] == 'AdvMulti-Select' || $customFields[$customFieldId]['html_type'] == 'CheckBox' ) && $customFields[$customFieldId]['data_type'] == 'String' && @@ -1639,7 +1690,7 @@ public static function formatCustomField( $value = 0; } - $fileId = NULL; + $fileID = NULL; if ($customFields[$customFieldId]['data_type'] == 'File') { if (empty($value)) { @@ -1648,9 +1699,6 @@ public static function formatCustomField( $config = CRM_Core_Config::singleton(); - $fName = $value['name']; - $mimeType = $value['type']; - // If we are already passing the file id as a value then retrieve and set the file data if (CRM_Utils_Rule::integer($value)) { $fileDAO = new CRM_Core_DAO_File(); @@ -1662,6 +1710,10 @@ public static function formatCustomField( $mimeType = $fileDAO->mime_type; } } + else { + $fName = $value['name']; + $mimeType = $value['type']; + } $filename = pathinfo($fName, PATHINFO_BASENAME); @@ -1677,20 +1729,20 @@ public static function formatCustomField( FROM $tableName WHERE id = %1"; $params = array(1 => array($customValueId, 'Integer')); - $fileId = CRM_Core_DAO::singleValueQuery($query, $params); + $fileID = CRM_Core_DAO::singleValueQuery($query, $params); } $fileDAO = new CRM_Core_DAO_File(); - if ($fileId) { - $fileDAO->id = $fileId; + if ($fileID) { + $fileDAO->id = $fileID; } $fileDAO->uri = $filename; $fileDAO->mime_type = $mimeType; $fileDAO->upload_date = date('YmdHis'); $fileDAO->save(); - $fileId = $fileDAO->id; + $fileID = $fileDAO->id; $value = $filename; } @@ -1718,7 +1770,7 @@ public static function formatCustomField( 'custom_group_id' => $groupID, 'table_name' => $tableName, 'column_name' => $columnName, - 'file_id' => $fileId, + 'file_id' => $fileID, 'is_multiple' => $customFields[$customFieldId]['is_multiple'], ); @@ -2008,6 +2060,8 @@ public static function getTableColumnGroup($fieldID, $force = FALSE) { /** * Get custom option groups. * + * @deprecated Use the API OptionGroup.get + * * @param array $includeFieldIds * Ids of custom fields for which option groups must be included. * @@ -2192,46 +2246,46 @@ public static function postProcess( } /** + * Get custom field ID from field/group name/title. * - */ - - /** - * Get custom field ID. - * - * @param string $fieldLabel - * @param null $groupTitle + * @param string $fieldName Field name or label + * @param string|null $groupName (Optional) Group name or label + * @param bool $fullString Whether to return "custom_123" or "123" * - * @return int|null + * @return string|int|null + * @throws \CiviCRM_API3_Exception */ - public static function getCustomFieldID($fieldLabel, $groupTitle = NULL) { - $params = array(1 => array($fieldLabel, 'String')); - if ($groupTitle) { - $params[2] = array($groupTitle, 'String'); - $sql = " -SELECT f.id -FROM civicrm_custom_field f -INNER JOIN civicrm_custom_group g ON f.custom_group_id = g.id -WHERE ( f.label = %1 OR f.name = %1 ) -AND ( g.title = %2 OR g.name = %2 ) -"; - } - else { - $sql = " -SELECT f.id -FROM civicrm_custom_field f -WHERE ( f.label = %1 OR f.name = %1 ) -"; - } + public static function getCustomFieldID($fieldName, $groupName = NULL, $fullString = FALSE) { + $cacheKey = $groupName . '.' . $fieldName; + if (!isset(Civi::$statics['CRM_Core_BAO_CustomField'][$cacheKey])) { + $customFieldParams = [ + 'name' => $fieldName, + 'label' => $fieldName, + 'options' => ['or' => [["name", "label"]]], + ]; + + if ($groupName) { + $customFieldParams['custom_group_id.name'] = $groupName; + $customFieldParams['custom_group_id.title'] = $groupName; + $customFieldParams['options'] = ['or' => [["name", "label"], ["custom_group_id.name", "custom_group_id.title"]]]; + } - $dao = CRM_Core_DAO::executeQuery($sql, $params); - if ($dao->fetch() && - $dao->N == 1 - ) { - return $dao->id; + $field = civicrm_api3('CustomField', 'get', $customFieldParams); + + if (empty($field['id'])) { + Civi::$statics['CRM_Core_BAO_CustomField'][$cacheKey]['id'] = NULL; + Civi::$statics['CRM_Core_BAO_CustomField'][$cacheKey]['string'] = NULL; + } + else { + Civi::$statics['CRM_Core_BAO_CustomField'][$cacheKey]['id'] = $field['id']; + Civi::$statics['CRM_Core_BAO_CustomField'][$cacheKey]['string'] = 'custom_' . $field['id']; + } } - else { - return NULL; + + if ($fullString) { + return Civi::$statics['CRM_Core_BAO_CustomField'][$cacheKey]['string']; } + return Civi::$statics['CRM_Core_BAO_CustomField'][$cacheKey]['id']; } /** @@ -2400,6 +2454,31 @@ public static function isMultiRecordField($customId) { return $isMultipleWithGid; } + /** + * Does this field type have any select options? + * + * @param array $field + * + * @return bool + */ + public static function hasOptions($field) { + // Fields retrieved via api are an array, or from the dao are an object. We'll accept either. + $field = (array) $field; + // This will include boolean fields with Yes/No options. + if (in_array($field['html_type'], ['Radio', 'CheckBox'])) { + return TRUE; + } + // Do this before the "Select" string search because date fields have a "Select Date" html_type + // and contactRef fields have an "Autocomplete-Select" html_type - contacts are an FK not an option list. + if (in_array($field['data_type'], ['ContactReference', 'Date'])) { + return FALSE; + } + if (strpos($field['html_type'], 'Select') !== FALSE) { + return TRUE; + } + return !empty($field['option_group_id']); + } + /** * Does this field store a serialized string? * diff --git a/CRM/Core/BAO/CustomGroup.php b/CRM/Core/BAO/CustomGroup.php index 0e3574b66efd..d20a3188d916 100644 --- a/CRM/Core/BAO/CustomGroup.php +++ b/CRM/Core/BAO/CustomGroup.php @@ -1,9 +1,9 @@ title = $params['title']; } - if (in_array($params['extends'][0], - array( - 'ParticipantRole', - 'ParticipantEventName', - 'ParticipantEventType', - ) - )) { + $extends = CRM_Utils_Array::value('extends', $params, []); + $extendsEntity = CRM_Utils_Array::value(0, $extends); + + $participantEntities = [ + 'ParticipantRole', + 'ParticipantEventName', + 'ParticipantEventType', + ]; + + if (in_array($extendsEntity, $participantEntities)) { $group->extends = 'Participant'; } else { - $group->extends = $params['extends'][0]; + $group->extends = $extendsEntity; } $group->extends_entity_column_id = 'null'; - if ( - $params['extends'][0] == 'ParticipantRole' || - $params['extends'][0] == 'ParticipantEventName' || - $params['extends'][0] == 'ParticipantEventType' + if (in_array($extendsEntity, $participantEntities) ) { - $group->extends_entity_column_id = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionValue', $params['extends'][0], 'value', 'name'); + $group->extends_entity_column_id = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionValue', $extendsEntity, 'value', 'name'); } - //this is format when form get submit. - $extendsChildType = CRM_Utils_Array::value(1, $params['extends']); - //lets allow user to pass direct child type value, CRM-6893 + // this is format when form get submit. + $extendsChildType = CRM_Utils_Array::value(1, $extends); + // lets allow user to pass direct child type value, CRM-6893 if (!empty($params['extends_entity_column_value'])) { $extendsChildType = $params['extends_entity_column_value']; } if (!CRM_Utils_System::isNull($extendsChildType)) { $extendsChildType = implode(CRM_Core_DAO::VALUE_SEPARATOR, $extendsChildType); - if (CRM_Utils_Array::value(0, $params['extends']) == 'Relationship') { - $extendsChildType = str_replace(array('_a_b', '_b_a'), array( + if (CRM_Utils_Array::value(0, $extends) == 'Relationship') { + $extendsChildType = str_replace(['_a_b', '_b_a'], [ '', '', - ), $extendsChildType); + ], $extendsChildType); } if (substr($extendsChildType, 0, 1) != CRM_Core_DAO::VALUE_SEPARATOR) { $extendsChildType = CRM_Core_DAO::VALUE_SEPARATOR . $extendsChildType . @@ -113,7 +114,7 @@ public static function create(&$params) { $oldWeight = 0; } $group->weight = CRM_Utils_Weight::updateOtherWeights('CRM_Core_DAO_CustomGroup', $oldWeight, CRM_Utils_Array::value('weight', $params, FALSE)); - $fields = array( + $fields = [ 'style', 'collapse_display', 'collapse_adv_display', @@ -121,33 +122,41 @@ public static function create(&$params) { 'help_post', 'is_active', 'is_multiple', - ); + ]; + $current_db_version = CRM_Core_DAO::singleValueQuery("SELECT version FROM civicrm_domain WHERE id = " . CRM_Core_Config::domainID()); + $is_public_version = $current_db_version >= '4.7.19' ? 1 : 0; + if ($is_public_version) { + $fields[] = 'is_public'; + } foreach ($fields as $field) { - if (isset($params[$field]) || $field == 'is_multiple') { - $group->$field = CRM_Utils_Array::value($field, $params, FALSE); + if (isset($params[$field])) { + $group->$field = $params[$field]; } } $group->max_multiple = isset($params['is_multiple']) ? (isset($params['max_multiple']) && $params['max_multiple'] >= '0' ) ? $params['max_multiple'] : 'null' : 'null'; - $tableName = $oldTableName = NULL; + $tableName = $tableNameNeedingIndexUpdate = NULL; if (isset($params['id'])) { $group->id = $params['id']; - //check whether custom group was changed from single-valued to multiple-valued - $isMultiple = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', - $params['id'], - 'is_multiple' - ); - if ((!empty($params['is_multiple']) || $isMultiple) && - ($params['is_multiple'] != $isMultiple) - ) { - $oldTableName = CRM_Core_DAO::getFieldValue( - 'CRM_Core_DAO_CustomGroup', + if (isset($params['is_multiple'])) { + // check whether custom group was changed from single-valued to multiple-valued + $isMultiple = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $params['id'], - 'table_name' + 'is_multiple' ); + + // dev/core#227 Fix issue where is_multiple in params maybe an empty string if checkbox is not rendered on the form. + $paramsIsMultiple = empty($params['is_multiple']) ? 0 : 1; + if ($paramsIsMultiple != $isMultiple) { + $tableNameNeedingIndexUpdate = CRM_Core_DAO::getFieldValue( + 'CRM_Core_DAO_CustomGroup', + $params['id'], + 'table_name' + ); + } } } else { @@ -167,7 +176,7 @@ public static function create(&$params) { if (CRM_Core_DAO_AllCoreTables::isCoreTable($tableName)) { // Bad idea. Prevent group creation because it might lead to a broken configuration. - CRM_Core_Error::fatal(ts("Cannot create custom table because %1 is already a core table.", array('1' => $tableName))); + CRM_Core_Error::fatal(ts("Cannot create custom table because %1 is already a core table.", ['1' => $tableName])); } } } @@ -184,7 +193,7 @@ public static function create(&$params) { $group->save(); if (!isset($params['id'])) { if (!isset($params['table_name'])) { - $munged_title = strtolower(CRM_Utils_String::munge($group->title, '_', 42)); + $munged_title = strtolower(CRM_Utils_String::munge($group->title, '_', 13)); $tableName = "civicrm_value_{$munged_title}_{$group->id}"; } $group->table_name = $tableName; @@ -197,8 +206,8 @@ public static function create(&$params) { // now create the table associated with this group self::createTable($group); } - elseif ($oldTableName) { - CRM_Core_BAO_SchemaHandler::changeUniqueToIndex($oldTableName, CRM_Utils_Array::value('is_multiple', $params)); + elseif ($tableNameNeedingIndexUpdate) { + CRM_Core_BAO_SchemaHandler::changeUniqueToIndex($tableNameNeedingIndexUpdate, CRM_Utils_Array::value('is_multiple', $params)); } if (CRM_Utils_Array::value('overrideFKConstraint', $params) == 1) { @@ -206,7 +215,7 @@ public static function create(&$params) { $params['id'], 'table_name' ); - CRM_Core_BAO_SchemaHandler::changeFKConstraint($table, self::mapTableName($params['extends'][0])); + CRM_Core_BAO_SchemaHandler::changeFKConstraint($table, self::mapTableName($extendsEntity)); } $transaction->commit(); @@ -245,8 +254,8 @@ public static function retrieve(&$params, &$defaults) { * @param bool $is_active * Value we want to set the is_active field. * - * @return Object - * DAO object on success, null otherwise + * @return bool + * true if we found and updated the object, else false */ public static function setIsActive($id, $is_active) { // reset the cache @@ -294,16 +303,17 @@ public static function autoCreateByActivityType($activityTypeId) { if (self::hasCustomGroup('Activity', NULL, $activityTypeId)) { return TRUE; } - $activityTypes = CRM_Core_PseudoConstant::activityType(TRUE, TRUE, FALSE, 'label', TRUE, FALSE); // everything - $params = array( + // everything + $activityTypes = CRM_Core_PseudoConstant::activityType(TRUE, TRUE, FALSE, 'label', TRUE, FALSE); + $params = [ 'version' => 3, 'extends' => 'Activity', 'extends_entity_column_id' => NULL, - 'extends_entity_column_value' => CRM_Utils_Array::implodePadded(array($activityTypeId)), - 'title' => ts('%1 Questions', array(1 => $activityTypes[$activityTypeId])), + 'extends_entity_column_value' => CRM_Utils_Array::implodePadded([$activityTypeId]), + 'title' => ts('%1 Questions', [1 => $activityTypes[$activityTypeId]]), 'style' => 'Inline', 'is_active' => 1, - ); + ]; $result = civicrm_api('CustomGroup', 'create', $params); return !$result['is_error']; } @@ -316,8 +326,8 @@ public static function autoCreateByActivityType($activityTypeId) { * * @param string $entityType * Of the contact whose contact type is needed. - * @param CRM_Core_Form $deprecated - * Not used. + * @param array $toReturn + * What data should be returned. ['custom_group' => ['id', 'name', etc.], 'custom_field' => ['id', 'label', etc.]] * @param int $entityID * @param int $groupID * @param array $subTypes @@ -330,6 +340,9 @@ public static function autoCreateByActivityType($activityTypeId) { * api - through which it is properly tested - so can be refactored with some comfort.) * * @param bool $checkPermission + * @param string|int $singleRecord + * holds 'new' or id if view/edit/copy form for a single record is being loaded. + * @param bool $showPublicOnly * * @return array * Custom field 'tree'. @@ -342,25 +355,29 @@ public static function autoCreateByActivityType($activityTypeId) { * @todo - review this - It also returns an array called 'info' with tables, select, from, where keys * The reason for the info array in unclear and it could be determined from parsing the group tree after creation * With caching the performance impact would be small & the function would be cleaner + * + * @throws \CRM_Core_Exception */ public static function getTree( $entityType, - $deprecated = NULL, + $toReturn = [], $entityID = NULL, $groupID = NULL, - $subTypes = array(), + $subTypes = [], $subName = NULL, $fromCache = TRUE, $onlySubType = NULL, $returnAll = FALSE, - $checkPermission = TRUE + $checkPermission = TRUE, + $singleRecord = NULL, + $showPublicOnly = FALSE ) { if ($entityID) { $entityID = CRM_Utils_Type::escape($entityID, 'Integer'); } if (!is_array($subTypes)) { if (empty($subTypes)) { - $subTypes = array(); + $subTypes = []; } else { if (stristr($subTypes, ',')) { @@ -373,12 +390,12 @@ public static function getTree( } // create a new tree - $strWhere = $orderBy = ''; - // using tableData to build the queryString - $tableData = array( - 'civicrm_custom_field' => array( + // legacy hardcoded list of data to return + $tableData = [ + 'custom_field' => [ 'id', + 'name', 'label', 'column_name', 'data_type', @@ -396,8 +413,8 @@ public static function getTree( 'time_format', 'option_group_id', 'in_selector', - ), - 'civicrm_custom_group' => array( + ], + 'custom_group' => [ 'id', 'name', 'table_name', @@ -411,15 +428,32 @@ public static function getTree( 'extends_entity_column_id', 'extends_entity_column_value', 'max_multiple', - ), - ); + ], + ]; + $current_db_version = CRM_Core_DAO::singleValueQuery("SELECT version FROM civicrm_domain WHERE id = " . CRM_Core_Config::domainID()); + $is_public_version = $current_db_version >= '4.7.19' ? 1 : 0; + if ($is_public_version) { + $tableData['custom_group'][] = 'is_public'; + } + if (!$toReturn || !is_array($toReturn)) { + $toReturn = $tableData; + } + else { + // Supply defaults and remove unknown array keys + $toReturn = array_intersect_key(array_filter($toReturn) + $tableData, $tableData); + // Merge in required fields that we must have + $toReturn['custom_field'] = array_unique(array_merge($toReturn['custom_field'], ['id', 'column_name', 'data_type'])); + $toReturn['custom_group'] = array_unique(array_merge($toReturn['custom_group'], ['id', 'is_multiple', 'table_name', 'name'])); + // Validate return fields + $toReturn['custom_field'] = array_intersect($toReturn['custom_field'], array_keys(CRM_Core_DAO_CustomField::fieldKeys())); + $toReturn['custom_group'] = array_intersect($toReturn['custom_group'], array_keys(CRM_Core_DAO_CustomGroup::fieldKeys())); + } // create select - $select = array(); - foreach ($tableData as $tableName => $tableColumn) { + $select = []; + foreach ($toReturn as $tableName => $tableColumn) { foreach ($tableColumn as $columnName) { - $alias = $tableName . "_" . $columnName; - $select[] = "{$tableName}.{$columnName} as {$tableName}_{$columnName}"; + $select[] = "civicrm_{$tableName}.{$columnName} as civicrm_{$tableName}_{$columnName}"; } } $strSelect = "SELECT " . implode(', ', $select); @@ -445,11 +479,13 @@ public static function getTree( $in = "'$entityType'"; } + $params = []; + $sqlParamKey = 1; if (!empty($subTypes)) { foreach ($subTypes as $key => $subType) { $subTypeClauses[] = self::whereListHas("civicrm_custom_group.extends_entity_column_value", self::validateSubTypeByEntity($entityType, $subType)); } - $subTypeClause = '(' . implode(' OR ', $subTypeClauses) . ')'; + $subTypeClause = '(' . implode(' OR ', $subTypeClauses) . ')'; if (!$onlySubType) { $subTypeClause = '(' . $subTypeClause . ' OR civicrm_custom_group.extends_entity_column_value IS NULL )'; } @@ -461,7 +497,9 @@ public static function getTree( AND $subTypeClause "; if ($subName) { - $strWhere .= " AND civicrm_custom_group.extends_entity_column_id = {$subName} "; + $strWhere .= " AND civicrm_custom_group.extends_entity_column_id = %{$sqlParamKey}"; + $params[$sqlParamKey] = [$subName, 'String']; + $sqlParamKey = $sqlParamKey + 1; } } else { @@ -475,11 +513,10 @@ public static function getTree( } } - $params = array(); if ($groupID > 0) { // since we want a specific group id we add it to the where clause - $strWhere .= " AND civicrm_custom_group.id = %1"; - $params[1] = array($groupID, 'Integer'); + $strWhere .= " AND civicrm_custom_group.id = %{$sqlParamKey}"; + $params[$sqlParamKey] = [$groupID, 'Integer']; } elseif (!$groupID) { // since groupID is false we need to show all Inline groups @@ -493,6 +530,10 @@ public static function getTree( ); } + if ($showPublicOnly && $is_public_version) { + $strWhere .= "AND civicrm_custom_group.is_public = 1"; + } + $orderBy = " ORDER BY civicrm_custom_group.weight, civicrm_custom_group.title, @@ -515,16 +556,15 @@ public static function getTree( $cacheKey = "CRM_Core_DAO_CustomGroup_Query " . md5($cacheString); $multipleFieldGroupCacheKey = "CRM_Core_DAO_CustomGroup_QueryMultipleFields " . md5($cacheString); $cache = CRM_Utils_Cache::singleton(); - $tablesWithEntityData = array(); if ($fromCache) { $groupTree = $cache->get($cacheKey); $multipleFieldGroups = $cache->get($multipleFieldGroupCacheKey); } if (empty($groupTree)) { - $groupTree = $multipleFieldGroups = array(); + $groupTree = $multipleFieldGroups = []; $crmDAO = CRM_Core_DAO::executeQuery($queryString, $params); - $customValueTables = array(); + $customValueTables = []; // process records while ($crmDAO->fetch()) { @@ -536,11 +576,11 @@ public static function getTree( } // create an array for groups if it does not exist if (!array_key_exists($groupID, $groupTree)) { - $groupTree[$groupID] = array(); + $groupTree[$groupID] = []; $groupTree[$groupID]['id'] = $groupID; // populate the group information - foreach ($tableData['civicrm_custom_group'] as $fieldName) { + foreach ($toReturn['custom_group'] as $fieldName) { $fullFieldName = "civicrm_custom_group_$fieldName"; if ($fieldName == 'id' || is_null($crmDAO->$fullFieldName) @@ -555,21 +595,21 @@ public static function getTree( } $groupTree[$groupID][$fieldName] = $crmDAO->$fullFieldName; } - $groupTree[$groupID]['fields'] = array(); + $groupTree[$groupID]['fields'] = []; - $customValueTables[$crmDAO->civicrm_custom_group_table_name] = array(); + $customValueTables[$crmDAO->civicrm_custom_group_table_name] = []; } // add the fields now (note - the query row will always contain a field) // we only reset this once, since multiple values come is as multiple rows if (!array_key_exists($fieldId, $groupTree[$groupID]['fields'])) { - $groupTree[$groupID]['fields'][$fieldId] = array(); + $groupTree[$groupID]['fields'][$fieldId] = []; } $customValueTables[$crmDAO->civicrm_custom_group_table_name][$crmDAO->civicrm_custom_field_column_name] = 1; $groupTree[$groupID]['fields'][$fieldId]['id'] = $fieldId; // populate information for a custom field - foreach ($tableData['civicrm_custom_field'] as $fieldName) { + foreach ($toReturn['custom_field'] as $fieldName) { $fullFieldName = "civicrm_custom_field_$fieldName"; if ($fieldName == 'id' || is_null($crmDAO->$fullFieldName) @@ -581,32 +621,32 @@ public static function getTree( } if (!empty($customValueTables)) { - $groupTree['info'] = array('tables' => $customValueTables); + $groupTree['info'] = ['tables' => $customValueTables]; } $cache->set($cacheKey, $groupTree); $cache->set($multipleFieldGroupCacheKey, $multipleFieldGroups); } - //entitySelectClauses is an array of select clauses for custom value tables which are not multiple + // entitySelectClauses is an array of select clauses for custom value tables which are not multiple // and have data for the given entities. $entityMultipleSelectClauses is the same for ones with multiple - $entitySingleSelectClauses = $entityMultipleSelectClauses = $groupTree['info']['select'] = array(); - $singleFieldTables = array(); + $entitySingleSelectClauses = $entityMultipleSelectClauses = $groupTree['info']['select'] = []; + $singleFieldTables = []; // now that we have all the groups and fields, lets get the values // since we need to know the table and field names // add info to groupTree if (isset($groupTree['info']) && !empty($groupTree['info']) && - !empty($groupTree['info']['tables']) + !empty($groupTree['info']['tables']) && $singleRecord != 'new' ) { - $select = $from = $where = array(); + $select = $from = $where = []; $groupTree['info']['where'] = NULL; foreach ($groupTree['info']['tables'] as $table => $fields) { $groupTree['info']['from'][] = $table; - $select = array( + $select = [ "{$table}.id as {$table}_id", "{$table}.entity_id as {$table}_entity_id", - ); + ]; foreach ($fields as $column => $dontCare) { $select[] = "{$table}.{$column} as {$table}_{$column}"; } @@ -630,7 +670,7 @@ public static function getTree( } $multipleFieldTablesWithEntityData = array_keys($entityMultipleSelectClauses); if (!empty($multipleFieldTablesWithEntityData)) { - self::buildEntityTreeMultipleFields($groupTree, $entityID, $entityMultipleSelectClauses, $multipleFieldTablesWithEntityData); + self::buildEntityTreeMultipleFields($groupTree, $entityID, $entityMultipleSelectClauses, $multipleFieldTablesWithEntityData, $singleRecord); } } @@ -645,7 +685,6 @@ public static function getTree( * * @return string * @throws \CRM_Core_Exception - * @throws \CiviCRM_API3_Exception */ protected static function validateSubTypeByEntity($entityType, $subType) { $subType = trim($subType, CRM_Core_DAO::VALUE_SEPARATOR); @@ -654,10 +693,13 @@ protected static function validateSubTypeByEntity($entityType, $subType) { } $contactTypes = CRM_Contact_BAO_ContactType::basicTypeInfo(TRUE); + $contactTypes = array_merge($contactTypes, ['Event' => 1]); + if ($entityType != 'Contact' && !array_key_exists($entityType, $contactTypes)) { throw new CRM_Core_Exception('Invalid Entity Filter'); } $subTypes = CRM_Contact_BAO_ContactType::subTypeInfo($entityType, TRUE); + $subTypes = array_merge($subTypes, CRM_Event_PseudoConstant::eventType()); if (!array_key_exists($subType, $subTypes)) { throw new CRM_Core_Exception('Invalid Filter'); } @@ -674,8 +716,9 @@ protected static function validateSubTypeByEntity($entityType, $subType) { * @return string * SQL condition. */ - static private function whereListHas($column, $value, $delimiter = CRM_Core_DAO::VALUE_SEPARATOR) { - $bareValue = trim($value, $delimiter); // ? + private static function whereListHas($column, $value, $delimiter = CRM_Core_DAO::VALUE_SEPARATOR) { + // ? + $bareValue = trim($value, $delimiter); $escapedValue = CRM_Utils_Type::escape("%{$delimiter}{$bareValue}{$delimiter}%", 'String', FALSE); return "($column LIKE \"$escapedValue\")"; } @@ -683,7 +726,6 @@ static private function whereListHas($column, $value, $delimiter = CRM_Core_DAO: /** * Check whether the custom group has any data for the given entity. * - * * @param int $entityID * Id of entity for whom we are checking data for. * @param string $table @@ -694,7 +736,7 @@ static private function whereListHas($column, $value, $delimiter = CRM_Core_DAO: * @return bool * does this entity have data in this custom table */ - static public function customGroupDataExistsForEntity($entityID, $table, $getCount = FALSE) { + public static function customGroupDataExistsForEntity($entityID, $table, $getCount = FALSE) { $query = " SELECT count(id) FROM $table @@ -723,7 +765,7 @@ static public function customGroupDataExistsForEntity($entityID, $table, $getCou * @param array $singleFieldTablesWithEntityData * Array of tables in which this entity has data. */ - static public function buildEntityTreeSingleFields(&$groupTree, $entityID, $entitySingleSelectClauses, $singleFieldTablesWithEntityData) { + public static function buildEntityTreeSingleFields(&$groupTree, $entityID, $entitySingleSelectClauses, $singleFieldTablesWithEntityData) { $select = implode(', ', $entitySingleSelectClauses); $fromSQL = " (SELECT $entityID as entity_id ) as first "; foreach ($singleFieldTablesWithEntityData as $table) { @@ -751,8 +793,10 @@ static public function buildEntityTreeSingleFields(&$groupTree, $entityID, $enti * Array of select clauses relevant to the entity. * @param array $multipleFieldTablesWithEntityData * Array of tables in which this entity has data. + * @param string|int $singleRecord + * holds 'new' or id if view/edit/copy form for a single record is being loaded. */ - static public function buildEntityTreeMultipleFields(&$groupTree, $entityID, $entityMultipleSelectClauses, $multipleFieldTablesWithEntityData) { + public static function buildEntityTreeMultipleFields(&$groupTree, $entityID, $entityMultipleSelectClauses, $multipleFieldTablesWithEntityData, $singleRecord = NULL) { foreach ($entityMultipleSelectClauses as $table => $selectClauses) { $select = implode(',', $selectClauses); $query = " @@ -760,7 +804,11 @@ static public function buildEntityTreeMultipleFields(&$groupTree, $entityID, $en FROM $table WHERE entity_id = $entityID "; - self::buildTreeEntityDataFromQuery($groupTree, $query, array($table)); + if ($singleRecord) { + $offset = $singleRecord - 1; + $query .= " LIMIT {$offset}, 1"; + } + self::buildTreeEntityDataFromQuery($groupTree, $query, [$table], $singleRecord); } } @@ -776,8 +824,10 @@ static public function buildEntityTreeMultipleFields(&$groupTree, $entityID, $en * @param array $includedTables * Tables to include - required because the function (for historical reasons). * iterates through the group tree + * @param string|int $singleRecord + * holds 'new' OR id if view/edit/copy form for a single record is being loaded. */ - static public function buildTreeEntityDataFromQuery(&$groupTree, $query, $includedTables) { + public static function buildTreeEntityDataFromQuery(&$groupTree, $query, $includedTables, $singleRecord = NULL) { $dao = CRM_Core_DAO::executeQuery($query); while ($dao->fetch()) { foreach ($groupTree as $groupID => $group) { @@ -792,7 +842,7 @@ static public function buildTreeEntityDataFromQuery(&$groupTree, $query, $includ continue; } foreach ($group['fields'] as $fieldID => $dontCare) { - self::buildCustomFieldData($dao, $groupTree, $table, $groupID, $fieldID); + self::buildCustomFieldData($dao, $groupTree, $table, $groupID, $fieldID, $singleRecord); } } } @@ -811,8 +861,10 @@ static public function buildTreeEntityDataFromQuery(&$groupTree, $query, $includ * Custom group ID. * @param int $fieldID * Custom field ID. + * @param string|int $singleRecord + * holds 'new' or id if loading view/edit/copy for a single record. */ - static public function buildCustomFieldData($dao, &$groupTree, $table, $groupID, $fieldID) { + public static function buildCustomFieldData($dao, &$groupTree, $table, $groupID, $fieldID, $singleRecord = NULL) { $column = $groupTree[$groupID]['fields'][$fieldID]['column_name']; $idName = "{$table}_id"; $fieldName = "{$table}_{$column}"; @@ -825,28 +877,30 @@ static public function buildCustomFieldData($dao, &$groupTree, $table, $groupID, if ($fileDAO->find(TRUE)) { $entityIDName = "{$table}_entity_id"; + $fileHash = CRM_Core_BAO_File::generateFileHash($dao->$entityIDName, $fileDAO->id); $customValue['id'] = $dao->$idName; $customValue['data'] = $fileDAO->uri; $customValue['fid'] = $fileDAO->id; - $customValue['fileURL'] = CRM_Utils_System::url('civicrm/file', "reset=1&id={$fileDAO->id}&eid={$dao->$entityIDName}"); + $customValue['fileURL'] = CRM_Utils_System::url('civicrm/file', "reset=1&id={$fileDAO->id}&eid={$dao->$entityIDName}&fcs=$fileHash"); $customValue['displayURL'] = NULL; $deleteExtra = ts('Are you sure you want to delete attached file.'); - $deleteURL = array( - CRM_Core_Action::DELETE => array( + $deleteURL = [ + CRM_Core_Action::DELETE => [ 'name' => ts('Delete Attached File'), 'url' => 'civicrm/file', - 'qs' => 'reset=1&id=%%id%%&eid=%%eid%%&fid=%%fid%%&action=delete', + 'qs' => 'reset=1&id=%%id%%&eid=%%eid%%&fid=%%fid%%&action=delete&fcs=%%fcs%%', 'extra' => 'onclick = "if (confirm( \'' . $deleteExtra . '\' ) ) this.href+=\'&confirmed=1\'; else return false;"', - ), - ); + ], + ]; $customValue['deleteURL'] = CRM_Core_Action::formLink($deleteURL, CRM_Core_Action::DELETE, - array( + [ 'id' => $fileDAO->id, 'eid' => $dao->$entityIDName, 'fid' => $fieldID, - ), + 'fcs' => $fileHash, + ], ts('more'), FALSE, 'file.manage.delete', @@ -869,7 +923,7 @@ static public function buildCustomFieldData($dao, &$groupTree, $table, $groupID, ); $customValue['imageURL'] = str_replace('persist/contribute', 'custom', $config->imageUploadURL) . $fileDAO->uri; - list($path) = CRM_Core_BAO_File::path($fileDAO->id, $entityId, NULL, NULL); + list($path) = CRM_Core_BAO_File::path($fileDAO->id, $entityId); if ($path && file_exists($path)) { list($imageWidth, $imageHeight) = getimagesize($path); list($imageThumbWidth, $imageThumbHeight) = CRM_Contact_BAO_Contact::getThumbSize($imageWidth, $imageHeight); @@ -880,24 +934,27 @@ static public function buildCustomFieldData($dao, &$groupTree, $table, $groupID, } } else { - $customValue = array( + $customValue = [ 'id' => $dao->$idName, 'data' => '', - ); + ]; } } else { - $customValue = array( + $customValue = [ 'id' => $dao->$idName, 'data' => $dao->$fieldName, - ); + ]; } if (!array_key_exists('customValue', $groupTree[$groupID]['fields'][$fieldID])) { - $groupTree[$groupID]['fields'][$fieldID]['customValue'] = array(); + $groupTree[$groupID]['fields'][$fieldID]['customValue'] = []; } - if (empty($groupTree[$groupID]['fields'][$fieldID]['customValue'])) { - $groupTree[$groupID]['fields'][$fieldID]['customValue'] = array(1 => $customValue); + if (empty($groupTree[$groupID]['fields'][$fieldID]['customValue']) && !empty($singleRecord)) { + $groupTree[$groupID]['fields'][$fieldID]['customValue'] = [$singleRecord => $customValue]; + } + elseif (empty($groupTree[$groupID]['fields'][$fieldID]['customValue'])) { + $groupTree[$groupID]['fields'][$fieldID]['customValue'] = [1 => $customValue]; } else { $groupTree[$groupID]['fields'][$fieldID]['customValue'][] = $customValue; @@ -929,21 +986,18 @@ public static function getTitle($id) { * @param array $extends * Which table does it extend if any. * - * @param null $inSelector + * @param bool $inSelector * * @return array * array consisting of all group and field details */ public static function &getGroupDetail($groupId = NULL, $searchable = NULL, &$extends = NULL, $inSelector = NULL) { // create a new tree - $groupTree = array(); - $select = $from = $where = $orderBy = ''; - - $tableData = array(); + $groupTree = []; // using tableData to build the queryString - $tableData = array( - 'civicrm_custom_field' => array( + $tableData = [ + 'civicrm_custom_field' => [ 'id', 'label', 'data_type', @@ -966,8 +1020,8 @@ public static function &getGroupDetail($groupId = NULL, $searchable = NULL, &$ex 'is_view', 'option_group_id', 'in_selector', - ), - 'civicrm_custom_group' => array( + ], + 'civicrm_custom_group' => [ 'id', 'name', 'title', @@ -979,26 +1033,25 @@ public static function &getGroupDetail($groupId = NULL, $searchable = NULL, &$ex 'extends_entity_column_value', 'table_name', 'is_multiple', - ), - ); + ], + ]; // create select - $select = "SELECT"; - $s = array(); + $s = []; foreach ($tableData as $tableName => $tableColumn) { foreach ($tableColumn as $columnName) { $s[] = "{$tableName}.{$columnName} as {$tableName}_{$columnName}"; } } $select = 'SELECT ' . implode(', ', $s); - $params = array(); + $params = []; // from, where, order by $from = " FROM civicrm_custom_field, civicrm_custom_group"; $where = " WHERE civicrm_custom_field.custom_group_id = civicrm_custom_group.id AND civicrm_custom_group.is_active = 1 AND civicrm_custom_field.is_active = 1 "; if ($groupId) { - $params[1] = array($groupId, 'Integer'); + $params[1] = [$groupId, 'Integer']; $where .= " AND civicrm_custom_group.id = %1"; } @@ -1011,7 +1064,7 @@ public static function &getGroupDetail($groupId = NULL, $searchable = NULL, &$ex } if ($extends) { - $clause = array(); + $clause = []; foreach ($extends as $e) { $clause[] = "civicrm_custom_group.extends = '$e'"; } @@ -1021,7 +1074,7 @@ public static function &getGroupDetail($groupId = NULL, $searchable = NULL, &$ex if (in_array('Activity', $extends)) { $extendValues = implode(',', array_keys(CRM_Core_PseudoConstant::activityType(TRUE, TRUE, FALSE, 'label', TRUE))); $where .= " AND ( civicrm_custom_group.extends_entity_column_value IS NULL OR REPLACE( civicrm_custom_group.extends_entity_column_value, %2, ' ') IN ($extendValues) ) "; - $params[2] = array(CRM_Core_DAO::VALUE_SEPARATOR, 'String'); + $params[2] = [CRM_Core_DAO::VALUE_SEPARATOR, 'String']; } } @@ -1046,7 +1099,7 @@ public static function &getGroupDetail($groupId = NULL, $searchable = NULL, &$ex // create an array for groups if it does not exist if (!array_key_exists($groupId, $groupTree)) { - $groupTree[$groupId] = array(); + $groupTree[$groupId] = []; $groupTree[$groupId]['id'] = $groupId; foreach ($tableData['civicrm_custom_group'] as $v) { @@ -1059,11 +1112,11 @@ public static function &getGroupDetail($groupId = NULL, $searchable = NULL, &$ex $groupTree[$groupId][$v] = $crmDAO->$fullField; } - $groupTree[$groupId]['fields'] = array(); + $groupTree[$groupId]['fields'] = []; } // add the fields now (note - the query row will always contain a field) - $groupTree[$groupId]['fields'][$fieldId] = array(); + $groupTree[$groupId]['fields'][$fieldId] = []; $groupTree[$groupId]['fields'][$fieldId]['id'] = $fieldId; foreach ($tableData['civicrm_custom_field'] as $v) { @@ -1096,7 +1149,7 @@ public static function &getActiveGroups($entityType, $path, $cidToken = '%%cid%% // add whereAdd for entity type self::_addWhereAdd($customGroupDAO, $entityType, $cidToken); - $groups = array(); + $groups = []; $permissionClause = CRM_Core_Permission::customGroupClause(CRM_Core_Permission::VIEW, NULL, TRUE); $customGroupDAO->whereAdd($permissionClause); @@ -1107,12 +1160,12 @@ public static function &getActiveGroups($entityType, $path, $cidToken = '%%cid%% // process each group with menu tab while ($customGroupDAO->fetch()) { - $group = array(); + $group = []; $group['id'] = $customGroupDAO->id; $group['path'] = $path; $group['title'] = "$customGroupDAO->title"; $group['query'] = "reset=1&gid={$customGroupDAO->id}&cid={$cidToken}"; - $group['extra'] = array('gid' => $customGroupDAO->id); + $group['extra'] = ['gid' => $customGroupDAO->id]; $group['table_name'] = $customGroupDAO->table_name; $group['is_multiple'] = $customGroupDAO->is_multiple; $groups[] = $group; @@ -1212,6 +1265,10 @@ public static function getAllCustomGroupsByBaseEntity($entityType) { */ private static function _addWhereAdd(&$customGroupDAO, $entityType, $entityID = NULL, $allSubtypes = FALSE) { $addSubtypeClause = FALSE; + // This function isn't really accessible with user data but since the string + // is not passed as a param to the query CRM_Core_DAO::escapeString seems like a harmless + // precaution. + $entityType = CRM_Core_DAO::escapeString($entityType); switch ($entityType) { case 'Contact': @@ -1234,13 +1291,7 @@ private static function _addWhereAdd(&$customGroupDAO, $entityType, $entityID = } break; - case 'Case': - case 'Location': - case 'Address': - case 'Activity': - case 'Contribution': - case 'Membership': - case 'Participant': + default: $customGroupDAO->whereAdd("extends IN ('$entityType')"); break; } @@ -1249,7 +1300,7 @@ private static function _addWhereAdd(&$customGroupDAO, $entityType, $entityID = $csType = is_numeric($entityID) ? CRM_Contact_BAO_Contact::getContactSubType($entityID) : FALSE; if (!empty($csType)) { - $subtypeClause = array(); + $subtypeClause = []; foreach ($csType as $subtype) { $subtype = CRM_Core_DAO::VALUE_SEPARATOR . $subtype . CRM_Core_DAO::VALUE_SEPARATOR; @@ -1341,9 +1392,8 @@ public static function setDefaults(&$groupTree, &$defaults, $viewMode = FALSE, $ switch ($field['html_type']) { case 'Multi-Select': - case 'AdvMulti-Select': case 'CheckBox': - $defaults[$elementName] = array(); + $defaults[$elementName] = []; $customOption = CRM_Core_BAO_CustomOption::getCustomOption($field['id'], $inactiveNeeded); if ($viewMode) { $checkedData = explode(CRM_Core_DAO::VALUE_SEPARATOR, substr($value, 1, -1)); @@ -1461,7 +1511,6 @@ public static function postProcess(&$groupTree, &$params, $skipFile = FALSE) { //added Multi-Select option in the below if-statement if ($field['html_type'] == 'CheckBox' || $field['html_type'] == 'Radio' || - $field['html_type'] == 'AdvMulti-Select' || $field['html_type'] == 'Multi-Select' ) { $groupTree[$groupID]['fields'][$fieldId]['customValue']['data'] = 'NULL'; @@ -1478,13 +1527,12 @@ public static function postProcess(&$groupTree, &$params, $skipFile = FALSE) { if (!isset($groupTree[$groupID]['fields'][$fieldId]['customValue'])) { // field exists in db so populate value from "form". - $groupTree[$groupID]['fields'][$fieldId]['customValue'] = array(); + $groupTree[$groupID]['fields'][$fieldId]['customValue'] = []; } switch ($groupTree[$groupID]['fields'][$fieldId]['html_type']) { - //added for CheckBox - + // added for CheckBox case 'CheckBox': if (!empty($v)) { $customValue = array_keys($v); @@ -1497,10 +1545,6 @@ public static function postProcess(&$groupTree, &$params, $skipFile = FALSE) { } break; - //added for Advanced Multi-Select - - case 'AdvMulti-Select': - //added for Multi-Select case 'Multi-Select': if (!empty($v)) { $groupTree[$groupID]['fields'][$fieldId]['customValue']['data'] = CRM_Core_DAO::VALUE_SEPARATOR @@ -1519,12 +1563,12 @@ public static function postProcess(&$groupTree, &$params, $skipFile = FALSE) { case 'File': if ($skipFile) { - continue; + break; } - //store the file in d/b + // store the file in d/b $entityId = explode('=', $groupTree['info']['where'][0]); - $fileParams = array('upload_date' => date('YmdHis')); + $fileParams = ['upload_date' => date('YmdHis')]; if ($groupTree[$groupID]['fields'][$fieldId]['customValue']['fid']) { $fileParams['id'] = $groupTree[$groupID]['fields'][$fieldId]['customValue']['fid']; @@ -1543,11 +1587,11 @@ public static function postProcess(&$groupTree, &$params, $skipFile = FALSE) { $v['type'] ); } - $defaults = array(); - $paramsFile = array( + $defaults = []; + $paramsFile = [ 'entity_table' => $groupTree[$groupID]['table_name'], 'entity_id' => $entityId[1], - ); + ]; CRM_Core_DAO::commonRetrieve('CRM_Core_DAO_EntityFile', $paramsFile, @@ -1576,6 +1620,8 @@ public static function postProcess(&$groupTree, &$params, $skipFile = FALSE) { * Return inactive custom groups. * @param string $prefix * Prefix for custom grouptree assigned to template. + * + * @throws \CiviCRM_API3_Exception */ public static function buildQuickForm(&$form, &$groupTree, $inactiveNeeded = FALSE, $prefix = '') { $form->assign_by_ref("{$prefix}groupTree", $groupTree); @@ -1594,6 +1640,9 @@ public static function buildQuickForm(&$form, &$groupTree, $inactiveNeeded = FAL $fieldId = $field['id']; $elementName = $field['element_name']; CRM_Core_BAO_CustomField::addQuickFormElement($form, $elementName, $fieldId, $required); + if ($form->getAction() == CRM_Core_Action::VIEW) { + $form->getElement($elementName)->freeze(); + } } } } @@ -1607,21 +1656,22 @@ public static function buildQuickForm(&$form, &$groupTree, $inactiveNeeded = FAL * The type of custom group we are using. * * @return array + * @throws \CRM_Core_Exception + * @throws \CiviCRM_API3_Exception */ public static function extractGetParams(&$form, $type) { if (empty($_GET)) { - return array(); + return []; } $groupTree = CRM_Core_BAO_CustomGroup::getTree($type); - $customValue = array(); - $htmlType = array( + $customValue = []; + $htmlType = [ 'CheckBox', 'Multi-Select', - 'AdvMulti-Select', 'Select', 'Radio', - ); + ]; foreach ($groupTree as $group) { if (!isset($group['fields'])) { @@ -1639,13 +1689,12 @@ public static function extractGetParams(&$form, $type) { $valid = CRM_Core_BAO_CustomValue::typecheck($field['data_type'], $value); } if ($field['html_type'] == 'CheckBox' || - $field['html_type'] == 'AdvMulti-Select' || $field['html_type'] == 'Multi-Select' ) { $value = str_replace("|", ",", $value); $mulValues = explode(',', $value); $customOption = CRM_Core_BAO_CustomOption::getCustomOption($key, TRUE); - $val = array(); + $val = []; foreach ($mulValues as $v1) { foreach ($customOption as $coID => $coValue) { if (strtolower(trim($coValue['label'])) == @@ -1679,19 +1728,7 @@ public static function extractGetParams(&$form, $type) { } } elseif ($field['data_type'] == 'Date') { - if (!empty($value)) { - $time = NULL; - if (!empty($field['time_format'])) { - $time = CRM_Utils_Request::retrieve($fieldName . - '_time', 'String', $form, FALSE, NULL, 'GET'); - } - list($value, $time) = CRM_Utils_Date::setDateDefaults($value . - ' ' . $time); - if (!empty($field['time_format'])) { - $customValue[$fieldName . '_time'] = $time; - } - } - $valid = TRUE; + $valid = CRM_Utils_Rule::date($value); } if ($valid) { @@ -1787,7 +1824,7 @@ public static function mapTableName($table) { default: $query = " SELECT IF( EXISTS(SELECT name FROM civicrm_contact_type WHERE name like %1), 1, 0 )"; - $qParams = array(1 => array($table, 'String')); + $qParams = [1 => [$table, 'String']]; $result = CRM_Core_DAO::singleValueQuery($query, $qParams); if ($result) { @@ -1805,13 +1842,15 @@ public static function mapTableName($table) { /** * @param $group + * + * @throws \Exception */ public static function createTable($group) { - $params = array( + $params = [ 'name' => $group->table_name, 'is_multiple' => $group->is_multiple ? 1 : 0, 'extends_name' => self::mapTableName($group->extends), - ); + ]; $tableParams = CRM_Core_BAO_CustomField::defaultCustomTableSchema($params); @@ -1819,18 +1858,19 @@ public static function createTable($group) { } /** - * Function returns formatted groupTree, sothat form can be easily build in template + * Function returns formatted groupTree, so that form can be easily built in template * * @param array $groupTree * @param int $groupCount - * Group count by default 1, but can varry for multiple value custom data. - * @param object $form + * Group count by default 1, but can vary for multiple value custom data. + * @param \CRM_Core_Form $form * * @return array + * @throws \CRM_Core_Exception */ public static function formatGroupTree(&$groupTree, $groupCount = 1, &$form = NULL) { - $formattedGroupTree = array(); - $uploadNames = $formValues = array(); + $formattedGroupTree = []; + $uploadNames = $formValues = []; // retrieve qf key from url $qfKey = CRM_Utils_Request::retrieve('qf', 'String'); @@ -1865,12 +1905,6 @@ public static function formatGroupTree(&$groupTree, $groupCount = 1, &$form = NU // add field information foreach ($value['fields'] as $k => $properties) { $properties['element_name'] = "custom_{$k}_-{$groupCount}"; - if ($value = CRM_Utils_Request::retrieve($properties['element_name'], 'String', $form, FALSE, NULL, 'POST')) { - $formValues[$properties['element_name']] = $value; - } - elseif (isset($submittedValues[$properties['element_name']])) { - $properties['element_value'] = $submittedValues[$properties['element_name']]; - } if (isset($properties['customValue']) && !CRM_Utils_System::isNull($properties['customValue']) && !isset($properties['element_value']) @@ -1887,6 +1921,12 @@ public static function formatGroupTree(&$groupTree, $groupCount = 1, &$form = NU } } } + if ($value = CRM_Utils_Request::retrieve($properties['element_name'], 'String', $form, FALSE, NULL, 'POST')) { + $formValues[$properties['element_name']] = $value; + } + elseif (isset($submittedValues[$properties['element_name']])) { + $properties['element_value'] = $submittedValues[$properties['element_name']]; + } unset($properties['customValue']); $formattedGroupTree[$key]['fields'][$k] = $properties; } @@ -1914,7 +1954,7 @@ public static function formatGroupTree(&$groupTree, $groupCount = 1, &$form = NU /** * Build custom data view. * - * @param CRM_Core_Form $form + * @param CRM_Core_Form|CRM_Core_Page $form * Page object. * @param array $groupTree * @param bool $returnCount @@ -1925,9 +1965,10 @@ public static function formatGroupTree(&$groupTree, $groupCount = 1, &$form = NU * @param int $entityId * * @return array|int + * @throws \Exception */ public static function buildCustomDataView(&$form, &$groupTree, $returnCount = FALSE, $gID = NULL, $prefix = NULL, $customValueId = NULL, $entityId = NULL) { - $details = array(); + $details = []; foreach ($groupTree as $key => $group) { if ($key === 'info') { continue; @@ -1947,13 +1988,20 @@ public static function buildCustomDataView(&$form, &$groupTree, $returnCount = F $details[$groupID][$values['id']]['collapse_display'] = CRM_Utils_Array::value('collapse_display', $group); $details[$groupID][$values['id']]['collapse_adv_display'] = CRM_Utils_Array::value('collapse_adv_display', $group); $details[$groupID][$values['id']]['style'] = CRM_Utils_Array::value('style', $group); - $details[$groupID][$values['id']]['fields'][$k] = array( + $details[$groupID][$values['id']]['fields'][$k] = [ 'field_title' => CRM_Utils_Array::value('label', $properties), 'field_type' => CRM_Utils_Array::value('html_type', $properties), 'field_data_type' => CRM_Utils_Array::value('data_type', $properties), 'field_value' => CRM_Core_BAO_CustomField::displayValue($values['data'], $properties['id'], $entityId), 'options_per_line' => CRM_Utils_Array::value('options_per_line', $properties), - ); + ]; + // editable = whether this set contains any non-read-only fields + if (!isset($details[$groupID][$values['id']]['editable'])) { + $details[$groupID][$values['id']]['editable'] = FALSE; + } + if (empty($properties['is_view'])) { + $details[$groupID][$values['id']]['editable'] = TRUE; + } // also return contact reference contact id if user has view all or edit all contacts perm if ((CRM_Core_Permission::check('view all contacts') || CRM_Core_Permission::check('edit all contacts')) @@ -1973,19 +2021,19 @@ public static function buildCustomDataView(&$form, &$groupTree, $returnCount = F $details[$groupID][0]['collapse_display'] = CRM_Utils_Array::value('collapse_display', $group); $details[$groupID][0]['collapse_adv_display'] = CRM_Utils_Array::value('collapse_adv_display', $group); $details[$groupID][0]['style'] = CRM_Utils_Array::value('style', $group); - $details[$groupID][0]['fields'][$k] = array('field_title' => CRM_Utils_Array::value('label', $properties)); + $details[$groupID][0]['fields'][$k] = ['field_title' => CRM_Utils_Array::value('label', $properties)]; } } } if ($returnCount) { - //return a single value count if group id is passed to function - //else return a groupId and count mapped array + // return a single value count if group id is passed to function + // else return a groupId and count mapped array if (!empty($gID)) { return count($details[$gID]); } else { - $countValue = array(); + $countValue = []; foreach ($details as $key => $value) { $countValue[$key] = count($details[$key]); } @@ -2012,7 +2060,7 @@ public static function getGroupTitles($fieldIds) { return NULL; } - $groupLabels = array(); + $groupLabels = []; $fIds = "(" . implode(',', $fieldIds) . ")"; $query = " @@ -2024,12 +2072,12 @@ public static function getGroupTitles($fieldIds) { $dao = CRM_Core_DAO::executeQuery($query); while ($dao->fetch()) { - $groupLabels[$dao->fieldID] = array( + $groupLabels[$dao->fieldID] = [ 'fieldID' => $dao->fieldID, 'fieldLabel' => $dao->fieldLabel, 'groupID' => $dao->groupID, 'groupTitle' => $dao->groupTitle, - ); + ]; } return $groupLabels; @@ -2082,13 +2130,14 @@ public static function isGroupEmpty($gID) { * * @return array * Array of types. + * @throws \Exception */ - public static function getExtendedObjectTypes(&$types = array()) { - static $flag = FALSE, $objTypes = array(); + public static function getExtendedObjectTypes(&$types = []) { + static $flag = FALSE, $objTypes = []; if (!$flag) { - $extendObjs = array(); - CRM_Core_OptionValue::getValues(array('name' => 'cg_extend_objects'), $extendObjs); + $extendObjs = []; + CRM_Core_OptionValue::getValues(['name' => 'cg_extend_objects'], $extendObjs); foreach ($extendObjs as $ovId => $ovValues) { if ($ovValues['description']) { @@ -2096,7 +2145,7 @@ public static function getExtendedObjectTypes(&$types = array()) { list($callback, $args) = explode(';', trim($ovValues['description'])); if (empty($args)) { - $args = array(); + $args = []; } if (!is_array($args)) { @@ -2104,8 +2153,7 @@ public static function getExtendedObjectTypes(&$types = array()) { } list($className) = explode('::', $callback); - require_once str_replace('_', DIRECTORY_SEPARATOR, $className) . - '.php'; + require_once str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php'; $objTypes[$ovValues['value']] = call_user_func_array($callback, $args); } @@ -2124,7 +2172,7 @@ public static function getExtendedObjectTypes(&$types = array()) { * @return bool */ public static function hasReachedMaxLimit($customGroupId, $entityId) { - //check whether the group is multiple + // check whether the group is multiple $isMultiple = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $customGroupId, 'is_multiple'); $isMultiple = ($isMultiple) ? TRUE : FALSE; $hasReachedMax = FALSE; @@ -2136,9 +2184,9 @@ public static function hasReachedMaxLimit($customGroupId, $entityId) { } else { $tableName = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $customGroupId, 'table_name'); - //count the number of entries for a entity + // count the number of entries for a entity $sql = "SELECT COUNT(id) FROM {$tableName} WHERE entity_id = %1"; - $params = array(1 => array($entityId, 'Integer')); + $params = [1 => [$entityId, 'Integer']]; $count = CRM_Core_DAO::singleValueQuery($sql, $params); if ($count >= $maxMultiple) { @@ -2153,9 +2201,10 @@ public static function hasReachedMaxLimit($customGroupId, $entityId) { * @return array */ public static function getMultipleFieldGroup() { - $multipleGroup = array(); + $multipleGroup = []; $dao = new CRM_Core_DAO_CustomGroup(); $dao->is_multiple = 1; + $dao->is_active = 1; $dao->find(); while ($dao->fetch()) { $multipleGroup[$dao->id] = $dao->title; diff --git a/CRM/Core/BAO/CustomOption.php b/CRM/Core/BAO/CustomOption.php index 6ddb37894322..9fa273922bbb 100644 --- a/CRM/Core/BAO/CustomOption.php +++ b/CRM/Core/BAO/CustomOption.php @@ -1,9 +1,9 @@ $label) { - $options[] = array( + $options[] = [ 'label' => $label, 'value' => $value, - ); + ]; } return $options; @@ -102,8 +102,8 @@ public static function getCustomOption( * -rp = rowcount * -page= offset */ - static public function getOptionListSelector(&$params) { - $options = array(); + public static function getOptionListSelector(&$params) { + $options = []; $field = CRM_Core_BAO_CustomField::getFieldObject($params['fid']); $defVal = CRM_Utils_Array::explodePadded($field->default_value); @@ -115,7 +115,7 @@ static public function getOptionListSelector(&$params) { if (!$field->option_group_id) { return $options; } - $queryParams = array(1 => array($field->option_group_id, 'Integer')); + $queryParams = [1 => [$field->option_group_id, 'Integer']]; $total = "SELECT COUNT(*) FROM civicrm_option_value WHERE option_group_id = %1"; $params['total'] = CRM_Core_DAO::singleValueQuery($total, $queryParams); @@ -126,10 +126,10 @@ static public function getOptionListSelector(&$params) { $dao = CRM_Core_DAO::executeQuery($query, $queryParams); $links = CRM_Custom_Page_Option::actionLinks(); - $fields = array('id', 'label', 'value'); + $fields = ['id', 'label', 'value']; $config = CRM_Core_Config::singleton(); while ($dao->fetch()) { - $options[$dao->id] = array(); + $options[$dao->id] = []; foreach ($fields as $k) { $options[$dao->id][$k] = $dao->$k; } @@ -143,7 +143,19 @@ static public function getOptionListSelector(&$params) { $class .= ' disabled'; $action -= CRM_Core_Action::DISABLE; } - if (in_array($field->html_type, array('CheckBox', 'AdvMulti-Select', 'Multi-Select'))) { + + $isGroupLocked = (bool) CRM_Core_DAO::getFieldValue( + CRM_Core_DAO_OptionGroup::class, + $field->option_group_id, + 'is_locked' + ); + + // disable deletion of option values for locked option groups + if (($action & CRM_Core_Action::DELETE) && $isGroupLocked) { + $action -= CRM_Core_Action::DELETE; + } + + if (in_array($field->html_type, ['CheckBox', 'Multi-Select'])) { if (isset($defVal) && in_array($dao->value, $defVal)) { $options[$dao->id]['is_default'] = ''; } @@ -159,16 +171,16 @@ static public function getOptionListSelector(&$params) { $options[$dao->id]['is_default'] = ''; } } - + $options[$dao->id]['description'] = $dao->description; $options[$dao->id]['class'] = $dao->id . ',' . $class; $options[$dao->id]['is_active'] = empty($dao->is_active) ? ts('No') : ts('Yes'); $options[$dao->id]['links'] = CRM_Core_Action::formLink($links, $action, - array( + [ 'id' => $dao->id, 'fid' => $params['fid'], 'gid' => $params['gid'], - ), + ], ts('more'), FALSE, 'customOption.row.actions', @@ -197,22 +209,22 @@ public static function del($optionId) { WHERE v.id = %1 AND g.id = f.option_group_id AND g.id = v.option_group_id"; - $params = array(1 => array($optionId, 'Integer')); + $params = [1 => [$optionId, 'Integer']]; $dao = CRM_Core_DAO::executeQuery($query, $params); if ($dao->fetch()) { if (in_array($dao->dataType, - array('Int', 'Float', 'Money', 'Boolean') + ['Int', 'Float', 'Money', 'Boolean'] )) { $value = 0; } else { $value = ''; } - $params = array( + $params = [ 'optionId' => $optionId, 'fieldId' => $dao->id, 'value' => $value, - ); + ]; // delete this value from the tables self::updateCustomValues($params); @@ -221,7 +233,7 @@ public static function del($optionId) { DELETE FROM civicrm_option_value WHERE id = %1"; - $params = array(1 => array($optionId, 'Integer')); + $params = [1 => [$optionId, 'Integer']]; CRM_Core_DAO::executeQuery($query, $params); } } @@ -247,7 +259,7 @@ public static function updateCustomValues($params) { civicrm_custom_field f WHERE f.custom_group_id = g.id AND f.id = %1"; - $queryParams = array(1 => array($params['fieldId'], 'Integer')); + $queryParams = [1 => [$params['fieldId'], 'Integer']]; $dao = CRM_Core_DAO::executeQuery($query, $queryParams); if ($dao->fetch()) { if ($dao->dataType == 'Money') { @@ -267,19 +279,18 @@ public static function updateCustomValues($params) { else { $dataType = $dao->dataType; } - $queryParams = array( - 1 => array( + $queryParams = [ + 1 => [ $params['value'], $dataType, - ), - 2 => array( + ], + 2 => [ $params['optionId'], 'Integer', - ), - ); + ], + ]; break; - case 'AdvMulti-Select': case 'Multi-Select': case 'CheckBox': $oldString = CRM_Core_DAO::VALUE_SEPARATOR . $oldValue . CRM_Core_DAO::VALUE_SEPARATOR; @@ -287,10 +298,10 @@ public static function updateCustomValues($params) { $query = " UPDATE {$dao->tableName} SET {$dao->columnName} = REPLACE( {$dao->columnName}, %1, %2 )"; - $queryParams = array( - 1 => array($oldString, 'String'), - 2 => array($newString, 'String'), - ); + $queryParams = [ + 1 => [$oldString, 'String'], + 2 => [$newString, 'String'], + ]; break; default: @@ -323,18 +334,18 @@ public static function updateValue($optionId, $newValue) { $customGroup->id = $customField->custom_group_id; $customGroup->find(TRUE); if (CRM_Core_BAO_CustomField::isSerialized($customField)) { - $params = array( - 1 => array(CRM_Utils_Array::implodePadded($oldValue), 'String'), - 2 => array(CRM_Utils_Array::implodePadded($newValue), 'String'), - 3 => array('%' . CRM_Utils_Array::implodePadded($oldValue) . '%', 'String'), - ); + $params = [ + 1 => [CRM_Utils_Array::implodePadded($oldValue), 'String'], + 2 => [CRM_Utils_Array::implodePadded($newValue), 'String'], + 3 => ['%' . CRM_Utils_Array::implodePadded($oldValue) . '%', 'String'], + ]; } else { - $params = array( - 1 => array($oldValue, 'String'), - 2 => array($newValue, 'String'), - 3 => array($oldValue, 'String'), - ); + $params = [ + 1 => [$oldValue, 'String'], + 2 => [$newValue, 'String'], + 3 => [$oldValue, 'String'], + ]; } $sql = "UPDATE `{$customGroup->table_name}` SET `{$customField->column_name}` = REPLACE(`{$customField->column_name}`, %1, %2) WHERE `{$customField->column_name}` LIKE %3"; $customGroup->free(); diff --git a/CRM/Core/BAO/CustomQuery.php b/CRM/Core/BAO/CustomQuery.php index 00e3edf73392..24b829e5f5b4 100644 --- a/CRM/Core/BAO/CustomQuery.php +++ b/CRM/Core/BAO/CustomQuery.php @@ -1,9 +1,9 @@ 'civicrm_contact', 'Individual' => 'civicrm_contact', 'Household' => 'civicrm_contact', @@ -126,7 +126,7 @@ class CRM_Core_BAO_CustomQuery { 'Address' => 'civicrm_address', 'Campaign' => 'civicrm_campaign', 'Survey' => 'civicrm_survey', - ); + ]; /** * Class constructor. @@ -140,19 +140,19 @@ class CRM_Core_BAO_CustomQuery { * @param bool $contactSearch * @param array $locationSpecificFields */ - public function __construct($ids, $contactSearch = FALSE, $locationSpecificFields = array()) { + public function __construct($ids, $contactSearch = FALSE, $locationSpecificFields = []) { $this->_ids = &$ids; $this->_locationSpecificCustomFields = $locationSpecificFields; - $this->_select = array(); - $this->_element = array(); - $this->_tables = array(); - $this->_whereTables = array(); - $this->_where = array(); - $this->_qill = array(); - $this->_options = array(); + $this->_select = []; + $this->_element = []; + $this->_tables = []; + $this->_whereTables = []; + $this->_where = []; + $this->_qill = []; + $this->_options = []; - $this->_fields = array(); + $this->_fields = []; $this->_contactSearch = $contactSearch; if (empty($this->_ids)) { @@ -179,6 +179,7 @@ public function __construct($ids, $contactSearch = FALSE, $locationSpecificField while ($dao->fetch()) { // get the group dao to figure which class this custom field extends $extends = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $dao->custom_group_id, 'extends'); + $extendsTable = ''; if (array_key_exists($extends, self::$extendsMap)) { $extendsTable = self::$extendsMap[$extends]; } @@ -186,7 +187,7 @@ public function __construct($ids, $contactSearch = FALSE, $locationSpecificField // if $extends is a subtype, refer contact table $extendsTable = self::$extendsMap['Contact']; } - $this->_fields[$dao->id] = array( + $this->_fields[$dao->id] = [ 'id' => $dao->id, 'label' => $dao->label, 'extends' => $extendsTable, @@ -196,18 +197,18 @@ public function __construct($ids, $contactSearch = FALSE, $locationSpecificField 'column_name' => $dao->column_name, 'table_name' => $dao->table_name, 'option_group_id' => $dao->option_group_id, - ); + ]; // Deprecated (and poorly named) cache of field attributes - $this->_options[$dao->id] = array( - 'attributes' => array( + $this->_options[$dao->id] = [ + 'attributes' => [ 'label' => $dao->label, 'data_type' => $dao->data_type, 'html_type' => $dao->html_type, - ), - ); + ], + ]; - $options = CRM_Core_PseudoConstant::get('CRM_Core_BAO_CustomField', 'custom_' . $dao->id, array(), 'search'); + $options = CRM_Core_PseudoConstant::get('CRM_Core_BAO_CustomField', 'custom_' . $dao->id, [], 'search'); if ($options) { $this->_options[$dao->id] += $options; } @@ -294,8 +295,6 @@ public function where() { continue; } - $strtolower = function_exists('mb_strtolower') ? 'mb_strtolower' : 'strtolower'; - foreach ($values as $tuple) { list($name, $op, $value, $grouping, $wildcard) = $tuple; @@ -334,12 +333,12 @@ public function where() { // fix $value here to escape sql injection attacks if (!is_array($value)) { if ($field['data_type'] == 'String') { - $value = CRM_Utils_Type::escape($strtolower($value), 'String'); + $value = CRM_Utils_Type::escape($value, 'String'); } - else { + elseif ($value) { $value = CRM_Utils_Type::escape($value, 'Integer'); } - $value = str_replace(array('[', ']', ','), array('\[', '\]', '[:comma:]'), $value); + $value = str_replace(['[', ']', ','], ['\[', '\]', '[:comma:]'], $value); $value = str_replace('|', '[:separator:]', $value); } elseif ($isSerialized) { @@ -350,8 +349,14 @@ public function where() { // CRM-19006: escape characters like comma, | before building regex pattern $value = (array) $value; foreach ($value as $key => $val) { - $value[$key] = str_replace(array('[', ']', ','), array('\[', '\]', '[:comma:]'), $val); + $value[$key] = str_replace(['[', ']', ','], ['\[', '\]', '[:comma:]'], $val); $value[$key] = str_replace('|', '[:separator:]', $value[$key]); + if ($field['data_type'] == 'String') { + $value[$key] = CRM_Utils_Type::escape($value[$key], 'String'); + } + elseif ($value) { + $value[$key] = CRM_Utils_Type::escape($value[$key], 'Integer'); + } } $value = implode(',', $value); } @@ -360,7 +365,7 @@ public function where() { if ($isSerialized && !CRM_Utils_System::isNull($value) && !strstr($op, 'NULL') && !strstr($op, 'LIKE')) { $sp = CRM_Core_DAO::VALUE_SEPARATOR; $value = str_replace(",", "$sp|$sp", $value); - $value = str_replace(array('[:comma:]', '(', ')'), array(',', '[[.left-parenthesis.]]', '[[.right-parenthesis.]]'), $value); + $value = str_replace(['[:comma:]', '(', ')'], [',', '[(]', '[)]'], $value); $op = (strstr($op, '!') || strstr($op, 'NOT')) ? 'NOT RLIKE' : 'RLIKE'; $value = $sp . $value . $sp; @@ -391,7 +396,7 @@ public function where() { case 'Int': $this->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause($fieldName, $op, $value, 'Integer'); - $this->_qill[$grouping][] = ts("%1 %2 %3", array(1 => $field['label'], 2 => $qillOp, 3 => $qillValue));; + $this->_qill[$grouping][] = ts("%1 %2 %3", [1 => $field['label'], 2 => $qillOp, 3 => $qillValue]);; break; case 'Boolean': @@ -406,34 +411,40 @@ public function where() { $qillValue = $value ? 'Yes' : 'No'; } $this->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause($fieldName, $op, $value, 'Integer'); - $this->_qill[$grouping][] = ts("%1 %2 %3", array(1 => $field['label'], 2 => $qillOp, 3 => $qillValue)); + $this->_qill[$grouping][] = ts("%1 %2 %3", [1 => $field['label'], 2 => $qillOp, 3 => $qillValue]); break; case 'Link': case 'Memo': $this->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause($fieldName, $op, $value, 'String'); - $this->_qill[$grouping][] = ts("%1 %2 %3", array(1 => $field['label'], 2 => $qillOp, 3 => $qillValue)); + $this->_qill[$grouping][] = ts("%1 %2 %3", [1 => $field['label'], 2 => $qillOp, 3 => $qillValue]); break; case 'Money': $value = CRM_Utils_Array::value($op, (array) $value, $value); if (is_array($value)) { foreach ($value as $key => $val) { - $value[$key] = CRM_Utils_Rule::cleanMoney($value[$key]); + // @todo - this clean money should be in the form layer - it's highly likely to be doing more harm than good here + // Note the only place I can find that this code is reached by is searching a custom money field in advanced search. + // with euro style comma separators this doesn't work - with or without this cleanMoney. + // So this should be removed but is not increasing the brokeness IMHO + $value[$op][$key] = CRM_Utils_Rule::cleanMoney($value[$key]); } } else { + // @todo - this clean money should be in the form layer - it's highly likely to be doing more harm than good here + // comments per above apply. cleanMoney $value = CRM_Utils_Rule::cleanMoney($value); } case 'Float': $this->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause($fieldName, $op, $value, 'Float'); - $this->_qill[$grouping][] = ts("%1 %2 %3", array(1 => $field['label'], 2 => $qillOp, 3 => $qillValue)); + $this->_qill[$grouping][] = ts("%1 %2 %3", [1 => $field['label'], 2 => $qillOp, 3 => $qillValue]); break; case 'Date': - $this->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause($fieldName, $op, $value, 'String'); - list($qillOp, $qillVal) = CRM_Contact_BAO_Query::buildQillForFieldValue(NULL, $field['label'], $value, $op, array(), CRM_Utils_Type::T_DATE); + $this->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause($fieldName, $op, $value, 'Date'); + list($qillOp, $qillVal) = CRM_Contact_BAO_Query::buildQillForFieldValue(NULL, $field['label'], $value, $op, [], CRM_Utils_Type::T_DATE); $this->_qill[$grouping][] = "{$field['label']} $qillOp '$qillVal'"; break; @@ -471,7 +482,7 @@ public function query() { $whereStr = NULL; if (!empty($this->_where)) { - $clauses = array(); + $clauses = []; foreach ($this->_where as $grouping => $values) { if (!empty($values)) { $clauses[] = ' ( ' . implode(' AND ', $values) . ' ) '; @@ -482,11 +493,11 @@ public function query() { } } - return array( + return [ implode(' , ', $this->_select), implode(' ', $this->_tables), $whereStr, - ); + ]; } } diff --git a/CRM/Core/BAO/CustomValue.php b/CRM/Core/BAO/CustomValue.php index a6a856b3057e..7df55cc393e1 100644 --- a/CRM/Core/BAO/CustomValue.php +++ b/CRM/Core/BAO/CustomValue.php @@ -1,9 +1,9 @@ $formValues[$key]); + $formValues[$key] = ['IN' => $formValues[$key]]; } } elseif (($htmlType == 'TextArea' || ($htmlType == 'Text' && $dataType == 'String') ) && strstr($formValues[$key], '%') ) { - $formValues[$key] = array('LIKE' => $formValues[$key]); + $formValues[$key] = ['LIKE' => $formValues[$key]]; } } } @@ -207,13 +206,18 @@ public static function deleteCustomValue($customValueID, $customGroupID) { // first we need to find custom value table, from custom group ID $tableName = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $customGroupID, 'table_name'); + // Retrieve the $entityId so we can pass that to the hook. + $entityID = CRM_Core_DAO::singleValueQuery("SELECT entity_id FROM {$tableName} WHERE id = %1", [ + 1 => [$customValueID, 'Integer'], + ]); + // delete custom value from corresponding custom value table $sql = "DELETE FROM {$tableName} WHERE id = {$customValueID}"; CRM_Core_DAO::executeQuery($sql); CRM_Utils_Hook::custom('delete', $customGroupID, - NULL, + $entityID, $customValueID ); } diff --git a/CRM/Core/BAO/CustomValueTable.php b/CRM/Core/BAO/CustomValueTable.php index 2c3b5e3a6728..dac03b007509 100644 --- a/CRM/Core/BAO/CustomValueTable.php +++ b/CRM/Core/BAO/CustomValueTable.php @@ -1,9 +1,9 @@ $tables) { foreach ($tables as $index => $fields) { $sqlOP = NULL; @@ -53,8 +55,8 @@ public static function create(&$customParams) { $hookOP = NULL; $entityID = NULL; $isMultiple = FALSE; - $set = array(); - $params = array(); + $set = []; + $params = []; $count = 1; foreach ($fields as $field) { if (!$sqlOP) { @@ -64,7 +66,7 @@ public static function create(&$customParams) { if (array_key_exists('id', $field)) { $sqlOP = "UPDATE $tableName "; $where = " WHERE id = %{$count}"; - $params[$count] = array($field['id'], 'Integer'); + $params[$count] = [$field['id'], 'Integer']; $count++; $hookOP = 'edit'; } @@ -88,9 +90,9 @@ public static function create(&$customParams) { elseif (!is_numeric($value) && !strstr($value, CRM_Core_DAO::VALUE_SEPARATOR)) { //fix for multi select state, CRM-3437 $mulValues = explode(',', $value); - $validStates = array(); + $validStates = []; foreach ($mulValues as $key => $stateVal) { - $states = array(); + $states = []; $states['state_province'] = trim($stateVal); CRM_Utils_Array::lookupValue($states, 'state_province', @@ -122,7 +124,6 @@ public static function create(&$customParams) { case 'Country': $type = 'Integer'; - $mulValues = explode(',', $value); if (is_array($value)) { $value = CRM_Core_DAO::VALUE_SEPARATOR . implode(CRM_Core_DAO::VALUE_SEPARATOR, $value) . CRM_Core_DAO::VALUE_SEPARATOR; $type = 'String'; @@ -130,9 +131,9 @@ public static function create(&$customParams) { elseif (!is_numeric($value) && !strstr($value, CRM_Core_DAO::VALUE_SEPARATOR)) { //fix for multi select country, CRM-3437 $mulValues = explode(',', $value); - $validCountries = array(); + $validCountries = []; foreach ($mulValues as $key => $countryVal) { - $countries = array(); + $countries = []; $countries['country'] = trim($countryVal); CRM_Utils_Array::lookupValue($countries, 'country', CRM_Core_PseudoConstant::country(), TRUE @@ -217,20 +218,31 @@ public static function create(&$customParams) { default: break; } - if (strtolower($value) === "null") { + if ($value === 'null') { // when unsetting a value to null, we don't need to validate the type // https://projectllr.atlassian.net/browse/VGQBMP-20 $set[$field['column_name']] = $value; } else { $set[$field['column_name']] = "%{$count}"; - $params[$count] = array($value, $type); + $params[$count] = [$value, $type]; $count++; } + + $fieldExtends = CRM_Utils_Array::value('extends', $field); + if ( + CRM_Utils_Array::value('entity_table', $field) == 'civicrm_contact' + || $fieldExtends == 'Contact' + || $fieldExtends == 'Individual' + || $fieldExtends == 'Organization' + || $fieldExtends == 'Household' + ) { + $paramFieldsExtendContactForEntities[$entityID]['custom_' . CRM_Utils_Array::value('custom_field_id', $field)] = CRM_Utils_Array::value('custom_field_id', $field); + } } if (!empty($set)) { - $setClause = array(); + $setClause = []; foreach ($set as $n => $v) { $setClause[] = "$n = $v"; } @@ -238,10 +250,10 @@ public static function create(&$customParams) { if (!$where) { // do this only for insert $set['entity_id'] = "%{$count}"; - $params[$count] = array($entityID, 'Integer'); + $params[$count] = [$entityID, 'Integer']; $count++; - $fieldNames = implode(',', array_keys($set)); + $fieldNames = implode(',', CRM_Utils_Type::escapeAll(array_keys($set), 'MysqlColumnNameOrAlias')); $fieldValues = implode(',', array_values($set)); $query = "$sqlOP ( $fieldNames ) VALUES ( $fieldValues )"; // for multiple values we dont do on duplicate key update @@ -262,6 +274,10 @@ public static function create(&$customParams) { } } } + + if (!empty($paramFieldsExtendContactForEntities)) { + CRM_Contact_BAO_Contact::updateGreetingsOnTokenFieldChange($paramFieldsExtendContactForEntities, ['contact_id' => $entityID]); + } } /** @@ -324,10 +340,10 @@ public static function fieldToSQLType($type, $maxLength = 255) { * @param int $entityID */ public static function store(&$params, $entityTable, $entityID) { - $cvParams = array(); + $cvParams = []; foreach ($params as $fieldID => $param) { foreach ($param as $index => $customValue) { - $cvParam = array( + $cvParam = [ 'entity_table' => $entityTable, 'entity_id' => $entityID, 'value' => $customValue['value'], @@ -338,7 +354,7 @@ public static function store(&$params, $entityTable, $entityID) { 'column_name' => $customValue['column_name'], 'is_multiple' => CRM_Utils_Array::value('is_multiple', $customValue), 'file_id' => $customValue['file_id'], - ); + ]; // Fix Date type to be timestamp, since that is how we store in db. if ($cvParam['type'] == 'Date') { @@ -349,11 +365,11 @@ public static function store(&$params, $entityTable, $entityID) { $cvParam['id'] = $customValue['id']; } if (!array_key_exists($customValue['table_name'], $cvParams)) { - $cvParams[$customValue['table_name']] = array(); + $cvParams[$customValue['table_name']] = []; } if (!array_key_exists($index, $cvParams[$customValue['table_name']])) { - $cvParams[$customValue['table_name']][$index] = array(); + $cvParams[$customValue['table_name']][$index] = []; } $cvParams[$customValue['table_name']][$index][] = $cvParam; @@ -415,7 +431,7 @@ public static function &getEntityValues($entityID, $entityType = NULL, $fieldIDs return NULL; } - $cond = array(); + $cond = []; if ($entityType) { $cond[] = "cg.extends IN ( '$entityType' )"; } @@ -455,12 +471,12 @@ public static function &getEntityValues($entityID, $entityType = NULL, $fieldIDs "; $dao = CRM_Core_DAO::executeQuery($query); - $select = $fields = $isMultiple = array(); + $select = $fields = $isMultiple = []; while ($dao->fetch()) { if (!array_key_exists($dao->table_name, $select)) { - $fields[$dao->table_name] = array(); - $select[$dao->table_name] = array(); + $fields[$dao->table_name] = []; + $select[$dao->table_name] = []; } $fields[$dao->table_name][] = $dao->fieldID; $select[$dao->table_name][] = "{$dao->column_name} AS custom_{$dao->fieldID}"; @@ -468,7 +484,7 @@ public static function &getEntityValues($entityID, $entityType = NULL, $fieldIDs $file[$dao->table_name][$dao->fieldID] = $dao->fieldDataType; } - $result = $sortedResult = array(); + $result = $sortedResult = []; foreach ($select as $tableName => $clauses) { if (!empty($DTparams['sort'])) { $query = CRM_Core_DAO::executeQuery("SELECT id FROM {$tableName} WHERE entity_id = {$entityID}"); @@ -529,27 +545,27 @@ public static function setValues(&$params) { // first collect all the id/value pairs. The format is: // custom_X => value or custom_X_VALUEID => value (for multiple values), VALUEID == -1, -2 etc for new insertions - $values = array(); - $fieldValues = array(); + $values = []; + $fieldValues = []; foreach ($params as $n => $v) { 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', - array(1 => $fieldID) + [1 => $fieldID] )); } if (!array_key_exists($fieldID, $fieldValues)) { - $fieldValues[$fieldID] = array(); + $fieldValues[$fieldID] = []; } $id = -1; if ($customFieldInfo[1]) { $id = (int ) $customFieldInfo[1]; } - $fieldValues[$fieldID][] = array( + $fieldValues[$fieldID][] = [ 'value' => $v, 'id' => $id, - ); + ]; } } @@ -560,6 +576,7 @@ public static function setValues(&$params) { SELECT cg.table_name as table_name , cg.id as cg_id , cg.is_multiple as is_multiple, + cg.extends as extends, cf.column_name as column_name, cf.id as cf_id , cf.data_type as data_type @@ -570,7 +587,7 @@ public static function setValues(&$params) { "; $dao = CRM_Core_DAO::executeQuery($sql); - $cvParams = array(); + $cvParams = []; while ($dao->fetch()) { $dataType = $dao->data_type == 'Date' ? 'Timestamp' : $dao->data_type; @@ -600,14 +617,14 @@ public static function setValues(&$params) { // 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', - array( + [ 1 => $fieldValue['value'], 2 => $dao->data_type, - ) + ] )); } - $cvParam = array( + $cvParam = [ 'entity_id' => $params['entityID'], 'value' => $fieldValue['value'], 'type' => $dataType, @@ -616,18 +633,23 @@ public static function setValues(&$params) { 'table_name' => $dao->table_name, 'column_name' => $dao->column_name, 'is_multiple' => $dao->is_multiple, - ); + 'extends' => $dao->extends, + ]; + + if (!empty($params['id'])) { + $cvParam['id'] = $params['id']; + } if ($cvParam['type'] == 'File') { $cvParam['file_id'] = $fieldValue['value']; } if (!array_key_exists($dao->table_name, $cvParams)) { - $cvParams[$dao->table_name] = array(); + $cvParams[$dao->table_name] = []; } if (!array_key_exists($fieldValue['id'], $cvParams[$dao->table_name])) { - $cvParams[$dao->table_name][$fieldValue['id']] = array(); + $cvParams[$dao->table_name][$fieldValue['id']] = []; } if ($fieldValue['id'] > 0) { @@ -639,7 +661,7 @@ public static function setValues(&$params) { if (!empty($cvParams)) { self::create($cvParams); - return array('is_error' => 0, 'result' => 1); + return ['is_error' => 0, 'result' => 1]; } return CRM_Core_Error::createAPIError(ts('Unknown error')); @@ -677,21 +699,21 @@ public static function &getValues(&$params) { // first collect all the ids. The format is: // custom_ID - $fieldIDs = array(); + $fieldIDs = []; foreach ($params as $n => $v) { $key = $idx = NULL; if (substr($n, 0, 7) == 'custom_') { $idx = substr($n, 7); if (CRM_Utils_Type::escape($idx, 'Integer', FALSE) === NULL) { return CRM_Core_Error::createAPIError(ts('field ID needs to be of type Integer for index %1', - array(1 => $idx) + [1 => $idx] )); } $fieldIDs[] = (int ) $idx; } } - $default = array('Contact', 'Individual', 'Household', 'Organization'); + $default = ['Contact', 'Individual', 'Household', 'Organization']; if (!($type = CRM_Utils_Array::value('entityType', $params)) || in_array($params['entityType'], $default) ) { @@ -720,17 +742,17 @@ public static function &getValues(&$params) { // note that this behaviour is undesirable from an API point of view - it should return an empty array // since this is also called by the merger code & not sure the consequences of changing // are just handling undoing this in the api layer. ie. converting the error back into a success - $result = array( + $result = [ 'is_error' => 1, 'error_message' => 'No values found for the specified entity ID and custom field(s).', - ); + ]; return $result; } else { - $result = array( + $result = [ 'is_error' => 0, 'entityID' => $params['entityID'], - ); + ]; foreach ($values as $id => $value) { $result["custom_{$id}"] = $value; } diff --git a/CRM/Core/BAO/Dashboard.php b/CRM/Core/BAO/Dashboard.php index 69926140eeb1..8b61cf0c0d66 100644 --- a/CRM/Core/BAO/Dashboard.php +++ b/CRM/Core/BAO/Dashboard.php @@ -1,9 +1,9 @@ id] = $values; } @@ -100,16 +101,16 @@ public static function getDashlets($all = TRUE, $checkPermission = TRUE) { * array of dashlets */ public static function getContactDashlets($contactID = NULL) { - $contactID = $contactID ? $contactID : CRM_Core_Session::singleton()->getLoggedInContactID(); - $dashlets = array(); + $contactID = $contactID ? $contactID : CRM_Core_Session::getLoggedInContactID(); + $dashlets = []; // Get contact dashboard dashlets. - $results = civicrm_api3('DashboardContact', 'get', array( + $results = civicrm_api3('DashboardContact', 'get', [ 'contact_id' => $contactID, 'is_active' => 1, 'dashboard_id.is_active' => 1, - 'options' => array('sort' => 'weight'), - 'return' => array( + 'options' => ['sort' => 'weight', 'limit' => 0], + 'return' => [ 'id', 'weight', 'column_no', @@ -121,12 +122,12 @@ public static function getContactDashlets($contactID = NULL) { 'dashboard_id.cache_minutes', 'dashboard_id.permission', 'dashboard_id.permission_operator', - ), - )); + ], + ]); foreach ($results['values'] as $item) { if (self::checkPermission(CRM_Utils_Array::value('dashboard_id.permission', $item), CRM_Utils_Array::value('dashboard_id.permission_operator', $item))) { - $dashlets[$item['id']] = array( + $dashlets[$item['id']] = [ 'dashboard_id' => $item['dashboard_id'], 'weight' => $item['weight'], 'column_no' => $item['column_no'], @@ -135,14 +136,14 @@ public static function getContactDashlets($contactID = NULL) { 'url' => $item['dashboard_id.url'], 'cache_minutes' => $item['dashboard_id.cache_minutes'], 'fullscreen_url' => CRM_Utils_Array::value('dashboard_id.fullscreen_url', $item), - ); + ]; } } // If empty, then initialize default dashlets for this user. if (!$results['count']) { // They may just have disabled all their dashlets. Check if any records exist for this contact. - if (!civicrm_api3('DashboardContact', 'getcount', array('contact_id' => $contactID))) { + if (!civicrm_api3('DashboardContact', 'getcount', ['contact_id' => $contactID])) { $dashlets = self::initializeDashlets(); } } @@ -154,16 +155,16 @@ public static function getContactDashlets($contactID = NULL) { * @return array */ public static function getContactDashletsForJS() { - $data = array(array(), array()); + $data = [[], []]; foreach (self::getContactDashlets() as $item) { - $data[$item['column_no']][] = array( + $data[$item['column_no']][] = [ 'id' => (int) $item['dashboard_id'], 'name' => $item['name'], 'title' => $item['label'], 'url' => self::parseUrl($item['url']), 'cacheMinutes' => $item['cache_minutes'], 'fullscreenUrl' => self::parseUrl($item['fullscreen_url']), - ); + ]; } return $data; } @@ -175,27 +176,27 @@ public static function getContactDashletsForJS() { * the default dashlets. * * @return array - * Array of dashboard_id's + * Array of dashboard_id's * @throws \CiviCRM_API3_Exception */ public static function initializeDashlets() { - $dashlets = array(); - $getDashlets = civicrm_api3("Dashboard", "get", array( - 'domain_id' => CRM_Core_Config::domainID(), - 'option.limit' => 0, - )); - $contactID = CRM_Core_Session::singleton()->getLoggedInContactID(); - $allDashlets = CRM_Utils_Array::index(array('name'), $getDashlets['values']); - $defaultDashlets = array(); - $defaults = array('blog' => 1, 'getting-started' => '0'); + $dashlets = []; + $getDashlets = civicrm_api3("Dashboard", "get", [ + 'domain_id' => CRM_Core_Config::domainID(), + 'option.limit' => 0, + ]); + $contactID = CRM_Core_Session::getLoggedInContactID(); + $allDashlets = CRM_Utils_Array::index(['name'], $getDashlets['values']); + $defaultDashlets = []; + $defaults = ['blog' => 1, 'getting-started' => '0']; foreach ($defaults as $name => $column) { if (!empty($allDashlets[$name]) && !empty($allDashlets[$name]['id'])) { - $defaultDashlets[$name] = array( + $defaultDashlets[$name] = [ 'dashboard_id' => $allDashlets[$name]['id'], 'is_active' => 1, 'column_no' => $column, 'contact_id' => $contactID, - ); + ]; } } CRM_Utils_Hook::dashboard_defaults($allDashlets, $defaultDashlets); @@ -209,7 +210,7 @@ public static function initializeDashlets() { else { $assignDashlets = civicrm_api3("dashboard_contact", "create", $defaultDashlet); $values = $assignDashlets['values'][$assignDashlets['id']]; - $dashlets[$assignDashlets['id']] = array( + $dashlets[$assignDashlets['id']] = [ 'dashboard_id' => $values['dashboard_id'], 'weight' => $values['weight'], 'column_no' => $values['column_no'], @@ -218,7 +219,7 @@ public static function initializeDashlets() { 'cache_minutes' => $dashlet['cache_minutes'], 'url' => $dashlet['url'], 'fullscreen_url' => CRM_Utils_Array::value('fullscreen_url', $dashlet), - ); + ]; } } } @@ -271,10 +272,8 @@ public static function checkPermission($permission, $operator) { } // hack to handle case permissions - if (!$componentName && in_array($key, array( - 'access my cases and activities', - 'access all cases and activities', - )) + if (!$componentName + && in_array($key, ['access my cases and activities', 'access all cases and activities']) ) { $componentName = 'CiviCase'; } @@ -335,7 +334,7 @@ public static function saveDashletChanges($columns, $contactID = NULL) { throw new RuntimeException("Failed to determine contact ID"); } - $dashletIDs = array(); + $dashletIDs = []; if (is_array($columns)) { foreach ($columns as $colNo => $dashlets) { if (!is_int($colNo)) { @@ -428,34 +427,37 @@ public static function addContactDashlet($dashlet) { // if dashlet is created by admin then you need to add it all contacts. // else just add to contact who is creating this dashlet - $contactIDs = array(); + $contactIDs = []; if ($admin) { $query = "SELECT distinct( contact_id ) - FROM civicrm_dashboard_contact - WHERE contact_id NOT IN ( - SELECT distinct( contact_id ) - FROM civicrm_dashboard_contact WHERE dashboard_id = {$dashlet->id} - )"; - + FROM civicrm_dashboard_contact"; $dao = CRM_Core_DAO::executeQuery($query); while ($dao->fetch()) { - $contactIDs[] = $dao->contact_id; + $contactIDs[$dao->contact_id] = NULL; } } else { //Get the id of Logged in User - $session = CRM_Core_Session::singleton(); - $contactID = $session->get('userID'); + $contactID = CRM_Core_Session::getLoggedInContactID(); if (!empty($contactID)) { - $contactIDs[] = $session->get('userID'); + $contactIDs[$contactID] = NULL; } } + // Remove contact ids that already have this dashlet to avoid DB + // constraint violation. + $query = "SELECT distinct( contact_id ) + FROM civicrm_dashboard_contact WHERE dashboard_id = {$dashlet->id}"; + $dao = CRM_Core_DAO::executeQuery($query); + while ($dao->fetch()) { + if (array_key_exists($dao->contact_id, $contactIDs)) { + unset($contactIDs[$dao->contact_id]); + } + } if (!empty($contactIDs)) { - foreach ($contactIDs as $contactID) { + foreach ($contactIDs as $contactID => $value) { $valuesArray[] = " ( {$dashlet->id}, {$contactID} )"; } - $valuesString = implode(',', $valuesArray); $query = " INSERT INTO civicrm_dashboard_contact ( dashboard_id, contact_id ) @@ -472,7 +474,7 @@ public static function addContactDashlet($dashlet) { */ public static function addContactDashletToDashboard(&$params) { $valuesString = NULL; - $columns = array(); + $columns = []; foreach ($params as $dashboardIDs) { $contactID = CRM_Utils_Array::value('contact_id', $dashboardIDs); $dashboardID = CRM_Utils_Array::value('dashboard_id', $dashboardIDs); @@ -493,6 +495,9 @@ public static function addContactDashletToDashboard(&$params) { public static function deleteDashlet($dashletID) { $dashlet = new CRM_Core_DAO_Dashboard(); $dashlet->id = $dashletID; + if (!$dashlet->find(TRUE)) { + return FALSE; + } $dashlet->delete(); return TRUE; } diff --git a/CRM/Core/BAO/Discount.php b/CRM/Core/BAO/Discount.php index 6469b355a292..dfef62e32882 100644 --- a/CRM/Core/BAO/Discount.php +++ b/CRM/Core/BAO/Discount.php @@ -1,9 +1,9 @@ entity_id = $entityId; $dao->entity_table = $entityTable; diff --git a/CRM/Core/BAO/Domain.php b/CRM/Core/BAO/Domain.php index 806407568c9e..d4ccd6c9d1d0 100644 --- a/CRM/Core/BAO/Domain.php +++ b/CRM/Core/BAO/Domain.php @@ -1,9 +1,9 @@ id = CRM_Core_Config::domainID(); if (!$domain->find(TRUE)) { - CRM_Core_Error::fatal(); + throw new CRM_Core_Exception('No domain in DB'); } } return $domain; } - /** - * Change active domain (ie. to perform a temporary action) such as changing - * config for all domains - * - * Switching around the global domain variable is very risky business. This - * is ONLY used as a hack to allow CRM_Core_BAO_Setting::setItems to manipulate - * the civicrm_domain.config_backend in multiple domains. When/if config_backend - * goes away, this hack should be removed. - * - * @param int $domainID - * Id for domain you want to set as current. - * @deprecated - * @see http://issues.civicrm.org/jira/browse/CRM-11204 - */ - public static function setDomain($domainID) { - CRM_Core_Config::domainID($domainID); - self::getDomain($domainID); - CRM_Core_Config::singleton(TRUE, TRUE); - } - - /** - * Reset domain to default (ie. as loaded from settings). This is the - * counterpart to CRM_Core_BAO_Domain::setDomain. - * - * @deprecated - * @see CRM_Core_BAO_Domain::setDomain - */ - public static function resetDomain() { - CRM_Core_Config::domainID(NULL, TRUE); - self::getDomain(NULL, TRUE); - CRM_Core_Config::singleton(TRUE, TRUE); - } - /** * @param bool $skipUsingCache * @@ -137,9 +105,9 @@ public static function version($skipUsingCache = FALSE) { public function &getLocationValues() { if ($this->_location == NULL) { $domain = self::getDomain(NULL); - $params = array( + $params = [ 'contact_id' => $domain->contact_id, - ); + ]; $this->_location = CRM_Core_BAO_Location::getValues($params, TRUE); if (empty($this->_location)) { @@ -176,7 +144,7 @@ public static function edit(&$params, &$id) { */ public static function create($params) { $domain = new CRM_Core_DAO_Domain(); - $domain->copyValues($params); + $domain->copyValues($params, TRUE); $domain->save(); return $domain; } @@ -198,30 +166,35 @@ public static function multipleDomains() { /** * @param bool $skipFatal - * + * @param bool $returnString * @return array * name & email for domain * @throws Exception */ - public static function getNameAndEmail($skipFatal = FALSE) { + public static function getNameAndEmail($skipFatal = FALSE, $returnString = FALSE) { $fromEmailAddress = CRM_Core_OptionGroup::values('from_email_address', NULL, NULL, NULL, ' AND is_default = 1'); if (!empty($fromEmailAddress)) { + if ($returnString) { + // Return a string like: "Demonstrators Anonymous" + return $fromEmailAddress; + } foreach ($fromEmailAddress as $key => $value) { $email = CRM_Utils_Mail::pluckEmailFromHeader($value); $fromArray = explode('"', $value); $fromName = CRM_Utils_Array::value(1, $fromArray); break; } - return array($fromName, $email); + return [$fromName, $email]; } - elseif ($skipFatal) { - return array('', ''); + + if ($skipFatal) { + return [NULL, NULL]; } - $url = CRM_Utils_System::url('civicrm/admin/domain', - 'action=update&reset=1' + $url = CRM_Utils_System::url('civicrm/admin/options/from_email_address', + 'reset=1' ); - $status = ts("There is no valid default from email address configured for the domain. You can configure here Configure From Email Address.", array(1 => $url)); + $status = ts("There is no valid default from email address configured for the domain. You can configure here Configure From Email Address.", [1 => $url]); CRM_Core_Error::fatal($status); } @@ -235,7 +208,7 @@ public static function addContactToDomainGroup($contactID) { $groupID = self::getGroupId(); if ($groupID) { - $contactIDs = array($contactID); + $contactIDs = [$contactID]; CRM_Contact_BAO_GroupContact::addContactsToGroup($contactIDs, $groupID); return $groupID; @@ -286,7 +259,7 @@ public static function isDomainGroup($groupId) { */ public static function getChildGroupIds() { $domainGroupID = self::getGroupId(); - $childGrps = array(); + $childGrps = []; if ($domainGroupID) { $childGrps = CRM_Contact_BAO_GroupNesting::getChildGroupIds($domainGroupID); @@ -302,7 +275,7 @@ public static function getChildGroupIds() { */ public static function getContactList() { $siteGroups = CRM_Core_BAO_Domain::getChildGroupIds(); - $siteContacts = array(); + $siteContacts = []; if (!empty($siteGroups)) { $query = " @@ -319,4 +292,41 @@ public static function getContactList() { return $siteContacts; } + /** + * CRM-20308 & CRM-19657 + * Return domain information / user information for the usage in receipts + * Try default from address then fall back to using logged in user details + */ + public static function getDefaultReceiptFrom() { + $domain = civicrm_api3('domain', 'getsingle', ['id' => CRM_Core_Config::domainID()]); + if (!empty($domain['from_email'])) { + return [$domain['from_name'], $domain['from_email']]; + } + if (!empty($domain['domain_email'])) { + return [$domain['name'], $domain['domain_email']]; + } + $userName = ''; + $userEmail = ''; + + if (!Civi::settings()->get('allow_mail_from_logged_in_contact')) { + return [$userName, $userEmail]; + } + + $userID = CRM_Core_Session::singleton()->getLoggedInContactID(); + if (!empty($userID)) { + list($userName, $userEmail) = CRM_Contact_BAO_Contact_Location::getEmailDetails($userID); + } + // If still empty fall back to the logged in user details. + // return empty values no matter what. + return [$userName, $userEmail]; + } + + /** + * Get address to be used for system from addresses when a reply is not expected. + */ + public static function getNoReplyEmailAddress() { + $emailDomain = CRM_Core_BAO_MailSettings::defaultDomain(); + return "do-not-reply@$emailDomain"; + } + } diff --git a/CRM/Core/BAO/Email.php b/CRM/Core/BAO/Email.php index cbdf43d9817c..20c6875d6a68 100644 --- a/CRM/Core/BAO/Email.php +++ b/CRM/Core/BAO/Email.php @@ -1,9 +1,9 @@ array( + $params = [ + 1 => [ $id, 'Integer', - ), - ); + ], + ]; - $emails = $values = array(); + $emails = $values = []; $dao = CRM_Core_DAO::executeQuery($query, $params); $count = 1; while ($dao->fetch()) { - $values = array( + $values = [ 'locationType' => $dao->locationType, 'is_primary' => $dao->is_primary, 'on_hold' => $dao->on_hold, 'id' => $dao->email_id, 'email' => $dao->email, 'locationTypeId' => $dao->locationTypeId, - ); + ]; if ($updateBlankLocInfo) { $emails[$count++] = $values; @@ -207,24 +207,24 @@ public static function allEntityEmails(&$entityElements) { AND ltype.id = e.location_type_id ORDER BY e.is_primary DESC, email_id ASC "; - $params = array( - 1 => array( + $params = [ + 1 => [ $entityId, 'Integer', - ), - ); + ], + ]; - $emails = array(); + $emails = []; $dao = CRM_Core_DAO::executeQuery($sql, $params); while ($dao->fetch()) { - $emails[$dao->email_id] = array( + $emails[$dao->email_id] = [ 'locationType' => $dao->locationType, 'is_primary' => $dao->is_primary, 'on_hold' => $dao->on_hold, 'id' => $dao->email_id, 'email' => $dao->email, 'locationTypeId' => $dao->locationTypeId, - ); + ]; } return $emails; @@ -237,10 +237,20 @@ public static function allEntityEmails(&$entityElements) { * Email object. */ public static function holdEmail(&$email) { + if ($email->id && $email->on_hold === NULL) { + // email is being updated but no change to on_hold. + return; + } + if ($email->on_hold === 'null' || $email->on_hold === NULL) { + // legacy handling, deprecated. + $email->on_hold = 0; + } + $email->on_hold = (int) $email->on_hold; + //check for update mode if ($email->id) { - $params = array(1 => array($email->id, 'Integer')); - if ($email->on_hold && $email->on_hold != 'null') { + $params = [1 => [$email->id, 'Integer']]; + if ($email->on_hold) { $sql = " SELECT id FROM civicrm_email @@ -252,7 +262,8 @@ public static function holdEmail(&$email) { $email->reset_date = 'null'; } } - elseif ($email->on_hold == 'null') { + elseif ($email->on_hold === 0) { + // we do this lookup to see if reset_date should be changed. $sql = " SELECT id FROM civicrm_email @@ -269,12 +280,26 @@ public static function holdEmail(&$email) { } } else { - if (($email->on_hold != 'null') && $email->on_hold) { + if ($email->on_hold) { $email->hold_date = date('YmdHis'); } } } + /** + * Generate an array of Domain email addresses. + * @return array $domainEmails; + */ + public static function domainEmails() { + $domainEmails = []; + $domainFrom = (array) CRM_Core_OptionGroup::values('from_email_address'); + foreach (array_keys($domainFrom) as $k) { + $domainEmail = $domainFrom[$k]; + $domainEmails[$domainEmail] = htmlspecialchars($domainEmail); + } + return $domainEmails; + } + /** * Build From Email as the combination of all the email ids of the logged in user and * the domain email id @@ -283,21 +308,19 @@ public static function holdEmail(&$email) { * an array of email ids */ public static function getFromEmail() { - $session = CRM_Core_Session::singleton(); - $contactID = $session->get('userID'); - $fromEmailValues = array(); - // add all configured FROM email addresses - $domainFrom = CRM_Core_OptionGroup::values('from_email_address'); - foreach (array_keys($domainFrom) as $k) { - $domainEmail = $domainFrom[$k]; - $fromEmailValues[$domainEmail] = htmlspecialchars($domainEmail); + $fromEmailValues = self::domainEmails(); + + if (!Civi::settings()->get('allow_mail_from_logged_in_contact')) { + return $fromEmailValues; } + $contactFromEmails = []; // add logged in user's active email ids + $contactID = CRM_Core_Session::singleton()->getLoggedInContactID(); if ($contactID) { $contactEmails = self::allEmails($contactID); - $fromDisplayName = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $contactID, 'display_name'); + $fromDisplayName = CRM_Core_Session::singleton()->getLoggedInContactDisplayName(); foreach ($contactEmails as $emailId => $emailVal) { $email = trim($emailVal['email']); @@ -310,10 +333,10 @@ public static function getFromEmail() { if (!empty($emailVal['is_primary'])) { $fromEmailHtml .= ' ' . ts('(preferred)'); } - $fromEmailValues[$fromEmail] = $fromEmailHtml; + $contactFromEmails[$emailId] = $fromEmailHtml; } } - return $fromEmailValues; + return CRM_Utils_Array::crmArrayMerge($contactFromEmails, $fromEmailValues); } /** diff --git a/CRM/Core/BAO/EntityTag.php b/CRM/Core/BAO/EntityTag.php index eec0888a27ca..e7ec387c7971 100644 --- a/CRM/Core/BAO/EntityTag.php +++ b/CRM/Core/BAO/EntityTag.php @@ -1,9 +1,9 @@ entity_id = $entityID; @@ -81,12 +81,15 @@ public static function add(&$params) { // dont save the object if it already exists, CRM-1276 if (!$entityTag->find(TRUE)) { + //invoke pre hook + CRM_Utils_Hook::pre('create', 'EntityTag', $params['tag_id'], $params); + $entityTag->save(); //invoke post hook on entityTag // we are using this format to keep things consistent between the single and bulk operations // so a bit different from other post hooks - $object = array(0 => array(0 => $params['entity_id']), 1 => $params['entity_table']); + $object = [0 => [0 => $params['entity_id']], 1 => $params['entity_table']]; CRM_Utils_Hook::post('create', 'EntityTag', $params['tag_id'], $object); } return $entityTag; @@ -111,13 +114,18 @@ public static function dataExists($params) { * (reference ) an assoc array of name/value pairs. */ public static function del(&$params) { + //invoke pre hook + if (!empty($params['tag_id'])) { + CRM_Utils_Hook::pre('delete', 'EntityTag', $params['tag_id'], $params); + } + $entityTag = new CRM_Core_BAO_EntityTag(); $entityTag->copyValues($params); $entityTag->delete(); //invoke post hook on entityTag if (!empty($params['tag_id'])) { - $object = array(0 => array(0 => $params['entity_id']), 1 => $params['entity_table']); + $object = [0 => [0 => $params['entity_id']], 1 => $params['entity_table']]; CRM_Utils_Hook::post('delete', 'EntityTag', $params['tag_id'], $object); } } @@ -140,7 +148,11 @@ public static function del(&$params) { public static function addEntitiesToTag(&$entityIds, $tagId, $entityTable, $applyPermissions) { $numEntitiesAdded = 0; $numEntitiesNotAdded = 0; - $entityIdsAdded = array(); + $entityIdsAdded = []; + + //invoke pre hook for entityTag + $preObject = [$entityIds, $entityTable]; + CRM_Utils_Hook::pre('create', 'EntityTag', $tagId, $preObject); foreach ($entityIds as $entityId) { // CRM-17350 - check if we have permission to edit the contact @@ -165,12 +177,12 @@ public static function addEntitiesToTag(&$entityIds, $tagId, $entityTable, $appl } //invoke post hook on entityTag - $object = array($entityIdsAdded, $entityTable); + $object = [$entityIdsAdded, $entityTable]; CRM_Utils_Hook::post('create', 'EntityTag', $tagId, $object); CRM_Contact_BAO_GroupContactCache::opportunisticCacheFlush(); - return array(count($entityIds), $numEntitiesAdded, $numEntitiesNotAdded); + return [count($entityIds), $numEntitiesAdded, $numEntitiesNotAdded]; } /** @@ -214,7 +226,11 @@ public static function checkPermissionOnEntityTag($entityID, $entityTable) { public static function removeEntitiesFromTag(&$entityIds, $tagId, $entityTable, $applyPermissions) { $numEntitiesRemoved = 0; $numEntitiesNotRemoved = 0; - $entityIdsRemoved = array(); + $entityIdsRemoved = []; + + //invoke pre hook for entityTag + $preObject = [$entityIds, $entityTable]; + CRM_Utils_Hook::pre('delete', 'EntityTag', $tagId, $preObject); foreach ($entityIds as $entityId) { // CRM-17350 - check if we have permission to edit the contact @@ -239,12 +255,12 @@ public static function removeEntitiesFromTag(&$entityIds, $tagId, $entityTable, } //invoke post hook on entityTag - $object = array($entityIdsRemoved, $entityTable); + $object = [$entityIdsRemoved, $entityTable]; CRM_Utils_Hook::post('delete', 'EntityTag', $tagId, $object); CRM_Contact_BAO_GroupContactCache::opportunisticCacheFlush(); - return array(count($entityIds), $numEntitiesRemoved, $numEntitiesNotRemoved); + return [count($entityIds), $numEntitiesRemoved, $numEntitiesNotRemoved]; } /** @@ -264,12 +280,12 @@ public static function create(&$params, $entityTable, $entityID) { // this fix is done to prevent warning generated by array_key_exits incase of empty array is given as input if (!is_array($params)) { - $params = array(); + $params = []; } // this fix is done to prevent warning generated by array_key_exits incase of empty array is given as input if (!is_array($entityTag)) { - $entityTag = array(); + $entityTag = []; } // check which values has to be inserted/deleted for contact @@ -299,7 +315,7 @@ public static function create(&$params, $entityTable, $entityID) { * array of entity ids */ public function getEntitiesByTag($tag) { - $entityIds = array(); + $entityIds = []; $entityTagDAO = new CRM_Core_DAO_EntityTag(); $entityTagDAO->tag_id = $tag->id; $entityTagDAO->find(); @@ -318,7 +334,7 @@ public function getEntitiesByTag($tag) { * @return array */ public static function getContactTags($contactID, $count = FALSE) { - $contactTags = array(); + $contactTags = []; if (!$count) { $select = "SELECT ct.id, ct.name "; } @@ -357,7 +373,7 @@ public static function getContactTags($contactID, $count = FALSE) { * @return array */ public static function getChildEntityTags($parentId, $entityId, $entityTable = 'civicrm_contact') { - $entityTags = array(); + $entityTags = []; $query = "SELECT ct.id as tag_id, name FROM civicrm_tag ct INNER JOIN civicrm_entity_tag et ON ( et.entity_id = {$entityId} AND et.entity_table = '{$entityTable}' AND et.tag_id = ct.id) @@ -366,17 +382,20 @@ public static function getChildEntityTags($parentId, $entityId, $entityTable = ' $dao = CRM_Core_DAO::executeQuery($query); while ($dao->fetch()) { - $entityTags[$dao->tag_id] = array( + $entityTags[$dao->tag_id] = [ 'id' => $dao->tag_id, 'name' => $dao->name, - ); + ]; } return $entityTags; } /** - * Merge two tags: tag B into tag A. + * Merge two tags + * + * Tag A will inherit all of tag B's properties. + * Tag B will be deleted. * * @param int $tagAId * @param int $tagBId @@ -384,38 +403,40 @@ public static function getChildEntityTags($parentId, $entityId, $entityTable = ' * @return array */ public function mergeTags($tagAId, $tagBId) { - $queryParams = array( - 1 => array($tagBId, 'Integer'), - 2 => array($tagAId, 'Integer'), - ); + $queryParams = [ + 1 => [$tagAId, 'Integer'], + 2 => [$tagBId, 'Integer'], + ]; // re-compute used_for field $query = "SELECT id, name, used_for FROM civicrm_tag WHERE id IN (%1, %2)"; $dao = CRM_Core_DAO::executeQuery($query, $queryParams); - $tags = array(); + $tags = []; while ($dao->fetch()) { $label = ($dao->id == $tagAId) ? 'tagA' : 'tagB'; $tags[$label] = $dao->name; - $tags["{$label}_used_for"] = $dao->used_for ? explode(",", $dao->used_for) : array(); + $tags["{$label}_used_for"] = $dao->used_for ? explode(",", $dao->used_for) : []; } $usedFor = array_merge($tags["tagA_used_for"], $tags["tagB_used_for"]); $usedFor = implode(',', array_unique($usedFor)); - $tags["tagB_used_for"] = explode(",", $usedFor); + $tags["used_for"] = explode(",", $usedFor); // get all merge queries together - $sqls = array( + $sqls = [ // 1. update entity tag entries "UPDATE IGNORE civicrm_entity_tag SET tag_id = %1 WHERE tag_id = %2", - // 2. update used_for info for tag B - "UPDATE civicrm_tag SET used_for = '{$usedFor}' WHERE id = %1", - // 3. remove tag A, if tag A is getting merged into B + // 2. move children + "UPDATE civicrm_tag SET parent_id = %1 WHERE parent_id = %2", + // 3. update used_for info for tag A & children + "UPDATE civicrm_tag SET used_for = '{$usedFor}' WHERE id = %1 OR parent_id = %1", + // 4. delete tag B "DELETE FROM civicrm_tag WHERE id = %2", - // 4. remove duplicate entity tag records + // 5. remove duplicate entity tag records "DELETE et2.* from civicrm_entity_tag et1 INNER JOIN civicrm_entity_tag et2 ON et1.entity_table = et2.entity_table AND et1.entity_id = et2.entity_id AND et1.tag_id = et2.tag_id WHERE et1.id < et2.id", - // 5. remove orphaned entity_tags + // 6. remove orphaned entity_tags "DELETE FROM civicrm_entity_tag WHERE tag_id = %2", - ); - $tables = array('civicrm_entity_tag', 'civicrm_tag'); + ]; + $tables = ['civicrm_entity_tag', 'civicrm_tag']; // Allow hook_civicrm_merge() to add SQL statements for the merge operation AND / OR // perform any other actions like logging @@ -446,8 +467,8 @@ public function mergeTags($tagAId, $tagBId) { * * @return array|bool */ - public static function buildOptions($fieldName, $context = NULL, $props = array()) { - $params = array(); + public static function buildOptions($fieldName, $context = NULL, $props = []) { + $params = []; if ($fieldName == 'tag' || $fieldName == 'tag_id') { if (!empty($props['entity_table'])) { @@ -458,12 +479,23 @@ public static function buildOptions($fieldName, $context = NULL, $props = array( // Output tag list as nested hierarchy // TODO: This will only work when api.entity is "entity_tag". What about others? if ($context == 'search' || $context == 'create') { - return CRM_Core_BAO_Tag::getTags(CRM_Utils_Array::value('entity_table', $props, 'civicrm_contact'), CRM_Core_DAO::$_nullArray, CRM_Utils_Array::value('parent_id', $params), '- '); + $dummyArray = []; + return CRM_Core_BAO_Tag::getTags(CRM_Utils_Array::value('entity_table', $props, 'civicrm_contact'), $dummyArray, CRM_Utils_Array::value('parent_id', $params), '- '); } } $options = CRM_Core_PseudoConstant::get(__CLASS__, $fieldName, $params, $context); + // Special formatting for validate/match context + if ($fieldName == 'entity_table' && in_array($context, ['validate', 'match'])) { + $options = []; + foreach (self::buildOptions($fieldName) as $tableName => $label) { + $bao = CRM_Core_DAO_AllCoreTables::getClassForTable($tableName); + $apiName = CRM_Core_DAO_AllCoreTables::getBriefName($bao); + $options[$tableName] = $apiName; + } + } + return $options; } diff --git a/CRM/Core/BAO/Extension.php b/CRM/Core/BAO/Extension.php index ebcec012a669..8bbe63fffacf 100644 --- a/CRM/Core/BAO/Extension.php +++ b/CRM/Core/BAO/Extension.php @@ -1,9 +1,9 @@ array($schemaVersion, 'String'), - 2 => array($fullName, 'String'), - ); + $params = [ + 1 => [$schemaVersion, 'String'], + 2 => [$fullName, 'String'], + ]; return CRM_Core_DAO::executeQuery($sql, $params); } @@ -98,9 +98,9 @@ public static function setSchemaVersion($fullName, $schemaVersion) { */ public static function getSchemaVersion($fullName) { $sql = 'SELECT schema_version FROM civicrm_extension WHERE full_name = %1'; - $params = array( - 1 => array($fullName, 'String'), - ); + $params = [ + 1 => [$fullName, 'String'], + ]; return CRM_Core_DAO::singleValueQuery($sql, $params); } diff --git a/CRM/Core/BAO/File.php b/CRM/Core/BAO/File.php index c2480ad9b8c2..c9a1265d671e 100644 --- a/CRM/Core/BAO/File.php +++ b/CRM/Core/BAO/File.php @@ -1,9 +1,9 @@ copyValues($params); + + if (empty($params['id']) && empty($params['created_id'])) { + $fileDAO->created_id = CRM_Core_Session::getLoggedInContactID(); + } + + $fileDAO->save(); + + CRM_Utils_Hook::post($op, 'File', $fileDAO->id, $fileDAO); + + return $fileDAO; + } /** * @param int $fileID * @param int $entityID - * @param null $entityTable * * @return array */ - public static function path($fileID, $entityID, $entityTable = NULL) { + public static function path($fileID, $entityID) { $entityFileDAO = new CRM_Core_DAO_EntityFile(); - if ($entityTable) { - $entityFileDAO->entity_table = $entityTable; - } $entityFileDAO->entity_id = $entityID; $entityFileDAO->file_id = $fileID; @@ -63,15 +87,14 @@ public static function path($fileID, $entityID, $entityTable = NULL) { $path = $config->customFileUploadDir . $fileDAO->uri; if (file_exists($path) && is_readable($path)) { - return array($path, $fileDAO->mime_type); + return [$path, $fileDAO->mime_type]; } } } - return array(NULL, NULL); + return [NULL, NULL]; } - /** * @param $data * @param int $fileTypeID @@ -97,7 +120,7 @@ public static function filePostProcess( $mimeType = NULL ) { if (!$mimeType) { - CRM_Core_Error::fatal(ts('Mime Type is now a required parameter')); + CRM_Core_Error::statusBounce(ts('Mime Type is now a required parameter for file upload')); } $config = CRM_Core_Config::singleton(); @@ -116,7 +139,7 @@ public static function filePostProcess( CRM_Utils_File::createDir($directoryName); if (!rename($data, $directoryName . DIRECTORY_SEPARATOR . $filename)) { - CRM_Core_Error::fatal(ts('Could not move custom file to custom upload directory')); + CRM_Core_Error::statusBounce(ts('Could not move custom file to custom upload directory')); } // to get id's @@ -165,7 +188,7 @@ public static function filePostProcess( //save free tags if (isset($fileParams['attachment_taglist']) && !empty($fileParams['attachment_taglist'])) { - CRM_Core_Form_Tag::postProcess($fileParams['attachment_taglist'], $entityFileDAO->id, 'civicrm_file', CRM_Core_DAO::$_nullObject); + CRM_Core_Form_Tag::postProcess($fileParams['attachment_taglist'], $entityFileDAO->id, 'civicrm_file'); } // lets call the post hook here so attachments code can do the right stuff @@ -203,7 +226,7 @@ public static function deleteFileReferences($fileID, $entityID, $fieldID) { $entityFileDAO->entity_table = $tableName; if (!$entityFileDAO->find(TRUE)) { - CRM_Core_Error::fatal(); + CRM_Core_Error::fatal(sprintf('No record found for given file ID - %d and entity ID - %d', $fileID, $entityID)); } $entityFileDAO->delete(); @@ -211,7 +234,7 @@ public static function deleteFileReferences($fileID, $entityID, $fieldID) { // also set the value to null of the table and column $query = "UPDATE $tableName SET $columnName = null WHERE $columnName = %1"; - $params = array(1 => array($fileID, 'Integer')); + $params = [1 => [$fileID, 'Integer']]; CRM_Core_DAO::executeQuery($query, $params); } @@ -246,8 +269,8 @@ public static function deleteEntityFile($entityTable, $entityID, $fileTypeID = N list($sql, $params) = self::sql($entityTable, $entityID, $fileTypeID, $fileID); $dao = CRM_Core_DAO::executeQuery($sql, $params); - $cfIDs = array(); - $cefIDs = array(); + $cfIDs = []; + $cefIDs = []; while ($dao->fetch()) { $cfIDs[$dao->cfID] = $dao->uri; $cefIDs[] = $dao->cefID; @@ -262,13 +285,13 @@ public static function deleteEntityFile($entityTable, $entityID, $fileTypeID = N if (!empty($cfIDs)) { // Delete file only if there no any entity using this file. - $deleteFiles = array(); + $deleteFiles = []; foreach ($cfIDs as $fId => $fUri) { //delete tags from entity tag table - $tagParams = array( + $tagParams = [ 'entity_table' => 'civicrm_file', 'entity_id' => $fId, - ); + ]; CRM_Core_BAO_EntityTag::del($tagParams); @@ -307,8 +330,9 @@ public static function getEntityFile($entityTable, $entityID, $addDeleteArgs = F list($sql, $params) = self::sql($entityTable, $entityID, NULL); $dao = CRM_Core_DAO::executeQuery($sql, $params); - $results = array(); + $results = []; while ($dao->fetch()) { + $fileHash = self::generateFileHash($dao->entity_id, $dao->cfID); $result['fileID'] = $dao->cfID; $result['entityID'] = $dao->cefID; $result['mime_type'] = $dao->mime_type; @@ -316,9 +340,10 @@ public static function getEntityFile($entityTable, $entityID, $addDeleteArgs = F $result['description'] = $dao->description; $result['cleanName'] = CRM_Utils_File::cleanFileName($dao->uri); $result['fullPath'] = $config->customFileUploadDir . DIRECTORY_SEPARATOR . $dao->uri; - $result['url'] = CRM_Utils_System::url('civicrm/file', "reset=1&id={$dao->cfID}&eid={$dao->entity_id}"); + $result['url'] = CRM_Utils_System::url('civicrm/file', "reset=1&id={$dao->cfID}&eid={$dao->entity_id}&fcs={$fileHash}"); $result['href'] = "{$result['cleanName']}"; $result['tag'] = CRM_Core_BAO_EntityTag::getTag($dao->cfID, 'civicrm_file'); + $result['icon'] = CRM_Utils_File::getIconFromMimeType($dao->mime_type); if ($addDeleteArgs) { $result['deleteURLArgs'] = self::deleteURLArgs($dao->entity_table, $dao->entity_id, $dao->cfID); } @@ -326,11 +351,11 @@ public static function getEntityFile($entityTable, $entityID, $addDeleteArgs = F } //fix tag names - $tags = CRM_Core_PseudoConstant::get('CRM_Core_DAO_EntityTag', 'tag_id', array('onlyActive' => FALSE)); + $tags = CRM_Core_PseudoConstant::get('CRM_Core_DAO_EntityTag', 'tag_id', ['onlyActive' => FALSE]); foreach ($results as &$values) { if (!empty($values['tag'])) { - $tagNames = array(); + $tagNames = []; foreach ($values['tag'] as $tid) { $tagNames[] = $tags[$tid]; } @@ -385,22 +410,22 @@ public static function sql($entityTable, $entityID, $fileTypeID = NULL, $fileID AND CEF.entity_id = %2"; } - $params = array( - 1 => array($entityTable, 'String'), - 2 => array($entityID, 'Integer'), - ); + $params = [ + 1 => [$entityTable, 'String'], + 2 => [$entityID, 'Integer'], + ]; if ($fileTypeID !== NULL) { $sql .= " AND CF.file_type_id = %3"; - $params[3] = array($fileTypeID, 'Integer'); + $params[3] = [$fileTypeID, 'Integer']; } if ($fileID !== NULL) { $sql .= " AND CF.id = %4"; - $params[4] = array($fileID, 'Integer'); + $params[4] = [$fileID, 'Integer']; } - return array($sql, $params); + return [$sql, $params]; } /** @@ -452,30 +477,30 @@ public static function buildAttachment(&$form, $entityTable, $entityID = NULL, $ // add attachments for ($i = 1; $i <= $numAttachments; $i++) { - $form->addElement('file', "attachFile_$i", ts('Attach File'), 'size=30 maxlength=60'); + $form->addElement('file', "attachFile_$i", ts('Attach File'), 'size=30 maxlength=221'); $form->addUploadElement("attachFile_$i"); $form->setMaxFileSize($maxFileSize * 1024 * 1024); $form->addRule("attachFile_$i", ts('File size should be less than %1 MByte(s)', - array(1 => $maxFileSize) + [1 => $maxFileSize] ), 'maxfilesize', $maxFileSize * 1024 * 1024 ); - $form->addElement('text', "attachDesc_$i", NULL, array( + $form->addElement('text', "attachDesc_$i", NULL, [ 'size' => 40, 'maxlength' => 255, 'placeholder' => ts('Description'), - )); + ]); if (!empty($tags)) { $form->add('select', "tag_$i", ts('Tags'), $tags, FALSE, - array( + [ 'id' => "tags_$i", 'multiple' => 'multiple', 'class' => 'huge crm-select2', 'placeholder' => ts('- none -'), - ) + ] ); } CRM_Core_Form_Tag::buildQuickForm($form, $parentNames, 'civicrm_file', NULL, FALSE, TRUE, "file_taglist_$i"); @@ -503,7 +528,7 @@ public static function attachmentInfo($entityTable, $entityID, $separator = '
    $attach) { $currentAttachmentURL[] = $attach['href']; } @@ -540,7 +565,7 @@ public static function formatAttachment( $attachFreeTags = "file_taglist_$i"; if (isset($formValues[$attachName]) && !empty($formValues[$attachName])) { // add static tags if selects - $tagParams = array(); + $tagParams = []; if (!empty($formValues[$attachTags])) { foreach ($formValues[$attachTags] as $tag) { $tagParams[$tag] = 1; @@ -549,11 +574,11 @@ public static function formatAttachment( // we dont care if the file is empty or not // CRM-7448 - $extraParams = array( + $extraParams = [ 'description' => $formValues[$attachDesc], 'tag' => $tagParams, - 'attachment_taglist' => CRM_Utils_Array::value($attachFreeTags, $formValues, array()), - ); + 'attachment_taglist' => CRM_Utils_Array::value($attachFreeTags, $formValues, []), + ]; CRM_Utils_File::formatFile($formValues, $attachName, $extraParams); @@ -600,7 +625,7 @@ public static function processAttachment(&$params, $entityTable, $entityID) { public static function uploadNames() { $numAttachments = Civi::settings()->get('max_attachments'); - $names = array(); + $names = []; for ($i = 1; $i <= $numAttachments; $i++) { $names[] = "attachFile_{$i}"; } @@ -654,7 +679,7 @@ public static function deleteURLArgs($entityTable, $entityID, $fileID) { * */ public static function deleteAttachment() { - $params = array(); + $params = []; $params['entityTable'] = CRM_Utils_Request::retrieve('entityTable', 'String', CRM_Core_DAO::$_nullObject, TRUE); $params['entityID'] = CRM_Utils_Request::retrieve('entityID', 'Positive', CRM_Core_DAO::$_nullObject, TRUE); $params['fileID'] = CRM_Utils_Request::retrieve('fileID', 'Positive', CRM_Core_DAO::$_nullObject, TRUE); @@ -666,10 +691,9 @@ public static function deleteAttachment() { CRM_Core_Error::fatal('Request signature is invalid'); } - CRM_Core_BAO_File::deleteEntityFile($params['entityTable'], $params['entityID'], NULL, $params['fileID']); + self::deleteEntityFile($params['entityTable'], $params['entityID'], NULL, $params['fileID']); } - /** * Display paper icon for a file attachment -- CRM-13624 * @@ -730,7 +754,7 @@ public static function paperIconAttachment($entityTable, $entityID) { * @return CRM_Core_FileSearchInterface|NULL */ public static function getSearchService() { - $fileSearches = array(); + $fileSearches = []; CRM_Utils_Hook::fileSearches($fileSearches); // use the first available search @@ -741,4 +765,58 @@ public static function getSearchService() { return NULL; } + /** + * Generates an access-token for downloading a specific file. + * + * @param int $entityId entity id the file is attached to + * @param int $fileId file ID + * @param int $genTs + * @param int $life + * @return string + */ + public static function generateFileHash($entityId = NULL, $fileId = NULL, $genTs = NULL, $life = NULL) { + // Use multiple (but stable) inputs for hash information. + $siteKey = CRM_Utils_Constant::value('CIVICRM_SITE_KEY'); + if (!$siteKey) { + throw new \CRM_Core_Exception("Cannot generate file access token. Please set CIVICRM_SITE_KEY."); + } + + if (!$genTs) { + $genTs = time(); + } + if (!$life) { + $days = Civi::settings()->get('checksum_timeout'); + $life = 24 * $days; + } + // Trim 8 chars off the string, make it slightly easier to find + // but reveals less information from the hash. + $cs = hash_hmac('sha256', "entity={$entityId}&file={$fileId}&life={$life}", $siteKey); + return "{$cs}_{$genTs}_{$life}"; + } + + /** + * Validate a file access token. + * + * @param string $hash + * @param int $entityId Entity Id the file is attached to + * @param int $fileId File Id + * @return bool + */ + public static function validateFileHash($hash, $entityId, $fileId) { + $input = CRM_Utils_System::explode('_', $hash, 3); + $inputTs = CRM_Utils_Array::value(1, $input); + $inputLF = CRM_Utils_Array::value(2, $input); + $testHash = CRM_Core_BAO_File::generateFileHash($entityId, $fileId, $inputTs, $inputLF); + if (hash_equals($testHash, $hash)) { + $now = time(); + if ($inputTs + ($inputLF * 60 * 60) >= $now) { + return TRUE; + } + else { + return FALSE; + } + } + return FALSE; + } + } diff --git a/CRM/Core/BAO/FinancialTrxn.php b/CRM/Core/BAO/FinancialTrxn.php index c3f63490c881..6b3d45336992 100644 --- a/CRM/Core/BAO/FinancialTrxn.php +++ b/CRM/Core/BAO/FinancialTrxn.php @@ -1,9 +1,9 @@ copyValues($params); - if (!CRM_Utils_Rule::currencyCode($trxn->currency)) { + if (empty($params['id']) && !CRM_Utils_Rule::currencyCode($trxn->currency)) { $trxn->currency = CRM_Core_Config::singleton()->defaultCurrency; } $trxn->save(); - // save to entity_financial_trxn table - $entityFinancialTrxnParams - = array( - 'entity_table' => "civicrm_contribution", - 'financial_trxn_id' => $trxn->id, - 'amount' => $params['total_amount'], - 'currency' => $trxn->currency, - ); - - if (!empty($trxnEntityTable)) { - $entityFinancialTrxnParams['entity_table'] = $trxnEntityTable['entity_table']; - $entityFinancialTrxnParams['entity_id'] = $trxnEntityTable['entity_id']; - } - else { - $entityFinancialTrxnParams['entity_id'] = $params['contribution_id']; + if (!empty($params['id'])) { + // For an update entity financial transaction record will already exist. Return early. + return $trxn; } + // Save to entity_financial_trxn table. + $entityFinancialTrxnParams = [ + 'entity_table' => CRM_Utils_Array::value('entity_table', $params, 'civicrm_contribution'), + 'entity_id' => CRM_Utils_Array::value('entity_id', $params, CRM_Utils_Array::value('contribution_id', $params)), + 'financial_trxn_id' => $trxn->id, + 'amount' => $params['total_amount'], + ]; + $entityTrxn = new CRM_Financial_DAO_EntityFinancialTrxn(); $entityTrxn->copyValues($entityFinancialTrxnParams); $entityTrxn->save(); @@ -98,15 +90,14 @@ public static function getBalanceTrxnAmt($contributionId, $contributionFinancial if (!$contributionFinancialTypeId) { $contributionFinancialTypeId = CRM_Core_DAO::getFieldValue('CRM_Contribute_BAO_Contribution', $contributionId, 'financial_type_id'); } - $relationTypeId = key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Accounts Receivable Account is' ")); - $toFinancialAccount = CRM_Contribute_PseudoConstant::financialAccountType($contributionFinancialTypeId, $relationTypeId); + $toFinancialAccount = CRM_Contribute_PseudoConstant::getRelationalFinancialAccount($contributionFinancialTypeId, 'Accounts Receivable Account is'); $q = "SELECT ft.id, ft.total_amount FROM civicrm_financial_trxn ft INNER JOIN civicrm_entity_financial_trxn eft ON (eft.financial_trxn_id = ft.id AND eft.entity_table = 'civicrm_contribution') WHERE eft.entity_id = %1 AND ft.to_financial_account_id = %2"; - $p[1] = array($contributionId, 'Integer'); - $p[2] = array($toFinancialAccount, 'Integer'); + $p[1] = [$contributionId, 'Integer']; + $p[2] = [$toFinancialAccount, 'Integer']; $balanceAmtDAO = CRM_Core_DAO::executeQuery($q, $p); - $ret = array(); + $ret = []; if ($balanceAmtDAO->N) { $ret['total_amount'] = 0; } @@ -126,7 +117,7 @@ public static function getBalanceTrxnAmt($contributionId, $contributionFinancial * @param array $defaults * (reference ) an assoc array to hold the flattened values. * - * @return CRM_Contribute_BAO_ContributionType + * @return \CRM_Financial_DAO_FinancialTrxn */ public static function retrieve(&$params, &$defaults) { $financialItem = new CRM_Financial_DAO_FinancialTrxn(); @@ -149,15 +140,16 @@ public static function retrieve(&$params, &$defaults) { * @param bool $newTrxn * @param string $whereClause * Additional where parameters + * @param int $fromAccountID * * @return array * array of category id's the contact belongs to. * */ public static function getFinancialTrxnId($entity_id, $orderBy = 'ASC', $newTrxn = FALSE, $whereClause = '', $fromAccountID = NULL) { - $ids = array('entityFinancialTrxnId' => NULL, 'financialTrxnId' => NULL); + $ids = ['entityFinancialTrxnId' => NULL, 'financialTrxnId' => NULL]; - $params = array(1 => array($entity_id, 'Integer')); + $params = [1 => [$entity_id, 'Integer']]; $condition = ""; if (!$newTrxn) { $condition = " AND ((ceft1.entity_table IS NOT NULL) OR (cft.payment_instrument_id IS NOT NULL AND ceft1.entity_table IS NULL)) "; @@ -165,7 +157,7 @@ public static function getFinancialTrxnId($entity_id, $orderBy = 'ASC', $newTrxn if ($fromAccountID) { $condition .= " AND (cft.from_financial_account_id <> %2 OR cft.from_financial_account_id IS NULL)"; - $params[2] = array($fromAccountID, 'Integer'); + $params[2] = [$fromAccountID, 'Integer']; } if ($orderBy) { $orderBy = CRM_Utils_Type::escape($orderBy, 'String'); @@ -207,7 +199,7 @@ public static function getRefundTransactionTrxnID($contributionID) { * Get the transaction id for the (latest) refund associated with a contribution. * * @param int $contributionID - * @return string + * @return array */ public static function getRefundTransactionIDs($contributionID) { $refundStatusID = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Refunded'); @@ -232,7 +224,7 @@ public static function getFinancialTrxnTotal($entity_id) { WHERE ft.entity_table = 'civicrm_contribution' AND ft.entity_id = %1 "; - $sqlParams = array(1 => array($entity_id, 'Integer')); + $sqlParams = [1 => [$entity_id, 'Integer']]; return CRM_Core_DAO::singleValueQuery($query, $sqlParams); } @@ -265,10 +257,10 @@ public static function getPayments($financial_trxn_id) { AND ef2.entity_table = 'civicrm_financial_trxn' AND ef1.entity_table = 'civicrm_financial_trxn'"; - $sqlParams = array(1 => array($financial_trxn_id, 'Integer')); + $sqlParams = [1 => [$financial_trxn_id, 'Integer']]; $dao = CRM_Core_DAO::executeQuery($query, $sqlParams); $i = 0; - $result = array(); + $result = []; while ($dao->fetch()) { $result[$i]['financial_trxn_id'] = $dao->financial_trxn_id; $result[$i]['amount'] = $dao->amount; @@ -277,7 +269,7 @@ public static function getPayments($financial_trxn_id) { if (empty($result)) { $query = "SELECT sum( amount ) amount FROM civicrm_entity_financial_trxn WHERE financial_trxn_id =%1 AND entity_table = 'civicrm_financial_item'"; - $sqlParams = array(1 => array($financial_trxn_id, 'Integer')); + $sqlParams = [1 => [$financial_trxn_id, 'Integer']]; $dao = CRM_Core_DAO::executeQuery($query, $sqlParams); if ($dao->fetch()) { @@ -307,7 +299,7 @@ public static function getFinancialTrxnLineTotal($entity_id, $entity_table = 'ci LEFT JOIN civicrm_line_item AS lt ON lt.id = fi.entity_id AND lt.entity_table = %2 WHERE lt.entity_id = %1 "; - $sqlParams = array(1 => array($entity_id, 'Integer'), 2 => array($entity_table, 'String')); + $sqlParams = [1 => [$entity_id, 'Integer'], 2 => [$entity_table, 'String']]; $dao = CRM_Core_DAO::executeQuery($query, $sqlParams); while ($dao->fetch()) { $result[$dao->financial_trxn_id][$dao->id] = $dao->amount; @@ -336,7 +328,7 @@ public static function deleteFinancialTrxn($entity_id) { LEFT JOIN civicrm_financial_item cfi ON ceft1.entity_table = 'civicrm_financial_item' and cfi.id = ceft1.entity_id WHERE ceft.entity_id = %1"; - CRM_Core_DAO::executeQuery($query, array(1 => array($entity_id, 'Integer'))); + CRM_Core_DAO::executeQuery($query, [1 => [$entity_id, 'Integer']]); return TRUE; } @@ -358,37 +350,34 @@ public static function createPremiumTrxn($params) { if (!empty($params['cost'])) { $contributionStatuses = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name'); - $financialAccountType = CRM_Contribute_PseudoConstant::financialAccountType($params['financial_type_id']); - $accountRelationship = CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name IN ('Premiums Inventory Account is', 'Cost of Sales Account is')"); - $toFinancialAccount = !empty($params['isDeleted']) ? 'Premiums Inventory Account is' : 'Cost of Sales Account is'; - $fromFinancialAccount = !empty($params['isDeleted']) ? 'Cost of Sales Account is' : 'Premiums Inventory Account is'; - $accountRelationship = array_flip($accountRelationship); - $financialtrxn = array( - 'to_financial_account_id' => $financialAccountType[$accountRelationship[$toFinancialAccount]], - 'from_financial_account_id' => $financialAccountType[$accountRelationship[$fromFinancialAccount]], + $toFinancialAccountType = !empty($params['isDeleted']) ? 'Premiums Inventory Account is' : 'Cost of Sales Account is'; + $fromFinancialAccountType = !empty($params['isDeleted']) ? 'Cost of Sales Account is' : 'Premiums Inventory Account is'; + $financialtrxn = [ + 'to_financial_account_id' => CRM_Contribute_PseudoConstant::getRelationalFinancialAccount($params['financial_type_id'], $toFinancialAccountType), + 'from_financial_account_id' => CRM_Contribute_PseudoConstant::getRelationalFinancialAccount($params['financial_type_id'], $fromFinancialAccountType), 'trxn_date' => date('YmdHis'), 'total_amount' => CRM_Utils_Array::value('cost', $params) ? $params['cost'] : 0, 'currency' => CRM_Utils_Array::value('currency', $params), 'status_id' => array_search('Completed', $contributionStatuses), - ); - $trxnEntityTable['entity_table'] = 'civicrm_contribution'; - $trxnEntityTable['entity_id'] = $params['contributionId']; - CRM_Core_BAO_FinancialTrxn::create($financialtrxn, $trxnEntityTable); + 'entity_table' => 'civicrm_contribution', + 'entity_id' => $params['contributionId'], + ]; + CRM_Core_BAO_FinancialTrxn::create($financialtrxn); } if (!empty($params['oldPremium'])) { - $premiumParams = array( + $premiumParams = [ 'id' => $params['oldPremium']['product_id'], - ); - $productDetails = array(); - CRM_Contribute_BAO_ManagePremiums::retrieve($premiumParams, $productDetails); - $params = array( + ]; + $productDetails = []; + CRM_Contribute_BAO_Product::retrieve($premiumParams, $productDetails); + $params = [ 'cost' => CRM_Utils_Array::value('cost', $productDetails), 'currency' => CRM_Utils_Array::value('currency', $productDetails), 'financial_type_id' => CRM_Utils_Array::value('financial_type_id', $productDetails), 'contributionId' => $params['oldPremium']['contribution_id'], 'isDeleted' => TRUE, - ); + ]; CRM_Core_BAO_FinancialTrxn::createPremiumTrxn($params); } } @@ -399,10 +388,9 @@ public static function createPremiumTrxn($params) { * @param array $params * To create trxn entries. * - * @return bool + * @return bool|void */ public static function recordFees($params) { - $expenseTypeId = key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Expense Account is' ")); $domainId = CRM_Core_Config::domainID(); $amount = 0; if (!empty($params['prevContribution'])) { @@ -419,7 +407,7 @@ public static function recordFees($params) { else { $financialTypeId = $params['financial_type_id']; } - $financialAccount = CRM_Contribute_PseudoConstant::financialAccountType($financialTypeId, $expenseTypeId); + $financialAccount = CRM_Financial_BAO_FinancialAccount::getFinancialAccountForFinancialTypeByRelationship($financialTypeId, 'Expense Account is'); $params['trxnParams']['from_financial_account_id'] = $params['to_financial_account_id']; $params['trxnParams']['to_financial_account_id'] = $financialAccount; @@ -427,141 +415,97 @@ public static function recordFees($params) { $params['trxnParams']['fee_amount'] = $params['trxnParams']['net_amount'] = 0; $params['trxnParams']['status_id'] = $params['contribution_status_id']; $params['trxnParams']['contribution_id'] = $contributionId; + $params['trxnParams']['is_payment'] = FALSE; $trxn = self::create($params['trxnParams']); if (empty($params['entity_id'])) { $financialTrxnID = CRM_Core_BAO_FinancialTrxn::getFinancialTrxnId($params['trxnParams']['contribution_id'], 'DESC'); $params['entity_id'] = $financialTrxnID['financialTrxnId']; } $fItemParams - = array( + = [ 'financial_account_id' => $financialAccount, 'contact_id' => CRM_Core_DAO::getFieldValue('CRM_Core_DAO_Domain', $domainId, 'contact_id'), 'created_date' => date('YmdHis'), 'transaction_date' => date('YmdHis'), 'amount' => $amount, 'description' => 'Fee', - 'status_id' => CRM_Core_OptionGroup::getValue('financial_item_status', 'Paid', 'name'), + 'status_id' => CRM_Core_Pseudoconstant::getKey('CRM_Financial_BAO_FinancialItem', 'status_id', 'Paid'), 'entity_table' => 'civicrm_financial_trxn', 'entity_id' => $params['entity_id'], 'currency' => $params['trxnParams']['currency'], - ); + ]; $trxnIDS['id'] = $trxn->id; - $financialItem = CRM_Financial_BAO_FinancialItem::create($fItemParams, NULL, $trxnIDS); + CRM_Financial_BAO_FinancialItem::create($fItemParams, NULL, $trxnIDS); } /** - * get partial payment amount and type of it. + * get partial payment amount. + * + * @deprecated + * + * This function basically calls CRM_Contribute_BAO_Contribution::getContributionBalance + * - just do that. If need be we could have a fn to get the contribution id but + * chances are the calling functions already know it anyway. * * @param int $entityId * @param string $entityName - * @param bool $returnType * @param int $lineItemTotal * - * @return array|int|NULL|string - * [payment type => amount] - * payment type: 'amount_owed' or 'refund_due' + * @return array */ - public static function getPartialPaymentWithType($entityId, $entityName = 'participant', $returnType = TRUE, $lineItemTotal = NULL) { + public static function getPartialPaymentWithType($entityId, $entityName = 'participant', $lineItemTotal = NULL) { $value = NULL; if (empty($entityName)) { return $value; } + // @todo - deprecate passing in entity & type - just figure out contribution id FIRST if ($entityName == 'participant') { $contributionId = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_ParticipantPayment', $entityId, 'contribution_id', 'participant_id'); - $financialTypeId = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_Contribution', $contributionId, 'financial_type_id'); - - if ($contributionId && $financialTypeId) { - $statusId = CRM_Core_OptionGroup::getValue('contribution_status', 'Completed', 'name'); - $refundStatusId = CRM_Core_OptionGroup::getValue('contribution_status', 'Refunded', 'name'); - - $relationTypeId = key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Accounts Receivable Account is' ")); - $toFinancialAccount = CRM_Contribute_PseudoConstant::financialAccountType($financialTypeId, $relationTypeId); - $feeRelationTypeId = key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Expense Account is' ")); - $feeFinancialAccount = CRM_Contribute_PseudoConstant::financialAccountType($financialTypeId, $feeRelationTypeId); - - if (empty($lineItemTotal)) { - $ids = CRM_Event_BAO_Participant::getParticipantIds($contributionId); - if (count($ids) > 1) { - $total = 0; - foreach ($ids as $val) { - $total += CRM_Price_BAO_LineItem::getLineTotal($val, 'civicrm_participant'); - } - $lineItemTotal = $total; - } - else { - $lineItemTotal = CRM_Price_BAO_LineItem::getLineTotal($entityId, 'civicrm_participant'); - } - } - $sqlFtTotalAmt = " -SELECT SUM(ft.total_amount) -FROM civicrm_financial_trxn ft - LEFT JOIN civicrm_entity_financial_trxn eft ON (ft.id = eft.financial_trxn_id AND eft.entity_table = 'civicrm_contribution') - LEFT JOIN civicrm_contribution c ON (eft.entity_id = c.id) - LEFT JOIN civicrm_participant_payment pp ON (pp.contribution_id = c.id) -WHERE pp.participant_id = {$entityId} AND ft.to_financial_account_id != {$toFinancialAccount} AND ft.to_financial_account_id != {$feeFinancialAccount} - AND ft.status_id IN ({$statusId}, {$refundStatusId}) -"; - $ftTotalAmt = CRM_Core_DAO::singleValueQuery($sqlFtTotalAmt); - $value = 0; - if ($ftTotalAmt) { - $value = $paymentVal = $lineItemTotal - $ftTotalAmt; - } - if ($returnType) { - $value = array(); - if ($paymentVal < 0) { - $value['refund_due'] = $paymentVal; - } - elseif ($paymentVal > 0) { - $value['amount_owed'] = $paymentVal; - } - elseif ($lineItemTotal == $ftTotalAmt) { - $value['full_paid'] = $ftTotalAmt; - } - } + } + elseif ($entityName == 'membership') { + $contributionId = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipPayment', $entityId, 'contribution_id', 'membership_id'); + } + else { + $contributionId = $entityId; + } + $financialTypeId = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_Contribution', $contributionId, 'financial_type_id'); + + if ($contributionId && $financialTypeId) { + + $paymentVal = CRM_Contribute_BAO_Contribution::getContributionBalance($contributionId, $lineItemTotal); + $value = []; + if ($paymentVal < 0) { + $value['refund_due'] = $paymentVal; + } + elseif ($paymentVal > 0) { + $value['amount_owed'] = $paymentVal; } } return $value; } /** - * @param int $contributionId + * @param int $contributionID + * @param bool $includeRefund * - * @return array + * @return string */ - public static function getTotalPayments($contributionId) { - $statusId = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'); + public static function getTotalPayments($contributionID, $includeRefund = FALSE) { + $statusIDs = [CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed')]; + + if ($includeRefund) { + $statusIDs[] = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Refunded'); + } $sql = "SELECT SUM(ft.total_amount) FROM civicrm_financial_trxn ft INNER JOIN civicrm_entity_financial_trxn eft ON (eft.financial_trxn_id = ft.id AND eft.entity_table = 'civicrm_contribution') - WHERE eft.entity_id = %1 AND ft.is_payment = 1 AND ft.status_id = %2"; - - $params = array( - 1 => array($contributionId, 'Integer'), - 2 => array($statusId, 'Integer'), - ); + WHERE eft.entity_id = %1 AND ft.is_payment = 1 AND ft.status_id IN (%2) "; - return CRM_Core_DAO::singleValueQuery($sql, $params); - } - - /** - * Function records partial payment, complete's contribution if payment is fully paid - * and returns latest payment ie financial trxn - * - * @param array $contribution - * @param array $params - * - * @return CRM_Core_BAO_FinancialTrxn - */ - public static function getPartialPaymentTrxn($contribution, $params) { - $trxn = CRM_Contribute_BAO_Contribution::recordPartialPayment($contribution, $params); - $paid = CRM_Core_BAO_FinancialTrxn::getTotalPayments($params['contribution_id']); - $total = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_Contribution', $params['contribution_id'], 'total_amount'); - $cmp = bccomp($total, $paid, 5); - if ($cmp == 0 || $cmp == -1) {// If paid amount is greater or equal to total amount - civicrm_api3('Contribution', 'completetransaction', array('id' => $contribution['id'])); - } - return $trxn; + return CRM_Core_DAO::singleValueQuery($sql, [ + 1 => [$contributionID, 'Integer'], + 2 => [implode(',', $statusIDs), 'CommaSeparatedIntegers'], + ]); } /** @@ -572,10 +516,10 @@ public static function getPartialPaymentTrxn($contribution, $params) { * @return array */ public static function getMembershipRevenueAmount($lineItem) { - $revenueAmount = array(); - $membershipDetail = civicrm_api3('Membership', 'getsingle', array( + $revenueAmount = []; + $membershipDetail = civicrm_api3('Membership', 'getsingle', [ 'id' => $lineItem['entity_id'], - )); + ]); if (empty($membershipDetail['end_date'])) { return $revenueAmount; } @@ -606,7 +550,7 @@ public static function getMembershipRevenueAmount($lineItem) { * * @param array $lineItems * - * @param array $contributionDetails + * @param CRM_Contribute_BAO_Contribution $contributionDetails * * @param bool $update * @@ -615,7 +559,7 @@ public static function getMembershipRevenueAmount($lineItem) { */ public static function createDeferredTrxn($lineItems, $contributionDetails, $update = FALSE, $context = NULL) { if (empty($lineItems)) { - return FALSE; + return; } $revenueRecognitionDate = $contributionDetails->revenue_recognition_date; if (!CRM_Utils_System::isNull($revenueRecognitionDate)) { @@ -628,7 +572,7 @@ public static function createDeferredTrxn($lineItems, $contributionDetails, $upd ) { return; } - $trxnParams = array( + $trxnParams = [ 'contribution_id' => $contributionDetails->id, 'fee_amount' => '0.00', 'currency' => $contributionDetails->currency, @@ -636,10 +580,9 @@ public static function createDeferredTrxn($lineItems, $contributionDetails, $upd 'status_id' => $contributionDetails->contribution_status_id, 'payment_instrument_id' => $contributionDetails->payment_instrument_id, 'check_number' => $contributionDetails->check_number, - 'is_payment' => 1, - ); + ]; - $deferredRevenues = array(); + $deferredRevenues = []; foreach ($lineItems as $priceSetID => $lineItem) { if (!$priceSetID) { continue; @@ -654,12 +597,12 @@ public static function createDeferredTrxn($lineItems, $contributionDetails, $upd $deferredRevenues[$key]['financial_type_id'] = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_LineItem', $item['id'], 'financial_type_id'); } if (in_array($item['entity_table'], - array('civicrm_participant', 'civicrm_contribution')) + ['civicrm_participant', 'civicrm_contribution']) ) { - $deferredRevenues[$key]['revenue'][] = array( + $deferredRevenues[$key]['revenue'][] = [ 'amount' => $lineTotal, 'revenue_date' => $revenueRecognitionDate, - ); + ]; } else { // for membership @@ -673,36 +616,125 @@ public static function createDeferredTrxn($lineItems, $contributionDetails, $upd CRM_Utils_Hook::alterDeferredRevenueItems($deferredRevenues, $contributionDetails, $update, $context); foreach ($deferredRevenues as $key => $deferredRevenue) { - $results = civicrm_api3('EntityFinancialAccount', 'get', array( + $results = civicrm_api3('EntityFinancialAccount', 'get', [ 'entity_table' => 'civicrm_financial_type', 'entity_id' => $deferredRevenue['financial_type_id'], - 'account_relationship' => array('IN' => array('Income Account is', 'Deferred Revenue Account is')), - )); + 'account_relationship' => ['IN' => ['Income Account is', 'Deferred Revenue Account is']], + ]); if ($results['count'] != 2) { continue; } foreach ($results['values'] as $result) { if ($result['account_relationship'] == $accountRel) { - $trxnParams['to_financial_account_id'] = $result['financial_account_id']; + $trxnParams['from_financial_account_id'] = $result['financial_account_id']; } else { - $trxnParams['from_financial_account_id'] = $result['financial_account_id']; + $trxnParams['to_financial_account_id'] = $result['financial_account_id']; } } foreach ($deferredRevenue['revenue'] as $revenue) { $trxnParams['total_amount'] = $trxnParams['net_amount'] = $revenue['amount']; $trxnParams['trxn_date'] = CRM_Utils_Date::isoToMysql($revenue['revenue_date']); $financialTxn = CRM_Core_BAO_FinancialTrxn::create($trxnParams); - $entityParams = array( + $entityParams = [ 'entity_id' => $deferredRevenue['financial_item_id'], 'entity_table' => 'civicrm_financial_item', 'amount' => $revenue['amount'], 'financial_trxn_id' => $financialTxn->id, - ); + ]; civicrm_api3('EntityFinancialTrxn', 'create', $entityParams); } } } } + /** + * Update Credit Card Details in civicrm_financial_trxn table. + * + * @param int $contributionID + * @param int $panTruncation + * @param int $cardType + * + */ + public static function updateCreditCardDetails($contributionID, $panTruncation, $cardType) { + $financialTrxn = civicrm_api3('EntityFinancialTrxn', 'get', [ + 'return' => ['financial_trxn_id.payment_processor_id', 'financial_trxn_id'], + 'entity_table' => 'civicrm_contribution', + 'entity_id' => $contributionID, + 'financial_trxn_id.is_payment' => TRUE, + 'options' => ['sort' => 'financial_trxn_id DESC', 'limit' => 1], + ]); + + // In case of Contribution status is Pending From Incomplete Transaction or Failed there is no Financial Entries created for Contribution. + // Above api will return 0 count, in such case we won't update card type and pan truncation field. + if (!$financialTrxn['count']) { + return NULL; + } + + $financialTrxn = $financialTrxn['values'][$financialTrxn['id']]; + $paymentProcessorID = CRM_Utils_Array::value('financial_trxn_id.payment_processor_id', $financialTrxn); + + if ($paymentProcessorID) { + return NULL; + } + + $financialTrxnId = $financialTrxn['financial_trxn_id']; + $trxnparams = ['id' => $financialTrxnId]; + if (isset($cardType)) { + $trxnparams['card_type_id'] = $cardType; + } + if (isset($panTruncation)) { + $trxnparams['pan_truncation'] = $panTruncation; + } + civicrm_api3('FinancialTrxn', 'create', $trxnparams); + } + + /** + * The function is responsible for handling financial entries if payment instrument is changed + * + * @param array $inputParams + * + */ + public static function updateFinancialAccountsOnPaymentInstrumentChange($inputParams) { + $prevContribution = $inputParams['prevContribution']; + $currentContribution = $inputParams['contribution']; + // ensure that there are all the information in updated contribution object identified by $currentContribution + $currentContribution->find(TRUE); + + $deferredFinancialAccount = CRM_Utils_Array::value('deferred_financial_account_id', $inputParams); + if (empty($deferredFinancialAccount)) { + $deferredFinancialAccount = CRM_Contribute_PseudoConstant::getRelationalFinancialAccount($prevContribution->financial_type_id, 'Deferred Revenue Account is'); + } + + $lastFinancialTrxnId = self::getFinancialTrxnId($prevContribution->id, 'DESC', FALSE, NULL, $deferredFinancialAccount); + + // there is no point to proceed as we can't find the last payment made + // @todo we should throw an exception here rather than return false. + if (empty($lastFinancialTrxnId['financialTrxnId'])) { + return FALSE; + } + + // If payment instrument is changed reverse the last payment + // in terms of reversing financial item and trxn + $lastFinancialTrxn = civicrm_api3('FinancialTrxn', 'getsingle', ['id' => $lastFinancialTrxnId['financialTrxnId']]); + unset($lastFinancialTrxn['id']); + $lastFinancialTrxn['trxn_date'] = $inputParams['trxnParams']['trxn_date']; + $lastFinancialTrxn['total_amount'] = -$inputParams['trxnParams']['total_amount']; + $lastFinancialTrxn['net_amount'] = -$inputParams['trxnParams']['net_amount']; + $lastFinancialTrxn['fee_amount'] = -$inputParams['trxnParams']['fee_amount']; + $lastFinancialTrxn['contribution_id'] = $prevContribution->id; + foreach ([$lastFinancialTrxn, $inputParams['trxnParams']] as $financialTrxnParams) { + $trxn = CRM_Core_BAO_FinancialTrxn::create($financialTrxnParams); + $trxnParams = [ + 'total_amount' => $trxn->total_amount, + 'contribution_id' => $currentContribution->id, + ]; + CRM_Contribute_BAO_Contribution::assignProportionalLineItems($trxnParams, $trxn->id, $prevContribution->total_amount); + } + + self::createDeferredTrxn(CRM_Utils_Array::value('line_item', $inputParams), $currentContribution, TRUE, 'changePaymentInstrument'); + + return TRUE; + } + } diff --git a/CRM/Core/BAO/IM.php b/CRM/Core/BAO/IM.php index 572a8962db51..a64d22341ede 100644 --- a/CRM/Core/BAO/IM.php +++ b/CRM/Core/BAO/IM.php @@ -1,9 +1,9 @@ array($id, 'Integer')); + $params = [1 => [$id, 'Integer']]; - $ims = $values = array(); + $ims = $values = []; $dao = CRM_Core_DAO::executeQuery($query, $params); $count = 1; while ($dao->fetch()) { - $values = array( + $values = [ 'locationType' => $dao->locationType, 'is_primary' => $dao->is_primary, 'id' => $dao->im_id, 'name' => $dao->im, 'locationTypeId' => $dao->locationTypeId, 'providerId' => $dao->providerId, - ); + ]; if ($updateBlankLocInfo) { $ims[$count++] = $values; @@ -147,18 +147,18 @@ public static function allEntityIMs(&$entityElements) { AND ltype.id = cim.location_type_id ORDER BY cim.is_primary DESC, im_id ASC "; - $params = array(1 => array($entityId, 'Integer')); + $params = [1 => [$entityId, 'Integer']]; - $ims = array(); + $ims = []; $dao = CRM_Core_DAO::executeQuery($sql, $params); while ($dao->fetch()) { - $ims[$dao->im_id] = array( + $ims[$dao->im_id] = [ 'locationType' => $dao->locationType, 'is_primary' => $dao->is_primary, 'id' => $dao->im_id, 'name' => $dao->im, 'locationTypeId' => $dao->locationTypeId, - ); + ]; } return $ims; } diff --git a/CRM/Core/BAO/Job.php b/CRM/Core/BAO/Job.php index e0e663f5d46a..0509a0af47b6 100644 --- a/CRM/Core/BAO/Job.php +++ b/CRM/Core/BAO/Job.php @@ -1,9 +1,9 @@ [ + 'name' => ' - ' . ts('Copy'), + ], + 'replace' => $params, + ]; + $copy = &CRM_Core_DAO::copyGeneric('CRM_Core_DAO_Job', ['id' => $id], NULL, $fieldsFix); + $copy->save(); + CRM_Utils_Hook::copy('Job', $copy); + + return $copy; + } + } diff --git a/CRM/Core/BAO/LabelFormat.php b/CRM/Core/BAO/LabelFormat.php index 38e8ba1dcf12..3ff7966611b0 100644 --- a/CRM/Core/BAO/LabelFormat.php +++ b/CRM/Core/BAO/LabelFormat.php @@ -1,7 +1,7 @@ array( + private static $optionValueFields = [ + 'paper-size' => [ // Paper size: names defined in option_value table (option_group = 'paper_size') 'name' => 'paper-size', 'type' => CRM_Utils_Type::T_STRING, 'default' => 'letter', - ), - 'orientation' => array( + ], + 'orientation' => [ // Paper orientation: 'portrait' or 'landscape' 'name' => 'orientation', 'type' => CRM_Utils_Type::T_STRING, 'default' => 'portrait', - ), - 'font-name' => array( + ], + 'font-name' => [ // Font name: 'courier', 'helvetica', 'times' 'name' => 'font-name', 'type' => CRM_Utils_Type::T_STRING, 'default' => 'helvetica', - ), - 'font-size' => array( + ], + 'font-size' => [ // Font size: always in points 'name' => 'font-size', 'type' => CRM_Utils_Type::T_INT, 'default' => 8, - ), - 'font-style' => array( + ], + 'font-style' => [ // Font style: 'B' bold, 'I' italic, 'BI' bold+italic 'name' => 'font-style', 'type' => CRM_Utils_Type::T_STRING, 'default' => '', - ), - 'NX' => array( + ], + 'NX' => [ // Number of labels horizontally 'name' => 'NX', 'type' => CRM_Utils_Type::T_INT, 'default' => 3, - ), - 'NY' => array( + ], + 'NY' => [ // Number of labels vertically 'name' => 'NY', 'type' => CRM_Utils_Type::T_INT, 'default' => 10, - ), - 'metric' => array( + ], + 'metric' => [ // Unit of measurement for all of the following fields 'name' => 'metric', 'type' => CRM_Utils_Type::T_STRING, 'default' => 'mm', - ), - 'lMargin' => array( + ], + 'lMargin' => [ // Left margin 'name' => 'lMargin', 'type' => CRM_Utils_Type::T_FLOAT, 'metric' => TRUE, 'default' => 4.7625, - ), - 'tMargin' => array( + ], + 'tMargin' => [ // Right margin 'name' => 'tMargin', 'type' => CRM_Utils_Type::T_FLOAT, 'metric' => TRUE, 'default' => 12.7, - ), - 'SpaceX' => array( + ], + 'SpaceX' => [ // Horizontal space between two labels 'name' => 'SpaceX', 'type' => CRM_Utils_Type::T_FLOAT, 'metric' => TRUE, 'default' => 3.96875, - ), - 'SpaceY' => array( + ], + 'SpaceY' => [ // Vertical space between two labels 'name' => 'SpaceY', 'type' => CRM_Utils_Type::T_FLOAT, 'metric' => TRUE, 'default' => 0, - ), - 'width' => array( + ], + 'width' => [ // Width of label 'name' => 'width', 'type' => CRM_Utils_Type::T_FLOAT, 'metric' => TRUE, 'default' => 65.875, - ), - 'height' => array( + ], + 'height' => [ // Height of label 'name' => 'height', 'type' => CRM_Utils_Type::T_FLOAT, 'metric' => TRUE, 'default' => 25.4, - ), - 'lPadding' => array( + ], + 'lPadding' => [ // Space between text and left edge of label 'name' => 'lPadding', 'type' => CRM_Utils_Type::T_FLOAT, 'metric' => TRUE, 'default' => 5.08, - ), - 'tPadding' => array( + ], + 'tPadding' => [ // Space between text and top edge of label 'name' => 'tPadding', 'type' => CRM_Utils_Type::T_FLOAT, 'metric' => TRUE, 'default' => 5.08, - ), - ); + ], + ]; /** * Get page orientations recognized by the DOMPDF package used to create PDF letters. @@ -159,10 +161,10 @@ class CRM_Core_BAO_LabelFormat extends CRM_Core_DAO_OptionValue { * array of page orientations */ public static function getPageOrientations() { - return array( + return [ 'portrait' => ts('Portrait'), 'landscape' => ts('Landscape'), - ); + ]; } /** @@ -186,9 +188,9 @@ public static function getFontNames($name = 'label_format') { * array of font sizes */ public static function getFontSizes() { - $fontSizes = array(); + $fontSizes = []; for ($i = 6; $i <= 60; $i++) { - $fontSizes[$i] = ts('%1 pt', array(1 => $i)); + $fontSizes[$i] = ts('%1 pt', [1 => $i]); } return $fontSizes; @@ -201,12 +203,12 @@ public static function getFontSizes() { * array of measurement units */ public static function getUnits() { - return array( + return [ 'in' => ts('Inches'), 'cm' => ts('Centimeters'), 'mm' => ts('Millimeters'), 'pt' => ts('Points'), - ); + ]; } /** @@ -216,11 +218,11 @@ public static function getUnits() { * array of alignments */ public static function getTextAlignments() { - return array( + return [ 'R' => ts('Right'), 'L' => ts('Left'), 'C' => ts('Center'), - ); + ]; } /** @@ -230,11 +232,11 @@ public static function getTextAlignments() { * array of alignments */ public static function getFontStyles() { - return array( + return [ '' => ts('Normal'), 'B' => ts('Bold'), 'I' => ts('Italic'), - ); + ]; } /** @@ -258,7 +260,7 @@ private static function _getGid($name = 'label_format') { /** * Add ordering fields to Label Format list. * - * @param array (reference) $list List of Label Formats + * @param array $list List of Label Formats * @param string $returnURL * URL of page calling this function. * @@ -283,7 +285,7 @@ public static function addOrder(&$list, $returnURL) { * (reference) label format list */ public static function &getList($namesOnly = FALSE, $groupName = 'label_format') { - static $list = array(); + static $list = []; if (self::_getGid($groupName)) { // get saved label formats from Option Value table $dao = new CRM_Core_DAO_OptionValue(); @@ -313,13 +315,13 @@ public static function &getList($namesOnly = FALSE, $groupName = 'label_format') * Name/value pairs containing the default Label Format values. */ public static function &getDefaultValues($groupName = 'label_format') { - $params = array('is_active' => 1, 'is_default' => 1); - $defaults = array(); + $params = ['is_active' => 1, 'is_default' => 1]; + $defaults = []; if (!self::retrieve($params, $defaults, $groupName)) { foreach (self::$optionValueFields as $name => $field) { $defaults[$name] = $field['default']; } - $filter = array('option_group_id' => self::_getGid($groupName)); + $filter = ['option_group_id' => self::_getGid($groupName)]; $defaults['weight'] = CRM_Utils_Weight::getDefaultWeight('CRM_Core_DAO_OptionValue', $filter); } return $defaults; @@ -339,8 +341,8 @@ public static function &getDefaultValues($groupName = 'label_format') { * (reference) associative array of name/value pairs */ public static function &getLabelFormat($field, $val, $groupName = 'label_format') { - $params = array('is_active' => 1, $field => $val); - $labelFormat = array(); + $params = ['is_active' => 1, $field => $val]; + $labelFormat = []; if (self::retrieve($params, $labelFormat, $groupName)) { return $labelFormat; } @@ -382,7 +384,7 @@ public static function &getById($id, $groupName = 'label_format') { * * @param string $field * Name of a label format field. - * @param array (reference) $values associative array of name/value pairs containing + * @param array $values associative array of name/value pairs containing * label format field selections * * @param null $default @@ -458,7 +460,7 @@ public static function customGroupName() { /** * Save the Label Format in the DB. * - * @param array (reference) $values associative array of name/value pairs + * @param array $values associative array of name/value pairs * @param int $id * Id of the database record (null = new record). * @param string $groupName @@ -519,7 +521,7 @@ public function saveLabelFormat(&$values, $id = NULL, $groupName = 'label_format $this->save(); // fix duplicate weights - $filter = array('option_group_id' => self::_getGid()); + $filter = ['option_group_id' => self::_getGid()]; CRM_Utils_Weight::correctDuplicateWeights('CRM_Core_DAO_OptionValue', $filter); } @@ -537,7 +539,7 @@ public static function del($id, $groupName) { $dao->id = $id; if ($dao->find(TRUE)) { if ($dao->option_group_id == self::_getGid($groupName)) { - $filter = array('option_group_id' => self::_getGid($groupName)); + $filter = ['option_group_id' => self::_getGid($groupName)]; CRM_Utils_Weight::delWeight('CRM_Core_DAO_OptionValue', $id, $filter); $dao->delete(); return; diff --git a/CRM/Core/BAO/Location.php b/CRM/Core/BAO/Location.php index c44431dd4c36..72899e00757a 100644 --- a/CRM/Core/BAO/Location.php +++ b/CRM/Core/BAO/Location.php @@ -1,9 +1,9 @@ $params['entity_table'], 'entity_id' => $params['entity_id'], - ); + ]; $location['id'] = self::createLocBlock($location, $entityElements); } @@ -102,18 +103,18 @@ public static function create(&$params, $fixAddress = TRUE, $entity = NULL) { */ public static function createLocBlock(&$location, &$entityElements) { $locId = self::findExisting($entityElements); - $locBlock = array(); + $locBlock = []; if ($locId) { $locBlock['id'] = $locId; } - foreach (array( - 'phone', - 'email', - 'im', - 'address', - ) as $loc) { + foreach ([ + 'phone', + 'email', + 'im', + 'address', + ] as $loc) { $locBlock["{$loc}_id"] = !empty($location["$loc"][0]) ? $location["$loc"][0]->id : NULL; $locBlock["{$loc}_2_id"] = !empty($location["$loc"][1]) ? $location["$loc"][1]->id : NULL; } @@ -150,7 +151,7 @@ public static function findExisting($entityElements) { FROM {$etable} e WHERE e.id = %1"; - $params = array(1 => array($eid, 'Integer')); + $params = [1 => [$eid, 'Integer']]; $dao = CRM_Core_DAO::executeQuery($query, $params); while ($dao->fetch()) { $locBlockId = $dao->locId; @@ -192,7 +193,7 @@ public static function deleteLocBlock($locBlockId) { $locBlock->find(TRUE); //resolve conflict of having same ids for multiple blocks - $store = array( + $store = [ 'IM_1' => $locBlock->im_id, 'IM_2' => $locBlock->im_2_id, 'Email_1' => $locBlock->email_id, @@ -201,7 +202,7 @@ public static function deleteLocBlock($locBlockId) { 'Phone_2' => $locBlock->phone_2_id, 'Address_1' => $locBlock->address_id, 'Address_2' => $locBlock->address_2_id, - ); + ]; $locBlock->delete(); foreach ($store as $daoName => $id) { if ($id) { @@ -249,12 +250,12 @@ public static function &getValues($entityBlock, $microformat = FALSE) { if (empty($entityBlock)) { return NULL; } - $blocks = array(); - $name_map = array( + $blocks = []; + $name_map = [ 'im' => 'IM', 'openid' => 'OpenID', - ); - $blocks = array(); + ]; + $blocks = []; //get all the blocks for this contact foreach (self::$blocks as $block) { if (array_key_exists($block, $name_map)) { @@ -293,9 +294,9 @@ public static function deleteLocationBlocks($contactId, $locationTypeId) { $locationTypeId = 'null'; } - static $blocks = array('Address', 'Phone', 'IM', 'OpenID', 'Email'); + static $blocks = ['Address', 'Phone', 'IM', 'OpenID', 'Email']; - $params = array('contact_id' => $contactId, 'location_type_id' => $locationTypeId); + $params = ['contact_id' => $contactId, 'location_type_id' => $locationTypeId]; foreach ($blocks as $name) { CRM_Core_BAO_Block::blockDelete($name, $params); } @@ -314,13 +315,13 @@ public static function deleteLocationBlocks($contactId, $locationTypeId) { */ public static function copyLocBlock($locBlockId, $updateLocBlockId = NULL) { //get the location info. - $defaults = $updateValues = array(); - $locBlock = array('id' => $locBlockId); + $defaults = $updateValues = []; + $locBlock = ['id' => $locBlockId]; CRM_Core_DAO::commonRetrieve('CRM_Core_DAO_LocBlock', $locBlock, $defaults); if ($updateLocBlockId) { //get the location info for update. - $copyLocationParams = array('id' => $updateLocBlockId); + $copyLocationParams = ['id' => $updateLocBlockId]; CRM_Core_DAO::commonRetrieve('CRM_Core_DAO_LocBlock', $copyLocationParams, $updateValues); foreach ($updateValues as $key => $value) { if ($key != 'id') { @@ -336,16 +337,16 @@ public static function copyLocBlock($locBlockId, $updateLocBlockId = NULL) { $name = ucfirst($tbl[0]); $updateParams = NULL; if ($updateId = CRM_Utils_Array::value($key, $updateValues)) { - $updateParams = array('id' => $updateId); + $updateParams = ['id' => $updateId]; } - $copy = CRM_Core_DAO::copyGeneric('CRM_Core_DAO_' . $name, array('id' => $value), $updateParams); + $copy = CRM_Core_DAO::copyGeneric('CRM_Core_DAO_' . $name, ['id' => $value], $updateParams); $copyLocationParams[$key] = $copy->id; } } $copyLocation = &CRM_Core_DAO::copyGeneric('CRM_Core_DAO_LocBlock', - array('id' => $locBlock['id']), + ['id' => $locBlock['id']], $copyLocationParams ); return $copyLocation->id; @@ -363,16 +364,16 @@ public static function checkPrimaryBlocks($contactId) { } // get the loc block ids. - $primaryLocBlockIds = CRM_Contact_BAO_Contact::getLocBlockIds($contactId, array('is_primary' => 1)); - $nonPrimaryBlockIds = CRM_Contact_BAO_Contact::getLocBlockIds($contactId, array('is_primary' => 0)); - - foreach (array( - 'Email', - 'IM', - 'Phone', - 'Address', - 'OpenID', - ) as $block) { + $primaryLocBlockIds = CRM_Contact_BAO_Contact::getLocBlockIds($contactId, ['is_primary' => 1]); + $nonPrimaryBlockIds = CRM_Contact_BAO_Contact::getLocBlockIds($contactId, ['is_primary' => 0]); + + foreach ([ + 'Email', + 'IM', + 'Phone', + 'Address', + 'OpenID', + ] as $block) { $name = strtolower($block); if (array_key_exists($name, $primaryLocBlockIds) && !CRM_Utils_System::isNull($primaryLocBlockIds[$name]) @@ -407,10 +408,10 @@ public static function checkPrimaryBlocks($contactId) { */ public static function getChainSelectValues($values, $valueType, $flatten = FALSE) { if (!$values) { - return array(); + return []; } $values = array_filter((array) $values); - $elements = array(); + $elements = []; $list = &$elements; $method = $valueType == 'country' ? 'stateProvinceForCountry' : 'countyForState'; foreach ($values as $val) { @@ -429,17 +430,17 @@ public static function getChainSelectValues($values, $valueType, $flatten = FALS else { // Option-groups for multiple categories if ($result && count($values) > 1) { - $elements[] = array( + $elements[] = [ 'value' => CRM_Core_PseudoConstant::$valueType($val, FALSE), - 'children' => array(), - ); + 'children' => [], + ]; $list = &$elements[count($elements) - 1]['children']; } foreach ($result as $id => $name) { - $list[] = array( + $list[] = [ 'value' => $name, 'key' => $id, - ); + ]; } } } diff --git a/CRM/Core/BAO/LocationType.php b/CRM/Core/BAO/LocationType.php index 4c52549a81fa..7f4cdab2df50 100644 --- a/CRM/Core/BAO/LocationType.php +++ b/CRM/Core/BAO/LocationType.php @@ -1,9 +1,9 @@ 1); - $defaults = array(); + $params = ['is_default' => 1]; + $defaults = []; self::$_defaultLocationType = self::retrieve($params, $defaults); } return self::$_defaultLocationType; @@ -107,7 +107,7 @@ public static function &getDefault() { */ public static function getBilling() { if (self::$_billingLocationType == NULL) { - $locationTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id', array(), 'validate'); + $locationTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id', [], 'validate'); self::$_billingLocationType = array_search('Billing', $locationTypes); } return self::$_billingLocationType; @@ -148,7 +148,7 @@ public static function create(&$params) { * */ public static function del($locationTypeId) { - $entity = array('address', 'phone', 'email', 'im'); + $entity = ['address', 'phone', 'email', 'im']; //check dependencies foreach ($entity as $key) { if ($key == 'im') { diff --git a/CRM/Core/BAO/Log.php b/CRM/Core/BAO/Log.php index 8e669e900fed..eb75c8370835 100644 --- a/CRM/Core/BAO/Log.php +++ b/CRM/Core/BAO/Log.php @@ -1,9 +1,9 @@ entity_id = $id; $log->orderBy('modified_date desc'); $log->limit(1); - $result = CRM_Core_DAO::$_nullObject; + $displayName = $result = $contactImage = NULL; if ($log->find(TRUE)) { - list($displayName, $contactImage) = CRM_Contact_BAO_Contact::getDisplayAndImage($log->modified_id); - $result = array( + if ($log->modified_id) { + list($displayName, $contactImage) = CRM_Contact_BAO_Contact::getDisplayAndImage($log->modified_id); + } + $result = [ 'id' => $log->modified_id, 'name' => $displayName, 'image' => $contactImage, 'date' => $log->modified_date, - ); + ]; } return $result; } @@ -93,7 +94,7 @@ public static function register( $userID = NULL ) { if (!self::$_processed) { - self::$_processed = array(); + self::$_processed = []; } if (!$userID) { @@ -127,7 +128,7 @@ public static function register( self::$_processed[$contactID][$userID] = 1; } else { - self::$_processed[$contactID] = array($userID => 1); + self::$_processed[$contactID] = [$userID => 1]; } $logData = "$tableName,$tableID"; @@ -166,24 +167,24 @@ public static function getContactLogCount($contactID) { } /** - * Function for find out whether to use logging schema entries for contact. - * summary, instead of normal log entries. + * Get the id of the report to use to display the change log. * - * @return int - * report id of Contact Logging Report (Summary) / false + * If logging is not enabled a return value of FALSE means to use the + * basic change log view. + * + * @return int|FALSE + * report id of Contact Logging Report (Summary) */ public static function useLoggingReport() { - // first check if logging is enabled - $config = CRM_Core_Config::singleton(); - if (!$config->logging) { + if (!\Civi::settings()->get('logging')) { return FALSE; } $loggingSchema = new CRM_Logging_Schema(); if ($loggingSchema->isEnabled()) { - $params = array('report_id' => 'logging/contact/summary'); - $instance = array(); + $params = ['report_id' => 'logging/contact/summary']; + $instance = []; CRM_Report_BAO_ReportInstance::retrieve($params, $instance); if (!empty($instance) && diff --git a/CRM/Core/BAO/MailSettings.php b/CRM/Core/BAO/MailSettings.php index 4fc3cb2d907a..05168c3e3ac0 100644 --- a/CRM/Core/BAO/MailSettings.php +++ b/CRM/Core/BAO/MailSettings.php @@ -1,9 +1,9 @@ array(CRM_Core_Config::domainID(), 'Integer')); + $queryParams = [1 => [CRM_Core_Config::domainID(), 'Integer']]; CRM_Core_DAO::executeQuery($query, $queryParams); } diff --git a/CRM/Core/BAO/Mapping.php b/CRM/Core/BAO/Mapping.php index 5914b1c2e72f..3a9e29f5e739 100644 --- a/CRM/Core/BAO/Mapping.php +++ b/CRM/Core/BAO/Mapping.php @@ -1,9 +1,9 @@ mapping_type_id = $mappingTypeId; - $mappingDAO->find(); - - while ($mappingDAO->fetch()) { - $mapping[$mappingDAO->id] = $mappingDAO->name; + public static function getMappings($mappingType) { + $result = civicrm_api3('Mapping', 'get', [ + 'mapping_type_id' => $mappingType, + 'options' => [ + 'sort' => 'name', + 'limit' => 0, + ], + ]); + $mapping = []; + + foreach ($result['values'] as $key => $value) { + $mapping[$key] = $value['name']; } - return $mapping; } + /** + * Get the mappings array, creating if it does not exist. + * + * @param string $mappingType + * Mapping type name. + * + * @return array + * Array of mapping names, keyed by id. + * + * @throws \CiviCRM_API3_Exception + */ + public static function getCreateMappingValues($mappingType) { + try { + return CRM_Core_BAO_Mapping::getMappings($mappingType); + } + catch (CiviCRM_API3_Exception $e) { + // Having a valid mapping_type_id is now enforced. However, rather than error let's + // add it. This is required for Multi value which could be done by upgrade script, but + // it feels like there could be other instances so this is safer. + $errorParams = $e->getExtraParams(); + if ($errorParams['error_field'] === 'mapping_type_id') { + $mappingValues = civicrm_api3('Mapping', 'getoptions', ['field' => 'mapping_type_id']); + civicrm_api3('OptionValue', 'create', [ + 'option_group_id' => 'mapping_type', + 'label' => $mappingType, + 'value' => max(array_keys($mappingValues['values'])) + 1, + 'is_reserved' => 1, + ]); + return CRM_Core_BAO_Mapping::getMappings($mappingType); + } + throw $e; + } + } + /** * Get the mapping fields. * * @param int $mappingId * Mapping id. * + * @param bool $addPrimary + * Add the key 'Primary' when the field is a location field AND there is + * no location type (meaning Primary)? + * * @return array * array of mapping fields */ - public static function getMappingFields($mappingId) { + public static function getMappingFields($mappingId, $addPrimary = FALSE) { //mapping is to be loaded from database $mapping = new CRM_Core_DAO_MappingField(); $mapping->mapping_id = $mappingId; $mapping->orderBy('column_number'); $mapping->find(); - $mappingName = $mappingLocation = $mappingContactType = $mappingPhoneType = array(); - $mappingImProvider = $mappingRelation = $mappingOperator = $mappingValue = $mappingWebsiteType = array(); + $mappingName = $mappingLocation = $mappingContactType = $mappingPhoneType = []; + $mappingImProvider = $mappingRelation = $mappingOperator = $mappingValue = $mappingWebsiteType = []; while ($mapping->fetch()) { $mappingName[$mapping->grouping][$mapping->column_number] = $mapping->name; $mappingContactType[$mapping->grouping][$mapping->column_number] = $mapping->contact_type; @@ -152,6 +193,14 @@ public static function getMappingFields($mappingId) { if (!empty($mapping->location_type_id)) { $mappingLocation[$mapping->grouping][$mapping->column_number] = $mapping->location_type_id; } + elseif ($addPrimary) { + if (CRM_Contact_BAO_Contact::isFieldHasLocationType($mapping->name)) { + $mappingLocation[$mapping->grouping][$mapping->column_number] = ts('Primary'); + } + else { + $mappingLocation[$mapping->grouping][$mapping->column_number] = NULL; + } + } if (!empty($mapping->phone_type_id)) { $mappingPhoneType[$mapping->grouping][$mapping->column_number] = $mapping->phone_type_id; @@ -179,7 +228,7 @@ public static function getMappingFields($mappingId) { } } - return array( + return [ $mappingName, $mappingContactType, $mappingLocation, @@ -189,7 +238,7 @@ public static function getMappingFields($mappingId) { $mappingOperator, $mappingValue, $mappingWebsiteType, - ); + ]; } /** @@ -219,7 +268,7 @@ public static function checkMapping($nameField, $mapTypeId) { * associated array of elements */ public static function getFormattedFields($smartGroupId) { - $returnFields = array(); + $returnFields = []; //get the fields from mapping table $dao = new CRM_Core_DAO_MappingField(); @@ -254,14 +303,14 @@ public static function getFormattedFields($smartGroupId) { */ public static function buildMappingForm(&$form, $mappingType, $mappingId, $columnNo, $blockCount, $exportMode = NULL) { - $hasLocationTypes = array(); - $hasRelationTypes = array(); - $fields = array(); + $hasLocationTypes = []; + $hasRelationTypes = []; + $fields = []; //get the saved mapping details if ($mappingType == 'Export') { - $columnCount = array('1' => $columnNo); + $columnCount = ['1' => $columnNo]; $form->applyFilter('saveMappingName', 'trim'); //to save the current mappings @@ -273,8 +322,8 @@ public static function buildMappingForm(&$form, $mappingType, $mappingId, $colum else { $form->assign('loadedMapping', $mappingId); - $params = array('id' => $mappingId); - $temp = array(); + $params = ['id' => $mappingId]; + $temp = []; $mappingDetails = CRM_Core_BAO_Mapping::retrieve($params, $temp); $form->assign('savedName', $mappingDetails->name); @@ -287,17 +336,17 @@ public static function buildMappingForm(&$form, $mappingType, $mappingId, $colum $form->add('text', 'saveMappingDesc', ts('Description')); } - $form->addElement('checkbox', 'saveMapping', $saveDetailsName, NULL, array('onclick' => "showSaveDetails(this)")); - $form->addFormRule(array('CRM_Export_Form_Map', 'formRule'), $form->get('mappingTypeId')); + $form->addElement('checkbox', 'saveMapping', $saveDetailsName, NULL, ['onclick' => "showSaveDetails(this)"]); + $form->addFormRule(['CRM_Export_Form_Map', 'formRule'], $form->get('mappingTypeId')); } elseif ($mappingType == 'Search Builder') { $columnCount = $columnNo; $form->addElement('submit', 'addBlock', ts('Also include contacts where'), - array('class' => 'submit-link') + ['class' => 'submit-link'] ); } - $contactType = array('Individual', 'Household', 'Organization'); + $contactType = ['Individual', 'Household', 'Organization']; foreach ($contactType as $value) { if ($mappingType == 'Search Builder') { // get multiple custom group fields in this context @@ -312,8 +361,8 @@ public static function buildMappingForm(&$form, $mappingType, $mappingId, $colum $fields[$value] = CRM_Core_BAO_Address::validateAddressOptions($contactFields); ksort($fields[$value]); if ($mappingType == 'Export') { - $relationships = array(); - $relationshipTypes = CRM_Contact_BAO_Relationship::getContactRelationshipType(NULL, NULL, NULL, $value); + $relationships = []; + $relationshipTypes = CRM_Contact_BAO_Relationship::getContactRelationshipType(NULL, NULL, NULL, $value, TRUE); asort($relationshipTypes); foreach ($relationshipTypes as $key => $var) { @@ -329,7 +378,7 @@ public static function buildMappingForm(&$form, $mappingType, $mappingId, $colum if (!empty($relationships)) { $fields[$value] = array_merge($fields[$value], - array('related' => array('title' => ts('- related contact info -'))), + ['related' => ['title' => ts('- related contact info -')]], $relationships ); } @@ -342,15 +391,15 @@ public static function buildMappingForm(&$form, $mappingType, $mappingId, $colum } // add component fields - $compArray = array(); + $compArray = []; //we need to unset groups, tags, notes for component export if ($exportMode != CRM_Export_Form_Select::CONTACT_EXPORT) { - foreach (array( - 'groups', - 'tags', - 'notes', - ) as $value) { + foreach ([ + 'groups', + 'tags', + 'notes', + ] as $value) { unset($fields['Individual'][$value]); unset($fields['Household'][$value]); unset($fields['Organization'][$value]); @@ -359,7 +408,7 @@ public static function buildMappingForm(&$form, $mappingType, $mappingId, $colum if ($mappingType == 'Search Builder') { //build the common contact fields array. - $fields['Contact'] = array(); + $fields['Contact'] = []; foreach ($fields['Individual'] as $key => $value) { if (!empty($fields['Household'][$key]) && !empty($fields['Organization'][$key])) { $fields['Contact'][$key] = $value; @@ -371,17 +420,17 @@ public static function buildMappingForm(&$form, $mappingType, $mappingId, $colum if (array_key_exists('note', $fields['Contact'])) { $noteTitle = $fields['Contact']['note']['title']; $fields['Contact']['note']['title'] = $noteTitle . ': ' . ts('Body and Subject'); - $fields['Contact']['note_body'] = array('title' => $noteTitle . ': ' . ts('Body Only'), 'name' => 'note_body'); - $fields['Contact']['note_subject'] = array( + $fields['Contact']['note_body'] = ['title' => $noteTitle . ': ' . ts('Body Only'), 'name' => 'note_body']; + $fields['Contact']['note_subject'] = [ 'title' => $noteTitle . ': ' . ts('Subject Only'), 'name' => 'note_subject', - ); + ]; } } if (($mappingType == 'Search Builder') || ($exportMode == CRM_Export_Form_Select::CONTRIBUTE_EXPORT)) { if (CRM_Core_Permission::access('CiviContribute')) { - $fields['Contribution'] = CRM_Contribute_BAO_Contribution::exportableFields(); + $fields['Contribution'] = CRM_Core_DAO::getExportableFieldsWithPseudoConstants('CRM_Contribute_BAO_Contribution'); unset($fields['Contribution']['contribution_contact_id']); $compArray['Contribution'] = ts('Contribution'); } @@ -391,10 +440,11 @@ public static function buildMappingForm(&$form, $mappingType, $mappingId, $colum if (CRM_Core_Permission::access('CiviEvent')) { $fields['Participant'] = CRM_Event_BAO_Participant::exportableFields(); //get the component payment fields + // @todo - review this - inconsistent with other entities & hacky. if ($exportMode == CRM_Export_Form_Select::EVENT_EXPORT) { - $componentPaymentFields = array(); + $componentPaymentFields = []; foreach (CRM_Export_BAO_Export::componentPaymentFields() as $payField => $payTitle) { - $componentPaymentFields[$payField] = array('title' => $payTitle); + $componentPaymentFields[$payField] = ['title' => $payTitle]; } $fields['Participant'] = array_merge($fields['Participant'], $componentPaymentFields); } @@ -447,12 +497,12 @@ public static function buildMappingForm(&$form, $mappingType, $mappingId, $colum } //Contact Sub Type For export - $contactSubTypes = array(); + $contactSubTypes = []; $subTypes = CRM_Contact_BAO_ContactType::subTypeInfo(); foreach ($subTypes as $subType => $val) { //adding subtype specific relationships CRM-5256 - $csRelationships = array(); + $csRelationships = []; if ($mappingType == 'Export') { $subTypeRelationshipTypes @@ -521,19 +571,19 @@ public static function buildMappingForm(&$form, $mappingType, $mappingId, $colum if ($defaultLocationType) { $defaultLocation = $locationTypes[$defaultLocationType->id]; unset($locationTypes[$defaultLocationType->id]); - $locationTypes = array($defaultLocationType->id => $defaultLocation) + $locationTypes; + $locationTypes = [$defaultLocationType->id => $defaultLocation] + $locationTypes; } - $locationTypes = array(' ' => ts('Primary')) + $locationTypes; + $locationTypes = [' ' => ts('Primary')] + $locationTypes; // since we need a hierarchical list to display contact types & subtypes, // this is what we going to display in first selector $contactTypes = CRM_Contact_BAO_ContactType::getSelectElements(FALSE, FALSE); if ($mappingType == 'Search Builder') { - $contactTypes = array('Contact' => ts('Contacts')) + $contactTypes; + $contactTypes = ['Contact' => ts('Contacts')] + $contactTypes; } - $sel1 = array('' => ts('- select record type -')) + $contactTypes + $compArray; + $sel1 = ['' => ts('- select record type -')] + $contactTypes + $compArray; foreach ($sel1 as $key => $sel) { if ($key) { @@ -542,7 +592,7 @@ public static function buildMappingForm(&$form, $mappingType, $mappingId, $colum if (!in_array($key, $contactType)) { asort($mapperFields[$key]); } - $sel2[$key] = array('' => ts('- select field -')) + $mapperFields[$key]; + $sel2[$key] = ['' => ts('- select field -')] + $mapperFields[$key]; } } @@ -586,10 +636,11 @@ public static function buildMappingForm(&$form, $mappingType, $mappingId, $colum if (isset($hasRelationTypes[$k][$field])) { list($id, $first, $second) = explode('_', $field); // FIX ME: For now let's not expose custom data related to relationship - $relationshipCustomFields = array(); + $relationshipCustomFields = []; //$relationshipCustomFields = self::getRelationTypeCustomGroupData( $id ); //asort($relationshipCustomFields); + $relatedFields = []; $relationshipType = new CRM_Contact_BAO_RelationshipType(); $relationshipType->id = $id; if ($relationshipType->find(TRUE)) { @@ -601,6 +652,14 @@ public static function buildMappingForm(&$form, $mappingType, $mappingId, $colum elseif (isset($relationshipType->$target_type)) { $relatedFields = array_merge((array) $relatedMapperFields[$relationshipType->$target_type], (array) $relationshipCustomFields); } + //CRM-20672 If contact target type not set e.g. "All Contacts" relationship - present user with all field options and let them determine what they expect to work + else { + $types = CRM_Contact_BAO_ContactType::basicTypes(FALSE); + foreach ($types as $contactType => $label) { + $relatedFields = array_merge($relatedFields, (array) $relatedMapperFields[$label]); + } + $relatedFields = array_merge($relatedFields, (array) $relationshipCustomFields); + } } $relationshipType->free(); asort($relatedFields); @@ -640,10 +699,11 @@ public static function buildMappingForm(&$form, $mappingType, $mappingId, $colum } //special fields that have location, hack for primary location - $specialFields = array( + $specialFields = [ 'street_address', 'supplemental_address_1', 'supplemental_address_2', + 'supplemental_address_3', 'city', 'postal_code', 'postal_code_suffix', @@ -654,7 +714,7 @@ public static function buildMappingForm(&$form, $mappingType, $mappingId, $colum 'phone', 'email', 'im', - ); + ]; if (isset($mappingId)) { list($mappingName, $mappingContactType, $mappingLocation, $mappingPhoneType, $mappingImProvider, @@ -681,13 +741,13 @@ public static function buildMappingForm(&$form, $mappingType, $mappingId, $colum $form->set('blockCount', $form->_blockCount); $form->set('columnCount', $form->_columnCount); - $defaults = $noneArray = $nullArray = array(); + $defaults = $noneArray = $nullArray = []; for ($x = 1; $x < $blockCount; $x++) { for ($i = 0; $i < $columnCount[$x]; $i++) { - $sel = &$form->addElement('hierselect', "mapper[$x][$i]", ts('Mapper for Field %1', array(1 => $i)), NULL); + $sel = &$form->addElement('hierselect', "mapper[$x][$i]", ts('Mapper for Field %1', [1 => $i]), NULL); $jsSet = FALSE; if (isset($mappingId)) { @@ -703,7 +763,7 @@ public static function buildMappingForm(&$form, $mappingType, $mappingId, $colum $relPhoneType = isset($mappingPhoneType[$x][$i]) ? $mappingPhoneType[$x][$i] : NULL; - $defaults["mapper[$x][$i]"] = array( + $defaults["mapper[$x][$i]"] = [ $mappingContactType[$x][$i], $mappingRelation[$x][$i], $locationId, @@ -711,24 +771,24 @@ public static function buildMappingForm(&$form, $mappingType, $mappingId, $colum $mappingName[$x][$i], $relLocationId, $relPhoneType, - ); + ]; if (!$locationId) { - $noneArray[] = array($x, $i, 2); + $noneArray[] = [$x, $i, 2]; } if (!$phoneType && !$imProvider) { - $noneArray[] = array($x, $i, 3); + $noneArray[] = [$x, $i, 3]; } if (!$mappingName[$x][$i]) { - $noneArray[] = array($x, $i, 4); + $noneArray[] = [$x, $i, 4]; } if (!$relLocationId) { - $noneArray[] = array($x, $i, 5); + $noneArray[] = [$x, $i, 5]; } if (!$relPhoneType) { - $noneArray[] = array($x, $i, 6); + $noneArray[] = [$x, $i, 6]; } - $noneArray[] = array($x, $i, 2); + $noneArray[] = [$x, $i, 2]; } else { $phoneType = isset($mappingPhoneType[$x][$i]) ? $mappingPhoneType[$x][$i] : NULL; @@ -737,25 +797,25 @@ public static function buildMappingForm(&$form, $mappingType, $mappingId, $colum $locationId = " "; } - $defaults["mapper[$x][$i]"] = array( + $defaults["mapper[$x][$i]"] = [ $mappingContactType[$x][$i], $mappingName[$x][$i], $locationId, $phoneType, - ); + ]; if (!$mappingName[$x][$i]) { - $noneArray[] = array($x, $i, 1); + $noneArray[] = [$x, $i, 1]; } if (!$locationId) { - $noneArray[] = array($x, $i, 2); + $noneArray[] = [$x, $i, 2]; } if (!$phoneType && !$imProvider) { - $noneArray[] = array($x, $i, 3); + $noneArray[] = [$x, $i, 3]; } - $noneArray[] = array($x, $i, 4); - $noneArray[] = array($x, $i, 5); - $noneArray[] = array($x, $i, 6); + $noneArray[] = [$x, $i, 4]; + $noneArray[] = [$x, $i, 5]; + $noneArray[] = [$x, $i, 6]; } $jsSet = TRUE; @@ -783,7 +843,7 @@ public static function buildMappingForm(&$form, $mappingType, $mappingId, $colum if (empty($formValues)) { // Incremented length for third select box(relationship type) for ($k = 1; $k < $j; $k++) { - $noneArray[] = array($x, $i, $k); + $noneArray[] = [$x, $i, $k]; } } else { @@ -793,17 +853,17 @@ public static function buildMappingForm(&$form, $mappingType, $mappingId, $colum if (!isset($formValues['mapper'][$x][$i][$k]) || (!$formValues['mapper'][$x][$i][$k]) ) { - $noneArray[] = array($x, $i, $k); + $noneArray[] = [$x, $i, $k]; } else { - $nullArray[] = array($x, $i, $k); + $nullArray[] = [$x, $i, $k]; } } } } else { for ($k = 1; $k < $j; $k++) { - $noneArray[] = array($x, $i, $k); + $noneArray[] = [$x, $i, $k]; } } } @@ -815,23 +875,23 @@ public static function buildMappingForm(&$form, $mappingType, $mappingId, $colum isset($formValues['mapper'][$x][$i][1]) && array_key_exists($formValues['mapper'][$x][$i][1], $relationshipTypes) ) { - $sel->setOptions(array($sel1, $sel2, $sel5, $sel6, $sel7, $sel3, $sel4)); + $sel->setOptions([$sel1, $sel2, $sel5, $sel6, $sel7, $sel3, $sel4]); } else { - $sel->setOptions(array($sel1, $sel2, $sel3, $sel4, $sel5, $sel6, $sel7)); + $sel->setOptions([$sel1, $sel2, $sel3, $sel4, $sel5, $sel6, $sel7]); } } else { - $sel->setOptions(array($sel1, $sel2, $sel3, $sel4, $sel5, $sel6, $sel7)); + $sel->setOptions([$sel1, $sel2, $sel3, $sel4, $sel5, $sel6, $sel7]); } } else { - $sel->setOptions(array($sel1, $sel2, $sel3, $sel4)); + $sel->setOptions([$sel1, $sel2, $sel3, $sel4]); } if ($mappingType == 'Search Builder') { //CRM -2292, restricted array set - $operatorArray = array('' => ts('-operator-')) + CRM_Core_SelectValues::getSearchBuilderOperators(); + $operatorArray = ['' => ts('-operator-')] + CRM_Core_SelectValues::getSearchBuilderOperators(); $form->add('select', "operator[$x][$i]", '', $operatorArray); $form->add('text', "value[$x][$i]", ''); @@ -845,7 +905,7 @@ public static function buildMappingForm(&$form, $mappingType, $mappingId, $colum $title = ts('Select more fields'); } - $form->addElement('submit', "addMore[$x]", $title, array('class' => 'submit-link')); + $form->addElement('submit', "addMore[$x]", $title, ['class' => 'submit-link']); } //end of block for @@ -853,8 +913,8 @@ public static function buildMappingForm(&$form, $mappingType, $mappingId, $colum $formName = "document." . (($mappingType == 'Export') ? 'Map' : 'Builder'); if (!empty($nullArray)) { $js .= "var nullArray = ["; - $elements = array(); - $seen = array(); + $elements = []; + $seen = []; foreach ($nullArray as $element) { $key = "{$element[0]}, {$element[1]}, {$element[2]}"; if (!isset($seen[$key])) { @@ -874,8 +934,8 @@ public static function buildMappingForm(&$form, $mappingType, $mappingId, $colum } if (!empty($noneArray)) { $js .= "var noneArray = ["; - $elements = array(); - $seen = array(); + $elements = []; + $seen = []; foreach ($noneArray as $element) { $key = "{$element[0]}, {$element[1]}, {$element[2]}"; if (!isset($seen[$key])) { @@ -916,14 +976,13 @@ public static function buildMappingForm(&$form, $mappingType, $mappingId, $colum public function getRelationTypeCustomGroupData($relationshipTypeId) { $customFields = CRM_Core_BAO_CustomField::getFields('Relationship', NULL, NULL, $relationshipTypeId, NULL, NULL); - $groupTitle = array(); + $groupTitle = []; foreach ($customFields as $krelation => $vrelation) { $groupTitle[$vrelation['label']] = $vrelation['groupTitle'] . '...' . $vrelation['label']; } return $groupTitle; } - /** * Function returns all Custom group Names. * @@ -938,9 +997,7 @@ public static function getCustomGroupName($customfieldId) { $customGroupId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomField', $customFieldId, 'custom_group_id'); $customGroupName = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $customGroupId, 'title'); - if (strlen($customGroupName) > 13) { - $customGroupName = substr($customGroupName, 0, 10) . '...'; - } + $customGroupName = CRM_Utils_String::ellipsify($customGroupName, 13); return $customGroupName; } @@ -959,20 +1016,20 @@ public static function getCustomGroupName($customfieldId) { * formatted associated array of elements */ public static function formattedFields(&$params, $row = FALSE) { - $fields = array(); + $fields = []; if (empty($params) || !isset($params['mapper'])) { return $fields; } - $types = array('Individual', 'Organization', 'Household'); + $types = ['Individual', 'Organization', 'Household']; foreach ($params['mapper'] as $key => $value) { $contactType = NULL; foreach ($value as $k => $v) { if (in_array($v[0], $types)) { if ($contactType && $contactType != $v[0]) { CRM_Core_Error::fatal(ts("Cannot have two clauses with different types: %1, %2", - array(1 => $contactType, 2 => $v[0]) + [1 => $contactType, 2 => $v[0]] )); } $contactType = $v[0]; @@ -1002,7 +1059,7 @@ public static function formattedFields(&$params, $row = FALSE) { // CRM-14983: verify if values are comma separated convert to array if (!is_array($value) && strstr($params['operator'][$key][$k], 'IN')) { $value = explode(',', $value); - $value = array($params['operator'][$key][$k] => $value); + $value = [$params['operator'][$key][$k] => $value]; } // CRM-19081 Fix legacy StateProvince Field Values. // These derive from smart groups created using search builder under older @@ -1012,45 +1069,45 @@ public static function formattedFields(&$params, $row = FALSE) { } if ($row) { - $fields[] = array( + $fields[] = [ $fldName, $params['operator'][$key][$k], $value, $key, $k, - ); + ]; } else { - $fields[] = array( + $fields[] = [ $fldName, $params['operator'][$key][$k], $value, $key, 0, - ); + ]; } } } if ($contactType) { - $fields[] = array( + $fields[] = [ 'contact_type', '=', $contactType, $key, 0, - ); + ]; } } //add sortByCharacter values if (isset($params['sortByCharacter'])) { - $fields[] = array( + $fields[] = [ 'sortByCharacter', '=', $params['sortByCharacter'], 0, 0, - ); + ]; } return $fields; } @@ -1061,17 +1118,17 @@ public static function formattedFields(&$params, $row = FALSE) { * @return array */ public static function &returnProperties(&$params) { - $fields = array( + $fields = [ 'contact_type' => 1, 'contact_sub_type' => 1, 'sort_name' => 1, - ); + ]; if (empty($params) || empty($params['mapper'])) { return $fields; } - $locationTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id'); + $locationTypes = CRM_Core_DAO_Address::buildOptions('location_type_id', 'validate'); foreach ($params['mapper'] as $key => $value) { foreach ($value as $k => $v) { if (isset($v[1])) { @@ -1081,13 +1138,13 @@ public static function &returnProperties(&$params) { if (isset($v[2]) && is_numeric($v[2])) { if (!array_key_exists('location', $fields)) { - $fields['location'] = array(); + $fields['location'] = []; } // make sure that we have a location fields and a location type for this $locationName = $locationTypes[$v[2]]; if (!array_key_exists($locationName, $fields['location'])) { - $fields['location'][$locationName] = array(); + $fields['location'][$locationName] = []; $fields['location'][$locationName]['location_type'] = $v[2]; } @@ -1125,7 +1182,7 @@ public static function &returnProperties(&$params) { * @return NULL */ public static function saveMappingFields(&$params, $mappingId) { - //delete mapping fields records for exixting mapping + //delete mapping fields records for existing mapping $mappingFields = new CRM_Core_DAO_MappingField(); $mappingFields->mapping_id = $mappingId; $mappingFields->delete(); diff --git a/CRM/Core/BAO/MessageTemplate.php b/CRM/Core/BAO/MessageTemplate.php index ecc1c5f72e81..6687dd0ee4c1 100644 --- a/CRM/Core/BAO/MessageTemplate.php +++ b/CRM/Core/BAO/MessageTemplate.php @@ -1,9 +1,9 @@ $params['id']]); + if (!empty($details['workflow_id'])) { + if (!CRM_Core_Permission::check('edit system workflow message templates')) { + throw new \Civi\API\Exception\UnauthorizedException(ts('%1', [1 => $systemWorkflowPermissionDeniedMessage])); + } + } + elseif (!CRM_Core_Permission::check('edit user-driven message templates')) { + throw new \Civi\API\Exception\UnauthorizedException(ts('%1', [1 => $userWorkflowPermissionDeniedMessage])); + } + } + else { + if (!empty($params['workflow_id']) && !CRM_Core_Permission::check('edit system workflow message templates')) { + throw new \Civi\API\Exception\UnauthorizedException(ts('%1', [1 => $systemWorkflowPermissionDeniedMessage])); + } + elseif (!CRM_Core_Permission::check('edit user-driven message templates')) { + throw new \Civi\API\Exception\UnauthorizedException(ts('%1', [1 => $userWorkflowPermissionDeniedMessage])); + } + } + } + } $hook = empty($params['id']) ? 'create' : 'edit'; CRM_Utils_Hook::pre($hook, 'MessageTemplate', CRM_Utils_Array::value('id', $params), $params); @@ -130,7 +157,7 @@ public static function del($messageTemplatesID) { SET msg_template_id = NULL WHERE msg_template_id = %1"; - $params = array(1 => array($messageTemplatesID, 'Integer')); + $params = [1 => [$messageTemplatesID, 'Integer']]; CRM_Core_DAO::executeQuery($query, $params); $messageTemplates = new CRM_Core_DAO_MessageTemplate(); @@ -150,7 +177,7 @@ public static function del($messageTemplatesID) { * @return object */ public static function getMessageTemplates($all = TRUE, $isSMS = FALSE) { - $msgTpls = array(); + $msgTpls = []; $messageTemplates = new CRM_Core_DAO_MessageTemplate(); $messageTemplates->is_active = 1; @@ -182,7 +209,7 @@ public static function sendReminder($contactId, $email, $messageTemplateID, $fro $domain = CRM_Core_BAO_Domain::getDomain(); $result = NULL; - $hookTokens = array(); + $hookTokens = []; if ($messageTemplates->find(TRUE)) { $body_text = $messageTemplates->msg_text; @@ -192,7 +219,7 @@ public static function sendReminder($contactId, $email, $messageTemplateID, $fro $body_text = CRM_Utils_String::htmlToText($body_html); } - $params = array(array('contact_id', '=', $contactId, 0, 0)); + $params = [['contact_id', '=', $contactId, 0, 0]]; list($contact, $_) = CRM_Contact_BAO_Query::apiQuery($params); //CRM-4524 @@ -210,13 +237,13 @@ public static function sendReminder($contactId, $email, $messageTemplateID, $fro CRM_Utils_Token::getTokens($body_subject)); // get replacement text for these tokens - $returnProperties = array("preferred_mail_format" => 1); + $returnProperties = ["preferred_mail_format" => 1]; if (isset($tokens['contact'])) { foreach ($tokens['contact'] as $key => $value) { $returnProperties[$value] = 1; } } - list($details) = CRM_Utils_Token::getTokenDetails(array($contactId), + list($details) = CRM_Utils_Token::getTokenDetails([$contactId], $returnProperties, NULL, NULL, FALSE, $tokens, @@ -224,12 +251,12 @@ public static function sendReminder($contactId, $email, $messageTemplateID, $fro $contact = reset($details); // call token hook - $hookTokens = array(); + $hookTokens = []; CRM_Utils_Hook::tokens($hookTokens); $categories = array_keys($hookTokens); // do replacements in text and html body - $type = array('html', 'text'); + $type = ['html', 'text']; foreach ($type as $key => $value) { $bodyType = "body_{$value}"; if ($$bodyType) { @@ -244,10 +271,10 @@ public static function sendReminder($contactId, $email, $messageTemplateID, $fro $text = $body_text; $smarty = CRM_Core_Smarty::singleton(); - foreach (array( - 'text', - 'html', - ) as $elem) { + foreach ([ + 'text', + 'html', + ] as $elem) { $$elem = $smarty->fetch("string:{$$elem}"); } @@ -260,13 +287,13 @@ public static function sendReminder($contactId, $email, $messageTemplateID, $fro $messageSubject = $smarty->fetch("string:{$messageSubject}"); // set up the parameters for CRM_Utils_Mail::send - $mailParams = array( + $mailParams = [ 'groupName' => 'Scheduled Reminder Sender', 'from' => $from, 'toName' => $contact['display_name'], 'toEmail' => $email, 'subject' => $messageSubject, - ); + ]; if (!$html || $contact['preferred_mail_format'] == 'Text' || $contact['preferred_mail_format'] == 'Both' ) { @@ -299,7 +326,7 @@ public static function revert($id) { $diverted->find(1); if ($diverted->N != 1) { - CRM_Core_Error::fatal(ts('Did not find a message template with id of %1.', array(1 => $id))); + CRM_Core_Error::fatal(ts('Did not find a message template with id of %1.', [1 => $id])); } $orig = new CRM_Core_BAO_MessageTemplate(); @@ -308,7 +335,7 @@ public static function revert($id) { $orig->find(1); if ($orig->N != 1) { - CRM_Core_Error::fatal(ts('Message template with id of %1 does not have a default to revert to.', array(1 => $id))); + CRM_Core_Error::fatal(ts('Message template with id of %1 does not have a default to revert to.', [1 => $id])); } $diverted->msg_subject = $orig->msg_subject; @@ -328,7 +355,7 @@ public static function revert($id) { * Array of four parameters: a boolean whether the email was sent, and the subject, text and HTML templates */ public static function sendTemplate($params) { - $defaults = array( + $defaults = [ // option group name of the template 'groupName' => NULL, // option value name of the template @@ -338,7 +365,7 @@ public static function sendTemplate($params) { // contact id if contact tokens are to be replaced 'contactId' => NULL, // additional template params (other than the ones already set in the template singleton) - 'tplParams' => array(), + 'tplParams' => [], // the From: header 'from' => NULL, // the recipient’s name @@ -357,9 +384,14 @@ public static function sendTemplate($params) { 'isTest' => FALSE, // filename of optional PDF version to add as attachment (do not include path) 'PDFFilename' => NULL, - ); + ]; $params = array_merge($defaults, $params); + // Core#644 - handle contact ID passed as "From". + if (isset($params['from'])) { + $params['from'] = CRM_Utils_Mail::formatFromAddress($params['from']); + } + CRM_Utils_Hook::alterMailParams($params, 'messageTemplate'); if ((!$params['groupName'] || @@ -375,7 +407,7 @@ public static function sendTemplate($params) { $query = 'SELECT msg_subject subject, msg_text text, msg_html html, pdf_format_id format FROM civicrm_msg_template mt WHERE mt.id = %1 AND mt.is_default = 1'; - $sqlParams = array(1 => array($params['messageTemplateID'], 'String')); + $sqlParams = [1 => [$params['messageTemplateID'], 'String']]; } else { // fetch the three elements from the db based on option_group and option_value names @@ -384,29 +416,32 @@ public static function sendTemplate($params) { JOIN civicrm_option_value ov ON workflow_id = ov.id JOIN civicrm_option_group og ON ov.option_group_id = og.id WHERE og.name = %1 AND ov.name = %2 AND mt.is_default = 1'; - $sqlParams = array(1 => array($params['groupName'], 'String'), 2 => array($params['valueName'], 'String')); + $sqlParams = [1 => [$params['groupName'], 'String'], 2 => [$params['valueName'], 'String']]; } $dao = CRM_Core_DAO::executeQuery($query, $sqlParams); $dao->fetch(); if (!$dao->N) { if ($params['messageTemplateID']) { - CRM_Core_Error::fatal(ts('No such message template: id=%1.', array(1 => $params['messageTemplateID']))); + CRM_Core_Error::fatal(ts('No such message template: id=%1.', [1 => $params['messageTemplateID']])); } else { - CRM_Core_Error::fatal(ts('No such message template: option group %1, option value %2.', array( - 1 => $params['groupName'], - 2 => $params['valueName'], - ))); + CRM_Core_Error::fatal(ts('No such message template: option group %1, option value %2.', [ + 1 => $params['groupName'], + 2 => $params['valueName'], + ])); } } - $mailContent = array( + $mailContent = [ 'subject' => $dao->subject, 'text' => $dao->text, 'html' => $dao->html, 'format' => $dao->format, - ); + 'groupName' => $params['groupName'], + 'valueName' => $params['valueName'], + 'messageTemplateID' => $params['messageTemplateID'], + ]; $dao->free(); CRM_Utils_Hook::alterMailContent($mailContent); @@ -429,7 +464,7 @@ public static function sendTemplate($params) { // replace tokens in the three elements (in subject as if it was the text body) $domain = CRM_Core_BAO_Domain::getDomain(); - $hookTokens = array(); + $hookTokens = []; $mailing = new CRM_Mailing_BAO_Mailing(); $mailing->subject = $mailContent['subject']; $mailing->body_text = $mailContent['text']; @@ -441,8 +476,8 @@ public static function sendTemplate($params) { $contactID = CRM_Utils_Array::value('contactId', $params); if ($contactID) { - $contactParams = array('contact_id' => $contactID); - $returnProperties = array(); + $contactParams = ['contact_id' => $contactID]; + $returnProperties = []; if (isset($tokens['subject']['contact'])) { foreach ($tokens['subject']['contact'] as $name) { @@ -476,18 +511,18 @@ public static function sendTemplate($params) { $contact = $contact[$contactID]; } - $mailContent['subject'] = CRM_Utils_Token::replaceDomainTokens($mailContent['subject'], $domain, FALSE, $tokens['text'], TRUE); + $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); if ($contactID) { - $mailContent['subject'] = CRM_Utils_Token::replaceContactTokens($mailContent['subject'], $contact, FALSE, $tokens['text'], FALSE, TRUE); + $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); - $contactArray = array($contactID => $contact); + $contactArray = [$contactID => $contact]; CRM_Utils_Hook::tokenValues($contactArray, - array($contactID), + [$contactID], NULL, CRM_Utils_Token::flattenTokens($tokens), // we should consider adding groupName and valueName here @@ -508,11 +543,11 @@ public static function sendTemplate($params) { foreach ($params['tplParams'] as $name => $value) { $smarty->assign($name, $value); } - foreach (array( + foreach ([ 'subject', 'text', 'html', - ) as $elem) { + ] as $elem) { $mailContent[$elem] = $smarty->fetch("string:{$mailContent[$elem]}"); } @@ -525,7 +560,7 @@ public static function sendTemplate($params) { $params['html'] = $mailContent['html']; if ($params['toEmail']) { - $contactParams = array(array('email', 'LIKE', $params['toEmail'], 0, 1)); + $contactParams = [['email', 'LIKE', $params['toEmail'], 0, 1]]; list($contact, $_) = CRM_Contact_BAO_Query::apiQuery($contactParams); $prefs = array_pop($contact); @@ -542,7 +577,7 @@ public static function sendTemplate($params) { if (isset($params['isEmailPdf']) && $params['isEmailPdf'] == 1) { $pdfHtml = CRM_Contribute_BAO_ContributionPage::addInvoicePdfToEmail($params['contributionId'], $params['contactId']); if (empty($params['attachments'])) { - $params['attachments'] = array(); + $params['attachments'] = []; } $params['attachments'][] = CRM_Utils_Mail::appendPDF('Invoice.pdf', $pdfHtml, $mailContent['format']); } @@ -552,7 +587,7 @@ public static function sendTemplate($params) { $params['html'] ) { if (empty($params['attachments'])) { - $params['attachments'] = array(); + $params['attachments'] = []; } $params['attachments'][] = CRM_Utils_Mail::appendPDF($params['PDFFilename'], $params['html'], $mailContent['format']); if (isset($params['tplParams']['email_comment'])) { @@ -568,7 +603,7 @@ public static function sendTemplate($params) { } } - return array($sent, $mailContent['subject'], $mailContent['text'], $mailContent['html']); + return [$sent, $mailContent['subject'], $mailContent['text'], $mailContent['html']]; } } diff --git a/CRM/Core/BAO/Navigation.php b/CRM/Core/BAO/Navigation.php index cafedc897483..90cc56fb1cf3 100644 --- a/CRM/Core/BAO/Navigation.php +++ b/CRM/Core/BAO/Navigation.php @@ -1,9 +1,9 @@ domain_id = CRM_Core_Config::domainID(); - $menu->find(); - - while ($menu->fetch()) { - if ($menu->title) { - $menus[$menu->path] = $menu->title; - } - } - return $menus; - } - /** * Add/update navigation record. * @@ -91,6 +70,7 @@ public static function add(&$params) { if (empty($params['id'])) { $params['is_active'] = CRM_Utils_Array::value('is_active', $params, FALSE); $params['has_separator'] = CRM_Utils_Array::value('has_separator', $params, FALSE); + $params['domain_id'] = CRM_Utils_Array::value('domain_id', $params, CRM_Core_Config::domainID()); } if (!isset($params['id']) || @@ -114,8 +94,6 @@ public static function add(&$params) { $navigation->copyValues($params); - $navigation->domain_id = CRM_Core_Config::domainID(); - $navigation->save(); return $navigation; } @@ -193,10 +171,10 @@ public static function getNavigationList() { $domainID = CRM_Core_Config::domainID(); $query = " SELECT id, label, parent_id, weight, is_active, name -FROM civicrm_navigation WHERE domain_id = $domainID {$whereClause} ORDER BY parent_id, weight ASC"; +FROM civicrm_navigation WHERE domain_id = $domainID"; $result = CRM_Core_DAO::executeQuery($query); - $pidGroups = array(); + $pidGroups = []; while ($result->fetch()) { $pidGroups[$result->parent_id][$result->label] = $result->id; } @@ -205,7 +183,7 @@ public static function getNavigationList() { $pidGroups[''][$label] = self::_getNavigationValue($val, $pidGroups); } - $navigations = array(); + $navigations = []; self::_getNavigationLabel($pidGroups[''], $navigations); CRM_Core_BAO_Cache::setItem($navigations, 'navigation', $cacheKeyString); @@ -229,7 +207,7 @@ public static function _getNavigationLabel($list, &$navigations, $separator = '' if ($label == 'navigation_id') { continue; } - $translatedLabel = $i18n->crm_translate($label, array('context' => 'menu')); + $translatedLabel = $i18n->crm_translate($label, ['context' => 'menu']); $navigations[is_array($val) ? $val['navigation_id'] : $val] = "{$separator}{$translatedLabel}"; if (is_array($val)) { self::_getNavigationLabel($val, $navigations, $separator . '    '); @@ -249,7 +227,7 @@ public static function _getNavigationLabel($list, &$navigations, $separator = '' */ public static function _getNavigationValue($val, &$pidGroups) { if (array_key_exists($val, $pidGroups)) { - $list = array('navigation_id' => $val); + $list = ['navigation_id' => $val]; foreach ($pidGroups[$val] as $label => $id) { $list[$label] = self::_getNavigationValue($id, $pidGroups); } @@ -264,126 +242,87 @@ public static function _getNavigationValue($val, &$pidGroups) { /** * Build navigation tree. * - * @param array $navigationTree - * Nested array of menus. - * @param int $parentID - * Parent id. - * @param bool $navigationMenu - * True when called for building top navigation menu. - * * @return array * nested array of menus */ - public static function buildNavigationTree(&$navigationTree, $parentID, $navigationMenu = TRUE) { - $whereClause = " parent_id IS NULL"; - - if ($parentID) { - $whereClause = " parent_id = {$parentID}"; - } - + public static function buildNavigationTree() { $domainID = CRM_Core_Config::domainID(); - - // get the list of menus - $query = " -SELECT id, label, url, permission, permission_operator, has_separator, parent_id, is_active, name -FROM civicrm_navigation -WHERE {$whereClause} -AND domain_id = $domainID -ORDER BY parent_id, weight"; - - $navigation = CRM_Core_DAO::executeQuery($query); - $config = CRM_Core_Config::singleton(); - while ($navigation->fetch()) { - $label = $navigation->label; - if (!$navigationMenu) { - $label = addcslashes($label, '"'); - } - - // for each menu get their children - $navigationTree[$navigation->id] = array( - 'attributes' => array( - 'label' => $label, - 'name' => $navigation->name, - 'url' => $navigation->url, - 'permission' => $navigation->permission, - 'operator' => $navigation->permission_operator, - 'separator' => $navigation->has_separator, - 'parentID' => $navigation->parent_id, - 'navID' => $navigation->id, - 'active' => $navigation->is_active, - ), - ); - self::buildNavigationTree($navigationTree[$navigation->id]['child'], $navigation->id, $navigationMenu); - } - - return $navigationTree; + $navigationTree = []; + + $navigationMenu = new self(); + $navigationMenu->domain_id = $domainID; + $navigationMenu->orderBy('parent_id, weight'); + $navigationMenu->find(); + + while ($navigationMenu->fetch()) { + $navigationTree[$navigationMenu->id] = [ + 'attributes' => [ + 'label' => $navigationMenu->label, + 'name' => $navigationMenu->name, + 'url' => $navigationMenu->url, + 'icon' => $navigationMenu->icon, + 'weight' => $navigationMenu->weight, + 'permission' => $navigationMenu->permission, + 'operator' => $navigationMenu->permission_operator, + 'separator' => $navigationMenu->has_separator, + 'parentID' => $navigationMenu->parent_id, + 'navID' => $navigationMenu->id, + 'active' => $navigationMenu->is_active, + ], + ]; + } + + return self::buildTree($navigationTree); } /** - * Build menu. + * Convert flat array to nested. * - * @param bool $json - * By default output is html. - * @param bool $navigationMenu - * True when called for building top navigation menu. + * @param array $elements + * @param int|null $parentId * - * @return string - * html or json string + * @return array */ - public static function buildNavigation($json = FALSE, $navigationMenu = TRUE) { - $navigations = array(); - self::buildNavigationTree($navigations, $parent = NULL, $navigationMenu); - $navigationString = NULL; + private static function buildTree($elements, $parentId = NULL) { + $branch = []; + + foreach ($elements as $id => $element) { + if ($element['attributes']['parentID'] == $parentId) { + $children = self::buildTree($elements, $id); + if ($children) { + $element['child'] = $children; + } + $branch[$id] = $element; + } + } - // run the Navigation through a hook so users can modify it - CRM_Utils_Hook::navigationMenu($navigations); - self::fixNavigationMenu($navigations); + return $branch; + } - $i18n = CRM_Core_I18n::singleton(); + /** + * buildNavigationTree retreives items in order. We call this function to + * ensure that any items added by the hook are also in the correct order. + */ + public static function orderByWeight(&$navigations) { + // sort each item in navigations by weight + usort($navigations, function($a, $b) { - //skip children menu item if user don't have access to parent menu item - $skipMenuItems = array(); - foreach ($navigations as $key => $value) { - if ($json) { - if ($navigationString) { - $navigationString .= '},'; - } - $data = $value['attributes']['label']; - $class = ''; - if (!$value['attributes']['active']) { - $class = ', "attr": { "class" : "disabled"} '; - } - $l10nName = $i18n->crm_translate($data, array('context' => 'menu')); - $navigationString .= ' { "attr": { "id" : "node_' . $key . '"}, "data": { "title":"' . $l10nName . '"' . $class . '}'; + // If no weight have been defined for an item put it at the end of the list + if (!isset($a['attributes']['weight'])) { + $a['attributes']['weight'] = 1000; } - else { - // Home is a special case - if ($value['attributes']['name'] != 'Home') { - $name = self::getMenuName($value, $skipMenuItems); - if ($name) { - //separator before - if (isset($value['attributes']['separator']) && $value['attributes']['separator'] == 2) { - $navigationString .= ''; - } - $removeCharacters = array('/', '!', '&', '*', ' ', '(', ')', '.'); - $navigationString .= '
    "; - if ($config->wkhtmltopdfPath) { + if (CRM_Core_Config::singleton()->wkhtmltopdfPath) { return self::_html2pdf_wkhtmltopdf($paper_size, $orientation, $margins, $html, $output, $fileName); } else { return self::_html2pdf_dompdf($paper_size, $orientation, $html, $output, $fileName); - //return self::_html2pdf_tcpdf($paper_size, $orientation, $margins, $html, $output, $fileName, $stationery_path); } } @@ -147,9 +142,10 @@ public static function _html2pdf_tcpdf($paper_size, $orientation, $margins, $htm // This function also uses the FPDI library documented at: http://www.setasign.com/products/fpdi/about/ // Syntax borrowed from https://github.com/jake-mw/CDNTaxReceipts/blob/master/cdntaxreceipts.functions.inc require_once 'tcpdf/tcpdf.php'; - require_once 'FPDI/fpdi.php'; // This library is only in the 'packages' area as of version 4.5 + // This library is only in the 'packages' area as of version 4.5 + require_once 'FPDI/fpdi.php'; - $paper_size_arr = array($paper_size[2], $paper_size[3]); + $paper_size_arr = [$paper_size[2], $paper_size[3]]; $pdf = new TCPDF($orientation, 'pt', $paper_size_arr); $pdf->Open(); @@ -184,7 +180,7 @@ public static function _html2pdf_tcpdf($paper_size, $orientation, $margins, $htm $pdf->Close(); $pdf_file = 'CiviLetter' . '.pdf'; $pdf->Output($pdf_file, 'D'); - CRM_Utils_System::civiExit(1); + CRM_Utils_System::civiExit(); } /** diff --git a/CRM/Utils/Pager.php b/CRM/Utils/Pager.php index 6b3b2e068daf..390764d766ac 100644 --- a/CRM/Utils/Pager.php +++ b/CRM/Utils/Pager.php @@ -1,9 +1,9 @@ $start, 2 => $end, 3 => $params['total'])); + $statusMessage = ts('%1 - %2 of %3', [1 => $start, 2 => $end, 3 => $params['total']]); } $params['status'] = str_replace('%%StatusMessage%%', $statusMessage, $params['status']); - $this->_response = array( + $this->_response = [ 'first' => $this->getFirstPageLink(), 'back' => $this->getBackPageLink(), 'next' => $this->getNextPageLink(), @@ -104,21 +104,21 @@ public function __construct($params) { 'buttonTop' => CRM_Utils_Array::value('buttonTop', $params), 'buttonBottom' => CRM_Utils_Array::value('buttonBottom', $params), 'currentLocation' => $this->getCurrentLocation(), - ); + ]; /** * A page cannot have two variables with the same form name. Hence in the * pager display, we have a form submission at the top with the normal * page variable, but a different form element for one at the bottom. */ - $this->_response['titleTop'] = ts('Page %1 of %2', array( - 1 => '', - 2 => $this->_response['numPages'], - )); - $this->_response['titleBottom'] = ts('Page %1 of %2', array( - 1 => '', - 2 => $this->_response['numPages'], - )); + $this->_response['titleTop'] = ts('Page %1 of %2', [ + 1 => '', + 2 => $this->_response['numPages'], + ]); + $this->_response['titleBottom'] = ts('Page %1 of %2', [ + 1 => '', + 2 => $this->_response['numPages'], + ]); } /** @@ -148,8 +148,8 @@ public function initialize(&$params) { $params['separator'] = ''; $params['spacesBeforeSeparator'] = 1; $params['spacesAfterSeparator'] = 1; - $params['extraVars'] = array('force' => 1); - $params['excludeVars'] = array('reset', 'snippet', 'section'); + $params['extraVars'] = ['force' => 1]; + $params['excludeVars'] = ['reset', 'snippet', 'section']; // set previous and next text labels $params['prevImg'] = ' ' . ts('< Previous'); @@ -248,7 +248,7 @@ public function getOffsetAndRowCount() { $offset = ($pageId - 1) * $this->_perPage; - return array($offset, $this->_perPage); + return [$offset, $this->_perPage]; } /** diff --git a/CRM/Utils/PagerAToZ.php b/CRM/Utils/PagerAToZ.php index b0269cec2a73..32da0ee8cc46 100644 --- a/CRM/Utils/PagerAToZ.php +++ b/CRM/Utils/PagerAToZ.php @@ -1,9 +1,9 @@ fetch()) { - $dynamicAlphabets[] = $result->sort_name; + $dynamicAlphabets[] = strtoupper($result->sort_name); } return $dynamicAlphabets; } @@ -151,16 +151,18 @@ public static function createLinks(&$query, $sortByCharacter, $isDAO) { $qfKey = CRM_Utils_Array::value('qfKey', $query->_formValues); } if (empty($qfKey)) { - $qfKey = CRM_Utils_Request::retrieve('qfKey', 'String', $this, FALSE, NULL, $_REQUEST); + // CRM-20943 Can only pass variables by reference and also cannot use $this so using $empty setting to NULL which is default. + $emptyVariable = NULL; + $qfKey = CRM_Utils_Request::retrieve('qfKey', 'String', $emptyVariable, FALSE, NULL, $_REQUEST); } - $aToZBar = array(); + $aToZBar = []; foreach ($AToZBar as $key => $link) { if ($link === NULL) { continue; } - $element = array(); + $element = []; if (in_array($link, $dynamicAlphabets)) { $klass = ''; if ($link == $sortByCharacter) { @@ -191,7 +193,7 @@ public static function createLinks(&$query, $sortByCharacter, $isDAO) { ), ts('All') ); - $aToZBar[] = array('item' => $url); + $aToZBar[] = ['item' => $url]; return $aToZBar; } diff --git a/CRM/Utils/PseudoConstant.php b/CRM/Utils/PseudoConstant.php index 5681fc884c48..7a3cf0d54768 100644 --- a/CRM/Utils/PseudoConstant.php +++ b/CRM/Utils/PseudoConstant.php @@ -1,9 +1,9 @@ hasDeclaredIndex($tableName, $columns) + ) { + $formattedQuery = $this->format($queryText, CRM_Utils_QueryFormatter::LANG_SQL_FTSBOOL); + + $prefixedFieldNames = []; + foreach ($columns as $fieldName) { + $prefixedFieldNames[] = "$tableAlias.$fieldName"; + } + + $clauses[] = sprintf("MATCH (%s) AGAINST ('%s' IN BOOLEAN MODE)", + implode(',', $prefixedFieldNames), + $strtolower(CRM_Core_DAO::escapeString($formattedQuery)) + ); + } + else { + //CRM_Core_Session::setStatus(ts('Cannot use FTS for %1 (%2)', array( + // 1 => $table, + // 2 => implode(', ', $fullTextFields), + //))); + + $formattedQuery = $this->format($queryText, CRM_Utils_QueryFormatter::LANG_SQL_LIKE); + $escapedText = $strtolower(CRM_Core_DAO::escapeString($formattedQuery)); + foreach ($columns as $fieldName) { + $clauses[] = "$tableAlias.$fieldName LIKE '{$escapedText}'"; + } + } + return implode(' OR ', $clauses); + } + /** * Format Fts. * @@ -211,6 +290,20 @@ protected function _formatFts($text, $mode) { */ protected function _formatFtsBool($text, $mode) { $result = NULL; + $operators = ['+', '-', '~', '(', ')']; + $wildCards = ['@', '%', '*']; + $expression = preg_quote(implode('', array_merge($operators, $wildCards)), '/'); + + //Return if searched string ends with an unsupported operator. + //Or if the string contains an invalid joint occurrence of operators. + foreach ($operators as $val) { + if ($text == '@' || CRM_Utils_String::endsWith($text, $val) || preg_match("/[{$expression}]{2,}/", $text)) { + $csid = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionValue', 'CRM_Contact_Form_Search_Custom_FullText', 'value', 'name'); + $url = CRM_Utils_System::url("civicrm/contact/search/custom", "csid={$csid}&reset=1"); + $operators = implode("', '", $operators); + CRM_Core_Error::statusBounce("Full-Text Search does not support the use of a search with two attached operators or string ending with any of these operators ('{$operators}' or a single '@'). Please adjust your search term and try again.", $url, 'Invalid Search String'); + } + } // normalize user-inputted wildcards $text = str_replace('%', '*', $text); @@ -233,7 +326,7 @@ protected function _formatFtsBool($text, $mode) { else { switch ($mode) { case self::MODE_NONE: - $result = $this->mapWords($text, '+word'); + $result = $this->mapWords($text, '+word', TRUE); break; case self::MODE_PHRASE: @@ -304,22 +397,45 @@ protected function _formatLike($text, $mode) { * User-supplied query string. * @param string $template * A prototypical description of each word, eg "word%" or "word*" or "*word*". + * @param bool $quotes + * True if each searched keyword need to be surrounded with quotes. * @return string */ - protected function mapWords($text, $template) { - $result = array(); - foreach ($this->parseWords($text) as $word) { + protected function mapWords($text, $template, $quotes = FALSE) { + $result = []; + foreach ($this->parseWords($text, $quotes) as $word) { $result[] = str_replace('word', $word, $template); } return implode(' ', $result); } /** - * @param $text + * @param string $text + * @param bool $quotes * @return array */ - protected function parseWords($text) { - return explode(' ', preg_replace('/[ \r\n\t]+/', ' ', trim($text))); + protected function parseWords($text, $quotes) { + //NYSS 9692 special handling for emails + if (preg_match('/^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/', $text)) { + $parts = explode('@', $text); + $parts[1] = stristr($parts[1], '.', TRUE); + $text = implode(' ', $parts); + } + + //NYSS also replace other occurrences of @ + $replacedText = preg_replace('/[ \r\n\t\@]+/', ' ', trim($text)); + //filter empty values if any + $keywords = array_filter(explode(' ', $replacedText)); + + //Ensure each searched keywords are wrapped in double quotes. + if ($quotes) { + foreach ($keywords as &$val) { + if (!is_numeric($val)) { + $val = "\"{$val}\""; + } + } + } + return $keywords; } /** @@ -345,13 +461,13 @@ protected function dedupeWildcards($text, $wildcard) { * @return array */ public static function getModes() { - return array( + return [ self::MODE_NONE, self::MODE_PHRASE, self::MODE_WILDPHRASE, self::MODE_WILDWORDS, self::MODE_WILDWORDS_SUFFIX, - ); + ]; } /** @@ -360,12 +476,12 @@ public static function getModes() { * @return array */ public static function getLanguages() { - return array( + return [ self::LANG_SOLR, self::LANG_SQL_FTS, self::LANG_SQL_FTSBOOL, self::LANG_SQL_LIKE, - ); + ]; } /** diff --git a/CRM/Utils/REST.php b/CRM/Utils/REST.php index c6c05788cde3..b1df33aee17c 100644 --- a/CRM/Utils/REST.php +++ b/CRM/Utils/REST.php @@ -1,9 +1,9 @@ get('key'); // $session->set( 'key', $var ); - return self::simple(array('message' => "PONG: $key")); + return self::simple(['message' => "PONG: $key"]); } /** @@ -78,10 +80,10 @@ public static function ping($var = NULL) { * @return array */ public static function error($message = 'Unknown Error') { - $values = array( + $values = [ 'error_message' => $message, 'is_error' => 1, - ); + ]; return $values; } @@ -92,7 +94,7 @@ public static function error($message = 'Unknown Error') { * @return array */ public static function simple($params) { - $values = array('is_error' => 0); + $values = ['is_error' => 0]; $values += $params; return $values; } @@ -129,7 +131,7 @@ public static function output(&$result) { if (!$result) { $result = 0; } - $result = self::simple(array('result' => $result)); + $result = self::simple(['result' => $result]); } elseif (is_array($result)) { if (CRM_Utils_Array::isHierarchical($result)) { @@ -146,6 +148,7 @@ public static function output(&$result) { if (!empty($requestParams['json'])) { if (!empty($requestParams['prettyprint'])) { // Don't set content-type header for api explorer output + return json_encode(array_merge($result), JSON_PRETTY_PRINT + JSON_UNESCAPED_SLASHES + JSON_UNESCAPED_UNICODE); return self::jsonFormated(array_merge($result)); } CRM_Utils_System::setHttpHeader('Content-Type', 'application/json'); @@ -181,100 +184,6 @@ public static function output(&$result) { return $xml; } - /** - * @param $data - * - * @deprecated - switch to native JSON_PRETTY_PRINT when we drop support for php 5.3 - * - * @return string - */ - public static function jsonFormated($data) { - // If php is 5.4+ we can use the native method - if (defined('JSON_PRETTY_PRINT')) { - return json_encode($data, JSON_PRETTY_PRINT + JSON_UNESCAPED_SLASHES + JSON_UNESCAPED_UNICODE); - } - - // PHP 5.3 shim - $json = str_replace('\/', '/', json_encode($data)); - $tabcount = 0; - $result = ''; - $inquote = FALSE; - $inarray = FALSE; - $ignorenext = FALSE; - - $tab = "\t"; - $newline = "\n"; - - for ($i = 0; $i < strlen($json); $i++) { - $char = $json[$i]; - - if ($ignorenext) { - $result .= $char; - $ignorenext = FALSE; - } - else { - switch ($char) { - case '{': - if ($inquote) { - $result .= $char; - } - else { - $inarray = FALSE; - $tabcount++; - $result .= $char . $newline . str_repeat($tab, $tabcount); - } - break; - - case '}': - if ($inquote) { - $result .= $char; - } - else { - $tabcount--; - $result = trim($result) . $newline . str_repeat($tab, $tabcount) . $char; - } - break; - - case ',': - if ($inquote || $inarray) { - $result .= $char; - } - else { - $result .= $char . $newline . str_repeat($tab, $tabcount); - } - break; - - case '"': - $inquote = !$inquote; - $result .= $char; - break; - - case '\\': - if ($inquote) { - $ignorenext = TRUE; - } - $result .= $char; - break; - - case '[': - $inarray = TRUE; - $result .= $char; - break; - - case ']': - $inarray = FALSE; - $result .= $char; - break; - - default: - $result .= $char; - } - } - } - - return $result; - } - /** * @return array|int */ @@ -309,7 +218,7 @@ public static function handle() { } else { // or the api format (entity+action) - $args = array(); + $args = []; $args[0] = 'civicrm'; $args[1] = CRM_Utils_Array::value('entity', $requestParams); $args[2] = CRM_Utils_Array::value('action', $requestParams); @@ -370,7 +279,7 @@ public static function process(&$args, $params) { return self::error('Unknown function invocation.'); } - return call_user_func(array($params['className'], $params['fnName']), $params); + return call_user_func([$params['className'], $params['fnName']], $params); } if (!array_key_exists('version', $params)) { @@ -385,22 +294,25 @@ public static function process(&$args, $params) { } if ($_SERVER['REQUEST_METHOD'] == 'GET' && - strtolower(substr($args[2], 0, 3)) != 'get' && - strtolower($args[2] != 'check')) { + strtolower(substr($args[2], 0, 3)) != 'get' && + strtolower($args[2] != 'check')) { // get only valid for non destructive methods require_once 'api/v3/utils.php'; return civicrm_api3_create_error("SECURITY: All requests that modify the database must be http POST, not GET.", - array( + [ 'IP' => $_SERVER['REMOTE_ADDR'], 'level' => 'security', 'referer' => $_SERVER['HTTP_REFERER'], 'reason' => 'Destructive HTTP GET', - ) + ] ); } // trap all fatal errors - $errorScope = CRM_Core_TemporaryErrorScope::create(array('CRM_Utils_REST', 'fatal')); + $errorScope = CRM_Core_TemporaryErrorScope::create([ + 'CRM_Utils_REST', + 'fatal', + ]); $result = civicrm_api($args[1], $args[2], $params); unset($errorScope); @@ -415,21 +327,25 @@ public static function process(&$args, $params) { */ public static function &buildParamList() { $requestParams = CRM_Utils_Request::exportValues(); - $params = array(); + $params = []; - $skipVars = array( + $skipVars = [ 'q' => 1, 'json' => 1, 'key' => 1, 'api_key' => 1, 'entity' => 1, 'action' => 1, - ); + ]; if (array_key_exists('json', $requestParams) && $requestParams['json'][0] == "{") { $params = json_decode($requestParams['json'], TRUE); if ($params === NULL) { - CRM_Utils_JSON::output(array('is_error' => 1, 'error_message', 'Unable to decode supplied JSON.')); + CRM_Utils_JSON::output([ + 'is_error' => 1, + 0 => 'error_message', + 1 => 'Unable to decode supplied JSON.', + ]); } } foreach ($requestParams as $n => $v) { @@ -450,7 +366,7 @@ public static function &buildParamList() { */ public static function fatal($pearError) { CRM_Utils_System::setHttpHeader('Content-Type', 'text/xml'); - $error = array(); + $error = []; $error['code'] = $pearError->getCode(); $error['error_message'] = $pearError->getMessage(); $error['mode'] = $pearError->getMode(); @@ -471,7 +387,7 @@ public static function fatal($pearError) { public static function loadTemplate() { $request = CRM_Utils_Request::retrieve('q', 'String'); if (FALSE !== strpos($request, '..')) { - die ("SECURITY FATAL: the url can't contain '..'. Please report the issue on the forum at civicrm.org"); + die("SECURITY FATAL: the url can't contain '..'. Please report the issue on the forum at civicrm.org"); } $request = explode('/', $request); @@ -483,15 +399,17 @@ public static function loadTemplate() { CRM_Utils_System::setTitle("$entity::$tplfile inline $tpl"); if (!$smarty->template_exists($tpl)) { CRM_Utils_System::setHttpHeader("Status", "404 Not Found"); - die ("Can't find the requested template file templates/$tpl"); + die("Can't find the requested template file templates/$tpl"); } - if (array_key_exists('id', $_GET)) {// special treatmenent, because it's often used - $smarty->assign('id', (int) $_GET['id']);// an id is always positive + // special treatmenent, because it's often used + if (array_key_exists('id', $_GET)) { + // an id is always positive + $smarty->assign('id', (int) $_GET['id']); } $pos = strpos(implode(array_keys($_GET)), '<'); if ($pos !== FALSE) { - die ("SECURITY FATAL: one of the param names contains <"); + die("SECURITY FATAL: one of the param names contains <"); } $param = array_map('htmlentities', $_GET); unset($param['q']); @@ -537,12 +455,12 @@ public static function ajaxJson() { ) ) { $error = civicrm_api3_create_error("SECURITY ALERT: Ajax requests can only be issued by javascript clients, eg. CRM.api3().", - array( + [ 'IP' => $_SERVER['REMOTE_ADDR'], 'level' => 'security', 'referer' => $_SERVER['HTTP_REFERER'], 'reason' => 'CSRF suspected', - ) + ] ); CRM_Utils_JSON::output($error); } @@ -558,21 +476,25 @@ public static function ajaxJson() { $entity = CRM_Utils_String::munge(CRM_Utils_Array::value('entity', $requestParams)); $action = CRM_Utils_String::munge(CRM_Utils_Array::value('action', $requestParams)); if (!is_array($params)) { - CRM_Utils_JSON::output(array( - 'is_error' => 1, - 'error_message' => 'invalid json format: ?{"param_with_double_quote":"value"}', - )); + CRM_Utils_JSON::output([ + 'is_error' => 1, + 'error_message' => 'invalid json format: ?{"param_with_double_quote":"value"}', + ]); } $params['check_permissions'] = TRUE; $params['version'] = 3; - $_GET['json'] = $requestParams['json'] = 1; // $requestParams is local-only; this line seems pointless unless there's a side-effect influencing other functions + // $requestParams is local-only; this line seems pointless unless there's a side-effect influencing other functions + $_GET['json'] = $requestParams['json'] = 1; if (!$params['sequential']) { $params['sequential'] = 1; } // trap all fatal errors - $errorScope = CRM_Core_TemporaryErrorScope::create(array('CRM_Utils_REST', 'fatal')); + $errorScope = CRM_Core_TemporaryErrorScope::create([ + 'CRM_Utils_REST', + 'fatal', + ]); $result = civicrm_api($entity, $action, $params); unset($errorScope); @@ -600,12 +522,12 @@ public static function ajax() { ) { require_once 'api/v3/utils.php'; $error = civicrm_api3_create_error("SECURITY ALERT: Ajax requests can only be issued by javascript clients, eg. CRM.api3().", - array( + [ 'IP' => $_SERVER['REMOTE_ADDR'], 'level' => 'security', 'referer' => $_SERVER['HTTP_REFERER'], 'reason' => 'CSRF suspected', - ) + ] ); CRM_Utils_JSON::output($error); } @@ -615,11 +537,14 @@ public static function ajax() { $entity = CRM_Utils_Array::value('entity', $requestParams); $action = CRM_Utils_Array::value('action', $requestParams); if (!$entity || !$action) { - $err = array('error_message' => 'missing mandatory params "entity=" or "action="', 'is_error' => 1); + $err = [ + 'error_message' => 'missing mandatory params "entity=" or "action="', + 'is_error' => 1, + ]; echo self::output($err); CRM_Utils_System::civiExit(); } - $args = array('civicrm', $entity, $action); + $args = ['civicrm', $entity, $action]; } else { $args = explode('/', $q); @@ -651,14 +576,14 @@ public static function ajax() { * @return array */ public static function processMultiple() { - $output = array(); + $output = []; foreach (json_decode($_REQUEST['json'], TRUE) as $key => $call) { - $args = array( + $args = [ 'civicrm', $call[0], $call[1], - ); - $output[$key] = self::process($args, CRM_Utils_Array::value(2, $call, array())); + ]; + $output[$key] = self::process($args, CRM_Utils_Array::value(2, $call, [])); } return $output; } @@ -676,8 +601,9 @@ public function loadCMSBootstrap() { // Proceed with bootstrap for "?q=civicrm/X/Y" but not "?q=civicrm/ping" if (!empty($q)) { if (count($args) == 2 && $args[1] == 'ping') { - CRM_Utils_System::loadBootStrap(array(), FALSE, FALSE); - return NULL; // this is pretty wonky but maybe there's some reason I can't see + CRM_Utils_System::loadBootStrap([], FALSE, FALSE); + // this is pretty wonky but maybe there's some reason I can't see + return NULL; } if (count($args) != 3) { return self::error('ERROR: Malformed REST path'); @@ -692,7 +618,7 @@ public function loadCMSBootstrap() { // FIXME: At time of writing, this doesn't actually do anything because // authenticateKey abends, but that's a bad behavior which sends a // malformed response. - CRM_Utils_System::loadBootStrap(array(), FALSE, FALSE); + CRM_Utils_System::loadBootStrap([], FALSE, FALSE); return self::error('Failed to authenticate key'); } @@ -701,7 +627,7 @@ public function loadCMSBootstrap() { $store = NULL; $api_key = CRM_Utils_Request::retrieve('api_key', 'String', $store, FALSE, NULL, 'REQUEST'); if (empty($api_key)) { - CRM_Utils_System::loadBootStrap(array(), FALSE, FALSE); + CRM_Utils_System::loadBootStrap([], FALSE, FALSE); return self::error("FATAL: mandatory param 'api_key' (user key) missing"); } $contact_id = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $api_key, 'id', 'api_key'); @@ -711,17 +637,17 @@ public function loadCMSBootstrap() { } if ($uid && $contact_id) { - CRM_Utils_System::loadBootStrap(array('uid' => $uid), TRUE, FALSE); + CRM_Utils_System::loadBootStrap(['uid' => $uid], TRUE, FALSE); $session = CRM_Core_Session::singleton(); $session->set('ufID', $uid); $session->set('userID', $contact_id); CRM_Core_DAO::executeQuery('SET @civicrm_user_id = %1', - array(1 => array($contact_id, 'Integer')) + [1 => [$contact_id, 'Integer']] ); return NULL; } else { - CRM_Utils_System::loadBootStrap(array(), FALSE, FALSE); + CRM_Utils_System::loadBootStrap([], FALSE, FALSE); return self::error('ERROR: No CMS user associated with given api-key'); } } diff --git a/CRM/Utils/ReCAPTCHA.php b/CRM/Utils/ReCAPTCHA.php index 41e5d8f68bea..31980430149b 100644 --- a/CRM/Utils/ReCAPTCHA.php +++ b/CRM/Utils/ReCAPTCHA.php @@ -1,9 +1,9 @@ recaptchaPublicKey == NULL || $config->recaptchaPublicKey == "") { + return FALSE; + } + return TRUE; + } + + /** + * Check if reCaptcha has to be added on form forcefully. + */ + public static function hasToAddForcefully() { + $config = CRM_Core_Config::singleton(); + if (!$config->forceRecaptcha) { + return FALSE; + } + return TRUE; + } + /** * Add element to form. * @@ -75,11 +97,10 @@ public static function add(&$form) { require_once 'packages/recaptcha/recaptchalib.php'; } - // See if we are using SSL - if (CRM_Utils_System::isSSL()) { - $useSSL = TRUE; - } - $html = recaptcha_get_html($config->recaptchaPublicKey, $error, $useSSL); + // Load the Recaptcha api.js over HTTPS + $useHTTPS = TRUE; + + $html = recaptcha_get_html($config->recaptchaPublicKey, $error, $useHTTPS); $form->assign('recaptchaHTML', $html); $form->assign('recaptchaOptions', $config->recaptchaOptions); diff --git a/CRM/Utils/Recent.php b/CRM/Utils/Recent.php index c596c7b8cb41..55ccf47b4b5c 100644 --- a/CRM/Utils/Recent.php +++ b/CRM/Utils/Recent.php @@ -1,9 +1,9 @@ get(self::STORE_NAME); if (!self::$_recent) { - self::$_recent = array(); + self::$_recent = []; } } } @@ -104,7 +104,7 @@ public static function add( $type, $contactId, $contactName, - $others = array() + $others = [] ) { self::initialize(); @@ -116,7 +116,7 @@ public static function add( // make sure item is not already present in list for ($i = 0; $i < count(self::$_recent); $i++) { - if (self::$_recent[$i]['url'] == $url) { + if (self::$_recent[$i]['type'] === $type && self::$_recent[$i]['id'] == $id) { // delete item from array array_splice(self::$_recent, $i, 1); break; @@ -124,11 +124,11 @@ public static function add( } if (!is_array($others)) { - $others = array(); + $others = []; } array_unshift(self::$_recent, - array( + [ 'title' => $title, 'url' => $url, 'id' => $id, @@ -140,7 +140,7 @@ public static function add( 'image_url' => CRM_Utils_Array::value('imageUrl', $others), 'edit_url' => CRM_Utils_Array::value('editUrl', $others), 'delete_url' => CRM_Utils_Array::value('deleteUrl', $others), - ) + ] ); if (count(self::$_recent) > self::$_maxItems) { @@ -162,7 +162,7 @@ public static function del($recentItem) { self::initialize(); $tempRecent = self::$_recent; - self::$_recent = ''; + self::$_recent = []; // make sure item is not already present in list for ($i = 0; $i < count($tempRecent); $i++) { @@ -174,6 +174,7 @@ public static function del($recentItem) { } } + CRM_Utils_Hook::recent(self::$_recent); $session = CRM_Core_Session::singleton(); $session->set(self::STORE_NAME, self::$_recent); } @@ -189,7 +190,7 @@ public static function delContact($id) { $tempRecent = self::$_recent; - self::$_recent = ''; + self::$_recent = []; // rebuild recent. for ($i = 0; $i < count($tempRecent); $i++) { @@ -200,15 +201,17 @@ public static function delContact($id) { self::$_recent[] = $tempRecent[$i]; } + CRM_Utils_Hook::recent(self::$_recent); $session = CRM_Core_Session::singleton(); $session->set(self::STORE_NAME, self::$_recent); } /** * Check if a provider is allowed to add stuff. - * If correspondig setting is empty, all are allowed + * If corresponding setting is empty, all are allowed * * @param string $providerName + * @return bool */ public static function isProviderEnabled($providerName) { @@ -230,9 +233,11 @@ public static function isProviderEnabled($providerName) { /** * Gets the list of available providers to civi's recent items stack + * + * @return array */ public static function getProviders() { - $providers = array( + $providers = [ 'Contact' => ts('Contacts'), 'Relationship' => ts('Relationships'), 'Activity' => ts('Activities'), @@ -246,7 +251,7 @@ public static function getProviders() { 'Pledge' => ts('Pledges'), 'Event' => ts('Events'), 'Campaign' => ts('Campaigns'), - ); + ]; return $providers; } diff --git a/CRM/Utils/Request.php b/CRM/Utils/Request.php index f5fd4b14b721..2f55cea32c37 100644 --- a/CRM/Utils/Request.php +++ b/CRM/Utils/Request.php @@ -1,9 +1,9 @@ $name))); + if ($isThrowException) { + throw new CRM_Core_Exception(ts("Could not find valid value for %1", [1 => $name])); + } + CRM_Core_Error::fatal(ts("Could not find valid value for %1", [1 => $name])); } if (!isset($value) && $default) { @@ -125,8 +126,10 @@ public static function retrieve($name, $type, &$store = NULL, $abort = FALSE, $d } // minor hack for action - if ($name == 'action' && is_string($value)) { - $value = CRM_Core_Action::resolve($value); + if ($name == 'action') { + if (!is_numeric($value) && is_string($value)) { + $value = CRM_Core_Action::resolve($value); + } } if (isset($value) && $store) { @@ -143,9 +146,9 @@ public static function retrieve($name, $type, &$store = NULL, $abort = FALSE, $d * @param array $method - '$_GET', '$_POST' or '$_REQUEST'. * * @return mixed - * The value of the variable + * The value of the variable */ - public static function getValue($name, $method) { + protected static function getValue($name, $method) { if (isset($method[$name])) { return $method[$name]; } @@ -165,6 +168,10 @@ public static function getValue($name, $method) { } /** + * @deprecated + * + * We should use a function that checks url values. + * * This is a replacement for $_REQUEST which includes $_GET/$_POST * but excludes $_COOKIE / $_ENV / $_SERVER. * @@ -176,7 +183,7 @@ public static function exportValues() { // http://www.php.net/manual/en/ini.core.php#ini.request-order // http://www.php.net/manual/en/ini.core.php#ini.variables-order - $result = array(); + $result = []; if ($_GET) { $result = array_merge($result, $_GET); } @@ -186,4 +193,63 @@ public static function exportValues() { return $result; } + /** + * Retrieve a variable from the http request. + * + * @param string $name + * Name of the variable to be retrieved. + * @param string $type + * Type of the variable (see CRM_Utils_Type for details). + * Most common options are: + * - 'Integer' + * - 'Positive' + * - 'CommaSeparatedIntegers' + * - 'Boolean' + * - 'String' + * + * @param mixed $defaultValue + * Default value of the variable if not present. + * @param bool $isRequired + * Is the variable required for this function to proceed without an exception. + * @param string $method + * Where to look for the value - GET|POST|REQUEST + * + * @return mixed + */ + public static function retrieveValue($name, $type, $defaultValue = NULL, $isRequired = FALSE, $method = 'REQUEST') { + $null = NULL; + return CRM_Utils_Request::retrieve((string) $name, (string) $type, $null, (bool) $isRequired, $defaultValue, $method, TRUE); + } + + /** + * Retrieve the component from the action attribute of a form. + * + * Contribution Page forms and Event Management forms detect the value of a + * component (and therefore the desired tab key) by reaching into the "action" + * attribute of a form and reading the final item of the path. In WordPress, + * however, the URL may be urlencoded, and so the URL may need to be decoded + * before parsing it. + * + * @see https://lab.civicrm.org/dev/wordpress/issues/12#note_10699 + * + * @param array $attributes + * The form attributes array. + * + * @return string + * The desired value. + */ + public static function retrieveComponent($attributes) { + $url = CRM_Utils_Array::value('action', $attributes); + // Whilst the following is a fallible universal test for urlencoded URLs, + // thankfully the "action" URL has a limited and predictable form and + // therefore this comparison is sufficient for our purposes. + if (rawurlencode(rawurldecode($url)) !== $url) { + $value = strtolower(basename(rawurldecode($url))); + } + else { + $value = strtolower(basename($url)); + } + return $value; + } + } diff --git a/CRM/Utils/Rule.php b/CRM/Utils/Rule.php index 0ea7c32e3cc3..940128d7156c 100644 --- a/CRM/Utils/Rule.php +++ b/CRM/Utils/Rule.php @@ -1,9 +1,9 @@ + * * @param $value + * @return bool + */ + public static function color($value) { + return (bool) preg_match('/^#([\da-fA-F]{6})$/', $value); + } + + /** + * Strip thousand separator from a money string. + * + * Note that this should be done at the form layer. Once we are processing + * money at the BAO or processor layer we should be working with something that + * is already in a normalised format. + * + * @param string $value * - * @return mixed + * @return string */ public static function cleanMoney($value) { // first remove all white space - $value = str_replace(array(' ', "\t", "\n"), '', $value); + $value = str_replace([' ', "\t", "\n"], '', $value); $config = CRM_Core_Config::singleton(); //CRM-14868 $currencySymbols = CRM_Core_PseudoConstant::get( 'CRM_Contribute_DAO_Contribution', - 'currency', array( + 'currency', [ 'keyColumn' => 'name', 'labelColumn' => 'symbol', - ) + ] ); $value = str_replace($currencySymbols, '', $value); @@ -554,7 +631,10 @@ public static function money($value) { return TRUE; } - return preg_match('/(^-?\d+\.\d?\d?$)|(^-?\.\d\d?$)/', $value) ? TRUE : FALSE; + // Allow values such as -0, 1.024555, -.1 + // We need to support multiple decimal places here, not just the number allowed by locale + // otherwise tax calculations break when you want the inclusive amount to be a round number (eg. £10 inc. VAT requires 8.333333333 here). + return preg_match('/(^-?\d+\.?\d*$)|(^-?\.\d+$)/', $value) ? TRUE : FALSE; } /** @@ -694,7 +774,7 @@ public static function htmlFile($elementValue) { * @param string $value * The value of the field we are checking. * @param array $options - * The daoName and fieldName (optional ). + * The daoName, fieldName (optional) and DomainID (optional). * * @return bool * true if object exists @@ -705,7 +785,7 @@ public static function objectExists($value, $options) { $name = $options[2]; } - return CRM_Core_DAO::objectExists($value, CRM_Utils_Array::value(0, $options), CRM_Utils_Array::value(1, $options), CRM_Utils_Array::value(2, $options, $name)); + return CRM_Core_DAO::objectExists($value, CRM_Utils_Array::value(0, $options), CRM_Utils_Array::value(1, $options), CRM_Utils_Array::value(2, $options, $name), CRM_Utils_Array::value(3, $options)); } /** @@ -715,7 +795,7 @@ public static function objectExists($value, $options) { * @return bool */ public static function optionExists($value, $options) { - return CRM_Core_OptionValue::optionExists($value, $options[0], $options[1], $options[2], CRM_Utils_Array::value(3, $options, 'name')); + return CRM_Core_OptionValue::optionExists($value, $options[0], $options[1], $options[2], CRM_Utils_Array::value(3, $options, 'name'), CRM_Utils_Array::value(4, $options, FALSE)); } /** @@ -725,7 +805,6 @@ public static function optionExists($value, $options) { * @return bool */ public static function creditCardNumber($value, $type) { - require_once 'Validate/Finance/CreditCard.php'; return Validate_Finance_CreditCard::number($value, $type); } @@ -736,8 +815,6 @@ public static function creditCardNumber($value, $type) { * @return bool */ public static function cvv($value, $type) { - require_once 'Validate/Finance/CreditCard.php'; - return Validate_Finance_CreditCard::cvv($value, $type); } @@ -773,6 +850,25 @@ public static function xssString($value) { } } + /** + * Validate json string for xss + * + * @param string $value + * + * @return bool + * False if invalid, true if valid / safe. + */ + public static function json($value) { + if (!self::xssString($value)) { + return FALSE; + } + $array = json_decode($value, TRUE); + if (!$array || !is_array($array)) { + return FALSE; + } + return self::arrayValue($array); + } + /** * @param $path * @@ -887,8 +983,39 @@ public static function validDateRange($fields, $fieldName, &$errors, $title) { $highDate = strtotime($fields[$fieldName . '_high']); if ($lowDate > $highDate) { - $errors[$fieldName . '_range_error'] = ts('%1: Please check that your date range is in correct chronological order.', array(1 => $title)); + $errors[$fieldName . '_range_error'] = ts('%1: Please check that your date range is in correct chronological order.', [1 => $title]); + } + } + + /** + * @param string $key Extension Key to check + * @return bool + */ + public static function checkExtensionKeyIsValid($key = NULL) { + if (!empty($key) && !preg_match('/^[0-9a-zA-Z._-]+$/', $key)) { + return FALSE; } + return TRUE; + } + + /** + * Validate array recursively checking keys and values. + * + * @param array $array + * @return bool + */ + protected static function arrayValue($array) { + foreach ($array as $key => $item) { + if (is_array($item)) { + if (!self::xssString($key) || !self::arrayValue($item)) { + return FALSE; + } + } + if (!self::xssString($key) || !self::xssString($item)) { + return FALSE; + } + } + return TRUE; } } diff --git a/CRM/Utils/SQL.php b/CRM/Utils/SQL.php index 994c698bad75..8c7f4c693854 100644 --- a/CRM/Utils/SQL.php +++ b/CRM/Utils/SQL.php @@ -1,9 +1,9 @@ addSelectWhereClause() as $field => $vals) { if ($vals && $field == $joinColumn) { $clauses = array_merge($clauses, (array) $vals); @@ -59,4 +59,117 @@ public static function mergeSubquery($entity, $joinColumn = 'id') { return $clauses; } + /** + * Get current sqlModes of the session + * @return array + */ + public static function getSqlModes() { + $sqlModes = explode(',', CRM_Core_DAO::singleValueQuery('SELECT @@sql_mode')); + return $sqlModes; + } + + /** + * Checks if this system enforce the MYSQL mode ONLY_FULL_GROUP_BY. + * This function should be named supportsAnyValueAndEnforcesFullGroupBY(), + * but should be deprecated instead. + * + * @return mixed + * @deprecated + */ + public static function supportsFullGroupBy() { + // CRM-21455 MariaDB 10.2 does not support ANY_VALUE + $version = self::getDatabaseVersion(); + + if (stripos($version, 'mariadb') !== FALSE) { + return FALSE; + } + + return version_compare($version, '5.7', '>='); + } + + /** + * Disable ONLY_FULL_GROUP_BY for MySQL versions lower then 5.7 + * + * @return bool + */ + public static function disableFullGroupByMode() { + $sqlModes = self::getSqlModes(); + + // Disable only_full_group_by mode for lower sql versions. + if (!self::supportsFullGroupBy() || (!empty($sqlModes) && !in_array('ONLY_FULL_GROUP_BY', $sqlModes))) { + if ($key = array_search('ONLY_FULL_GROUP_BY', $sqlModes)) { + unset($sqlModes[$key]); + CRM_Core_DAO::executeQuery("SET SESSION sql_mode = '" . implode(',', $sqlModes) . "'"); + } + return TRUE; + } + + return FALSE; + } + + /** + * CHeck if ONLY_FULL_GROUP_BY is in the global sql_modes + * @return bool + */ + public static function isGroupByModeInDefault() { + $sqlModes = explode(',', CRM_Core_DAO::singleValueQuery('SELECT @@global.sql_mode')); + if (!in_array('ONLY_FULL_GROUP_BY', $sqlModes)) { + return FALSE; + } + return TRUE; + } + + /** + * Is the Database set up to handle acceents. + * @warning This function was introduced in attempt to determine the reason why the test getInternationalStrings was failing on ubu1604 but passing on ubu1204-5 + * This function should not be used as the basis of further work as the reasoning is not perfact and is giving false failures. + * @return bool + */ + public static function supportStorageOfAccents() { + $charSetDB = CRM_Core_DAO::executeQuery("SHOW VARIABLES LIKE 'character_set_database'")->fetchAll(); + $charSet = $charSetDB[0]['Value']; + if ($charSet == 'utf8') { + return TRUE; + } + return FALSE; + } + + /** + * Does the DB version support mutliple locks per + * + * https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html#function_get-lock + * + * As an interim measure we ALSO require CIVICRM_SUPPORT_MULTIPLE_LOCKS to be defined. + * + * This is a conservative measure to introduce the change which we expect to deprecate later. + * + * @todo we only check mariadb & mysql right now but maybe can add percona. + */ + public static function supportsMultipleLocks() { + if (!defined('CIVICRM_SUPPORT_MULTIPLE_LOCKS')) { + return FALSE; + } + static $isSupportLocks = NULL; + if (!isset($isSupportLocks)) { + $version = self::getDatabaseVersion(); + if (stripos($version, 'mariadb') !== FALSE) { + $isSupportLocks = version_compare($version, '10.0.2', '>='); + } + else { + $isSupportLocks = version_compare($version, '5.7.5', '>='); + } + } + + return $isSupportLocks; + } + + /** + * Get the version string for the database. + * + * @return string + */ + public static function getDatabaseVersion() { + return CRM_Core_DAO::singleValueQuery('SELECT VERSION()'); + } + } diff --git a/CRM/Utils/SQL/BaseParamQuery.php b/CRM/Utils/SQL/BaseParamQuery.php new file mode 100644 index 000000000000..f48d05556c6d --- /dev/null +++ b/CRM/Utils/SQL/BaseParamQuery.php @@ -0,0 +1,241 @@ +strict = $strict; + return $this; + } + + /** + * Given a string like "field_name = @value", replace "@value" with an escaped SQL string + * + * @param string $expr SQL expression + * @param null|array $args a list of values to insert into the SQL expression; keys are prefix-coded: + * prefix '@' => escape SQL + * prefix '#' => literal number, skip escaping but do validation + * prefix '!' => literal, skip escaping and validation + * if a value is an array, then it will be imploded + * + * PHP NULL's will be treated as SQL NULL's. The PHP string "null" will be treated as a string. + * + * @param string $activeMode + * + * @return string + */ + public function interpolate($expr, $args, $activeMode = self::INTERPOLATE_INPUT) { + if ($args === NULL) { + return $expr; + } + else { + if ($this->mode === self::INTERPOLATE_AUTO) { + $this->mode = $activeMode; + } + elseif ($activeMode !== $this->mode) { + throw new RuntimeException("Cannot mix interpolation modes."); + } + + $select = $this; + return preg_replace_callback('/([#!@])([a-zA-Z0-9_]+)/', function($m) use ($select, $args) { + if (isset($args[$m[2]])) { + $values = $args[$m[2]]; + } + elseif (isset($args[$m[1] . $m[2]])) { + // Backward compat. Keys in $args look like "#myNumber" or "@myString". + $values = $args[$m[1] . $m[2]]; + } + elseif ($select->strict) { + throw new CRM_Core_Exception('Cannot build query. Variable "' . $m[1] . $m[2] . '" is unknown.'); + } + else { + // Unrecognized variables are ignored. Mitigate risk of accidents. + return $m[0]; + } + $values = is_array($values) ? $values : [$values]; + switch ($m[1]) { + case '@': + $parts = array_map([$select, 'escapeString'], $values); + return implode(', ', $parts); + + // TODO: ensure all uses of this un-escaped literal are safe + case '!': + return implode(', ', $values); + + case '#': + foreach ($values as $valueKey => $value) { + if ($value === NULL) { + $values[$valueKey] = 'NULL'; + } + elseif (!is_numeric($value)) { + //throw new API_Exception("Failed encoding non-numeric value" . var_export(array($m[0] => $values), TRUE)); + throw new CRM_Core_Exception("Failed encoding non-numeric value (" . $m[0] . ")"); + } + } + return implode(', ', $values); + + default: + throw new CRM_Core_Exception("Unrecognized prefix"); + } + }, $expr); + } + } + + /** + * @param string|NULL $value + * @return string + * SQL expression, e.g. "it\'s great" (with-quotes) or NULL (without-quotes) + */ + public function escapeString($value) { + return $value === NULL ? 'NULL' : '"' . CRM_Core_DAO::escapeString($value) . '"'; + } + + /** + * Set one (or multiple) parameters to interpolate into the query. + * + * @param array|string $keys + * Key name, or an array of key-value pairs. + * @param null|mixed $value + * The new value of the parameter. + * Values may be strings, ints, or arrays thereof -- provided that the + * SQL query uses appropriate prefix (e.g. "@", "!", "#"). + * @return $this + */ + public function param($keys, $value = NULL) { + if ($this->mode === self::INTERPOLATE_AUTO) { + $this->mode = self::INTERPOLATE_OUTPUT; + } + elseif ($this->mode !== self::INTERPOLATE_OUTPUT) { + throw new RuntimeException("Select::param() only makes sense when interpolating on output."); + } + + if (is_array($keys)) { + foreach ($keys as $k => $v) { + $this->params[$k] = $v; + } + } + else { + $this->params[$keys] = $value; + } + return $this; + } + + /** + * Has an offset been set. + * + * @param string $offset + * + * @return bool + */ + public function offsetExists($offset) { + return isset($this->params[$offset]); + } + + /** + * Get the value of a SQL parameter. + * + * @code + * $select['cid'] = 123; + * $select->where('contact.id = #cid'); + * echo $select['cid']; + * @endCode + * + * @param string $offset + * @return mixed + * @see param() + * @see ArrayAccess::offsetGet + */ + public function offsetGet($offset) { + return $this->params[$offset]; + } + + /** + * Set the value of a SQL parameter. + * + * @code + * $select['cid'] = 123; + * $select->where('contact.id = #cid'); + * echo $select['cid']; + * @endCode + * + * @param string $offset + * @param mixed $value + * The new value of the parameter. + * Values may be strings, ints, or arrays thereof -- provided that the + * SQL query uses appropriate prefix (e.g. "@", "!", "#"). + * @see param() + * @see ArrayAccess::offsetSet + */ + public function offsetSet($offset, $value) { + $this->param($offset, $value); + } + + /** + * Unset the value of a SQL parameter. + * + * @param string $offset + * @see param() + * @see ArrayAccess::offsetUnset + */ + public function offsetUnset($offset) { + unset($this->params[$offset]); + } + +} diff --git a/CRM/Utils/SQL/Delete.php b/CRM/Utils/SQL/Delete.php new file mode 100644 index 000000000000..24d0f885fbb4 --- /dev/null +++ b/CRM/Utils/SQL/Delete.php @@ -0,0 +1,269 @@ +where('activity_type_id = #type', array('type' => 234)) + * ->where('status_id IN (#statuses)', array('statuses' => array(1,2,3)) + * ->where('subject like @subj', array('subj' => '%hello%')) + * ->where('!dynamicColumn = 1', array('dynamicColumn' => 'coalesce(is_active,0)')) + * ->where('!column = @value', array( + * 'column' => $customField->column_name, + * 'value' => $form['foo'] + * )) + * echo $del->toSQL(); + * @endcode + * + * Design principles: + * - Portable + * - No knowledge of the underlying SQL API (except for escaping -- CRM_Core_DAO::escapeString) + * - No knowledge of the underlying data model + * - SQL clauses correspond to PHP functions ($select->where("foo_id=123")) + * - Variable escaping is concise and controllable based on prefixes, eg + * - similar to Drupal's t() + * - use "@varname" to insert the escaped value + * - use "!varname" to insert raw (unescaped) values + * - use "#varname" to insert a numerical value (these are validated but not escaped) + * - to disable any preprocessing, simply omit the variable list + * - control characters (@!#) are mandatory in expressions but optional in arg-keys + * - Variables may be individual values or arrays; arrays are imploded with commas + * - Conditionals are AND'd; if you need OR's, do it yourself + * - Use classes/functions with documentation (rather than undocumented array-trees) + * - For any given string, interpolation is only performed once. After an interpolation, + * a string may never again be subjected to interpolation. + * + * The "interpolate-once" principle can be enforced by either interpolating on input + * xor output. The notations for input and output interpolation are a bit different, + * and they may not be mixed. + * + * @code + * // Interpolate on input. Set params when using them. + * $select->where('activity_type_id = #type', array( + * 'type' => 234, + * )); + * + * // Interpolate on output. Set params independently. + * $select + * ->where('activity_type_id = #type') + * ->param('type', 234), + * @endcode + * + * @package CRM + * @copyright CiviCRM LLC (c) 2004-2019 + */ +class CRM_Utils_SQL_Delete extends CRM_Utils_SQL_BaseParamQuery { + + private $from; + private $wheres = []; + + /** + * Create a new DELETE query. + * + * @param string $from + * Table-name and optional alias. + * @param array $options + * @return CRM_Utils_SQL_Delete + */ + public static function from($from, $options = []) { + return new self($from, $options); + } + + /** + * Create a new DELETE query. + * + * @param string $from + * Table-name and optional alias. + * @param array $options + */ + public function __construct($from, $options = []) { + $this->from = $from; + $this->mode = isset($options['mode']) ? $options['mode'] : self::INTERPOLATE_AUTO; + } + + /** + * Make a new copy of this query. + * + * @return CRM_Utils_SQL_Delete + */ + public function copy() { + return clone $this; + } + + /** + * Merge something or other. + * + * @param CRM_Utils_SQL_Delete $other + * @param array|NULL $parts + * ex: 'wheres' + * @return CRM_Utils_SQL_Delete + */ + public function merge($other, $parts = NULL) { + if ($other === NULL) { + return $this; + } + + if ($this->mode === self::INTERPOLATE_AUTO) { + $this->mode = $other->mode; + } + elseif ($other->mode === self::INTERPOLATE_AUTO) { + // Noop. + } + elseif ($this->mode !== $other->mode) { + // Mixing modes will lead to someone getting an expected substitution. + throw new RuntimeException("Cannot merge queries that use different interpolation modes ({$this->mode} vs {$other->mode})."); + } + + $arrayFields = ['wheres', 'params']; + foreach ($arrayFields as $f) { + if ($parts === NULL || in_array($f, $parts)) { + $this->{$f} = array_merge($this->{$f}, $other->{$f}); + } + } + + $flatFields = ['from']; + foreach ($flatFields as $f) { + if ($parts === NULL || in_array($f, $parts)) { + if ($other->{$f} !== NULL) { + $this->{$f} = $other->{$f}; + } + } + } + + return $this; + } + + /** + * Limit results by adding extra condition(s) to the WHERE clause + * + * @param string|array $exprs list of SQL expressions + * @param null|array $args use NULL to disable interpolation; use an array of variables to enable + * @return CRM_Utils_SQL_Delete + */ + public function where($exprs, $args = NULL) { + $exprs = (array) $exprs; + foreach ($exprs as $expr) { + $evaluatedExpr = $this->interpolate($expr, $args); + $this->wheres[$evaluatedExpr] = $evaluatedExpr; + } + return $this; + } + + /** + * Set one (or multiple) parameters to interpolate into the query. + * + * @param array|string $keys + * Key name, or an array of key-value pairs. + * @param null|mixed $value + * The new value of the parameter. + * Values may be strings, ints, or arrays thereof -- provided that the + * SQL query uses appropriate prefix (e.g. "@", "!", "#"). + * @return \CRM_Utils_SQL_Delete + */ + public function param($keys, $value = NULL) { + // Why bother with an override? To provide better type-hinting in `@return`. + return parent::param($keys, $value); + } + + /** + * @param array|NULL $parts + * List of fields to check (e.g. 'wheres'). + * Defaults to all. + * @return bool + */ + public function isEmpty($parts = NULL) { + $empty = TRUE; + $fields = [ + 'from', + 'wheres', + ]; + if ($parts !== NULL) { + $fields = array_intersect($fields, $parts); + } + foreach ($fields as $field) { + if (!empty($this->{$field})) { + $empty = FALSE; + } + } + return $empty; + } + + /** + * @return string + * SQL statement + */ + public function toSQL() { + $sql = 'DELETE '; + + if ($this->from !== NULL) { + $sql .= 'FROM ' . $this->from . "\n"; + } + if ($this->wheres) { + $sql .= 'WHERE (' . implode(') AND (', $this->wheres) . ")\n"; + } + if ($this->mode === self::INTERPOLATE_OUTPUT) { + $sql = $this->interpolate($sql, $this->params, self::INTERPOLATE_OUTPUT); + } + return $sql; + } + + /** + * Execute the query. + * + * To examine the results, use a function like `fetch()`, `fetchAll()`, + * `fetchValue()`, or `fetchMap()`. + * + * @param string|NULL $daoName + * The return object should be an instance of this class. + * Ex: 'CRM_Contact_BAO_Contact'. + * @param bool $i18nRewrite + * If the system has multilingual features, should the field/table + * names be rewritten? + * @return CRM_Core_DAO + * @see CRM_Core_DAO::executeQuery + * @see CRM_Core_I18n_Schema::rewriteQuery + */ + public function execute($daoName = NULL, $i18nRewrite = TRUE) { + // Don't pass through $params. toSQL() handles interpolation. + $params = []; + + // Don't pass through $abort, $trapException. Just use straight-up exceptions. + $abort = TRUE; + $trapException = FALSE; + $errorScope = CRM_Core_TemporaryErrorScope::useException(); + + // Don't pass through freeDAO. You can do it yourself. + $freeDAO = FALSE; + + return CRM_Core_DAO::executeQuery($this->toSQL(), $params, $abort, $daoName, + $freeDAO, $i18nRewrite, $trapException); + } + +} diff --git a/CRM/Utils/SQL/Insert.php b/CRM/Utils/SQL/Insert.php index fc8f13635830..816c89f605d0 100644 --- a/CRM/Utils/SQL/Insert.php +++ b/CRM/Utils/SQL/Insert.php @@ -36,6 +36,7 @@ class CRM_Utils_SQL_Insert { /** * Array list of column names + * @var array */ private $columns; @@ -59,10 +60,11 @@ public static function into($table) { */ public static function dao(CRM_Core_DAO $dao) { $table = CRM_Core_DAO::getLocaleTableName($dao->getTableName()); - $row = array(); + $row = []; foreach ((array) $dao as $key => $value) { if ($value === 'null') { - $value = NULL; // Blerg!!! + // Blerg!!! + $value = NULL; } // Skip '_foobar' and '{\u00}*_options' and 'N'. if (preg_match('/[a-zA-Z]/', $key{0}) && $key !== 'N') { @@ -80,7 +82,7 @@ public static function dao(CRM_Core_DAO $dao) { */ public function __construct($table) { $this->table = $table; - $this->rows = array(); + $this->rows = []; } /** @@ -128,11 +130,11 @@ public function row($row) { sort($columns); $this->columns = $columns; } - elseif (array_diff($this->columns, $columns) !== array()) { + elseif (array_diff($this->columns, $columns) !== []) { throw new CRM_Core_Exception("Inconsistent column names"); } - $escapedRow = array(); + $escapedRow = []; foreach ($this->columns as $column) { $escapedRow[$column] = $this->escapeString($row[$column]); } diff --git a/CRM/Utils/SQL/Select.php b/CRM/Utils/SQL/Select.php index 55d401731d23..bdb892ed4d9e 100644 --- a/CRM/Utils/SQL/Select.php +++ b/CRM/Utils/SQL/Select.php @@ -1,9 +1,9 @@ where("foo_id=123")) * - Variable escaping is concise and controllable based on prefixes, eg * - similar to Drupal's t() @@ -79,57 +78,24 @@ * @endcode * * @package CRM - * @copyright CiviCRM LLC (c) 2004-2016 + * @copyright CiviCRM LLC (c) 2004-2019 */ -class CRM_Utils_SQL_Select implements ArrayAccess { +class CRM_Utils_SQL_Select extends CRM_Utils_SQL_BaseParamQuery { - /** - * Interpolate values as soon as they are passed in (where(), join(), etc). - * - * Default. - * - * Pro: Every clause has its own unique namespace for parameters. - * Con: Probably slower. - * Advice: Use this when aggregating SQL fragments from agents who - * maintained by different parties. - */ - const INTERPOLATE_INPUT = 'in'; - - /** - * Interpolate values when rendering SQL output (toSQL()). - * - * Pro: Probably faster. - * Con: Must maintain an aggregated list of all parameters. - * Advice: Use this when you have control over the entire query. - */ - const INTERPOLATE_OUTPUT = 'out'; - - /** - * Determine mode automatically. When the first attempt is made - * to use input-interpolation (eg `where(..., array(...))`) or - * output-interpolation (eg `param(...)`), the mode will be - * set. Subsequent calls will be validated using the same mode. - */ - const INTERPOLATE_AUTO = 'auto'; - - private $mode = NULL; private $insertInto = NULL; - private $insertIntoFields = array(); - private $selects = array(); + private $insertVerb = 'INSERT INTO '; + private $insertIntoFields = []; + private $selects = []; private $from; - private $joins = array(); - private $wheres = array(); - private $groupBys = array(); - private $havings = array(); - private $orderBys = array(); + private $joins = []; + private $wheres = []; + private $groupBys = []; + private $havings = []; + private $orderBys = []; private $limit = NULL; private $offset = NULL; - private $params = array(); private $distinct = NULL; - // Public to work-around PHP 5.3 limit. - public $strict = NULL; - /** * Create a new SELECT query. * @@ -138,7 +104,7 @@ class CRM_Utils_SQL_Select implements ArrayAccess { * @param array $options * @return CRM_Utils_SQL_Select */ - public static function from($from, $options = array()) { + public static function from($from, $options = []) { return new self($from, $options); } @@ -148,7 +114,7 @@ public static function from($from, $options = array()) { * @param array $options * @return CRM_Utils_SQL_Select */ - public static function fragment($options = array()) { + public static function fragment($options = []) { return new self(NULL, $options); } @@ -159,7 +125,7 @@ public static function fragment($options = array()) { * Table-name and optional alias. * @param array $options */ - public function __construct($from, $options = array()) { + public function __construct($from, $options = []) { $this->from = $from; $this->mode = isset($options['mode']) ? $options['mode'] : self::INTERPOLATE_AUTO; } @@ -176,7 +142,7 @@ public function copy() { /** * Merge something or other. * - * @param CRM_Utils_SQL_Select $other + * @param array|CRM_Utils_SQL_Select $other * @param array|NULL $parts * ex: 'joins', 'wheres' * @return CRM_Utils_SQL_Select @@ -186,6 +152,13 @@ public function merge($other, $parts = NULL) { return $this; } + if (is_array($other)) { + foreach ($other as $fragment) { + $this->merge($fragment, $parts); + } + return $this; + } + if ($this->mode === self::INTERPOLATE_AUTO) { $this->mode = $other->mode; } @@ -197,14 +170,14 @@ public function merge($other, $parts = NULL) { throw new RuntimeException("Cannot merge queries that use different interpolation modes ({$this->mode} vs {$other->mode})."); } - $arrayFields = array('insertIntoFields', 'selects', 'joins', 'wheres', 'groupBys', 'havings', 'orderBys', 'params'); + $arrayFields = ['insertIntoFields', 'selects', 'joins', 'wheres', 'groupBys', 'havings', 'orderBys', 'params']; foreach ($arrayFields as $f) { if ($parts === NULL || in_array($f, $parts)) { $this->{$f} = array_merge($this->{$f}, $other->{$f}); } } - $flatFields = array('insertInto', 'from', 'limit', 'offset'); + $flatFields = ['insertInto', 'from', 'limit', 'offset']; foreach ($flatFields as $f) { if ($parts === NULL || in_array($f, $parts)) { if ($other->{$f} !== NULL) { @@ -323,13 +296,15 @@ public function having($exprs, $args = NULL) { * * @param string|array $exprs list of SQL expressions * @param null|array $args use NULL to disable interpolation; use an array of variables to enable - * @return CRM_Utils_SQL_Select + * @param int $weight + * @return \CRM_Utils_SQL_Select */ - public function orderBy($exprs, $args = NULL) { + public function orderBy($exprs, $args = NULL, $weight = 0) { + static $guid = 0; $exprs = (array) $exprs; foreach ($exprs as $expr) { $evaluatedExpr = $this->interpolate($expr, $args); - $this->orderBys[$evaluatedExpr] = $evaluatedExpr; + $this->orderBys[$evaluatedExpr] = ['value' => $evaluatedExpr, 'weight' => $weight, 'guid' => $guid++]; } return $this; } @@ -340,25 +315,14 @@ public function orderBy($exprs, $args = NULL) { * @param array|string $keys * Key name, or an array of key-value pairs. * @param null|mixed $value + * The new value of the parameter. + * Values may be strings, ints, or arrays thereof -- provided that the + * SQL query uses appropriate prefix (e.g. "@", "!", "#"). * @return \CRM_Utils_SQL_Select */ public function param($keys, $value = NULL) { - if ($this->mode === self::INTERPOLATE_AUTO) { - $this->mode = self::INTERPOLATE_OUTPUT; - } - elseif ($this->mode !== self::INTERPOLATE_OUTPUT) { - throw new RuntimeException("Select::param() only makes sense when interpolating on output."); - } - - if (is_array($keys)) { - foreach ($keys as $k => $v) { - $this->params[$k] = $v; - } - } - else { - $this->params[$keys] = $value; - } - return $this; + // Why bother with an override? To provide bett er type-hinting in `@return`. + return parent::param($keys, $value); } /** @@ -392,12 +356,39 @@ public function limit($limit, $offset = 0) { * @return CRM_Utils_SQL_Select * @see insertIntoField */ - public function insertInto($table, $fields = array()) { + public function insertInto($table, $fields = []) { $this->insertInto = $table; $this->insertIntoField($fields); return $this; } + /** + * Wrapper function of insertInto fn but sets insertVerb = "INSERT IGNORE INTO " + * + * @param string $table + * The name of the other table (which receives new data). + * @param array $fields + * The fields to fill in the other table (in order). + * @return CRM_Utils_SQL_Select + */ + public function insertIgnoreInto($table, $fields = []) { + $this->insertVerb = "INSERT IGNORE INTO "; + return $this->insertInto($table, $fields); + } + + /** + * Wrapper function of insertInto fn but sets insertVerb = "REPLACE INTO " + * + * @param string $table + * The name of the other table (which receives new data). + * @param array $fields + * The fields to fill in the other table (in order). + */ + public function replaceInto($table, $fields = []) { + $this->insertVerb = "REPLACE INTO "; + return $this->insertInto($table, $fields); + } + /** * @param array $fields * The fields to fill in the other table (in order). @@ -419,7 +410,7 @@ public function insertIntoField($fields) { */ public function isEmpty($parts = NULL) { $empty = TRUE; - $fields = array( + $fields = [ 'insertInto', 'insertIntoFields', 'selects', @@ -431,7 +422,7 @@ public function isEmpty($parts = NULL) { 'orderBys', 'limit', 'offset', - ); + ]; if ($parts !== NULL) { $fields = array_intersect($fields, $parts); } @@ -443,101 +434,6 @@ public function isEmpty($parts = NULL) { return $empty; } - /** - * Enable (or disable) strict mode. - * - * In strict mode, unknown variables will generate exceptions. - * - * @param bool $strict - * @return CRM_Utils_SQL_Select - */ - public function strict($strict = TRUE) { - $this->strict = $strict; - return $this; - } - - /** - * Given a string like "field_name = @value", replace "@value" with an escaped SQL string - * - * @param $expr SQL expression - * @param null|array $args a list of values to insert into the SQL expression; keys are prefix-coded: - * prefix '@' => escape SQL - * prefix '#' => literal number, skip escaping but do validation - * prefix '!' => literal, skip escaping and validation - * if a value is an array, then it will be imploded - * - * PHP NULL's will be treated as SQL NULL's. The PHP string "null" will be treated as a string. - * - * @param string $activeMode - * - * @return string - */ - public function interpolate($expr, $args, $activeMode = self::INTERPOLATE_INPUT) { - if ($args === NULL) { - return $expr; - } - else { - if ($this->mode === self::INTERPOLATE_AUTO) { - $this->mode = $activeMode; - } - elseif ($activeMode !== $this->mode) { - throw new RuntimeException("Cannot mix interpolation modes."); - } - - $select = $this; - return preg_replace_callback('/([#!@])([a-zA-Z0-9_]+)/', function($m) use ($select, $args) { - if (isset($args[$m[2]])) { - $values = $args[$m[2]]; - } - elseif (isset($args[$m[1] . $m[2]])) { - // Backward compat. Keys in $args look like "#myNumber" or "@myString". - $values = $args[$m[1] . $m[2]]; - } - elseif ($select->strict) { - throw new CRM_Core_Exception('Cannot build query. Variable "' . $m[1] . $m[2] . '" is unknown.'); - } - else { - // Unrecognized variables are ignored. Mitigate risk of accidents. - return $m[0]; - } - $values = is_array($values) ? $values : array($values); - switch ($m[1]) { - case '@': - $parts = array_map(array($select, 'escapeString'), $values); - return implode(', ', $parts); - - // TODO: ensure all uses of this un-escaped literal are safe - case '!': - return implode(', ', $values); - - case '#': - foreach ($values as $valueKey => $value) { - if ($value === NULL) { - $values[$valueKey] = 'NULL'; - } - elseif (!is_numeric($value)) { - //throw new API_Exception("Failed encoding non-numeric value" . var_export(array($m[0] => $values), TRUE)); - throw new CRM_Core_Exception("Failed encoding non-numeric value (" . $m[0] . ")"); - } - } - return implode(', ', $values); - - default: - throw new CRM_Core_Exception("Unrecognized prefix"); - } - }, $expr); - } - } - - /** - * @param string|NULL $value - * @return string - * SQL expression, e.g. "it\'s great" (with-quotes) or NULL (without-quotes) - */ - public function escapeString($value) { - return $value === NULL ? 'NULL' : '"' . CRM_Core_DAO::escapeString($value) . '"'; - } - /** * @return string * SQL statement @@ -545,10 +441,11 @@ public function escapeString($value) { public function toSQL() { $sql = ''; if ($this->insertInto) { - $sql .= 'INSERT INTO ' . $this->insertInto . ' ('; + $sql .= $this->insertVerb . $this->insertInto . ' ('; $sql .= implode(', ', $this->insertIntoFields); $sql .= ")\n"; } + if ($this->selects) { $sql .= 'SELECT ' . $this->distinct . implode(', ', $this->selects) . "\n"; } @@ -571,7 +468,10 @@ public function toSQL() { $sql .= 'HAVING (' . implode(') AND (', $this->havings) . ")\n"; } if ($this->orderBys) { - $sql .= 'ORDER BY ' . implode(', ', $this->orderBys) . "\n"; + $orderBys = CRM_Utils_Array::crmArraySortByField($this->orderBys, + ['weight', 'guid']); + $orderBys = CRM_Utils_Array::collect('value', $orderBys); + $sql .= 'ORDER BY ' . implode(', ', $orderBys) . "\n"; } if ($this->limit !== NULL) { $sql .= 'LIMIT ' . $this->limit . "\n"; @@ -585,20 +485,36 @@ public function toSQL() { return $sql; } - public function offsetExists($offset) { - return isset($this->params[$offset]); - } + /** + * Execute the query. + * + * To examine the results, use a function like `fetch()`, `fetchAll()`, + * `fetchValue()`, or `fetchMap()`. + * + * @param string|NULL $daoName + * The return object should be an instance of this class. + * Ex: 'CRM_Contact_BAO_Contact'. + * @param bool $i18nRewrite + * If the system has multilingual features, should the field/table + * names be rewritten? + * @return CRM_Core_DAO + * @see CRM_Core_DAO::executeQuery + * @see CRM_Core_I18n_Schema::rewriteQuery + */ + public function execute($daoName = NULL, $i18nRewrite = TRUE) { + // Don't pass through $params. toSQL() handles interpolation. + $params = []; - public function offsetGet($offset) { - return $this->params[$offset]; - } + // Don't pass through $abort, $trapException. Just use straight-up exceptions. + $abort = TRUE; + $trapException = FALSE; + $errorScope = CRM_Core_TemporaryErrorScope::useException(); - public function offsetSet($offset, $value) { - $this->param($offset, $value); - } + // Don't pass through freeDAO. You can do it yourself. + $freeDAO = FALSE; - public function offsetUnset($offset) { - unset($this->params[$offset]); + return CRM_Core_DAO::executeQuery($this->toSQL(), $params, $abort, $daoName, + $freeDAO, $i18nRewrite, $trapException); } } diff --git a/CRM/Utils/SQL/TempTable.php b/CRM/Utils/SQL/TempTable.php new file mode 100644 index 000000000000..5433b17c416b --- /dev/null +++ b/CRM/Utils/SQL/TempTable.php @@ -0,0 +1,333 @@ +getName(); + * $name = CRM_Utils_SQL_TempTable::build()->setDurable()->getName(); + * $name = CRM_Utils_SQL_TempTable::build()->setCategory('contactstats')->setId($contact['id'])->getName(); + * + * Example 2: Create a temp table using the results of a SELECT query. + * + * $tmpTbl = CRM_Utils_SQL_TempTable::build()->createWithQuery('SELECT id, display_name FROM civicrm_contact'); + * $tmpTbl = CRM_Utils_SQL_TempTable::build()->createWithQuery(CRM_Utils_SQL_Select::from('civicrm_contact')->select('display_name')); + * + * Example 3: Create an empty temp table with list of columns. + * + * $tmpTbl = CRM_Utils_SQL_TempTable::build()->setDurable()->setUtf8()->createWithColumns('id int(10, name varchar(64)'); + * + * Example 4: Drop a table that you previously created. + * + * $tmpTbl->drop(); + * + * Example 5: Auto-drop a temp table when $tmpTbl falls out of scope + * + * $tmpTbl->setAutodrop(); + * + */ +class CRM_Utils_SQL_TempTable { + + 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 + const ID_LENGTH = 37; + const ID_REGEXP = ';^[a-zA-Z0-9_]+$;'; + const INNODB = 'ENGINE=InnoDB'; + const MEMORY = 'ENGINE=MEMORY'; + + /** + * @var bool + */ + protected $durable; + + /** + * @var bool + */ + protected $utf8; + + protected $category; + + protected $id; + + protected $autodrop; + + protected $memory; + + protected $createSql; + + /** + * @return CRM_Utils_SQL_TempTable + */ + public static function build() { + $t = new CRM_Utils_SQL_TempTable(); + $t->category = NULL; + $t->id = md5(uniqid('', TRUE)); + // The constant CIVICRM_TEMP_FORCE_DURABLE is for local debugging. + $t->durable = CRM_Utils_Constant::value('CIVICRM_TEMP_FORCE_DURABLE', FALSE); + // @deprecated This constant is deprecated and will be removed. + $t->utf8 = CRM_Utils_Constant::value('CIVICRM_TEMP_FORCE_UTF8', TRUE); + $t->autodrop = FALSE; + $t->memory = FALSE; + return $t; + } + + public function __destruct() { + if ($this->autodrop) { + $this->drop(); + } + } + + /** + * Determine the full table name. + * + * @return string + * Ex: 'civicrm_tmp_d_foo_abcd1234abcd1234' + */ + public function getName() { + $parts = ['civicrm', 'tmp']; + $parts[] = ($this->durable ? 'd' : 'e'); + $parts[] = $this->category ? $this->category : 'dflt'; + $parts[] = $this->id ? $this->id : 'dflt'; + return implode('_', $parts); + } + + /** + * Create the table using results from a SELECT query. + * + * @param string|CRM_Utils_SQL_Select $selectQuery + * @return CRM_Utils_SQL_TempTable + */ + public function createWithQuery($selectQuery) { + $sql = sprintf('%s %s %s AS %s', + $this->toSQL('CREATE'), + $this->memory ? self::MEMORY : self::INNODB, + $this->utf8 ? self::UTF8 : '', + ($selectQuery instanceof CRM_Utils_SQL_Select ? $selectQuery->toSQL() : $selectQuery) + ); + CRM_Core_DAO::executeQuery($sql, [], TRUE, NULL, TRUE, FALSE); + $this->createSql = $sql; + return $this; + } + + /** + * Create the empty table. + * + * @parma string $columns + * SQL column listing. + * Ex: 'id int(10), name varchar(64)'. + * @return CRM_Utils_SQL_TempTable + */ + public function createWithColumns($columns) { + $sql = sprintf('%s (%s) %s %s', + $this->toSQL('CREATE'), + $columns, + $this->memory ? self::MEMORY : self::INNODB, + $this->utf8 ? self::UTF8 : '' + ); + CRM_Core_DAO::executeQuery($sql, [], TRUE, NULL, TRUE, FALSE); + $this->createSql = $sql; + return $this; + } + + /** + * Drop the table. + * + * @return CRM_Utils_SQL_TempTable + */ + public function drop() { + $sql = $this->toSQL('DROP', 'IF EXISTS'); + CRM_Core_DAO::executeQuery($sql, [], TRUE, NULL, TRUE, FALSE); + return $this; + } + + /** + * @param string $action + * Ex: 'CREATE', 'DROP' + * @param string|NULL $ifne + * Ex: 'IF EXISTS', 'IF NOT EXISTS'. + * @return string + * Ex: 'CREATE TEMPORARY TABLE `civicrm_tmp_e_foo_abcd1234`' + * Ex: 'CREATE TABLE IF NOT EXISTS `civicrm_tmp_d_foo_abcd1234`' + */ + private function toSQL($action, $ifne = NULL) { + $parts = []; + $parts[] = $action; + if (!$this->durable) { + $parts[] = 'TEMPORARY'; + } + $parts[] = 'TABLE'; + if ($ifne) { + $parts[] = $ifne; + } + $parts[] = '`' . $this->getName() . '`'; + return implode(' ', $parts); + } + + /** + * @return string|NULL + */ + public function getCategory() { + return $this->category; + } + + /** + * @return string|NULL + */ + public function getId() { + return $this->id; + } + + /** + * @return string|NULL + */ + public function getCreateSql() { + return $this->createSql; + } + + /** + * @return bool + */ + public function isAutodrop() { + return $this->autodrop; + } + + /** + * @return bool + */ + public function isDurable() { + return $this->durable; + } + + /** + * @return bool + */ + public function isMemory() { + return $this->memory; + } + + /** + * @return bool + */ + public function isUtf8() { + return $this->utf8; + } + + /** + * @param bool $autodrop + * @return CRM_Utils_SQL_TempTable + */ + public function setAutodrop($autodrop = TRUE) { + $this->autodrop = $autodrop; + return $this; + } + + /** + * @param string|NULL $category + * + * @return CRM_Utils_SQL_TempTable + */ + public function setCategory($category) { + if ($category && !preg_match(self::CATEGORY_REGEXP, $category) || strlen($category) > self::CATEGORY_LENGTH) { + throw new \RuntimeException("Malformed temp table category"); + } + $this->category = $category; + return $this; + } + + /** + * Set whether the table should be durable. + * + * Durable tables are not TEMPORARY in the mysql sense. + * + * @param bool $durable + * + * @return CRM_Utils_SQL_TempTable + */ + public function setDurable($durable = TRUE) { + $this->durable = $durable; + return $this; + } + + /** + * Setter for id + * + * @param mixed $id + * + * @return CRM_Utils_SQL_TempTable + */ + public function setId($id) { + if ($id && !preg_match(self::ID_REGEXP, $id) || strlen($id) > self::ID_LENGTH) { + throw new \RuntimeException("Malformed temp table id"); + } + $this->id = $id; + return $this; + } + + /** + * Set table engine to MEMORY. + * + * @param bool $value + * + * @return $this + */ + public function setMemory($value = TRUE) { + $this->memory = $value; + return $this; + } + + /** + * Set table collation to UTF8. + * + * This would make sense as a default but cautiousness during phasing in has made it opt-in. + * + * @param bool $value + * + * @return $this + */ + public function setUtf8($value = TRUE) { + $this->utf8 = $value; + return $this; + } + +} diff --git a/CRM/Utils/Signer.php b/CRM/Utils/Signer.php index 4569a6c8b670..6323b8931d7d 100644 --- a/CRM/Utils/Signer.php +++ b/CRM/Utils/Signer.php @@ -1,9 +1,9 @@ secret = $secret; $this->paramNames = $paramNames; - $this->signDelim = "_"; // chosen to be valid in URLs but not in salt or md5 + // chosen to be valid in URLs but not in salt or md5 + $this->signDelim = "_"; $this->defaultSalt = CRM_Utils_String::createRandom(self::SALT_LEN, CRM_Utils_String::ALPHANUMERIC); } @@ -81,9 +83,9 @@ public function __construct($secret, $paramNames) { * @return string, the full public token representing the signature */ public function sign($params, $salt = NULL) { - $message = array(); + $message = []; $message['secret'] = $this->secret; - $message['payload'] = array(); + $message['payload'] = []; if (empty($salt)) { $message['salt'] = $this->createSalt(); } diff --git a/CRM/Utils/SoapServer.php b/CRM/Utils/SoapServer.php index 5d92a210919c..395cd549ebd9 100644 --- a/CRM/Utils/SoapServer.php +++ b/CRM/Utils/SoapServer.php @@ -1,9 +1,9 @@ verify($key); - $params = array( + $params = [ 'job_id' => $job, 'time_stamp' => date('YmdHis'), 'event_queue_id' => $queue, 'hash' => $hash, 'body' => $body, 'version' => 3, - ); - return civicrm_api('Mailing', 'event_bounce', $params); + ]; + $result = civicrm_api('Mailing', 'event_bounce', $params); + return CRM_Utils_Array::encode_items($result); } /** @@ -173,15 +176,16 @@ public function mailer_event_bounce($key, $job, $queue, $hash, $body) { */ public function mailer_event_unsubscribe($key, $job, $queue, $hash) { $this->verify($key); - $params = array( + $params = [ 'job_id' => $job, 'time_stamp' => date('YmdHis'), 'org_unsubscribe' => 0, 'event_queue_id' => $queue, 'hash' => $hash, 'version' => 3, - ); - return civicrm_api('MailingGroup', 'event_unsubscribe', $params); + ]; + $result = civicrm_api('MailingGroup', 'event_unsubscribe', $params); + return CRM_Utils_Array::encode_items($result); } /** @@ -195,15 +199,16 @@ public function mailer_event_unsubscribe($key, $job, $queue, $hash) { */ public function mailer_event_domain_unsubscribe($key, $job, $queue, $hash) { $this->verify($key); - $params = array( + $params = [ 'job_id' => $job, 'time_stamp' => date('YmdHis'), 'org_unsubscribe' => 1, 'event_queue_id' => $queue, 'hash' => $hash, 'version' => 3, - ); - return civicrm_api('MailingGroup', 'event_domain_unsubscribe', $params); + ]; + $result = civicrm_api('MailingGroup', 'event_domain_unsubscribe', $params); + return CRM_Utils_Array::encode_items($result); } /** @@ -217,15 +222,16 @@ public function mailer_event_domain_unsubscribe($key, $job, $queue, $hash) { */ public function mailer_event_resubscribe($key, $job, $queue, $hash) { $this->verify($key); - $params = array( + $params = [ 'job_id' => $job, 'time_stamp' => date('YmdHis'), 'org_unsubscribe' => 0, 'event_queue_id' => $queue, 'hash' => $hash, 'version' => 3, - ); - return civicrm_api('MailingGroup', 'event_resubscribe', $params); + ]; + $result = civicrm_api('MailingGroup', 'event_resubscribe', $params); + return CRM_Utils_Array::encode_items($result); } /** @@ -239,12 +245,13 @@ public function mailer_event_resubscribe($key, $job, $queue, $hash) { */ public function mailer_event_subscribe($key, $email, $domain, $group) { $this->verify($key); - $params = array( + $params = [ 'email' => $email, 'group_id' => $group, 'version' => 3, - ); - return civicrm_api('MailingGroup', 'event_subscribe', $params); + ]; + $result = civicrm_api('MailingGroup', 'event_subscribe', $params); + return CRM_Utils_Array::encode_items($result); } /** @@ -258,15 +265,16 @@ public function mailer_event_subscribe($key, $email, $domain, $group) { */ public function mailer_event_confirm($key, $contact, $subscribe, $hash) { $this->verify($key); - $params = array( + $params = [ 'contact_id' => $contact, 'subscribe_id' => $subscribe, 'time_stamp' => date('YmdHis'), 'event_subscribe_id' => $subscribe, 'hash' => $hash, 'version' => 3, - ); - return civicrm_api('Mailing', 'event_confirm', $params); + ]; + $result = civicrm_api('Mailing', 'event_confirm', $params); + return CRM_Utils_Array::encode_items($result); } /** @@ -284,7 +292,7 @@ public function mailer_event_confirm($key, $contact, $subscribe, $hash) { */ public function mailer_event_reply($key, $job, $queue, $hash, $bodyTxt, $rt, $bodyHTML = NULL, $fullEmail = NULL) { $this->verify($key); - $params = array( + $params = [ 'job_id' => $job, 'event_queue_id' => $queue, 'hash' => $hash, @@ -294,8 +302,9 @@ public function mailer_event_reply($key, $job, $queue, $hash, $bodyTxt, $rt, $bo 'fullEmail' => $fullEmail, 'time_stamp' => date('YmdHis'), 'version' => 3, - ); - return civicrm_api('Mailing', 'event_reply', $params); + ]; + $result = civicrm_api('Mailing', 'event_reply', $params); + return CRM_Utils_Array::encode_items($result); } /** @@ -310,14 +319,15 @@ public function mailer_event_reply($key, $job, $queue, $hash, $bodyTxt, $rt, $bo */ public function mailer_event_forward($key, $job, $queue, $hash, $email) { $this->verify($key); - $params = array( + $params = [ 'job_id' => $job, 'event_queue_id' => $queue, 'hash' => $hash, 'email' => $email, 'version' => 3, - ); - return civicrm_api('Mailing', 'event_forward', $params); + ]; + $result = civicrm_api('Mailing', 'event_forward', $params); + return CRM_Utils_Array::encode_items($result); } /** @@ -330,7 +340,8 @@ public function mailer_event_forward($key, $job, $queue, $hash, $email) { public function get_contact($key, $params) { $this->verify($key); $params['version'] = 3; - return civicrm_api('contact', 'get', $params); + $result = civicrm_api('contact', 'get', $params); + return CRM_Utils_Array::encode_items($result); } } diff --git a/CRM/Utils/Sort.php b/CRM/Utils/Sort.php index 55e2b97704f8..a844d3d2d3ea 100644 --- a/CRM/Utils/Sort.php +++ b/CRM/Utils/Sort.php @@ -1,9 +1,9 @@ _vars = array(); - $this->_response = array(); + $this->_vars = []; + $this->_response = []; foreach ($vars as $weight => $value) { - $this->_vars[$weight] = array( + $this->_vars[$weight] = [ 'name' => CRM_Utils_Type::validate($value['sort'], 'MysqlColumnNameOrAlias'), 'direction' => CRM_Utils_Array::value('direction', $value), 'title' => $value['name'], - ); + ]; } $this->_currentSortID = 1; @@ -219,12 +219,12 @@ public function initSortID($defaultSortOrder) { public function initialize($defaultSortOrder) { $this->initSortID($defaultSortOrder); - $this->_response = array(); + $this->_response = []; $current = $this->_currentSortID; foreach ($this->_vars as $index => $item) { $name = $item['name']; - $this->_response[$name] = array(); + $this->_response[$name] = []; $newDirection = ($item['direction'] == self::ASCENDING) ? self::DESCENDING : self::ASCENDING; @@ -274,7 +274,7 @@ public function getCurrentSortDirection() { * (-1 or 1) */ public static function cmpFunc($a, $b) { - $cmp_order = array('weight', 'id', 'title', 'name'); + $cmp_order = ['weight', 'id', 'title', 'name']; foreach ($cmp_order as $attribute) { if (isset($a[$attribute]) && isset($b[$attribute])) { if ($a[$attribute] < $b[$attribute]) { diff --git a/CRM/Utils/String.php b/CRM/Utils/String.php index e95d056b6e2b..c740e72a1f38 100644 --- a/CRM/Utils/String.php +++ b/CRM/Utils/String.php @@ -1,9 +1,9 @@ 'Acl', 'ACL' => 'Acl', 'im' => 'Im', 'IM' => 'Im', - ); + ]; if (isset($map[$string])) { return $map[$string]; } @@ -115,8 +120,12 @@ public static function convertStringToCamel($string) { $fragments = explode('_', $string); foreach ($fragments as & $fragment) { $fragment = ucfirst($fragment); + // Special case: UFGroup, UFJoin, UFMatch, UFField (if passed in without underscores) + if (strpos($fragment, 'Uf') === 0 && strlen($string) > 2) { + $fragment = 'UF' . ucfirst(substr($fragment, 2)); + } } - // Special case: UFGroup, UFJoin, UFMatch, UFField + // Special case: UFGroup, UFJoin, UFMatch, UFField (if passed in underscore-separated) if ($fragments[0] === 'Uf') { $fragments[0] = 'UF'; } @@ -153,7 +162,7 @@ public static function rename($name, $len = 4) { * The last component */ public static function getClassName($string, $char = '_') { - $names = array(); + $names = []; if (!is_array($string)) { $names = explode($char, $string); } @@ -236,7 +245,7 @@ public static function isAscii($str, $utf8 = TRUE) { return TRUE; } else { - $order = array('ASCII'); + $order = ['ASCII']; if ($utf8) { $order[] = 'UTF-8'; } @@ -260,7 +269,7 @@ public static function isAscii($str, $utf8 = TRUE) { public static function regex($str, $regexRules) { // redact the regular expressions if (!empty($regexRules) && isset($str)) { - static $matches, $totalMatches, $match = array(); + static $matches, $totalMatches, $match = []; foreach ($regexRules as $pattern => $replacement) { preg_match_all($pattern, $str, $matches); if (!empty($matches[0])) { @@ -328,7 +337,7 @@ public static function isUtf8($str) { // iconv('ISO-8859-1', 'UTF-8', $str); } else { - $enc = mb_detect_encoding($str, array('UTF-8'), TRUE); + $enc = mb_detect_encoding($str, ['UTF-8'], TRUE); return ($enc !== FALSE); } } @@ -508,7 +517,7 @@ public static function &makeArray($string) { $string = trim($string); $values = explode("\n", $string); - $result = array(); + $result = []; foreach ($values as $value) { list($n, $v) = CRM_Utils_System::explode('=', $value, 2); if (!empty($v)) { @@ -529,7 +538,7 @@ public static function &makeArray($string) { * only the first alternative found (or the text without alternatives) */ public static function stripAlternatives($full) { - $matches = array(); + $matches = []; preg_match('/-ALTERNATIVE ITEM 0-(.*?)-ALTERNATIVE ITEM 1-.*-ALTERNATIVE END-/s', $full, $matches); if (isset($matches[1]) && @@ -583,7 +592,7 @@ public static function stripPathChars( } if ($_searchChars == NULL) { - $_searchChars = array( + $_searchChars = [ '&', ';', ',', @@ -601,7 +610,7 @@ public static function stripPathChars( "\r\n", "\n", "\t", - ); + ]; $_replaceChar = '_'; } @@ -616,7 +625,6 @@ public static function stripPathChars( return str_replace($search, $replace, $string); } - /** * Use HTMLPurifier to clean up a text string and remove any potential * xss attacks. This is primarily used in public facing pages which @@ -633,6 +641,7 @@ public static function purifyHTML($string) { if (!$_filter) { $config = HTMLPurifier_Config::createDefault(); $config->set('Core.Encoding', 'UTF-8'); + $config->set('Attr.AllowedFrameTargets', ['_blank', '_self', '_parent', '_top']); // Disable the cache entirely $config->set('Cache.DefinitionImpl', NULL); @@ -652,18 +661,10 @@ public static function purifyHTML($string) { * @return string */ public static function ellipsify($string, $maxLen) { - $len = strlen($string); - if ($len <= $maxLen) { + if (mb_strlen($string, 'UTF-8') <= $maxLen) { return $string; } - else { - $end = $maxLen - 3; - while (strlen($string) > $maxLen - 3) { - $string = mb_substr($string, 0, $end, 'UTF-8'); - $end = $end - 1; - } - return $string . '...'; - } + return mb_substr($string, 0, $maxLen - 3, 'UTF-8') . '...'; } /** @@ -698,10 +699,10 @@ public static function createRandom($len, $alphabet) { public static function parsePrefix($delim, $string, $defaultPrefix = NULL) { $pos = strpos($string, $delim); if ($pos === FALSE) { - return array($defaultPrefix, $string); + return [$defaultPrefix, $string]; } else { - return array(substr($string, 0, $pos), substr($string, 1 + $pos)); + return [substr($string, 0, $pos), substr($string, 1 + $pos)]; } } @@ -784,6 +785,80 @@ public static function unstupifyUrl($htmlUrl) { return str_replace('&', '&', $htmlUrl); } + /** + * When a user supplies a URL (e.g. to an image), we'd like to: + * - Remove the protocol and domain name if the URL points to the current + * site. + * - Keep the domain name for remote URLs. + * - Optionally, force remote URLs to use https instead of http (which is + * useful for images) + * + * @param string $url + * The URL to simplify. Examples: + * "https://example.org/sites/default/files/coffee-mug.jpg" + * "sites/default/files/coffee-mug.jpg" + * "http://i.stack.imgur.com/9jb2ial01b.png" + * @param bool $forceHttps = FALSE + * If TRUE, ensure that remote URLs use https. If a URL with + * http is supplied, then we'll change it to https. + * This is useful for situations like showing a premium product on a + * contribution, because (as reported in CRM-14283) if the user gets a + * browser warning like "page contains insecure elements" on a contribution + * page, that's a very bad thing. Thus, even if changing http to https + * breaks the image, that's better than leaving http content in a + * contribution page. + * + * @return string + * The simplified URL. Examples: + * "/sites/default/files/coffee-mug.jpg" + * "https://i.stack.imgur.com/9jb2ial01b.png" + */ + public static function simplifyURL($url, $forceHttps = FALSE) { + $config = CRM_Core_Config::singleton(); + $siteURLParts = self::simpleParseUrl($config->userFrameworkBaseURL); + $urlParts = self::simpleParseUrl($url); + + // If the image is locally hosted, then only give the path to the image + $urlIsLocal + = ($urlParts['host+port'] == '') + | ($urlParts['host+port'] == $siteURLParts['host+port']); + if ($urlIsLocal) { + // and make sure it begins with one forward slash + return preg_replace('_^/*(?=.)_', '/', $urlParts['path+query']); + } + + // If the URL is external, then keep the full URL as supplied + else { + return $forceHttps ? preg_replace('_^http://_', 'https://', $url) : $url; + } + } + + /** + * A simplified version of PHP's parse_url() function. + * + * @param string $url + * e.g. "https://example.com:8000/foo/bar/?id=1#fragment" + * + * @return array + * Will always contain keys 'host+port' and 'path+query', even if they're + * empty strings. Example: + * [ + * 'host+port' => "example.com:8000", + * 'path+query' => "/foo/bar/?id=1", + * ] + */ + public static function simpleParseUrl($url) { + $parts = parse_url($url); + $host = isset($parts['host']) ? $parts['host'] : ''; + $port = isset($parts['port']) ? ':' . $parts['port'] : ''; + $path = isset($parts['path']) ? $parts['path'] : ''; + $query = isset($parts['query']) ? '?' . $parts['query'] : ''; + return [ + 'host+port' => "$host$port", + 'path+query' => "$path$query", + ]; + } + /** * Formats a string of attributes for insertion in an html tag. * @@ -842,7 +917,7 @@ public static function endsWith($string, $fragment) { */ public static function filterByWildcards($patterns, $allStrings, $allowNew = FALSE) { $patterns = (array) $patterns; - $result = array(); + $result = []; foreach ($patterns as $pattern) { if (!\CRM_Utils_String::endsWith($pattern, '*')) { if ($allowNew || in_array($pattern, $allStrings)) { diff --git a/CRM/Utils/System.php b/CRM/Utils/System.php index d8f94e0815ba..1ac8d03d0be2 100644 --- a/CRM/Utils/System.php +++ b/CRM/Utils/System.php @@ -1,9 +1,9 @@ userSystem; - return call_user_func_array(array($userSystem, $name), $arguments); + return call_user_func_array([$userSystem, $name], $arguments); } /** @@ -103,8 +103,7 @@ public static function makeURL($urlVar, $includeReset = FALSE, $includeForce = T } } - return - self::url( + return self::url( $path, CRM_Utils_System::getLinksUrl($urlVar, $includeReset, $includeForce), $absolute @@ -133,9 +132,9 @@ public static function makeURL($urlVar, $includeReset = FALSE, $includeForce = T */ public static function getLinksUrl($urlVar, $includeReset = FALSE, $includeForce = TRUE, $skipUFVar = TRUE) { // Sort out query string to prevent messy urls - $querystring = array(); - $qs = array(); - $arrays = array(); + $querystring = []; + $qs = []; + $arrays = []; if (!empty($_SERVER['QUERY_STRING'])) { $qs = explode('&', str_replace('&', '&', $_SERVER['QUERY_STRING'])); @@ -300,6 +299,18 @@ public static function url( return $url; } + /** + * Path of the current page e.g. 'civicrm/contact/view' + * + * @return string|null + */ + public static function getUrlPath() { + if (isset($_GET[CRM_Core_Config::singleton()->userFrameworkURLVar])) { + return $_GET[CRM_Core_Config::singleton()->userFrameworkURLVar]; + } + return NULL; + } + /** * Get href. * @@ -421,8 +432,10 @@ public static function getClassName($object) { * * @param string $url * The URL to provide to the browser via the Location header. + * @param array $context + * Optional additional information for the hook. */ - public static function redirect($url = NULL) { + public static function redirect($url = NULL, $context = []) { if (!$url) { $url = self::url('civicrm/dashboard', 'reset=1'); } @@ -430,12 +443,18 @@ public static function redirect($url = NULL) { // this is kinda hackish but not sure how to do it right $url = str_replace('&', '&', $url); + $context['output'] = CRM_Utils_Array::value('snippet', $_GET); + + $parsedUrl = CRM_Utils_Url::parseUrl($url); + CRM_Utils_Hook::alterRedirect($parsedUrl, $context); + $url = CRM_Utils_Url::unparseUrl($parsedUrl); + // If we are in a json context, respond appropriately - if (CRM_Utils_Array::value('snippet', $_GET) === 'json') { - CRM_Core_Page_AJAX::returnJsonResponse(array( + if ($context['output'] === 'json') { + CRM_Core_Page_AJAX::returnJsonResponse([ 'status' => 'redirect', 'userContext' => $url, - )); + ]); } self::setHttpHeader('Location', $url); @@ -607,7 +626,7 @@ public static function authenticateScript($abort = TRUE, $name = NULL, $pass = N list($userID, $ufID, $randomNumber) = $result; if ($userID && $ufID) { $config = CRM_Core_Config::singleton(); - $config->userSystem->setUserSession(array($userID, $ufID)); + $config->userSystem->setUserSession([$userID, $ufID]); } else { return self::authenticateAbort( @@ -668,7 +687,6 @@ public static function setUFMessage($message) { return $config->userSystem->setMessage($message); } - /** * Determine whether a value is null-ish. * @@ -729,7 +747,7 @@ private static function parsePHPModules() { $s = preg_replace('/]*>([^<]+)<\/th>/', "\\1", $s); $s = preg_replace('/]*>([^<]+)<\/td>/', "\\1", $s); $vTmp = preg_split('/(

    [^<]+<\/h2>)/', $s, -1, PREG_SPLIT_DELIM_CAPTURE); - $vModules = array(); + $vModules = []; for ($i = 1; $i < count($vTmp); $i++) { if (preg_match('/

    ([^<]+)<\/h2>/', $vTmp[$i], $vMat)) { $vName = trim($vMat[1]); @@ -740,7 +758,7 @@ private static function parsePHPModules() { $vPat2 = "/$vPat\s*$vPat/"; // 3cols if (preg_match($vPat3, $vOne, $vMat)) { - $vModules[$vName][trim($vMat[1])] = array(trim($vMat[2]), trim($vMat[3])); + $vModules[$vName][trim($vMat[1])] = [trim($vMat[2]), trim($vMat[3])]; // 2cols } elseif (preg_match($vPat2, $vOne, $vMat)) { @@ -887,7 +905,7 @@ public static function fixURL($url) { */ public static function validCallback($callback) { if (self::$_callbacks === NULL) { - self::$_callbacks = array(); + self::$_callbacks = []; } if (!array_key_exists($callback, self::$_callbacks)) { @@ -986,7 +1004,7 @@ public static function checkPHPVersion($ver = 5, $abort = TRUE) { if ($abort) { CRM_Core_Error::fatal(ts('This feature requires PHP Version %1 or greater', - array(1 => $ver) + [1 => $ver] )); } return FALSE; @@ -1062,25 +1080,12 @@ public static function version() { if (!$version) { $verFile = implode(DIRECTORY_SEPARATOR, - array(dirname(__FILE__), '..', '..', 'civicrm-version.php') + [dirname(__FILE__), '..', '..', 'xml', 'version.xml'] ); if (file_exists($verFile)) { - require_once $verFile; - if (function_exists('civicrmVersion')) { - $info = civicrmVersion(); - $version = $info['version']; - } - } - else { - // svn installs don't have version.txt by default. In that case version.xml should help - - $verFile = implode(DIRECTORY_SEPARATOR, - array(dirname(__FILE__), '..', '..', 'xml', 'version.xml') - ); - if (file_exists($verFile)) { - $str = file_get_contents($verFile); - $xmlObj = simplexml_load_string($str); - $version = (string) $xmlObj->version_no; - } + $str = file_get_contents($verFile); + $xmlObj = simplexml_load_string($str); + $version = (string) $xmlObj->version_no; } // pattern check @@ -1124,7 +1129,7 @@ public static function getAllHeaders() { // emulate get all headers // http://www.php.net/manual/en/function.getallheaders.php#66335 - $headers = array(); + $headers = []; foreach ($_SERVER as $name => $value) { if (substr($name, 0, 5) == 'HTTP_') { $headers[str_replace(' ', @@ -1161,8 +1166,7 @@ public static function getRequestHeaders() { * this function, please go and change the code in the install script as well. */ public static function isSSL() { - return - (isset($_SERVER['HTTPS']) && + return (isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) != 'off') ? TRUE : FALSE; } @@ -1184,6 +1188,8 @@ public static function redirectToSSL($abort = FALSE) { ) { // ensure that SSL is enabled on a civicrm url (for cookie reasons etc) $url = "https://{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}"; + // @see https://lab.civicrm.org/dev/core/issues/425 if you're seeing this message. + Civi::log()->warning('CiviCRM thinks site is not SSL, redirecting to {url}', ['url' => $url]); if (!self::checkURL($url, TRUE)) { if ($abort) { CRM_Core_Error::fatal('HTTPS is not set up on this machine'); @@ -1257,7 +1263,7 @@ public static function refererPath() { */ public static function getDocBaseURL() { // FIXME: move this to configuration at some stage - return 'http://book.civicrm.org/'; + return 'https://docs.civicrm.org/'; } /** @@ -1303,18 +1309,19 @@ public static function docURL2($page, $URLonly = FALSE, $text = NULL, $title = N } else { $docBaseURL = self::getDocBaseURL(); + $page = self::formatDocUrl($page); } return $docBaseURL . str_replace(' ', '+', $page); } else { - $params = array( + $params = [ 'page' => $page, 'URLonly' => $URLonly, 'text' => $text, 'title' => $title, 'style' => $style, 'resource' => $resource, - ); + ]; return self::docURL($params); } } @@ -1341,6 +1348,7 @@ public static function docURL($params) { } else { $docBaseURL = self::getDocBaseURL(); + $params['page'] = self::formatDocUrl($params['page']); } if (!isset($params['title']) or $params['title'] === NULL) { @@ -1368,6 +1376,18 @@ public static function docURL($params) { } } + /** + * Add language and version parameters to the doc url. + * + * Note that this function may run before CiviCRM is initialized and so should not call ts() or perform any db lookups. + * + * @param $url + * @return mixed + */ + public static function formatDocUrl($url) { + return preg_replace('#^user/#', 'user/en/stable/', $url); + } + /** * Exit with provided exit code. * @@ -1375,9 +1395,21 @@ public static function docURL($params) { * (optional) Code with which to exit. */ public static function civiExit($status = 0) { + + if ($status > 0) { + http_response_code(500); + } // move things to CiviCRM cache as needed CRM_Core_Session::storeSessionObjects(); + if (Civi\Core\Container::isContainerBooted()) { + Civi::dispatcher()->dispatch('civi.core.exit'); + } + + $userSystem = CRM_Core_Config::singleton()->userSystem; + if (is_callable([$userSystem, 'onCiviExit'])) { + $userSystem->onCiviExit(); + } exit($status); } @@ -1387,8 +1419,22 @@ public static function civiExit($status = 0) { public static function flushCache() { // flush out all cache entries so we can reload new data // a bit aggressive, but livable for now - $cache = CRM_Utils_Cache::singleton(); - $cache->flush(); + CRM_Utils_Cache::singleton()->flush(); + + // Traditionally, systems running on memory-backed caches were quite + // zealous about destroying *all* memory-backed caches during a flush(). + // These flushes simulate that legacy behavior. However, they should probably + // be removed at some point. + $localDrivers = ['CRM_Utils_Cache_Arraycache', 'CRM_Utils_Cache_NoCache']; + if (Civi\Core\Container::isContainerBooted() + && !in_array(get_class(CRM_Utils_Cache::singleton()), $localDrivers)) { + Civi::cache('long')->flush(); + Civi::cache('settings')->flush(); + Civi::cache('js_strings')->flush(); + Civi::cache('community_messages')->flush(); + CRM_Extension_System::singleton()->getCache()->flush(); + CRM_Cxn_CiviCxnHttp::singleton()->getCache()->flush(); + } // also reset the various static memory caches @@ -1402,7 +1448,7 @@ public static function flushCache() { CRM_Contact_BAO_Contact::$_importableFields = CRM_Contact_BAO_Contact::$_exportableFields = CRM_Contribute_BAO_Contribution::$_importableFields = CRM_Contribute_BAO_Contribution::$_exportableFields - = CRM_Pledge_BAO_Pledge::$_exportableFields = CRM_Contribute_BAO_Query::$_contributionFields + = CRM_Pledge_BAO_Pledge::$_exportableFields = CRM_Core_BAO_CustomField::$_importFields = CRM_Core_BAO_Cache::$_cache = CRM_Core_DAO::$_dbColumnValueCache = NULL; @@ -1420,12 +1466,16 @@ public static function flushCache() { * @param bool $throwError * @param string $realPath */ - public static function loadBootStrap($params = array(), $loadUser = TRUE, $throwError = TRUE, $realPath = NULL) { + public static function loadBootStrap($params = [], $loadUser = TRUE, $throwError = TRUE, $realPath = NULL) { if (!is_array($params)) { - $params = array(); + $params = []; } $config = CRM_Core_Config::singleton(); - return $config->userSystem->loadBootStrap($params, $loadUser, $throwError, $realPath); + $result = $config->userSystem->loadBootStrap($params, $loadUser, $throwError, $realPath); + if (is_callable([$config->userSystem, 'setMySQLTimeZone'])) { + $config->userSystem->setMySQLTimeZone(); + } + return $result; } /** @@ -1616,7 +1666,7 @@ public static function appendTPLFile( * include path. */ public static function listIncludeFiles($relpath) { - $file_list = array(); + $file_list = []; $inc_dirs = explode(PATH_SEPARATOR, get_include_path()); foreach ($inc_dirs as $inc_dir) { $target_dir = $inc_dir . DIRECTORY_SEPARATOR . $relpath; @@ -1654,9 +1704,9 @@ public static function listIncludeFiles($relpath) { * List of plugins, where the plugin name is both the key and the value of * each element. */ - public static function getPluginList($relpath, $fext = '.php', $skipList = array()) { + public static function getPluginList($relpath, $fext = '.php', $skipList = []) { $fext_len = strlen($fext); - $plugins = array(); + $plugins = []; $inc_files = CRM_Utils_System::listIncludeFiles($relpath); foreach ($inc_files as $inc_file) { if (substr($inc_file, 0 - $fext_len) == $fext) { @@ -1698,7 +1748,7 @@ public static function evalUrl($url) { } else { $config = CRM_Core_Config::singleton(); - $vars = array( + $vars = [ '{ver}' => CRM_Utils_System::version(), '{uf}' => $config->userFramework, '{php}' => phpversion(), @@ -1706,7 +1756,7 @@ public static function evalUrl($url) { '{baseUrl}' => $config->userFrameworkBaseURL, '{lang}' => $config->lcMessages, '{co}' => $config->defaultContactCountry, - ); + ]; return strtr($url, array_map('urlencode', $vars)); } } @@ -1723,7 +1773,7 @@ public static function getSiteID() { if (!$sid) { $config = CRM_Core_Config::singleton(); $sid = md5('sid_' . (defined('CIVICRM_SITE_KEY') ? CIVICRM_SITE_KEY : '') . '_' . $config->userFrameworkBaseURL); - civicrm_api3('Setting', 'create', array('domain_id' => 'all', 'site_id' => $sid)); + civicrm_api3('Setting', 'create', ['domain_id' => 'all', 'site_id' => $sid]); } return $sid; } @@ -1795,9 +1845,9 @@ public static function createDefaultCrudLink($crudLinkSpec) { return NULL; } - $link = array(); + $link = []; CRM_Utils_Hook::crudLink($crudLinkSpec, $bao, $link); - if (empty($link) && is_callable(array($bao, 'createDefaultCrudLink'))) { + if (empty($link) && is_callable([$bao, 'createDefaultCrudLink'])) { $link = $bao->createDefaultCrudLink($crudLinkSpec); } diff --git a/CRM/Utils/System/Backdrop.php b/CRM/Utils/System/Backdrop.php index 72c177ebe008..7a3589a250a6 100644 --- a/CRM/Utils/System/Backdrop.php +++ b/CRM/Utils/System/Backdrop.php @@ -1,9 +1,9 @@ $params['cms_name'], 'mail' => $params[$mail], 'op' => 'Create new account', - ); + ]; $admin = user_access('administer users'); if (!config_get('system.core', 'user_email_verification') || $admin) { - $form_state['input']['pass'] = array('pass1' => $params['cms_pass'], 'pass2' => $params['cms_pass']); + $form_state['input']['pass'] = ['pass1' => $params['cms_pass'], 'pass2' => $params['cms_pass']]; } if (!empty($params['notify'])) { @@ -61,7 +61,7 @@ public function createUser(&$params, $mail) { $form_state['programmed'] = TRUE; $form_state['complete form'] = FALSE; $form_state['method'] = 'post'; - $form_state['build_info']['args'] = array(); + $form_state['build_info']['args'] = []; /* * if we want to submit this form more than once in a process (e.g. create more than one user) * we must force it to validate each time for this form. Otherwise it will not validate @@ -73,12 +73,12 @@ public function createUser(&$params, $mail) { // we also need to redirect b $config->inCiviCRM = TRUE; - $form = drupal_retrieve_form('user_register_form', $form_state); + $form = backdrop_retrieve_form('user_register_form', $form_state); $form_state['process_input'] = 1; $form_state['submitted'] = 1; - $form['#array_parents'] = array(); + $form['#array_parents'] = []; $form['#tree'] = FALSE; - drupal_process_form('user_register_form', $form, $form_state); + backdrop_process_form('user_register_form', $form, $form_state); $config->inCiviCRM = FALSE; @@ -103,7 +103,7 @@ public function updateCMSName($ufID, $email) { } /** - * Check if username and email exists in the drupal db. + * Check if username and email exists in the Backdrop db. * * @param array $params * Array of name and mail values. @@ -115,7 +115,7 @@ public function updateCMSName($ufID, $email) { public static function checkUserNameEmailExists(&$params, &$errors, $emailName = 'email') { $errors = form_get_errors(); if ($errors) { - // unset drupal messages to avoid twice display of errors + // unset Backdrop messages to avoid twice display of errors unset($_SESSION['messages']); } @@ -124,23 +124,23 @@ public static function checkUserNameEmailExists(&$params, &$errors, $emailName = $errors['cms_name'] = $nameError; } else { - $uid = db_query("SELECT uid FROM {users} WHERE name = :name", array(':name' => $params['name']))->fetchField(); + $uid = db_query("SELECT uid FROM {users} WHERE name = :name", [':name' => $params['name']])->fetchField(); if ((bool) $uid) { - $errors['cms_name'] = ts('The username %1 is already taken. Please select another username.', array(1 => $params['name'])); + $errors['cms_name'] = ts('The username %1 is already taken. Please select another username.', [1 => $params['name']]); } } } if (!empty($params['mail'])) { if (!valid_email_address($params['mail'])) { - $errors[$emailName] = ts('The e-mail address %1 is not valid.', array('%1' => $params['mail'])); + $errors[$emailName] = ts('The e-mail address %1 is not valid.', ['%1' => $params['mail']]); } else { - $uid = db_query("SELECT uid FROM {users} WHERE mail = :mail", array(':mail' => $params['mail']))->fetchField(); + $uid = db_query("SELECT uid FROM {users} WHERE mail = :mail", [':mail' => $params['mail']])->fetchField(); if ((bool) $uid) { $resetUrl = url('user/password'); $errors[$emailName] = ts('The email address %1 already has an account associated with it. Have you forgotten your password?', - array(1 => $params['mail'], 2 => $resetUrl) + [1 => $params['mail'], 2 => $resetUrl] ); } } @@ -151,8 +151,8 @@ public static function checkUserNameEmailExists(&$params, &$errors, $emailName = * @inheritDoc */ public function getLoginURL($destination = '') { - $query = $destination ? array('destination' => $destination) : array(); - return url('user', array('query' => $query, 'absolute' => TRUE)); + $query = $destination ? ['destination' => $destination] : []; + return url('user', ['query' => $query, 'absolute' => TRUE]); } /** @@ -164,7 +164,7 @@ public function setTitle($title, $pageTitle = NULL) { $pageTitle = $title; } - drupal_set_title($pageTitle, PASS_THROUGH); + backdrop_set_title($pageTitle, PASS_THROUGH); } } @@ -172,12 +172,12 @@ public function setTitle($title, $pageTitle = NULL) { * @inheritDoc */ public function appendBreadCrumb($breadCrumbs) { - $breadCrumb = drupal_get_breadcrumb(); + $breadCrumb = backdrop_get_breadcrumb(); if (is_array($breadCrumbs)) { foreach ($breadCrumbs as $crumbs) { if (stripos($crumbs['url'], 'id%%')) { - $args = array('cid', 'mid'); + $args = ['cid', 'mid']; foreach ($args as $a) { $val = CRM_Utils_Request::retrieve($a, 'Positive', CRM_Core_DAO::$_nullObject, FALSE, NULL, $_GET @@ -190,15 +190,15 @@ public function appendBreadCrumb($breadCrumbs) { $breadCrumb[] = "{$crumbs['title']}"; } } - drupal_set_breadcrumb($breadCrumb); + backdrop_set_breadcrumb($breadCrumb); } /** * @inheritDoc */ public function resetBreadCrumb() { - $bc = array(); - drupal_set_breadcrumb($bc); + $bc = []; + backdrop_set_breadcrumb($bc); } /** @@ -208,11 +208,11 @@ public function addHTMLHead($header) { static $count = 0; if (!empty($header)) { $key = 'civi_' . ++$count; - $data = array( + $data = [ '#type' => 'markup', '#markup' => $header, - ); - drupal_add_html_head($data, $key); + ]; + backdrop_add_html_head($data, $key); } } @@ -220,7 +220,7 @@ public function addHTMLHead($header) { * @inheritDoc */ public function addScriptUrl($url, $region) { - $params = array('group' => JS_LIBRARY, 'weight' => 10); + $params = ['group' => JS_LIBRARY, 'weight' => 10]; switch ($region) { case 'html-header': case 'page-footer': @@ -230,9 +230,9 @@ public function addScriptUrl($url, $region) { default: return FALSE; } - // If the path is within the drupal directory we can use the more efficient 'file' setting + // If the path is within the Backdrop directory we can use the more efficient 'file' setting $params['type'] = $this->formatResourceUrl($url) ? 'file' : 'external'; - drupal_add_js($url, $params); + backdrop_add_js($url, $params); return TRUE; } @@ -240,7 +240,7 @@ public function addScriptUrl($url, $region) { * @inheritDoc */ public function addScript($code, $region) { - $params = array('type' => 'inline', 'group' => JS_LIBRARY, 'weight' => 10); + $params = ['type' => 'inline', 'group' => JS_LIBRARY, 'weight' => 10]; switch ($region) { case 'html-header': case 'page-footer': @@ -250,7 +250,7 @@ public function addScript($code, $region) { default: return FALSE; } - drupal_add_js($code, $params); + backdrop_add_js($code, $params); return TRUE; } @@ -261,10 +261,10 @@ public function addStyleUrl($url, $region) { if ($region != 'html-header') { return FALSE; } - $params = array(); - // If the path is within the drupal directory we can use the more efficient 'file' setting + $params = []; + // If the path is within the Backdrop directory we can use the more efficient 'file' setting $params['type'] = $this->formatResourceUrl($url) ? 'file' : 'external'; - drupal_add_css($url, $params); + backdrop_add_css($url, $params); return TRUE; } @@ -275,8 +275,8 @@ public function addStyle($code, $region) { if ($region != 'html-header') { return FALSE; } - $params = array('type' => 'inline'); - drupal_add_css($code, $params); + $params = ['type' => 'inline']; + backdrop_add_css($code, $params); return TRUE; } @@ -288,6 +288,11 @@ public function mapConfigToSSL() { $base_url = str_replace('http://', 'https://', $base_url); } + /** + * Get the name of the table that stores the user details. + * + * @return string + */ protected function getUsersTableName() { $userFrameworkUsersTableName = Civi::settings()->get('userFrameworkUsersTableName'); if (empty($userFrameworkUsersTableName)) { @@ -309,12 +314,12 @@ public function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realP $account = $userUid = $userMail = NULL; if ($loadCMSBootstrap) { - $bootStrapParams = array(); + $bootStrapParams = []; if ($name && $password) { - $bootStrapParams = array( + $bootStrapParams = [ 'name' => $name, 'pass' => $password, - ); + ]; } CRM_Utils_System::loadBootStrap($bootStrapParams, TRUE, TRUE, $realPath); @@ -326,16 +331,20 @@ public function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realP } else { // CRM-8638 - // SOAP cannot load drupal bootstrap and hence we do it the old way + // SOAP cannot load Backdrop bootstrap and hence we do it the old way // Contact CiviSMTP folks if we run into issues with this :) $cmsPath = $this->cmsRootPath(); - + if (!defined('BACKDROP_ROOT')) { + define(BACKDROP_ROOT, $cmsPath); + } require_once "$cmsPath/core/includes/bootstrap.inc"; require_once "$cmsPath/core/includes/password.inc"; $strtolower = function_exists('mb_strtolower') ? 'mb_strtolower' : 'strtolower'; $name = $dbBackdrop->escapeSimple($strtolower($name)); $userFrameworkUsersTableName = $this->getUsersTableName(); + + // LOWER in query below roughly translates to 'hurt my database without deriving any benefit' See CRM-19811. $sql = " SELECT u.* FROM {$userFrameworkUsersTableName} u @@ -364,7 +373,7 @@ public function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realP if (!$contactID) { return FALSE; } - return array($contactID, $userUid, mt_rand()); + return [$contactID, $userUid, mt_rand()]; } return FALSE; } @@ -399,16 +408,36 @@ public function loadUser($username) { * @param array $params * The array of form values submitted by the user. */ - public function userLoginFinalize($params = array()) { + public function userLoginFinalize($params = []) { user_login_finalize($params); } + /** + * @inheritDoc + */ + public function isUserRegistrationPermitted() { + if (config_get('system.core', 'user_register') == 'admin_only') { + return FALSE; + } + return TRUE; + } + + /** + * @inheritDoc + */ + public function isPasswordUserGenerated() { + if (config_get('system.core', 'user_email_verification') == TRUE) { + return FALSE; + } + return TRUE; + } + /** * @inheritDoc */ public function getUFLocale() { - // return CiviCRM’s xx_YY locale that either matches Drupal’s Chinese locale - // (for CRM-6281), Drupal’s xx_YY or is retrieved based on Drupal’s xx + // return CiviCRM’s xx_YY locale that either matches Backdrop’s Chinese locale + // (for CRM-6281), Backdrop’s xx_YY or is retrieved based on Backdrop’s xx // sometimes for CLI based on order called, this might not be set and/or empty global $language; @@ -498,7 +527,7 @@ public function getDefaultBlockLocation() { * * @return bool */ - public function loadBootStrap($params = array(), $loadUser = TRUE, $throwError = TRUE, $realPath = NULL) { + public function loadBootStrap($params = [], $loadUser = TRUE, $throwError = TRUE, $realPath = NULL) { $cmsPath = $this->cmsRootPath($realPath); if (!file_exists("$cmsPath/core/includes/bootstrap.inc")) { @@ -508,11 +537,11 @@ public function loadBootStrap($params = array(), $loadUser = TRUE, $throwError = } return FALSE; } - // load drupal bootstrap + // load Backdrop bootstrap chdir($cmsPath); define('BACKDROP_ROOT', $cmsPath); - // For drupal multi-site CRM-11313 + // For Backdrop multi-site CRM-11313 if ($realPath && strpos($realPath, 'sites/all/modules/') === FALSE) { preg_match('@sites/([^/]*)/modules@s', $realPath, $matches); if (!empty($matches[1])) { @@ -520,6 +549,7 @@ public function loadBootStrap($params = array(), $loadUser = TRUE, $throwError = } } require_once "$cmsPath/core/includes/bootstrap.inc"; + require_once "$cmsPath/core/includes/config.inc"; backdrop_bootstrap(BACKDROP_BOOTSTRAP_FULL); // Explicitly setting error reporting, since we cannot handle Backdrop @@ -599,6 +629,11 @@ public function loadBootStrap($params = array(), $loadUser = TRUE, $throwError = * @inheritDoc */ public function cmsRootPath($scriptFilename = NULL) { + global $civicrm_paths; + if (!empty($civicrm_paths['cms.root']['path'])) { + return $civicrm_paths['cms.root']['path']; + } + $cmsRoot = NULL; $valid = NULL; @@ -659,7 +694,7 @@ public function getLoggedInUfID() { user_is_logged_in() && function_exists('user_uid_optional_to_arg') ) { - $ufID = user_uid_optional_to_arg(array()); + $ufID = user_uid_optional_to_arg([]); } return $ufID; @@ -740,7 +775,7 @@ public function replacePermission($oldPerm, $newPerms) { $roles = user_roles(FALSE, $oldPerm); if (!empty($roles)) { foreach (array_keys($roles) as $rid) { - user_role_revoke_permissions($rid, array($oldPerm)); + user_role_revoke_permissions($rid, [$oldPerm]); user_role_grant_permissions($rid, $newPerms); } } @@ -761,11 +796,11 @@ public function og_membership_create($ogID, $userID) { // @TODO Find more solid way to check - try system_get_info('module', 'og'). // // Also, since we don't know how to get the entity type of the // group, we'll assume it's 'node' - og_group('node', $ogID, array('entity' => user_load($userID))); + og_group('node', $ogID, ['entity' => user_load($userID)]); } else { // Works for the OG 7.x-1.x branch - og_group($ogID, array('entity' => user_load($userID))); + og_group($ogID, ['entity' => user_load($userID)]); } } @@ -850,11 +885,11 @@ public function synchronizeUsers() { } } - return array( + return [ 'contactCount' => $contactCount, 'contactMatching' => $contactMatching, 'contactCreated' => $contactCreated, - ); + ]; } /** @@ -875,7 +910,143 @@ public function clearResourceCache() { */ public function permissionEmails($permissionName) { // FIXME!!!! - return array(); + return []; + } + + /** + * @inheritdoc + */ + public function getDefaultFileStorage() { + $config = CRM_Core_Config::singleton(); + $baseURL = CRM_Utils_System::languageNegotiationURL($config->userFrameworkBaseURL, FALSE, TRUE); + + $siteName = $this->parseBackdropSiteNameFromRequest('/files/civicrm'); + if ($siteName) { + $filesURL = $baseURL . "sites/$siteName/files/civicrm/"; + } + else { + $filesURL = $baseURL . "files/civicrm/"; + } + + return [ + 'url' => $filesURL, + 'path' => CRM_Utils_File::baseFilePath(), + ]; + } + + /** + * Check if a resource url is within the Backdrop directory and format appropriately. + * + * @param $url (reference) + * + * @return bool + * TRUE for internal paths, FALSE for external. The backdrop_add_js fn is able to add js more + * efficiently if it is known to be in the Backdrop site + */ + public function formatResourceUrl(&$url) { + $internal = FALSE; + $base = CRM_Core_Config::singleton()->resourceBase; + global $base_url; + // Handle absolute urls + // compares $url (which is some unknown/untrusted value from a third-party dev) to the CMS's base url (which is independent of civi's url) + // to see if the url is within our Backdrop dir, if it is we are able to treated it as an internal url + if (strpos($url, $base_url) === 0) { + $file = trim(str_replace($base_url, '', $url), '/'); + // CRM-18130: Custom CSS URL not working if aliased or rewritten + if (file_exists(BACKDROP_ROOT . $file)) { + $url = $file; + $internal = TRUE; + } + } + // Handle relative urls that are within the CiviCRM module directory + elseif (strpos($url, $base) === 0) { + $internal = TRUE; + $url = $this->appendCoreDirectoryToResourceBase(dirname(backdrop_get_path('module', 'civicrm')) . '/') . trim(substr($url, strlen($base)), '/'); + } + // Strip query string + $q = strpos($url, '?'); + if ($q && $internal) { + $url = substr($url, 0, $q); + } + return $internal; + } + + /** + * @inheritDoc + */ + public function setMessage($message) { + backdrop_set_message($message); + } + + /** + * @inheritDoc + */ + public function permissionDenied() { + backdrop_access_denied(); + } + + /** + * @inheritDoc + */ + public function flush() { + backdrop_flush_all_caches(); + } + + /** + * Determine if Backdrop multi-site applies to the current request -- and, + * specifically, determine the name of the multisite folder. + * + * @param string $flagFile + * Check if $flagFile exists inside the site dir. + * @return null|string + * string, e.g. `bar.example.com` if using multisite. + * NULL if using the default site. + */ + private function parseBackdropSiteNameFromRequest($flagFile = '') { + $phpSelf = array_key_exists('PHP_SELF', $_SERVER) ? $_SERVER['PHP_SELF'] : ''; + $httpHost = array_key_exists('HTTP_HOST', $_SERVER) ? $_SERVER['HTTP_HOST'] : ''; + if (empty($httpHost)) { + $httpHost = parse_url(CIVICRM_UF_BASEURL, PHP_URL_HOST); + if (parse_url(CIVICRM_UF_BASEURL, PHP_URL_PORT)) { + $httpHost .= ':' . parse_url(CIVICRM_UF_BASEURL, PHP_URL_PORT); + } + } + + $confdir = $this->cmsRootPath() . '/sites'; + + if (file_exists($confdir . "/sites.php")) { + include $confdir . "/sites.php"; + } + else { + $sites = []; + } + + $uri = explode('/', $phpSelf); + $server = explode('.', implode('.', array_reverse(explode(':', rtrim($httpHost, '.'))))); + for ($i = count($uri) - 1; $i > 0; $i--) { + for ($j = count($server); $j > 0; $j--) { + $dir = implode('.', array_slice($server, -$j)) . implode('.', array_slice($uri, 0, $i)); + if (file_exists("$confdir/$dir" . $flagFile)) { + \Civi::$statics[__CLASS__]['drupalSiteName'] = $dir; + return \Civi::$statics[__CLASS__]['drupalSiteName']; + } + // check for alias + if (isset($sites[$dir]) && file_exists("$confdir/{$sites[$dir]}" . $flagFile)) { + \Civi::$statics[__CLASS__]['drupalSiteName'] = $sites[$dir]; + return \Civi::$statics[__CLASS__]['drupalSiteName']; + } + } + } + } + + /** + * Append Backdrop CSS and JS to coreResourcesList. + * + * @param array $list + */ + public function appendCoreResources(&$list) { + $list[] = 'css/backdrop.css'; + $list[] = 'js/crm.backdrop.js'; } } diff --git a/CRM/Utils/System/Base.php b/CRM/Utils/System/Base.php index b3f8dcb4435b..bf6830d8e49f 100644 --- a/CRM/Utils/System/Base.php +++ b/CRM/Utils/System/Base.php @@ -11,44 +11,44 @@ abstract class CRM_Utils_System_Base { * The correct method is to have functions on the UF classes for all UF specific * functions and leave the codebase oblivious to the type of CMS * - * @deprecated * @var bool + * @deprecated * TRUE, if the CMS is Drupal. */ - var $is_drupal = FALSE; + public $is_drupal = FALSE; /** * Deprecated property to check if this is a joomla install. The correct method is to have functions on the UF classes for all UF specific * functions and leave the codebase oblivious to the type of CMS * - * @deprecated * @var bool + * @deprecated * TRUE, if the CMS is Joomla!. */ - var $is_joomla = FALSE; + public $is_joomla = FALSE; /** * deprecated property to check if this is a wordpress install. The correct method is to have functions on the UF classes for all UF specific * functions and leave the codebase oblivious to the type of CMS * - * @deprecated * @var bool + * @deprecated * TRUE, if the CMS is WordPress. */ - var $is_wordpress = FALSE; + public $is_wordpress = FALSE; /** * Does this CMS / UF support a CMS specific logging mechanism? - * @todo - we should think about offering up logging mechanisms in a way that is also extensible by extensions * @var bool + * @todo - we should think about offering up logging mechanisms in a way that is also extensible by extensions */ - var $supports_UF_Logging = FALSE; + public $supports_UF_Logging = FALSE; /** * @var bool * TRUE, if the CMS allows CMS forms to be extended by hooks. */ - var $supports_form_extensions = FALSE; + public $supports_form_extensions = FALSE; public function initialize() { if (\CRM_Utils_System::isSSL()) { @@ -56,6 +56,8 @@ public function initialize() { } } + abstract public function loadBootStrap($params = [], $loadUser = TRUE, $throwError = TRUE, $realPath = NULL); + /** * Append an additional breadcrumb tag to the existing breadcrumb. * @@ -244,7 +246,7 @@ function_exists('theme') && if ($region = CRM_Core_Region::instance('html-header', FALSE)) { CRM_Utils_System::addHTMLHead($region->render('')); } - print theme('maintenance_page', array('content' => $content)); + print theme('maintenance_page', ['content' => $content]); exit(); } // TODO: Figure out why D7 returns but everyone else prints @@ -287,6 +289,11 @@ public function getDefaultBlockLocation() { return 'left'; } + /** + * Get the absolute path to the site's base url. + * + * @return bool|mixed|string + */ public function getAbsoluteBaseURL() { if (!defined('CIVICRM_UF_BASEURL')) { return FALSE; @@ -304,6 +311,11 @@ public function getAbsoluteBaseURL() { return $url; } + /** + * Get the relative path to the sites base url. + * + * @return bool + */ public function getRelativeBaseURL() { $absoluteBaseURL = $this->getAbsoluteBaseURL(); if ($absoluteBaseURL === FALSE) { @@ -385,6 +397,24 @@ public function isUserLoggedIn() { return FALSE; } + /** + * Check if user registration is permitted. + * + * @return bool + */ + public function isUserRegistrationPermitted() { + return FALSE; + } + + /** + * Check if user can create passwords or is initially assigned a system-generated one. + * + * @return bool + */ + public function isPasswordUserGenerated() { + return FALSE; + } + /** * Get user login URL for hosting CMS (method declared in each CMS system class) * @@ -394,7 +424,7 @@ public function isUserLoggedIn() { * @return string * loginURL for the current CMS */ - public abstract function getLoginURL($destination = ''); + abstract public function getLoginURL($destination = ''); /** * Get the login destination string. @@ -423,6 +453,13 @@ public function getUfId($username) { throw new CRM_Core_Exception("Not implemented: {$className}->getUfId"); } + /** + * Set the localisation from the user framework. + * + * @param string $civicrm_language + * + * @return bool + */ public function setUFLocale($civicrm_language) { return TRUE; } @@ -546,7 +583,7 @@ public function setTitle($title, $pageTitle = NULL) { public function getDefaultSiteSettings($dir) { $config = CRM_Core_Config::singleton(); $url = $config->userFrameworkBaseURL; - return array($url, NULL, NULL); + return [$url, NULL, NULL]; } /** @@ -576,15 +613,6 @@ public function getDefaultFileStorage() { $tempURL = str_replace("/administrator/", "/", $baseURL); $filesURL = $tempURL . "media/civicrm/"; } - elseif ($this->is_drupal) { - $siteName = $config->userSystem->parseDrupalSiteName($civicrm_root); - if ($siteName) { - $filesURL = $baseURL . "sites/$siteName/files/civicrm/"; - } - else { - $filesURL = $baseURL . "sites/default/files/civicrm/"; - } - } elseif ($config->userFramework == 'UnitTests') { $filesURL = $baseURL . "sites/default/files/civicrm/"; } @@ -592,10 +620,10 @@ public function getDefaultFileStorage() { throw new CRM_Core_Exception("Failed to locate default file storage ($config->userFramework)"); } - return array( + return [ 'url' => $filesURL, 'path' => CRM_Utils_File::baseFilePath(), - ); + ]; } /** @@ -642,7 +670,7 @@ public function getCiviSourceStorage() { str_replace('\\', '/', $civicrm_root) ); - $siteName = $config->userSystem->parseDrupalSiteName($civicrm_root); + $siteName = $config->userSystem->parseDrupalSiteNameFromRoot($civicrm_root); if ($siteName) { $civicrmDirName = trim(basename($civicrm_root)); $userFrameworkResourceURL = $baseURL . "sites/$siteName/modules/$civicrmDirName/"; @@ -652,10 +680,10 @@ public function getCiviSourceStorage() { $userFrameworkResourceURL = NULL; } - return array( + return [ 'url' => CRM_Utils_File::addTrailingSlash($userFrameworkResourceURL), 'path' => CRM_Utils_File::addTrailingSlash($civicrm_root), - ); + ]; } /** @@ -668,7 +696,7 @@ public function getCiviSourceStorage() { * * FIXME: Document values accepted/required by $params */ - public function userLoginFinalize($params = array()) { + public function userLoginFinalize($params = []) { } /** @@ -682,7 +710,6 @@ public function setMySQLTimeZone() { } } - /** * Get timezone from CMS. * @@ -699,6 +726,11 @@ public function getTimeZoneOffset() { $dateTime = new DateTime("now", $tzObj); $tz = $tzObj->getOffset($dateTime); + if ($tz === 0) { + // CRM-21422 + return '+00:00'; + } + if (empty($tz)) { return FALSE; } @@ -758,14 +790,14 @@ public function getUserIDFromUserObject($user) { * - name (ie the system user name. */ public function getUser($contactID) { - $ufMatch = civicrm_api3('UFMatch', 'getsingle', array( + $ufMatch = civicrm_api3('UFMatch', 'getsingle', [ 'contact_id' => $contactID, 'domain_id' => CRM_Core_Config::domainID(), - )); - return array( + ]); + return [ 'id' => $ufMatch['uf_id'], 'name' => $ufMatch['uf_name'], - ); + ]; } /** @@ -838,7 +870,7 @@ public function getBestUFUniqueIdentifier($user = NULL) { * [CRM_Core_Module] */ public function getModules() { - return array(); + return []; } /** @@ -903,7 +935,7 @@ public function setHttpHeader($name, $value) { */ public function synchronizeUsers() { throw new Exception('CMS user creation not supported for this framework'); - return array(); + return []; } } diff --git a/CRM/Utils/System/Drupal.php b/CRM/Utils/System/Drupal.php index 8beb99405cd3..ade4ff8da034 100644 --- a/CRM/Utils/System/Drupal.php +++ b/CRM/Utils/System/Drupal.php @@ -1,9 +1,9 @@ $params['cms_name'], 'mail' => $params[$mail], 'op' => 'Create new account', - ); + ]; $admin = user_access('administer users'); if (!variable_get('user_email_verification', TRUE) || $admin) { - $form_state['input']['pass'] = array('pass1' => $params['cms_pass'], 'pass2' => $params['cms_pass']); + $form_state['input']['pass'] = ['pass1' => $params['cms_pass'], 'pass2' => $params['cms_pass']]; } if (!empty($params['notify'])) { @@ -61,7 +61,7 @@ public function createUser(&$params, $mail) { $form_state['programmed'] = TRUE; $form_state['complete form'] = FALSE; $form_state['method'] = 'post'; - $form_state['build_info']['args'] = array(); + $form_state['build_info']['args'] = []; /* * if we want to submit this form more than once in a process (e.g. create more than one user) * we must force it to validate each time for this form. Otherwise it will not validate @@ -76,7 +76,7 @@ public function createUser(&$params, $mail) { $form = drupal_retrieve_form('user_register_form', $form_state); $form_state['process_input'] = 1; $form_state['submitted'] = 1; - $form['#array_parents'] = array(); + $form['#array_parents'] = []; $form['#tree'] = FALSE; drupal_process_form('user_register_form', $form, $form_state); @@ -96,7 +96,7 @@ public function updateCMSName($ufID, $ufName) { if (function_exists('user_load')) { $user = user_load($ufID); if ($user->mail != $ufName) { - user_save($user, array('mail' => $ufName)); + user_save($user, ['mail' => $ufName]); $user = user_load($ufID); } } @@ -131,10 +131,10 @@ public static function checkUserNameEmailExists(&$params, &$errors, $emailName = else { $uid = db_query( "SELECT uid FROM {users} WHERE name = :name", - array(':name' => $params['name']) + [':name' => $params['name']] )->fetchField(); if ((bool) $uid) { - $errors['cms_name'] = ts('The username %1 is already taken. Please select another username.', array(1 => $params['name'])); + $errors['cms_name'] = ts('The username %1 is already taken. Please select another username.', [1 => $params['name']]); } } } @@ -146,12 +146,12 @@ public static function checkUserNameEmailExists(&$params, &$errors, $emailName = else { $uid = db_query( "SELECT uid FROM {users} WHERE mail = :mail", - array(':mail' => $params['mail']) + [':mail' => $params['mail']] )->fetchField(); if ((bool) $uid) { $resetUrl = url('user/password'); $errors[$emailName] = ts('The email address %1 already has an account associated with it. Have you forgotten your password?', - array(1 => $params['mail'], 2 => $resetUrl) + [1 => $params['mail'], 2 => $resetUrl] ); } } @@ -162,8 +162,8 @@ public static function checkUserNameEmailExists(&$params, &$errors, $emailName = * @inheritDoc */ public function getLoginURL($destination = '') { - $query = $destination ? array('destination' => $destination) : array(); - return url('user', array('query' => $query), TRUE); + $query = $destination ? ['destination' => $destination] : NULL; + return CRM_Utils_System::url('user', $query, TRUE); } /** @@ -188,7 +188,7 @@ public function appendBreadCrumb($breadCrumbs) { if (is_array($breadCrumbs)) { foreach ($breadCrumbs as $crumbs) { if (stripos($crumbs['url'], 'id%%')) { - $args = array('cid', 'mid'); + $args = ['cid', 'mid']; foreach ($args as $a) { $val = CRM_Utils_Request::retrieve($a, 'Positive', CRM_Core_DAO::$_nullObject, FALSE, NULL, $_GET @@ -208,7 +208,7 @@ public function appendBreadCrumb($breadCrumbs) { * @inheritDoc */ public function resetBreadCrumb() { - $bc = array(); + $bc = []; drupal_set_breadcrumb($bc); } @@ -219,10 +219,10 @@ public function addHTMLHead($header) { static $count = 0; if (!empty($header)) { $key = 'civi_' . ++$count; - $data = array( + $data = [ '#type' => 'markup', '#markup' => $header, - ); + ]; drupal_add_html_head($data, $key); } } @@ -231,7 +231,7 @@ public function addHTMLHead($header) { * @inheritDoc */ public function addScriptUrl($url, $region) { - $params = array('group' => JS_LIBRARY, 'weight' => 10); + $params = ['group' => JS_LIBRARY, 'weight' => 10]; switch ($region) { case 'html-header': case 'page-footer': @@ -251,7 +251,7 @@ public function addScriptUrl($url, $region) { * @inheritDoc */ public function addScript($code, $region) { - $params = array('type' => 'inline', 'group' => JS_LIBRARY, 'weight' => 10); + $params = ['type' => 'inline', 'group' => JS_LIBRARY, 'weight' => 10]; switch ($region) { case 'html-header': case 'page-footer': @@ -272,7 +272,7 @@ public function addStyleUrl($url, $region) { if ($region != 'html-header') { return FALSE; } - $params = array(); + $params = []; // If the path is within the drupal directory we can use the more efficient 'file' setting $params['type'] = $this->formatResourceUrl($url) ? 'file' : 'external'; drupal_add_css($url, $params); @@ -286,7 +286,7 @@ public function addStyle($code, $region) { if ($region != 'html-header') { return FALSE; } - $params = array('type' => 'inline'); + $params = ['type' => 'inline']; drupal_add_css($code, $params); return TRUE; } @@ -299,6 +299,11 @@ public function mapConfigToSSL() { $base_url = str_replace('http://', 'https://', $base_url); } + /** + * Get the name of the users table. + * + * @return string + */ protected function getUsersTableName() { $userFrameworkUsersTableName = Civi::settings()->get('userFrameworkUsersTableName'); if (empty($userFrameworkUsersTableName)) { @@ -322,12 +327,12 @@ public function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realP $account = $userUid = $userMail = NULL; if ($loadCMSBootstrap) { - $bootStrapParams = array(); + $bootStrapParams = []; if ($name && $password) { - $bootStrapParams = array( + $bootStrapParams = [ 'name' => $name, 'pass' => $password, - ); + ]; } CRM_Utils_System::loadBootStrap($bootStrapParams, TRUE, TRUE, $realPath); @@ -349,6 +354,8 @@ public function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realP $strtolower = function_exists('mb_strtolower') ? 'mb_strtolower' : 'strtolower'; $name = $dbDrupal->escapeSimple($strtolower($name)); $userFrameworkUsersTableName = $this->getUsersTableName(); + + // LOWER in query below roughly translates to 'hurt my database without deriving any benefit' See CRM-19811. $sql = " SELECT u.* FROM {$userFrameworkUsersTableName} u @@ -377,7 +384,7 @@ public function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realP if (!$contactID) { return FALSE; } - return array($contactID, $userUid, mt_rand()); + return [$contactID, $userUid, mt_rand()]; } return FALSE; } @@ -413,7 +420,7 @@ public function loadUser($username) { * * FIXME: Document values accepted/required by $params */ - public function userLoginFinalize($params = array()) { + public function userLoginFinalize($params = []) { user_login_finalize($params); } @@ -461,14 +468,13 @@ public function getDefaultBlockLocation() { * * @return bool */ - public function loadBootStrap($params = array(), $loadUser = TRUE, $throwError = TRUE, $realPath = NULL) { + public function loadBootStrap($params = [], $loadUser = TRUE, $throwError = TRUE, $realPath = NULL) { //take the cms root path. $cmsPath = $this->cmsRootPath($realPath); if (!file_exists("$cmsPath/includes/bootstrap.inc")) { if ($throwError) { - echo '
    Sorry, could not locate bootstrap.inc\n'; - exit(); + throw new Exception('Sorry, could not locate bootstrap.inc'); } return FALSE; } @@ -488,11 +494,18 @@ public function loadBootStrap($params = array(), $loadUser = TRUE, $throwError = @drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); // explicitly setting error reporting, since we cannot handle drupal related notices + // @todo 1 = E_ERROR, but more to the point setting error reporting deep in code + // causes grief with debugging scripts error_reporting(1); - if (!function_exists('module_exists') || !module_exists('civicrm')) { + if (!function_exists('module_exists')) { + if ($throwError) { + throw new Exception('Sorry, could not load drupal bootstrap.'); + } + return FALSE; + } + if (!module_exists('civicrm')) { if ($throwError) { - echo '
    Sorry, could not load drupal bootstrap.'; - exit(); + throw new Exception('Sorry, drupal cannot find CiviCRM'); } return FALSE; } @@ -524,8 +537,7 @@ public function loadBootStrap($params = array(), $loadUser = TRUE, $throwError = $uid = user_authenticate($name, $pass); if (!$uid) { if ($throwError) { - echo '
    Sorry, unrecognized username or password.'; - exit(); + throw new Exception('Sorry, unrecognized username or password.'); } return FALSE; } @@ -542,8 +554,7 @@ public function loadBootStrap($params = array(), $loadUser = TRUE, $throwError = } if ($throwError) { - echo '
    Sorry, can not load CMS user account.'; - exit(); + throw new Exception('Sorry, can not load CMS user account.'); } // CRM-6948: When using loadBootStrap, it's implicit that CiviCRM has already loaded its settings @@ -579,6 +590,12 @@ public function cmsRootPath($scriptFilename = NULL) { // drush anyway takes care of multisite install etc return drush_get_context('DRUSH_DRUPAL_ROOT'); } + + global $civicrm_paths; + if (!empty($civicrm_paths['cms.root']['path'])) { + return $civicrm_paths['cms.root']['path']; + } + // CRM-7582 $pathVars = explode('/', str_replace('//', '/', @@ -590,7 +607,7 @@ public function cmsRootPath($scriptFilename = NULL) { //need to get back for windows. $firstVar = array_shift($pathVars); - //lets remove sript name to reduce one iteration. + // Remove the script name to remove an necessary iteration of the loop. array_pop($pathVars); // CRM-7429 -- do check for uppermost 'includes' dir, which would @@ -631,7 +648,7 @@ public function getLoggedInUfID() { user_is_logged_in() && function_exists('user_uid_optional_to_arg') ) { - $ufID = user_uid_optional_to_arg(array()); + $ufID = user_uid_optional_to_arg([]); } return $ufID; @@ -714,7 +731,7 @@ public function replacePermission($oldPerm, $newPerms) { $roles = user_roles(FALSE, $oldPerm); if (!empty($roles)) { foreach (array_keys($roles) as $rid) { - user_role_revoke_permissions($rid, array($oldPerm)); + user_role_revoke_permissions($rid, [$oldPerm]); user_role_grant_permissions($rid, $newPerms); } } @@ -735,11 +752,11 @@ public function og_membership_create($ogID, $drupalID) { // @TODO Find more solid way to check - try system_get_info('module', 'og'). // // Also, since we don't know how to get the entity type of the // group, we'll assume it's 'node' - og_group('node', $ogID, array('entity' => user_load($drupalID))); + og_group('node', $ogID, ['entity' => user_load($drupalID)]); } else { // Works for the OG 7.x-1.x branch - og_group($ogID, array('entity' => user_load($drupalID))); + og_group($ogID, ['entity' => user_load($drupalID)]); } } @@ -824,11 +841,24 @@ public function synchronizeUsers() { } } - return array( + return [ 'contactCount' => $contactCount, 'contactMatching' => $contactMatching, 'contactCreated' => $contactCreated, - ); + ]; + } + + /** + * Commit the session before exiting. + * Similar to drupal_exit(). + */ + public function onCiviExit() { + if (function_exists('module_invoke_all')) { + if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') { + module_invoke_all('exit'); + } + drupal_session_commit(); + } } } diff --git a/CRM/Utils/System/Drupal6.php b/CRM/Utils/System/Drupal6.php index 435e70b28216..402452fa3858 100644 --- a/CRM/Utils/System/Drupal6.php +++ b/CRM/Utils/System/Drupal6.php @@ -1,9 +1,9 @@ $params['cms_name'], 'mail' => $params[$mail], 'op' => 'Create new account', - ); + ]; $admin = user_access('administer users'); if (!variable_get('user_email_verification', TRUE) || $admin) { @@ -122,10 +122,10 @@ public function createUser(&$params, $mail) { public function updateCMSName($ufID, $ufName) { // CRM-5555 if (function_exists('user_load')) { - $user = user_load(array('uid' => $ufID)); + $user = user_load(['uid' => $ufID]); if ($user->mail != $ufName) { - user_save($user, array('mail' => $ufName)); - $user = user_load(array('uid' => $ufID)); + user_save($user, ['mail' => $ufName]); + $user = user_load(['uid' => $ufID]); } } } @@ -165,6 +165,7 @@ public function checkUserNameEmailExists(&$params, &$errors, $emailName = 'email $errors['cms_name'] = $nameError; } + // LOWER in query below roughly translates to 'hurt my database without deriving any benefit' See CRM-19811. $sql = " SELECT name, mail FROM {users} @@ -184,18 +185,18 @@ public function checkUserNameEmailExists(&$params, &$errors, $emailName = 'email $dbEmail = CRM_Utils_Array::value('mail', $row); if (strtolower($dbName) == strtolower($name)) { $errors['cms_name'] = ts('The username %1 is already taken. Please select another username.', - array(1 => $name) + [1 => $name] ); } if (strtolower($dbEmail) == strtolower($email)) { if (empty($email)) { $errors[$emailName] = ts('You cannot create an email account for a contact with no email', - array(1 => $email) + [1 => $email] ); } else { $errors[$emailName] = ts('This email %1 already has an account associated with it. Please select another email.', - array(1 => $email) + [1 => $email] ); } } @@ -224,7 +225,7 @@ public function appendBreadCrumb($breadCrumbs) { if (is_array($breadCrumbs)) { foreach ($breadCrumbs as $crumbs) { if (stripos($crumbs['url'], 'id%%')) { - $args = array('cid', 'mid'); + $args = ['cid', 'mid']; foreach ($args as $a) { $val = CRM_Utils_Request::retrieve($a, 'Positive', CRM_Core_DAO::$_nullObject, FALSE, NULL, $_GET @@ -244,7 +245,7 @@ public function appendBreadCrumb($breadCrumbs) { * @inheritDoc */ public function resetBreadCrumb() { - $bc = array(); + $bc = []; drupal_set_breadcrumb($bc); } @@ -287,6 +288,11 @@ public function mapConfigToSSL() { $base_url = str_replace('http://', 'https://', $base_url); } + /** + * Get the name of the table that stores the user details. + * + * @return string + */ protected function getUsersTableName() { $userFrameworkUsersTableName = Civi::settings()->get('userFrameworkUsersTableName'); if (empty($userFrameworkUsersTableName)) { @@ -333,16 +339,16 @@ public function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realP else { //success if ($loadCMSBootstrap) { - $bootStrapParams = array(); + $bootStrapParams = []; if ($name && $password) { - $bootStrapParams = array( + $bootStrapParams = [ 'name' => $name, 'pass' => $password, - ); + ]; } CRM_Utils_System::loadBootStrap($bootStrapParams, TRUE, TRUE, $realPath); } - return array($contactID, $row['uid'], mt_rand()); + return [$contactID, $row['uid'], mt_rand()]; } } return FALSE; @@ -353,7 +359,7 @@ public function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realP */ public function loadUser($username) { global $user; - $user = user_load(array('name' => $username)); + $user = user_load(['name' => $username]); if (empty($user->uid)) { return FALSE; } @@ -377,7 +383,7 @@ public function loadUser($username) { * * FIXME: Document values accepted/required by $params */ - public function userLoginFinalize($params = array()) { + public function userLoginFinalize($params = []) { user_authenticate_finalize($params); } @@ -388,7 +394,7 @@ public function userLoginFinalize($params = array()) { * @return int|NULL */ public function getUfId($username) { - $user = user_load(array('name' => $username)); + $user = user_load(['name' => $username]); if (empty($user->uid)) { return NULL; } @@ -416,7 +422,7 @@ public function logout() { * * @return bool */ - public function loadBootStrap($params = array(), $loadUser = TRUE, $throwError = TRUE, $realPath = NULL) { + public function loadBootStrap($params = [], $loadUser = TRUE, $throwError = TRUE, $realPath = NULL) { //take the cms root path. $cmsPath = $this->cmsRootPath($realPath); @@ -477,7 +483,7 @@ public function loadBootStrap($params = array(), $loadUser = TRUE, $throwError = $pass = CRM_Utils_Array::value('pass', $params, FALSE) ? $params['pass'] : trim(CRM_Utils_Array::value('pass', $_REQUEST)); if ($name) { - $user = user_authenticate(array('name' => $name, 'pass' => $pass)); + $user = user_authenticate(['name' => $name, 'pass' => $pass]); if (!$user->uid) { if ($throwError) { echo '
    Sorry, unrecognized username or password.'; @@ -537,6 +543,12 @@ public function cmsRootPath($scriptFilename = NULL) { // drush anyway takes care of multisite install etc return drush_get_context('DRUSH_DRUPAL_ROOT'); } + + global $civicrm_paths; + if (!empty($civicrm_paths['cms.root']['path'])) { + return $civicrm_paths['cms.root']['path']; + } + // CRM-7582 $pathVars = explode('/', str_replace('//', '/', @@ -589,7 +601,7 @@ public function getLoggedInUfID() { user_is_logged_in() && function_exists('user_uid_optional_to_arg') ) { - $ufID = user_uid_optional_to_arg(array()); + $ufID = user_uid_optional_to_arg([]); } return $ufID; @@ -616,10 +628,10 @@ public function languageNegotiationURL($url, $addLanguagePart = TRUE, $removeLan //url prefix / path. if (isset($language->prefix) && $language->prefix && - in_array($mode, array( + in_array($mode, [ LANGUAGE_NEGOTIATION_PATH, LANGUAGE_NEGOTIATION_PATH_DEFAULT, - )) + ]) ) { if ($addLanguagePart) { @@ -688,7 +700,7 @@ public function replacePermission($oldPerm, $newPerms) { * @inheritDoc */ public function getModules() { - $result = array(); + $result = []; $q = db_query('SELECT name, status FROM {system} WHERE type = \'module\' AND schema_version <> -1'); while ($row = db_fetch_object($q)) { $result[] = new CRM_Core_Module('drupal.' . $row->name, ($row->status == 1) ? TRUE : FALSE); @@ -719,7 +731,7 @@ public function getLoginURL($destination = '') { * Drupal User ID. */ public function og_membership_create($ogID, $drupalID) { - og_save_subscription($ogID, $drupalID, array('is_active' => 1)); + og_save_subscription($ogID, $drupalID, ['is_active' => 1]); } /** @@ -746,9 +758,18 @@ public function getTimeZoneString() { else { $timezone = variable_get('date_default_timezone', NULL); } + + // Retrieved timezone will be represented as GMT offset in seconds but, according + // to the doc for the overridden method, ought to be returned as a region string + // (e.g., America/Havana). + if (strlen($timezone)) { + $timezone = timezone_name_from_abbr("", (int) $timezone); + } + if (!$timezone) { $timezone = parent::getTimeZoneString(); } + return $timezone; } @@ -767,7 +788,7 @@ public function synchronizeUsers() { if (PHP_SAPI != 'cli') { set_time_limit(300); } - $rows = array(); + $rows = []; $id = 'uid'; $mail = 'mail'; $name = 'name'; @@ -799,11 +820,11 @@ public function synchronizeUsers() { } } - return array( + return [ 'contactCount' => $contactCount, 'contactMatching' => $contactMatching, 'contactCreated' => $contactCreated, - ); + ]; } } diff --git a/CRM/Utils/System/Drupal8.php b/CRM/Utils/System/Drupal8.php index a9892d0610d2..d34007e5478a 100644 --- a/CRM/Utils/System/Drupal8.php +++ b/CRM/Utils/System/Drupal8.php @@ -1,9 +1,9 @@ setUsername($params['cms_name'])->setEmail($params[$mail]); @@ -64,6 +65,9 @@ public function createUser(&$params, $mail) { if ($user_register_conf != 'visitors' && !$user->hasPermission('administer users')) { $account->block(); } + elseif (!$verify_mail_conf) { + $account->activate(); + } // Validate the user object $violations = $account->validate(); @@ -71,10 +75,16 @@ public function createUser(&$params, $mail) { return FALSE; } + // Let the Drupal module know we're already in CiviCRM. + $config = CRM_Core_Config::singleton(); + $config->inCiviCRM = TRUE; + try { $account->save(); + $config->inCiviCRM = FALSE; } catch (\Drupal\Core\Entity\EntityStorageException $e) { + $config->inCiviCRM = FALSE; return FALSE; } @@ -100,6 +110,11 @@ public function createUser(&$params, $mail) { break; } + // If this is a user creating their own account, login them in! + if ($account->isActive() && $user->isAnonymous()) { + \user_login_finalize($account); + } + return $account->id(); } @@ -139,10 +154,10 @@ public static function checkUserNameEmailExists(&$params, &$errors, $emailName = $violations = iterator_to_array($user->validate()); // We only care about violations on the username field; discard the rest. $violations = array_filter($violations, function ($v) { - return $v->getPropertyPath() == 'name.0.value'; + return $v->getPropertyPath() == 'name'; }); if (count($violations) > 0) { - $errors['cms_name'] = $violations[0]->getMessage(); + $errors['cms_name'] = (string) $violations[0]->getMessage(); } } @@ -157,10 +172,10 @@ public static function checkUserNameEmailExists(&$params, &$errors, $emailName = $violations = iterator_to_array($user->validate()); // We only care about violations on the email field; discard the rest. $violations = array_filter($violations, function ($v) { - return $v->getPropertyPath() == 'mail.0.value'; + return $v->getPropertyPath() == 'mail'; }); if (count($violations) > 0) { - $errors[$emailName] = $violations[0]->getMessage(); + $errors[$emailName] = (string) $violations[0]->getMessage(); } } } @@ -169,8 +184,8 @@ public static function checkUserNameEmailExists(&$params, &$errors, $emailName = * @inheritDoc */ public function getLoginURL($destination = '') { - $query = $destination ? array('destination' => $destination) : array(); - return \Drupal::url('user.page', array(), array('query' => $query)); + $query = $destination ? ['destination' => $destination] : []; + return \Drupal::url('user.page', [], ['query' => $query]); } /** @@ -207,54 +222,6 @@ public function addHTMLHead($header) { \Drupal::service('civicrm.page_state')->addHtmlHeader($header); } - /** - * @inheritDoc - */ - public function addScriptUrl($url, $region) { - static $weight = 0; - - switch ($region) { - case 'html-header': - case 'page-footer': - break; - - default: - return FALSE; - } - - $script = array( - '#tag' => 'script', - '#attributes' => array( - 'src' => $url, - ), - '#weight' => $weight, - ); - $weight++; - \Drupal::service('civicrm.page_state')->addJS($script); - return TRUE; - } - - /** - * @inheritDoc - */ - public function addScript($code, $region) { - switch ($region) { - case 'html-header': - case 'page-footer': - break; - - default: - return FALSE; - } - - $script = array( - '#tag' => 'script', - '#value' => $code, - ); - \Drupal::service('civicrm.page_state')->addJS($script); - return TRUE; - } - /** * @inheritDoc */ @@ -262,13 +229,13 @@ public function addStyleUrl($url, $region) { if ($region != 'html-header') { return FALSE; } - $css = array( + $css = [ '#tag' => 'link', - '#attributes' => array( + '#attributes' => [ 'href' => $url, 'rel' => 'stylesheet', - ), - ); + ], + ]; \Drupal::service('civicrm.page_state')->addCSS($css); return TRUE; } @@ -280,10 +247,10 @@ public function addStyle($code, $region) { if ($region != 'html-header') { return FALSE; } - $css = array( + $css = [ '#tag' => 'style', '#value' => $code, - ); + ]; \Drupal::service('civicrm.page_state')->addCSS($css); return TRUE; } @@ -337,11 +304,11 @@ public function url( // Not all links that CiviCRM generates are Drupal routes, so we use the weaker ::fromUri method. try { - $url = \Drupal\Core\Url::fromUri("base:{$url['path']}", array( + $url = \Drupal\Core\Url::fromUri("base:{$url['path']}", [ 'query' => $url['query'], 'fragment' => $fragment, 'absolute' => $absolute, - ))->toString(); + ])->toString(); } catch (Exception $e) { // @Todo: log to watchdog @@ -364,12 +331,17 @@ public function url( */ public function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realPath = NULL) { $system = new CRM_Utils_System_Drupal8(); - $system->loadBootStrap(array(), FALSE); + $system->loadBootStrap([], FALSE); $uid = \Drupal::service('user.auth')->authenticate($name, $password); - $contact_id = CRM_Core_BAO_UFMatch::getContactId($uid); + if ($uid) { + if ($this->loadUser($name)) { + $contact_id = CRM_Core_BAO_UFMatch::getContactId($uid); + return [$contact_id, $uid, mt_rand()]; + } + } - return array($contact_id, $uid, mt_rand()); + return FALSE; } /** @@ -435,7 +407,7 @@ public function logout() { * @return bool * @Todo Handle setting cleanurls configuration for CiviCRM? */ - public function loadBootStrap($params = array(), $loadUser = TRUE, $throwError = TRUE, $realPath = NULL) { + public function loadBootStrap($params = [], $loadUser = TRUE, $throwError = TRUE, $realPath = NULL) { static $run_once = FALSE; if ($run_once) { return TRUE; @@ -450,25 +422,28 @@ public function loadBootStrap($params = array(), $loadUser = TRUE, $throwError = chdir($root); // Create a mock $request object - $autoloader = require_once $root . '/vendor/autoload.php'; + $autoloader = require_once $root . '/autoload.php'; + if ($autoloader === TRUE) { + $autoloader = ComposerAutoloaderInitDrupal8::getLoader(); + } // @Todo: do we need to handle case where $_SERVER has no HTTP_HOST key, ie. when run via cli? - $request = new \Symfony\Component\HttpFoundation\Request(array(), array(), array(), array(), array(), $_SERVER); + $request = new \Symfony\Component\HttpFoundation\Request([], [], [], [], [], $_SERVER); // Create a kernel and boot it. \Drupal\Core\DrupalKernel::createFromRequest($request, $autoloader, 'prod')->prepareLegacyRequest($request); // Initialize Civicrm - \Drupal::service('civicrm'); + \Drupal::service('civicrm')->initialize(); // We need to call the config hook again, since we now know // all the modules that are listening on it (CRM-8655). CRM_Utils_Hook::config($config); if ($loadUser) { - if (!empty($params['uid']) && $username = \Drupal\user\Entity\User::load($uid)->getUsername()) { + if (!empty($params['uid']) && $username = \Drupal\user\Entity\User::load($params['uid'])->getUsername()) { $this->loadUser($username); } - elseif (!empty($params['name']) && !empty($params['pass']) && $this->authenticate($params['name'], $params['pass'])) { + elseif (!empty($params['name']) && !empty($params['pass']) && \Drupal::service('user.auth')->authenticate($params['name'], $params['pass'])) { $this->loadUser($params['name']); } } @@ -483,6 +458,11 @@ public function loadBootStrap($params = array(), $loadUser = TRUE, $throwError = * @return NULL|string */ public function cmsRootPath($path = NULL) { + global $civicrm_paths; + if (!empty($civicrm_paths['cms.root']['path'])) { + return $civicrm_paths['cms.root']['path']; + } + if (defined('DRUPAL_ROOT')) { return DRUPAL_ROOT; } @@ -518,6 +498,33 @@ public function isUserLoggedIn() { return \Drupal::currentUser()->isAuthenticated(); } + /** + * @inheritDoc + */ + public function isUserRegistrationPermitted() { + if (\Drupal::config('user.settings')->get('register') == 'admin_only') { + return FALSE; + } + return TRUE; + } + + /** + * @inheritDoc + */ + public function isPasswordUserGenerated() { + if (\Drupal::config('user.settings')->get('verify_mail') == TRUE) { + return FALSE; + } + return TRUE; + } + + /** + * @inheritDoc + */ + public function updateCategories() { + // @todo Is anything necessary? + } + /** * @inheritDoc */ @@ -550,7 +557,7 @@ public function flush() { * @inheritDoc */ public function getModules() { - $modules = array(); + $modules = []; $module_data = system_rebuild_module_data(); foreach ($module_data as $module_name => $extension) { @@ -562,6 +569,16 @@ public function getModules() { return $modules; } + /** + * @inheritDoc + */ + public function getUser($contactID) { + $user_details = parent::getUser($contactID); + $user_details['name'] = $user_details['name']->value; + $user_details['email'] = $user_details['email']->value; + return $user_details; + } + /** * @inheritDoc */ @@ -585,7 +602,7 @@ public function synchronizeUsers() { set_time_limit(300); } - $users = array(); + $users = []; $users = \Drupal::entityTypeManager()->getStorage('user')->loadByProperties(); $uf = $config->userFramework; @@ -610,11 +627,50 @@ public function synchronizeUsers() { } } - return array( + return [ 'contactCount' => $contactCount, 'contactMatching' => $contactMatching, 'contactCreated' => $contactCreated, - ); + ]; + } + + /** + * Drupal 8 has a different function to get current path, hence + * overriding the postURL function + * + * @param string $action + * + * @return string + */ + public function postURL($action) { + if (!empty($action)) { + return $action; + } + $current_path = \Drupal::service('path.current')->getPath(); + return $this->url($current_path); + } + + /** + * Function to return current language of Drupal8 + * + * @return string + */ + public function getCurrentLanguage() { + // Drupal might not be bootstrapped if being called by the REST API. + if (!class_exists('Drupal')) { + return NULL; + } + + return \Drupal::languageManager()->getCurrentLanguage()->getId(); + } + + /** + * Append Drupal8 js to coreResourcesList. + * + * @param array $list + */ + public function appendCoreResources(&$list) { + $list[] = 'js/crm.drupal8.js'; } } diff --git a/CRM/Utils/System/DrupalBase.php b/CRM/Utils/System/DrupalBase.php index 3129073181ed..75d04ca71252 100644 --- a/CRM/Utils/System/DrupalBase.php +++ b/CRM/Utils/System/DrupalBase.php @@ -1,9 +1,9 @@ supports_form_extensions = TRUE; } + /** + * @inheritdoc + */ + public function getDefaultFileStorage() { + $config = CRM_Core_Config::singleton(); + $baseURL = CRM_Utils_System::languageNegotiationURL($config->userFrameworkBaseURL, FALSE, TRUE); + + $siteName = $this->parseDrupalSiteNameFromRequest('/files/civicrm'); + if ($siteName) { + $filesURL = $baseURL . "sites/$siteName/files/civicrm/"; + } + else { + $filesURL = $baseURL . "sites/default/files/civicrm/"; + } + + return [ + 'url' => $filesURL, + 'path' => CRM_Utils_File::baseFilePath(), + ]; + } + /** * @inheritDoc */ public function getDefaultSiteSettings($dir) { $config = CRM_Core_Config::singleton(); $siteName = $siteRoot = NULL; - $matches = array(); + $matches = []; if (preg_match( '|/sites/([\w\.\-\_]+)/|', $config->templateCompileDir, @@ -80,7 +101,7 @@ public function getDefaultSiteSettings($dir) { } } $url = $config->userFrameworkBaseURL; - return array($url, $siteName, $siteRoot); + return [$url, $siteName, $siteRoot]; } /** @@ -96,26 +117,24 @@ public function formatResourceUrl(&$url) { $internal = FALSE; $base = CRM_Core_Config::singleton()->resourceBase; global $base_url; + // Strip query string + $q = strpos($url, '?'); + $url_path = $q ? substr($url, 0, $q) : $url; // Handle absolute urls // compares $url (which is some unknown/untrusted value from a third-party dev) to the CMS's base url (which is independent of civi's url) // to see if the url is within our drupal dir, if it is we are able to treated it as an internal url - if (strpos($url, $base_url) === 0) { - $file = trim(str_replace($base_url, '', $url), '/'); + if (strpos($url_path, $base_url) === 0) { + $file = trim(str_replace($base_url, '', $url_path), '/'); // CRM-18130: Custom CSS URL not working if aliased or rewritten - if (file_exists(DRUPAL_ROOT . $file)) { + if (file_exists(DRUPAL_ROOT . '/' . $file)) { $url = $file; $internal = TRUE; } } // Handle relative urls that are within the CiviCRM module directory - elseif (strpos($url, $base) === 0) { + elseif (strpos($url_path, $base) === 0) { $internal = TRUE; - $url = $this->appendCoreDirectoryToResourceBase(dirname(drupal_get_path('module', 'civicrm')) . '/') . trim(substr($url, strlen($base)), '/'); - } - // Strip query string - $q = strpos($url, '?'); - if ($q && $internal) { - $url = substr($url, 0, $q); + $url = $this->appendCoreDirectoryToResourceBase(dirname(drupal_get_path('module', 'civicrm')) . '/') . trim(substr($url_path, strlen($base)), '/'); } return $internal; } @@ -247,12 +266,12 @@ public function permissionDenied() { public function getUserRecordUrl($contactID) { $uid = CRM_Core_BAO_UFMatch::getUFId($contactID); if (CRM_Core_Session::singleton() - ->get('userID') == $contactID || CRM_Core_Permission::checkAnyPerm(array( - 'cms:administer users', - 'cms:view user account', - )) + ->get('userID') == $contactID || CRM_Core_Permission::checkAnyPerm([ + 'cms:administer users', + 'cms:view user account', + ]) ) { - return url('user/' . $uid); + return $this->url('user/' . $uid); }; } @@ -268,7 +287,7 @@ public function checkPermissionAddUser() { */ public function logger($message) { if (CRM_Core_Config::singleton()->userFrameworkLogging && function_exists('watchdog')) { - watchdog('civicrm', '%message', array('%message' => $message), NULL, WATCHDOG_DEBUG); + watchdog('civicrm', '%message', ['%message' => $message], NULL, WATCHDOG_DEBUG); } } @@ -279,15 +298,6 @@ public function clearResourceCache() { _drupal_flush_css_js(); } - /** - * Append Drupal js to coreResourcesList. - * - * @param array $list - */ - public function appendCoreResources(&$list) { - $list[] = 'js/crm.drupal.js'; - } - /** * @inheritDoc */ @@ -299,7 +309,7 @@ public function flush() { * @inheritDoc */ public function getModules() { - $result = array(); + $result = []; $q = db_query('SELECT name, status FROM {system} WHERE type = \'module\' AND schema_version <> -1'); foreach ($q as $row) { $result[] = new CRM_Core_Module('drupal.' . $row->name, ($row->status == 1) ? TRUE : FALSE); @@ -321,7 +331,7 @@ public function replacePermission($oldPerm, $newPerms) { $roles = user_roles(FALSE, $oldPerm); if (!empty($roles)) { foreach (array_keys($roles) as $rid) { - user_role_revoke_permissions($rid, array($oldPerm)); + user_role_revoke_permissions($rid, [$oldPerm]); user_role_grant_permissions($rid, $newPerms); } } @@ -393,6 +403,26 @@ public function getVersion() { return defined('VERSION') ? VERSION : 'Unknown'; } + /** + * @inheritDoc + */ + public function isUserRegistrationPermitted() { + if (!variable_get('user_register', TRUE)) { + return FALSE; + } + return TRUE; + } + + /** + * @inheritDoc + */ + public function isPasswordUserGenerated() { + if (variable_get('user_email_verification', TRUE)) { + return FALSE; + } + return TRUE; + } + /** * @inheritDoc */ @@ -409,25 +439,25 @@ public function getUFLocale() { // return CiviCRM’s xx_YY locale that either matches Drupal’s Chinese locale // (for CRM-6281), Drupal’s xx_YY or is retrieved based on Drupal’s xx // sometimes for CLI based on order called, this might not be set and/or empty - global $language; + $language = $this->getCurrentLanguage(); if (empty($language)) { return NULL; } - if ($language->language == 'zh-hans') { + if ($language == 'zh-hans') { return 'zh_CN'; } - if ($language->language == 'zh-hant') { + if ($language == 'zh-hant') { return 'zh_TW'; } - if (preg_match('/^.._..$/', $language->language)) { - return $language->language; + if (preg_match('/^.._..$/', $language)) { + return $language; } - return CRM_Core_I18n_PseudoConstant::longForShort(substr($language->language, 0, 2)); + return CRM_Core_I18n_PseudoConstant::longForShort(substr($language, 0, 2)); } /** @@ -462,7 +492,7 @@ public function setUFLocale($civicrm_language) { * * FIXME: Document values accepted/required by $params */ - public function userLoginFinalize($params = array()) { + public function userLoginFinalize($params = []) { user_login_finalize($params); } @@ -545,7 +575,15 @@ public function getUserObject($userID) { return user_load($userID); } - public function parseDrupalSiteName($civicrm_root) { + /** + * Parse the name of the drupal site. + * + * @param string $civicrm_root + * + * @return null|string + * @deprecated + */ + public function parseDrupalSiteNameFromRoot($civicrm_root) { $siteName = NULL; if (strpos($civicrm_root, DIRECTORY_SEPARATOR . 'sites' . DIRECTORY_SEPARATOR . 'all' . DIRECTORY_SEPARATOR . 'modules' @@ -568,4 +606,61 @@ public function parseDrupalSiteName($civicrm_root) { return $siteName; } + /** + * Determine if Drupal multi-site applies to the current request -- and, + * specifically, determine the name of the multisite folder. + * + * @param string $flagFile + * Check if $flagFile exists inside the site dir. + * @return null|string + * string, e.g. `bar.example.com` if using multisite. + * NULL if using the default site. + */ + private function parseDrupalSiteNameFromRequest($flagFile = '') { + $phpSelf = array_key_exists('PHP_SELF', $_SERVER) ? $_SERVER['PHP_SELF'] : ''; + $httpHost = array_key_exists('HTTP_HOST', $_SERVER) ? $_SERVER['HTTP_HOST'] : ''; + if (empty($httpHost)) { + $httpHost = parse_url(CIVICRM_UF_BASEURL, PHP_URL_HOST); + if (parse_url(CIVICRM_UF_BASEURL, PHP_URL_PORT)) { + $httpHost .= ':' . parse_url(CIVICRM_UF_BASEURL, PHP_URL_PORT); + } + } + + $confdir = $this->cmsRootPath() . '/sites'; + + if (file_exists($confdir . "/sites.php")) { + include $confdir . "/sites.php"; + } + else { + $sites = []; + } + + $uri = explode('/', $phpSelf); + $server = explode('.', implode('.', array_reverse(explode(':', rtrim($httpHost, '.'))))); + for ($i = count($uri) - 1; $i > 0; $i--) { + for ($j = count($server); $j > 0; $j--) { + $dir = implode('.', array_slice($server, -$j)) . implode('.', array_slice($uri, 0, $i)); + if (file_exists("$confdir/$dir" . $flagFile)) { + \Civi::$statics[__CLASS__]['drupalSiteName'] = $dir; + return \Civi::$statics[__CLASS__]['drupalSiteName']; + } + // check for alias + if (isset($sites[$dir]) && file_exists("$confdir/{$sites[$dir]}" . $flagFile)) { + \Civi::$statics[__CLASS__]['drupalSiteName'] = $sites[$dir]; + return \Civi::$statics[__CLASS__]['drupalSiteName']; + } + } + } + } + + /** + * Function to return current language of Drupal + * + * @return string + */ + public function getCurrentLanguage() { + global $language; + return (!empty($language->language)) ? $language->language : $language; + } + } diff --git a/CRM/Utils/System/Joomla.php b/CRM/Utils/System/Joomla.php index 325a169717ba..91085841a262 100644 --- a/CRM/Utils/System/Joomla.php +++ b/CRM/Utils/System/Joomla.php @@ -1,9 +1,9 @@ getQuery(TRUE); $query->select('username, email'); $query->from($JUserTable->getTableName()); + + // LOWER in query below roughly translates to 'hurt my database without deriving any benefit' See CRM-19811. $query->where('(LOWER(username) = LOWER(\'' . $name . '\')) OR (LOWER(email) = LOWER(\'' . $email . '\'))'); $db->setQuery($query, 0, 10); $users = $db->loadAssocList(); - $row = array(); + $row = []; if (count($users)) { $row = $users[0]; } @@ -150,13 +153,13 @@ public function checkUserNameEmailExists(&$params, &$errors, $emailName = 'email $dbEmail = CRM_Utils_Array::value('email', $row); if (strtolower($dbName) == strtolower($name)) { $errors['cms_name'] = ts('The username %1 is already taken. Please select another username.', - array(1 => $name) + [1 => $name] ); } if (strtolower($dbEmail) == strtolower($email)) { $resetUrl = str_replace('administrator/', '', $config->userFrameworkBaseURL) . 'index.php?option=com_users&view=reset'; $errors[$emailName] = ts('The email address %1 already has an account associated with it. Have you forgotten your password?', - array(1 => $email, 2 => $resetUrl) + [1 => $email, 2 => $resetUrl] ); } } @@ -187,7 +190,7 @@ public function appendBreadCrumb($breadCrumbs) { if (is_array($breadCrumbs)) { foreach ($breadCrumbs as $crumbs) { if (stripos($crumbs['url'], 'id%%')) { - $args = array('cid', 'mid'); + $args = ['cid', 'mid']; foreach ($args as $a) { $val = CRM_Utils_Request::retrieve($a, 'Positive', CRM_Core_DAO::$_nullObject, FALSE, NULL, $_GET @@ -262,7 +265,7 @@ public function url( if ($config->userFrameworkFrontend) { $script = 'index.php'; - if (JRequest::getVar("Itemid")) { + if (JRequest::getVar("Itemid") && (strpos($path, 'civicrm/payment/ipn') === FALSE)) { $Itemid = "{$separator}Itemid=" . JRequest::getVar("Itemid"); } } @@ -315,8 +318,8 @@ public function setEmail(&$user) { global $database; $query = $db->getQuery(TRUE); $query->select($db->quoteName('email')) - ->from($db->quoteName('#__users')) - ->where($db->quoteName('id') . ' = ' . $user->id); + ->from($db->quoteName('#__users')) + ->where($db->quoteName('id') . ' = ' . $user->id); $database->setQuery($query); $user->email = $database->loadResult(); } @@ -331,12 +334,12 @@ public function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realP $user = NULL; if ($loadCMSBootstrap) { - $bootStrapParams = array(); + $bootStrapParams = []; if ($name && $password) { - $bootStrapParams = array( + $bootStrapParams = [ 'name' => $name, 'pass' => $password, - ); + ]; } CRM_Utils_System::loadBootStrap($bootStrapParams, TRUE, TRUE, FALSE); } @@ -355,17 +358,13 @@ public function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realP $db->setQuery($query, 0, 0); $users = $db->loadObjectList(); - $row = array(); + $row = []; if (count($users)) { $row = $users[0]; } - $joomlaBase = dirname(dirname(dirname(dirname(dirname(dirname(dirname(dirname(__FILE__)))))))); - if (!defined('JVERSION')) { - require $joomlaBase . '/libraries/cms/version/version.php'; - $jversion = new JVersion(); - define('JVERSION', $jversion->getShortVersion()); - } + $joomlaBase = self::getBasePath(); + self::getJVersion($joomlaBase); if (!empty($row)) { $dbPassword = $row->password; @@ -387,8 +386,13 @@ public function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realP return FALSE; } + if (version_compare(JVERSION, '3.8.0', 'ge')) { + jimport('joomla.application.helper'); + jimport('joomla.application.cms'); + jimport('joomla.application.administrator'); + } //include additional files required by Joomla 3.2.1+ - if (version_compare(JVERSION, '3.2.1', 'ge')) { + elseif (version_compare(JVERSION, '3.2.1', 'ge')) { require_once $joomlaBase . '/libraries/cms/application/helper.php'; require_once $joomlaBase . '/libraries/cms/application/cms.php'; require_once $joomlaBase . '/libraries/cms/application/administrator.php'; @@ -400,7 +404,7 @@ public function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realP if (!$contactID) { return FALSE; } - return array($contactID, $dbId, mt_rand()); + return [$contactID, $dbId, mt_rand()]; } return FALSE; @@ -443,10 +447,10 @@ public function loadUser($username, $password = NULL) { $contactID = CRM_Core_BAO_UFMatch::getContactId($uid); if (!empty($password)) { $instance = JFactory::getApplication('site'); - $params = array( + $params = [ 'username' => $username, 'password' => $password, - ); + ]; //perform the login action $instance->login($params); } @@ -505,6 +509,32 @@ public function getVersion() { } } + public function getJVersion($joomlaBase) { + // Files may be in different places depending on Joomla version + if (!defined('JVERSION')) { + // Joomla 3.8.0+ + $versionPhp = $joomlaBase . '/libraries/src/Version.php'; + if (!file_exists($versionPhp)) { + // Joomla < 3.8.0 + $versionPhp = $joomlaBase . '/libraries/cms/version/version.php'; + } + require $versionPhp; + $jversion = new JVersion(); + define('JVERSION', $jversion->getShortVersion()); + } + } + + /** + * Setup the base path related constant. + * @return mixed + */ + public function getBasePath() { + global $civicrm_root; + $joomlaPath = explode(DIRECTORY_SEPARATOR . 'administrator', $civicrm_root); + $joomlaBase = $joomlaPath[0]; + return $joomlaBase; + } + /** * Load joomla bootstrap. * @@ -518,9 +548,8 @@ public function getVersion() { * * @return bool */ - public function loadBootStrap($params = array(), $loadUser = TRUE, $throwError = TRUE, $realPath = NULL, $loadDefines = TRUE) { - // Setup the base path related constant. - $joomlaBase = dirname(dirname(dirname(dirname(dirname(dirname(dirname(dirname(__FILE__)))))))); + public function loadBootStrap($params = [], $loadUser = TRUE, $throwError = TRUE, $realPath = NULL, $loadDefines = TRUE) { + $joomlaBase = self::getBasePath(); // load BootStrap here if needed // We are a valid Joomla entry point. @@ -535,27 +564,39 @@ public function loadBootStrap($params = array(), $loadUser = TRUE, $throwError = if (file_exists($joomlaBase . '/libraries/import.legacy.php')) { require $joomlaBase . '/libraries/import.legacy.php'; } - require $joomlaBase . '/libraries/import.php'; - require $joomlaBase . '/libraries/joomla/event/dispatcher.php'; - require $joomlaBase . '/configuration.php'; + require $joomlaBase . '/libraries/cms.php'; + self::getJVersion($joomlaBase); - // Files may be in different places depending on Joomla version - if (!defined('JVERSION')) { - require $joomlaBase . '/libraries/cms/version/version.php'; - $jversion = new JVersion(); - define('JVERSION', $jversion->getShortVersion()); + if (version_compare(JVERSION, '3.8', 'lt')) { + require $joomlaBase . '/libraries/import.php'; + require $joomlaBase . '/libraries/joomla/event/dispatcher.php'; } + require_once $joomlaBase . '/configuration.php'; + if (version_compare(JVERSION, '3.0', 'lt')) { require $joomlaBase . '/libraries/joomla/environment/uri.php'; require $joomlaBase . '/libraries/joomla/application/component/helper.php'; } - else { - require $joomlaBase . '/libraries/cms.php'; - require $joomlaBase . '/libraries/joomla/uri/uri.php'; + elseif (version_compare(JVERSION, '3.8', 'lt')) { + jimport('joomla.environment.uri'); + } + + if (version_compare(JVERSION, '3.8', 'lt')) { + jimport('joomla.application.cli'); + } + + if (!defined('JDEBUG')) { + define('JDEBUG', FALSE); } - jimport('joomla.application.cli'); + // Set timezone for Joomla on Cron + $config = JFactory::getConfig(); + $timezone = $config->get('offset'); + if ($timezone) { + date_default_timezone_set($timezone); + CRM_Core_Config::singleton()->userSystem->setMySQLTimeZone(); + } // CRM-14281 Joomla wasn't available during bootstrap, so hook_civicrm_config never executes. $config = CRM_Core_Config::singleton(); @@ -572,6 +613,24 @@ public function isUserLoggedIn() { return ($user->guest) ? FALSE : TRUE; } + /** + * @inheritDoc + */ + public function isUserRegistrationPermitted() { + $userParams = JComponentHelper::getParams('com_users'); + if (!$userParams->get('allowUserRegistration')) { + return FALSE; + } + return TRUE; + } + + /** + * @inheritDoc + */ + public function isPasswordUserGenerated() { + return TRUE; + } + /** * @inheritDoc */ @@ -588,6 +647,16 @@ public function getLoggedInUniqueIdentifier() { return $this->getUniqueIdentifierFromUserObject($user); } + /** + * @inheritDoc + */ + public function getUser($contactID) { + $user_details = parent::getUser($contactID); + $user = JFactory::getUser($user_details['id']); + $user_details['name'] = $user->name; + return $user_details; + } + /** * @inheritDoc */ @@ -617,7 +686,7 @@ public function getTimeZoneString() { * CRM_Core_Module */ public function getModules() { - $result = array(); + $result = []; $db = JFactory::getDbo(); $query = $db->getQuery(TRUE); @@ -627,7 +696,7 @@ public function getModules() { $plugins = $db->setQuery($query)->loadAssocList(); foreach ($plugins as $plugin) { // question: is the folder really a critical part of the plugin's name? - $name = implode('.', array('joomla', $plugin['type'], $plugin['folder'], $plugin['element'])); + $name = implode('.', ['joomla', $plugin['type'], $plugin['folder'], $plugin['element']]); $result[] = new CRM_Core_Module($name, $plugin['enabled'] ? TRUE : FALSE); } @@ -692,9 +761,13 @@ public function getLoginDestination(&$form) { * local file system path to CMS root, or NULL if it cannot be determined */ public function cmsRootPath() { + global $civicrm_paths; + if (!empty($civicrm_paths['cms.root']['path'])) { + return $civicrm_paths['cms.root']['path']; + } + list($url, $siteName, $siteRoot) = $this->getDefaultSiteSettings(); - $includePath = "$siteRoot/libraries/cms/version"; - if (file_exists("$includePath/version.php")) { + if (file_exists("$siteRoot/administrator/index.php")) { return $siteRoot; } return NULL; @@ -710,12 +783,24 @@ public function getDefaultSiteSettings($dir = NULL) { '', $config->userFrameworkBaseURL ); + // CRM-19453 revisited. Under Windows, the pattern wasn't recognised. + // This is the original pattern, but it doesn't work under Windows. + // By setting the pattern to the one used before the change first and only + // changing it means that the change code only affects Windows users. + $pattern = '|/media/civicrm/.*$|'; + if (DIRECTORY_SEPARATOR == '\\') { + // This regular expression will handle Windows as well as Linux + // and any combination of forward and back slashes in directory + // separators. We only apply it if the directory separator is the one + // used by Windows. + $pattern = '|[\\\\/]media[\\\\/]civicrm[\\\\/].*$|'; + } $siteRoot = preg_replace( - '|/media/civicrm/.*$|', + $pattern, '', $config->imageUploadDir ); - return array($url, NULL, $siteRoot); + return [$url, NULL, $siteRoot]; } /** @@ -759,15 +844,6 @@ public function outputError($content) { } } - /** - * Append Joomla js to coreResourcesList. - * - * @param array $list - */ - public function appendCoreResources(&$list) { - $list[] = 'js/crm.joomla.js'; - } - /** * @inheritDoc */ @@ -820,11 +896,11 @@ public function synchronizeUsers() { } } - return array( + return [ 'contactCount' => $contactCount, 'contactMatching' => $contactMatching, 'contactCreated' => $contactCreated, - ); + ]; } } diff --git a/CRM/Utils/System/Soap.php b/CRM/Utils/System/Soap.php index 9ec8040d5ebc..2edca70da7be 100644 --- a/CRM/Utils/System/Soap.php +++ b/CRM/Utils/System/Soap.php @@ -1,9 +1,9 @@ CRM_Utils_File::addTrailingSlash($filesURL, '/'), 'path' => CRM_Utils_File::addTrailingSlash($filesPath), - ); + ]; } /** @@ -114,10 +115,10 @@ public function getCiviSourceStorage() { } $civiRelPath = CRM_Utils_File::relativize(realpath($civicrm_root), realpath($cmsPath)); $civiUrl = rtrim($cmsUrl, '/') . '/' . ltrim($civiRelPath, ' /'); - return array( + return [ 'url' => CRM_Utils_File::addTrailingSlash($civiUrl, '/'), 'path' => CRM_Utils_File::addTrailingSlash($civicrm_root), - ); + ]; } /** @@ -129,7 +130,7 @@ public function appendBreadCrumb($breadCrumbs) { if (is_array($breadCrumbs)) { foreach ($breadCrumbs as $crumbs) { if (stripos($crumbs['url'], 'id%%')) { - $args = array('cid', 'mid'); + $args = ['cid', 'mid']; foreach ($args as $a) { $val = CRM_Utils_Request::retrieve($a, 'Positive', CRM_Core_DAO::$_nullObject, FALSE, NULL, $_GET @@ -152,7 +153,7 @@ public function appendBreadCrumb($breadCrumbs) { * @inheritDoc */ public function resetBreadCrumb() { - $bc = array(); + $bc = []; wp_set_breadcrumb($bc); } @@ -163,13 +164,13 @@ public function addHTMLHead($head) { static $registered = FALSE; if (!$registered) { // front-end view - add_action('wp_head', array(__CLASS__, '_showHTMLHead')); + add_action('wp_head', [__CLASS__, '_showHTMLHead']); // back-end views - add_action('admin_head', array(__CLASS__, '_showHTMLHead')); + add_action('admin_head', [__CLASS__, '_showHTMLHead']); } - CRM_Core_Region::instance('wp_head')->add(array( + CRM_Core_Region::instance('wp_head')->add([ 'markup' => $head, - )); + ]); } /** @@ -208,6 +209,7 @@ public function url( $fragment = isset($fragment) ? ('#' . $fragment) : ''; $path = CRM_Utils_String::stripPathChars($path); + $basepage = FALSE; //this means wp function we are trying to use is not available, //so load bootStrap @@ -215,16 +217,20 @@ public function url( if (!function_exists('get_option')) { $this->loadBootStrap(); } + if ($config->userFrameworkFrontend) { + global $post; if (get_option('permalink_structure') != '') { - global $post; $script = get_permalink($post->ID); } - + if ($config->wpBasePage == $post->post_name) { + $basepage = TRUE; + } // when shortcode is included in page // also make sure we have valid query object + // FIXME: $wpPageParam has no effect and is only set on the *basepage* global $wp_query; - if (method_exists($wp_query, 'get')) { + if (get_option('permalink_structure') == '' && method_exists($wp_query, 'get')) { if (get_query_var('page_id')) { $wpPageParam = "page_id=" . get_query_var('page_id'); } @@ -250,22 +256,66 @@ public function url( $base = $script; } - $queryParts = array(); - if (isset($path)) { - $queryParts[] = 'page=CiviCRM'; - $queryParts[] = "q={$path}"; - } - if ($wpPageParam) { - $queryParts[] = $wpPageParam; + $queryParts = []; + + if ( + // not using clean URLs + !$config->cleanURL + // requesting an admin URL + || ((is_admin() && !$frontend) || $forceBackend) + // is shortcode + || (!$basepage && $script != '') + ) { + + // pre-existing logic + if (isset($path)) { + $queryParts[] = 'page=CiviCRM'; + // Encode all but the *path* placeholder + if ($path !== '*path*') { + $path = rawurlencode($path); + } + $queryParts[] = "q={$path}"; + } + if ($wpPageParam) { + $queryParts[] = $wpPageParam; + } + if (!empty($query)) { + $queryParts[] = $query; + } + + $final = $base . '?' . implode($separator, $queryParts) . $fragment; + } - if (isset($query)) { - $queryParts[] = $query; + else { + + // clean URLs + if (isset($path)) { + $base = trailingslashit($base) . str_replace('civicrm/', '', $path) . '/'; + } + if (isset($query)) { + $query = ltrim($query, '=?&'); + $queryParts[] = $query; + } + + if (!empty($queryParts)) { + $final = $base . '?' . implode($separator, $queryParts) . $fragment; + } + else { + $final = $base . $fragment; + } + } - return $base . '?' . implode($separator, $queryParts) . $fragment; + return $final; } /** + * 27-09-2016 + * CRM-16421 CRM-17633 WIP Changes to support WP in it's own directory + * https://wiki.civicrm.org/confluence/display/CRM/WordPress+installed+in+its+own+directory+issues + * For now leave hard coded wp-admin references. + * TODO: remove wp-admin references and replace with admin_url() in the future. Look at best way to get path to admin_url + * * @param $absolute * @param $frontend * @param $forceBackend @@ -274,22 +324,12 @@ public function url( */ private function getBaseUrl($absolute, $frontend, $forceBackend) { $config = CRM_Core_Config::singleton(); - - $base = $absolute ? $config->userFrameworkBaseURL : $config->useFrameworkRelativeBase; - if ((is_admin() && !$frontend) || $forceBackend) { - $base .= 'wp-admin/admin.php'; - return $base; - } - elseif (defined('CIVICRM_UF_WP_BASEPAGE')) { - $base .= CIVICRM_UF_WP_BASEPAGE; - return $base; + return Civi::paths()->getUrl('[wp.backend]/.', $absolute ? 'absolute' : 'relative'); } - elseif (isset($config->wpBasePage)) { - $base .= $config->wpBasePage; - return $base; + else { + return Civi::paths()->getUrl('[wp.frontend]/.', $absolute ? 'absolute' : 'relative'); } - return $base; } /** @@ -299,7 +339,10 @@ public function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realP $config = CRM_Core_Config::singleton(); if ($loadCMSBootstrap) { - $config->userSystem->loadBootStrap($name, $password); + $config->userSystem->loadBootStrap([ + 'name' => $name, + 'pass' => $password, + ]); } $user = wp_authenticate($name, $password); @@ -314,7 +357,7 @@ public function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realP if (!$contactID) { return FALSE; } - return array($contactID, $user->data->ID, mt_rand()); + return [$contactID, $user->data->ID, mt_rand()]; } /** @@ -354,6 +397,20 @@ public function permissionDenied() { CRM_Core_Error::fatal(ts('You do not have permission to access this page.')); } + /** + * Determine the native ID of the CMS user. + * + * @param string $username + * @return int|NULL + */ + public function getUfId($username) { + $userdata = get_user_by('login', $username); + if (!$userdata->data->ID) { + return NULL; + } + return $userdata->data->ID; + } + /** * @inheritDoc */ @@ -370,14 +427,18 @@ public function logout() { * @inheritDoc */ public function getUFLocale() { + // Polylang plugin + if (function_exists('pll_current_language')) { + $language = pll_current_language(); + } // WPML plugin - if (defined('ICL_LANGUAGE_CODE')) { + elseif (defined('ICL_LANGUAGE_CODE')) { $language = ICL_LANGUAGE_CODE; } // TODO: set language variable for others WordPress plugin - if (isset($language)) { + if (!empty($language)) { return CRM_Core_I18n_PseudoConstant::longForShort(substr($language, 0, 2)); } else { @@ -396,16 +457,22 @@ public function setUFLocale($civicrm_language) { /** * Load wordpress bootstrap. * - * @param string $name - * optional username for login. - * @param string $pass - * optional password for login. + * @param array $params + * Optional credentials + * - name: string, cms username + * - pass: string, cms password + * @param bool $loadUser + * @param bool $throwError + * @param mixed $realPath * * @return bool */ - public function loadBootStrap($name = NULL, $pass = NULL) { + public function loadBootStrap($params = [], $loadUser = TRUE, $throwError = TRUE, $realPath = NULL) { global $wp, $wp_rewrite, $wp_the_query, $wp_query, $wpdb, $current_site, $current_blog, $current_user; + $name = CRM_Utils_Array::value('name', $params); + $pass = CRM_Utils_Array::value('pass', $params); + if (!defined('WP_USE_THEMES')) { define('WP_USE_THEMES', FALSE); } @@ -430,7 +497,7 @@ public function loadBootStrap($name = NULL, $pass = NULL) { CRM_Core_Config::singleton()->userSystem->setMySQLTimeZone(); } require_once $cmsRootPath . DIRECTORY_SEPARATOR . 'wp-includes/pluggable.php'; - $uid = CRM_Utils_Array::value('uid', $name); + $uid = CRM_Utils_Array::value('uid', $params); if (!$uid) { $name = $name ? $name : trim(CRM_Utils_Array::value('name', $_REQUEST)); $pass = $pass ? $pass : trim(CRM_Utils_Array::value('pass', $_REQUEST)); @@ -468,7 +535,7 @@ public function loadBootStrap($name = NULL, $pass = NULL) { */ public function validInstallDir($dir) { $includePath = "$dir/wp-includes"; - if (file_exists("$includePath/version.php")) { + if (@file_exists("$includePath/version.php")) { return TRUE; } return FALSE; @@ -481,6 +548,11 @@ public function validInstallDir($dir) { * local file system path to CMS root, or NULL if it cannot be determined */ public function cmsRootPath() { + global $civicrm_paths; + if (!empty($civicrm_paths['cms.root']['path'])) { + return $civicrm_paths['cms.root']['path']; + } + $cmsRoot = $valid = NULL; if (defined('CIVICRM_CMSDIR')) { if ($this->validInstallDir(CIVICRM_CMSDIR)) { @@ -515,14 +587,14 @@ public function cmsRootPath() { * @inheritDoc */ public function createUser(&$params, $mail) { - $user_data = array( + $user_data = [ 'ID' => '', 'user_pass' => $params['cms_pass'], 'user_login' => $params['cms_name'], 'user_email' => $params[$mail], 'nickname' => $params['cms_name'], 'role' => get_option('default_role'), - ); + ]; if (isset($params['contactID'])) { $contactType = CRM_Contact_BAO_Contact::getContactType($params['contactID']); if ($contactType == 'Individual') { @@ -537,7 +609,7 @@ public function createUser(&$params, $mail) { $uid = wp_insert_user($user_data); - $creds = array(); + $creds = []; $creds['user_login'] = $params['cms_name']; $creds['user_password'] = $params['cms_pass']; $creds['remember'] = TRUE; @@ -556,7 +628,7 @@ public function updateCMSName($ufID, $ufName) { $ufID = CRM_Utils_Type::escape($ufID, 'Integer'); $ufName = CRM_Utils_Type::escape($ufName, 'String'); - $values = array('ID' => $ufID, 'user_email' => $ufName); + $values = ['ID' => $ufID, 'user_email' => $ufName]; if ($ufID) { wp_update_user($values); } @@ -580,7 +652,7 @@ public function checkUserNameEmailExists(&$params, &$errors, $emailName = 'email $errors['cms_name'] = ts("Your username contains invalid characters"); } elseif (username_exists(sanitize_user($params['name']))) { - $errors['cms_name'] = ts('The username %1 is already taken. Please select another username.', array(1 => $params['name'])); + $errors['cms_name'] = ts('The username %1 is already taken. Please select another username.', [1 => $params['name']]); } } @@ -590,7 +662,7 @@ public function checkUserNameEmailExists(&$params, &$errors, $emailName = 'email } elseif (email_exists($params['mail'])) { $errors[$emailName] = ts('The email address %1 already has an account associated with it. Have you forgotten your password?', - array(1 => $params['mail'], 2 => wp_lostpassword_url()) + [1 => $params['mail'], 2 => wp_lostpassword_url()] ); } } @@ -608,6 +680,23 @@ public function isUserLoggedIn() { return $isloggedIn; } + /** + * @inheritDoc + */ + public function isUserRegistrationPermitted() { + if (!get_option('users_can_register')) { + return FALSE; + } + return TRUE; + } + + /** + * @inheritDoc + */ + public function isPasswordUserGenerated() { + return TRUE; + } + /** * @return mixed */ @@ -700,7 +789,7 @@ public function getTimeZoneString() { public function getUserRecordUrl($contactID) { $uid = CRM_Core_BAO_UFMatch::getUFId($contactID); if (CRM_Core_Session::singleton() - ->get('userID') == $contactID || CRM_Core_Permission::checkAnyPerm(array('cms:administer users')) + ->get('userID') == $contactID || CRM_Core_Permission::checkAnyPerm(['cms:administer users']) ) { return CRM_Core_Config::singleton()->userFrameworkBaseURL . "wp-admin/user-edit.php?user_id=" . $uid; } @@ -756,11 +845,11 @@ public function synchronizeUsers() { } } - return array( + return [ 'contactCount' => $contactCount, 'contactMatching' => $contactMatching, 'contactCreated' => $contactCreated, - ); + ]; } } diff --git a/CRM/Utils/SystemLogger.php b/CRM/Utils/SystemLogger.php index 0dfabdecd434..641058894140 100644 --- a/CRM/Utils/SystemLogger.php +++ b/CRM/Utils/SystemLogger.php @@ -1,9 +1,9 @@ {$separateField} = $context[$separateField]; diff --git a/CRM/Utils/Time.php b/CRM/Utils/Time.php index 6eeb20981ef0..bf4dd44255f9 100644 --- a/CRM/Utils/Time.php +++ b/CRM/Utils/Time.php @@ -1,9 +1,9 @@ array( + public static $_tokens = [ + 'action' => [ 'forward', 'optOut', 'optOutUrl', @@ -48,8 +48,8 @@ class CRM_Utils_Token { 'resubscribe', 'resubscribeUrl', 'subscribeUrl', - ), - 'mailing' => array( + ], + 'mailing' => [ 'id', 'name', 'group', @@ -62,47 +62,48 @@ class CRM_Utils_Token { 'approveUrl', 'creator', 'creatorEmail', - ), - 'user' => array( + ], + 'user' => [ // we extract the stuff after the role / permission and return the // civicrm email addresses of all users with that role / permission // useful with rules integration 'permission:', 'role:', - ), + ], // populate this dynamically 'contact' => NULL, // populate this dynamically 'contribution' => NULL, - 'domain' => array( + 'domain' => [ 'name', 'phone', 'address', 'email', 'id', 'description', - ), - 'subscribe' => array('group'), - 'unsubscribe' => array('group'), - 'resubscribe' => array('group'), - 'welcome' => array('group'), - ); - + ], + 'subscribe' => ['group'], + 'unsubscribe' => ['group'], + 'resubscribe' => ['group'], + 'welcome' => ['group'], + ]; /** + * @deprecated + * This is used by CiviMail but will be made redundant by FlexMailer. * @return array */ public static function getRequiredTokens() { if (self::$_requiredTokens == NULL) { - self::$_requiredTokens = array( + self::$_requiredTokens = [ 'domain.address' => ts("Domain address - displays your organization's postal address."), - 'action.optOutUrl or action.unsubscribeUrl' => array( + 'action.optOutUrl or action.unsubscribeUrl' => [ 'action.optOut' => ts("'Opt out via email' - displays an email address for recipients to opt out of receiving emails from your organization."), 'action.optOutUrl' => ts("'Opt out via web page' - creates a link for recipients to click if they want to opt out of receiving emails from your organization. Alternatively, you can include the 'Opt out via email' token."), 'action.unsubscribe' => ts("'Unsubscribe via email' - displays an email address for recipients to unsubscribe from the specific mailing list used to send this message."), 'action.unsubscribeUrl' => ts("'Unsubscribe via web page' - creates a link for recipients to unsubscribe from the specific mailing list used to send this message. Alternatively, you can include the 'Unsubscribe via email' token or one of the Opt-out tokens."), - ), - ); + ], + ]; } return self::$_requiredTokens; } @@ -114,13 +115,14 @@ public static function getRequiredTokens() { * The message. * * @return bool|array - * true if all required tokens are found, + * true if all required tokens are found, * else an array of the missing tokens */ public static function requiredTokens(&$str) { - $requiredTokens = self::getRequiredTokens(); + // FlexMailer is a refactoring of CiviMail which provides new hooks/APIs/docs. If the sysadmin has opted to enable it, then use that instead of CiviMail. + $requiredTokens = defined('CIVICRM_FLEXMAILER_HACK_REQUIRED_TOKENS') ? Civi\Core\Resolver::singleton()->call(CIVICRM_FLEXMAILER_HACK_REQUIRED_TOKENS, []) : CRM_Utils_Token::getRequiredTokens(); - $missing = array(); + $missing = []; foreach ($requiredTokens as $token => $value) { if (!is_array($value)) { if (!preg_match('/(^|[^\{])' . preg_quote('{' . $token . '}') . '/', $str)) { @@ -175,7 +177,7 @@ public static function token_match($type, $var, &$str) { * The token variable. * @param string $value * The value to substitute for the token. - * @param string (reference) $str The string to replace in + * @param string $str (reference) The string to replace in * * @param bool $escapeSmarty * @@ -218,7 +220,7 @@ private static function tokenRegex($token_type) { */ public static function tokenEscapeSmarty($string) { // need to use negative look-behind, as both str_replace() and preg_replace() are sequential - return preg_replace(array('/{/', '/(?getGroupNames() : array('Mailing Groups'); + $groups = $mailing ? $mailing->getGroupNames() : ['Mailing Groups']; $value = implode(', ', $groups); break; @@ -657,17 +659,14 @@ public static function &replaceContactTokens( $returnBlankToken = FALSE, $escapeSmarty = FALSE ) { - $key = 'contact'; - if (self::$_tokens[$key] == NULL) { - // This should come from UF - - self::$_tokens[$key] - = array_merge( - array_keys(CRM_Contact_BAO_Contact::exportableFields('All')), - array('checksum', 'contact_id') - ); - } + // Refresh contact tokens in case they have changed. There is heavy caching + // in exportable fields so there is no benefit in doing this conditionally. + self::$_tokens['contact'] = array_merge( + array_keys(CRM_Contact_BAO_Contact::exportableFields('All')), + ['checksum', 'contact_id'] + ); + $key = 'contact'; // here we intersect with the list of pre-configured valid tokens // so that we remove anything we do not recognize // I hope to move this step out of here soon and @@ -710,7 +709,7 @@ public static function getContactTokenReplacement( self::$_tokens['contact'] = array_merge( array_keys(CRM_Contact_BAO_Contact::exportableFields('All')), - array('checksum', 'contact_id') + ['checksum', 'contact_id'] ); } @@ -753,6 +752,9 @@ public static function getContactTokenReplacement( $value = CRM_Core_PseudoConstant::getLabel('CRM_Contact_BAO_Contact', $token, $value); } } + elseif ($value && CRM_Utils_String::endsWith($token, '_date')) { + $value = CRM_Utils_Date::customFormat($value); + } } if (!$html) { @@ -1101,8 +1103,8 @@ public static function &replaceComponentTokens(&$str, $contact, $components, $es * array of tokens mentioned in field */ public static function getTokens($string) { - $matches = array(); - $tokens = array(); + $matches = []; + $tokens = []; preg_match_all('/(? $contactID) { - $params[] = array( + + $params = []; + foreach ($contactIDs as $contactID) { + $params[] = [ CRM_Core_Form::CB_PREFIX . $contactID, '=', 1, 0, 0, - ); + ]; } // fix for CRM-2613 if ($skipDeceased) { - $params[] = array('is_deceased', '=', 0, 0, 0); + $params[] = ['is_deceased', '=', 0, 0, 0]; } //fix for CRM-3798 if ($skipOnHold) { - $params[] = array('on_hold', '=', 0, 0, 0); + $params[] = ['on_hold', '=', 0, 0, 0]; } if ($extraParams) { @@ -1214,21 +1212,21 @@ public static function getTokenDetails( // if return properties are not passed then get all return properties if (empty($returnProperties)) { $fields = array_merge(array_keys(CRM_Contact_BAO_Contact::exportableFields()), - array('display_name', 'checksum', 'contact_id') + ['display_name', 'checksum', 'contact_id'] ); - foreach ($fields as $key => $val) { + foreach ($fields as $val) { // The unavailable fields are not available as tokens, do not have a one-2-one relationship // with contacts and are expensive to resolve. // @todo see CRM-17253 - there are some other fields (e.g note) that should be excluded // and upstream calls to this should populate return properties. - $unavailableFields = array('group', 'tag'); + $unavailableFields = ['group', 'tag']; if (!in_array($val, $unavailableFields)) { $returnProperties[$val] = 1; } } } - $custom = array(); + $custom = []; foreach ($returnProperties as $name => $dontCare) { $cfID = CRM_Core_BAO_CustomField::getKeyID($name); if ($cfID) { @@ -1236,32 +1234,21 @@ public static function getTokenDetails( } } - //get the total number of contacts to fetch from database. - $numberofContacts = count($contactIDs); - $query = new CRM_Contact_BAO_Query($params, $returnProperties); - - $details = $query->apiQuery($params, $returnProperties, NULL, NULL, 0, $numberofContacts); + $details = CRM_Contact_BAO_Query::apiQuery($params, $returnProperties, NULL, NULL, 0, count($contactIDs), TRUE, FALSE, TRUE, CRM_Contact_BAO_Query::MODE_CONTACTS, NULL, TRUE); $contactDetails = &$details[0]; - foreach ($contactIDs as $key => $contactID) { + foreach ($contactIDs as $contactID) { if (array_key_exists($contactID, $contactDetails)) { - if (CRM_Utils_Array::value('preferred_communication_method', $returnProperties) == 1 - && array_key_exists('preferred_communication_method', $contactDetails[$contactID]) + if (!empty($contactDetails[$contactID]['preferred_communication_method']) ) { - $pcm = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'preferred_communication_method'); - - // communication Preference - $contactPcm = explode(CRM_Core_DAO::VALUE_SEPARATOR, - $contactDetails[$contactID]['preferred_communication_method'] - ); - $result = array(); - foreach ($contactPcm as $key => $val) { + $communicationPreferences = []; + foreach ($contactDetails[$contactID]['preferred_communication_method'] as $val) { if ($val) { - $result[$val] = $pcm[$val]; + $communicationPreferences[$val] = CRM_Core_PseudoConstant::getLabel('CRM_Contact_DAO_Contact', 'preferred_communication_method', $val); } } - $contactDetails[$contactID]['preferred_communication_method'] = implode(', ', $result); + $contactDetails[$contactID]['preferred_communication_method'] = implode(', ', $communicationPreferences); } foreach ($custom as $cfID) { @@ -1271,11 +1258,11 @@ public static function getTokenDetails( } // special case for greeting replacement - foreach (array( - 'email_greeting', - 'postal_greeting', - 'addressee', - ) as $val) { + foreach ([ + 'email_greeting', + 'postal_greeting', + 'addressee', + ] as $val) { if (!empty($contactDetails[$contactID][$val])) { $contactDetails[$contactID][$val] = $contactDetails[$contactID]["{$val}_display"]; } @@ -1311,17 +1298,17 @@ public static function getTokenDetails( * @return array * contactDetails with hooks swapped out */ - public function getAnonymousTokenDetails($contactIDs = array( - 0, - ), + public static function getAnonymousTokenDetails($contactIDs = [ + 0, + ], $returnProperties = NULL, $skipOnHold = TRUE, $skipDeceased = TRUE, $extraParams = NULL, - $tokens = array(), + $tokens = [], $className = NULL, $jobID = NULL) { - $details = array(0 => array()); + $details = [0 => []]; // also call a hook and get token details CRM_Utils_Hook::tokenValues($details[0], $contactIDs, @@ -1332,89 +1319,31 @@ public function getAnonymousTokenDetails($contactIDs = array( return $details; } - /** - * Gives required details of contribuion in an indexed array format so we - * can iterate in a nice loop and do token evaluation - * - * @param array $contributionIDs - * @param array $returnProperties - * Of required properties. - * @param array $extraParams - * Extra params. - * @param array $tokens - * The list of tokens we've extracted from the content. - * @param string $className - * - * @return array - */ - public static function getContributionTokenDetails( - $contributionIDs, - $returnProperties = NULL, - $extraParams = NULL, - $tokens = array(), - $className = NULL - ) { - // @todo this function basically replicates calling - // civicrm_api3('contribution', 'get', array('id' => array('IN' => array()) - if (empty($contributionIDs)) { - // putting a fatal here so we can track if/when this happens - CRM_Core_Error::fatal(); - } - - $details = array(); - - // no apiQuery helper yet, so do a loop and find contribution by id - foreach ($contributionIDs as $contributionID) { - - $dao = new CRM_Contribute_DAO_Contribution(); - $dao->id = $contributionID; - - if ($dao->find(TRUE)) { - - $details[$dao->id] = array(); - CRM_Core_DAO::storeValues($dao, $details[$dao->id]); - - // do the necessary transformation - if (!empty($details[$dao->id]['payment_instrument_id'])) { - $piId = $details[$dao->id]['payment_instrument_id']; - $pis = CRM_Contribute_PseudoConstant::paymentInstrument(); - $details[$dao->id]['payment_instrument'] = $pis[$piId]; - } - if (!empty($details[$dao->id]['campaign_id'])) { - $campaignId = $details[$dao->id]['campaign_id']; - $campaigns = CRM_Campaign_BAO_Campaign::getCampaigns($campaignId); - $details[$dao->id]['campaign'] = $campaigns[$campaignId]; - } - - if (!empty($details[$dao->id]['financial_type_id'])) { - $financialtypeId = $details[$dao->id]['financial_type_id']; - $ftis = CRM_Contribute_PseudoConstant::financialType(); - $details[$dao->id]['financial_type'] = $ftis[$financialtypeId]; - } - - // @todo call a hook to get token contribution details - } - } - - return $details; - } - /** * Get Membership Token Details. * @param array $membershipIDs * Array of membership IDS. */ public static function getMembershipTokenDetails($membershipIDs) { - $memberships = civicrm_api3('membership', 'get', array( - 'options' => array('limit' => 200000), - 'membership_id' => array('IN' => (array) $membershipIDs), - )); + $memberships = civicrm_api3('membership', 'get', [ + 'options' => ['limit' => 0], + 'membership_id' => ['IN' => (array) $membershipIDs], + ]); return $memberships['values']; } /** * Replace existing greeting tokens in message/subject. * + * This function operates by reference, modifying the first parameter. Other + * methods for token replacement in this class return the modified string. + * This leads to inconsistency in how these methods must be applied. + * + * @TODO Remove that inconsistency in usage. + * + * ::replaceContactTokens() may need to be called after this method, to + * replace tokens supplied from this method. + * * @param string $tokenString * @param array $contactDetails * @param int $contactId @@ -1436,6 +1365,7 @@ public static function replaceGreetingTokens(&$tokenString, $contactDetails = NU $tokenString = CRM_Utils_Token::replaceContactTokens($tokenString, $contactDetails, TRUE, $greetingTokens, TRUE, $escapeSmarty); } + self::removeNullContactTokens($tokenString, $contactDetails, $greetingTokens); // check if there are any unevaluated tokens $greetingTokens = self::getTokens($tokenString); @@ -1445,7 +1375,7 @@ public static function replaceGreetingTokens(&$tokenString, $contactDetails = NU if (!empty($greetingTokens) && array_key_exists('contact', $greetingTokens)) { $greetingsReturnProperties = array_flip(CRM_Utils_Array::value('contact', $greetingTokens)); $greetingsReturnProperties = array_fill_keys(array_keys($greetingsReturnProperties), 1); - $contactParams = array('contact_id' => $contactId); + $contactParams = ['contact_id' => $contactId]; $greetingDetails = self::getTokenDetails($contactParams, $greetingsReturnProperties, @@ -1472,13 +1402,13 @@ public static function replaceGreetingTokens(&$tokenString, $contactDetails = NU // Fill the return properties array $greetingTokens = $remainingTokens; reset($greetingTokens); - $greetingsReturnProperties = array(); - while (list($key) = each($greetingTokens)) { - $props = array_flip(CRM_Utils_Array::value($key, $greetingTokens)); + $greetingsReturnProperties = []; + foreach ($greetingTokens as $value) { + $props = array_flip($value); $props = array_fill_keys(array_keys($props), 1); $greetingsReturnProperties = $greetingsReturnProperties + $props; } - $contactParams = array('contact_id' => $contactId); + $contactParams = ['contact_id' => $contactId]; $greetingDetails = self::getTokenDetails($contactParams, $greetingsReturnProperties, FALSE, FALSE, NULL, @@ -1494,25 +1424,69 @@ public static function replaceGreetingTokens(&$tokenString, $contactDetails = NU } } + /** + * At this point, $contactDetails has loaded the contact from the DAO. Any + * (non-custom) missing fields are null. By removing them, we can avoid + * expensive calls to CRM_Contact_BAO_Query. + * + * @param string $tokenString + * @param array $contactDetails + * @param array $greetingTokens + */ + private static function removeNullContactTokens(&$tokenString, $contactDetails, &$greetingTokens) { + $greetingTokensOriginal = $greetingTokens; + $contactFieldList = CRM_Contact_DAO_Contact::fields(); + // Sometimes contactDetails are in a multidemensional array, sometimes a + // single-dimension array. + if (array_key_exists(0, $contactDetails) && is_array($contactDetails[0])) { + $contactDetails = current($contactDetails[0]); + } + $nullFields = array_keys(array_diff_key($contactFieldList, $contactDetails)); + + // Handle legacy tokens + foreach (self::legacyContactTokens() as $oldToken => $newToken) { + if (CRM_Utils_Array::key($newToken, $nullFields)) { + $nullFields[] = $oldToken; + } + } + + // Remove null contact fields from $greetingTokens + $greetingTokens['contact'] = array_diff($greetingTokens['contact'], $nullFields); + + // Also remove them from $tokenString + $removedTokens = array_diff($greetingTokensOriginal['contact'], $greetingTokens['contact']); + // Handle legacy tokens again, sigh + if (!empty($removedTokens)) { + foreach ($removedTokens as $token) { + if (CRM_Utils_Array::value($token, self::legacyContactTokens()) !== NULL) { + $removedTokens[] = CRM_Utils_Array::value($token, self::legacyContactTokens()); + } + } + foreach ($removedTokens as $token) { + $tokenString = str_replace("{contact.$token}", '', $tokenString); + } + } + } + /** * @param $tokens * * @return array */ public static function flattenTokens(&$tokens) { - $flattenTokens = array(); + $flattenTokens = []; - foreach (array( - 'html', - 'text', - 'subject', - ) as $prop) { + foreach ([ + 'html', + 'text', + 'subject', + ] as $prop) { if (!isset($tokens[$prop])) { continue; } foreach ($tokens[$prop] as $type => $names) { if (!isset($flattenTokens[$type])) { - $flattenTokens[$type] = array(); + $flattenTokens[$type] = []; } foreach ($names as $name) { $flattenTokens[$type][$name] = 1; @@ -1585,7 +1559,8 @@ protected static function _buildContributionTokens() { $key = 'contribution'; if (self::$_tokens[$key] == NULL) { self::$_tokens[$key] = array_keys(array_merge(CRM_Contribute_BAO_Contribution::exportableFields('All'), - array('campaign', 'financial_type') + ['campaign', 'financial_type'], + self::getCustomFieldTokens('Contribution') )); } } @@ -1596,7 +1571,7 @@ protected static function _buildContributionTokens() { protected static function _buildMembershipTokens() { $key = 'membership'; if (!isset(self::$_tokens[$key]) || self::$_tokens[$key] == NULL) { - $membershipTokens = array(); + $membershipTokens = []; $tokens = CRM_Core_SelectValues::membershipTokens(); foreach ($tokens as $token => $dontCare) { $membershipTokens[] = substr($token, (strpos($token, '.') + 1), -1); @@ -1618,13 +1593,13 @@ protected static function _buildMembershipTokens() { * @return string * string with replacements made */ - public static function replaceEntityTokens($entity, $entityArray, $str, $knownTokens = array(), $escapeSmarty = FALSE) { + public static function replaceEntityTokens($entity, $entityArray, $str, $knownTokens = [], $escapeSmarty = FALSE) { if (!$knownTokens || empty($knownTokens[$entity])) { return $str; } $fn = 'get' . ucfirst($entity) . 'TokenReplacement'; - $fn = is_callable(array('CRM_Utils_Token', $fn)) ? $fn : 'getApiTokenReplacement'; + $fn = is_callable(['CRM_Utils_Token', $fn]) ? $fn : 'getApiTokenReplacement'; // since we already know the tokens lets just use them & do str_replace which is faster & simpler than preg_replace foreach ($knownTokens[$entity] as $token) { $replacement = self::$fn($entity, $token, $entityArray); @@ -1644,11 +1619,11 @@ public static function replaceEntityTokens($entity, $entityArray, $str, $knownTo * @return string * @throws \CiviCRM_API3_Exception */ - public static function replaceCaseTokens($caseId, $str, $knownTokens = array(), $escapeSmarty = FALSE) { + public static function replaceCaseTokens($caseId, $str, $knownTokens = [], $escapeSmarty = FALSE) { if (!$knownTokens || empty($knownTokens['case'])) { return $str; } - $case = civicrm_api3('case', 'getsingle', array('id' => $caseId)); + $case = civicrm_api3('case', 'getsingle', ['id' => $caseId]); return self::replaceEntityTokens('case', $case, $str, $knownTokens, $escapeSmarty); } @@ -1665,16 +1640,16 @@ public static function getApiTokenReplacement($entity, $token, $entityArray) { if (!isset($entityArray[$token])) { return ''; } - $field = civicrm_api3($entity, 'getfield', array('action' => 'get', 'name' => $token, 'get_options' => 'get')); + $field = civicrm_api3($entity, 'getfield', ['action' => 'get', 'name' => $token, 'get_options' => 'get']); $field = $field['values']; $fieldType = CRM_Utils_Array::value('type', $field); // Boolean fields if ($fieldType == CRM_Utils_Type::T_BOOLEAN && empty($field['options'])) { - $field['options'] = array(ts('No'), ts('Yes')); + $field['options'] = [ts('No'), ts('Yes')]; } // Match pseudoconstants if (!empty($field['options'])) { - $ret = array(); + $ret = []; foreach ((array) $entityArray[$token] as $val) { $ret[] = $field['options'][$val]; } @@ -1701,7 +1676,8 @@ public static function getApiTokenReplacement($entity, $token, $entityArray) { public static function replaceContributionTokens($str, &$contribution, $html = FALSE, $knownTokens = NULL, $escapeSmarty = FALSE) { $key = 'contribution'; if (!$knownTokens || !CRM_Utils_Array::value($key, $knownTokens)) { - return $str; //early return + //early return + return $str; } self::_buildContributionTokens(); @@ -1747,10 +1723,10 @@ public static function replaceMultipleContributionTokens($separator, $str, &$con } if (in_array('receive_date', $knownTokens['contribution'])) { - $formattedDates = array(); + $formattedDates = []; $dates = explode($separator, $contribution['receive_date']); foreach ($dates as $date) { - $formattedDates[] = CRM_Utils_Date::customFormat($date, NULL, array('j', 'm', 'Y')); + $formattedDates[] = CRM_Utils_Date::customFormat($date, NULL, ['j', 'm', 'Y']); } $str = str_replace("{contribution.receive_date}", implode($separator, $formattedDates), $str); unset($knownTokens['contribution']['receive_date']); @@ -1783,10 +1759,11 @@ public static function getMembershipTokenReplacement($entity, $token, $membershi case 'fee': try { - $value = civicrm_api3('membership_type', 'getvalue', array( - 'id' => $membership['membership_type_id'], - 'return' => 'minimum_fee', - )); + $value = civicrm_api3('membership_type', 'getvalue', [ + 'id' => $membership['membership_type_id'], + 'return' => 'minimum_fee', + ]); + $value = CRM_Utils_Money::format($value, NULL, NULL, TRUE); } catch (CiviCRM_API3_Exception $e) { // we can anticipate we will get an error if the minimum fee is set to 'NULL' because of the way the @@ -1798,6 +1775,9 @@ public static function getMembershipTokenReplacement($entity, $token, $membershi default: if (in_array($token, self::$_tokens[$entity])) { $value = $membership[$token]; + if (CRM_Utils_String::endsWith($token, '_date')) { + $value = CRM_Utils_Date::customFormat($value); + } } else { // ie unchanged @@ -1830,7 +1810,7 @@ public static function getContributionTokenReplacement($token, &$contribution, $ case 'receive_date': $value = CRM_Utils_Array::retrieveValueRecursive($contribution, $token); - $value = CRM_Utils_Date::customFormat($value, NULL, array('j', 'm', 'Y')); + $value = CRM_Utils_Date::customFormat($value, NULL, ['j', 'm', 'Y']); break; default: @@ -1854,12 +1834,31 @@ public static function getContributionTokenReplacement($token, &$contribution, $ * [legacy_token => new_token] */ public static function legacyContactTokens() { - return array( + return [ 'individual_prefix' => 'prefix_id', 'individual_suffix' => 'suffix_id', 'gender' => 'gender_id', 'communication_style' => 'communication_style_id', - ); + ]; + } + + /** + * Get all custom field tokens of $entity + * + * @param string $entity + * @param bool $usedForTokenWidget + * + * @return array + * return custom field tokens in array('custom_N' => 'label') format + */ + public static function getCustomFieldTokens($entity, $usedForTokenWidget = FALSE) { + $customTokens = []; + $tokenName = $usedForTokenWidget ? "{contribution.custom_%d}" : "custom_%d"; + foreach (CRM_Core_BAO_CustomField::getFields($entity) as $id => $info) { + $customTokens[sprintf($tokenName, $id)] = $info['label']; + } + + return $customTokens; } /** @@ -1869,7 +1868,7 @@ public static function legacyContactTokens() { * @return array */ public static function formatTokensForDisplay($tokens) { - $sorted = $output = array(); + $sorted = $output = []; // Sort in ascending order by ignoring word case natcasesort($tokens); @@ -1880,7 +1879,7 @@ public static function formatTokensForDisplay($tokens) { // Check to see if this token is already in a group e.g. for custom fields $split = explode(' :: ', $v); if (!empty($split[1])) { - $sorted[$split[1]][] = array('id' => $k, 'text' => $split[0]); + $sorted[$split[1]][] = ['id' => $k, 'text' => $split[0]]; } // Group by entity else { @@ -1891,13 +1890,13 @@ public static function formatTokensForDisplay($tokens) { else { $entity = 'Contact'; } - $sorted[ts($entity)][] = array('id' => $k, 'text' => $v); + $sorted[ts($entity)][] = ['id' => $k, 'text' => $v]; } } ksort($sorted); foreach ($sorted as $k => $v) { - $output[] = array('text' => $k, 'children' => $v); + $output[] = ['text' => $k, 'children' => $v]; } return $output; diff --git a/CRM/Utils/Type.php b/CRM/Utils/Type.php index 9244e8c0dd49..e2786712a420 100644 --- a/CRM/Utils/Type.php +++ b/CRM/Utils/Type.php @@ -1,9 +1,9 @@ 'int representing type' + */ + public static function getValidTypes() { + return [ + 'Int' => self::T_INT, + 'String' => self::T_STRING, + 'Enum' => self::T_ENUM, + 'Date' => self::T_DATE, + 'Time' => self::T_TIME, + 'Boolean' => self::T_BOOLEAN, + 'Text' => self::T_TEXT, + 'Blob' => self::T_BLOB, + 'Timestamp' => self::T_TIMESTAMP, + 'Float' => self::T_FLOAT, + 'Money' => self::T_MONEY, + 'Email' => self::T_EMAIL, + 'Mediumblob' => self::T_MEDIUMBLOB, + ]; + } + + /** + * Get the data_type for the field. + * + * @param array $fieldMetadata + * Metadata about the field. + * + * @return string + */ + public static function getDataTypeFromFieldMetadata($fieldMetadata) { + if (isset($fieldMetadata['data_type'])) { + return $fieldMetadata['data_type']; + } + if (empty($fieldMetadata['type'])) { + // I would prefer to throw an e-notice but there is some, + // probably unnecessary logic, that only retrieves activity fields + // if they are 'in the profile' and probably they are not 'in' + // until they are added - which might lead to ? who knows! + return ''; + } + return self::typeToString($fieldMetadata['type']); + } + + /** + * Helper function to call escape on arrays. * * @see escape */ @@ -290,8 +344,34 @@ public static function escape($data, $type, $abort = TRUE) { case 'MysqlOrderBy': if (CRM_Utils_Rule::mysqlOrderBy($data)) { $parts = explode(',', $data); - foreach ($parts as &$part) { - $part = preg_replace_callback('/^(?:(?:((?:`[\w-]{1,64}`|[\w-]{1,64}))(?:\.))?(`[\w-]{1,64}`|[\w-]{1,64})(?: (asc|desc))?)$/i', array('CRM_Utils_Type', 'mysqlOrderByCallback'), trim($part)); + + // The field() syntax is tricky here because it uses commas & when + // we separate by them we break it up. But we want to keep the clauses in order. + // so we just clumsily re-assemble it. Test cover exists. + $fieldClauseStart = NULL; + foreach ($parts as $index => &$part) { + if (substr($part, 0, 6) === 'field(') { + // Looking to escape a string like 'field(contribution_status_id,3,4,5) asc' + // to 'field(`contribution_status_id`,3,4,5) asc' + $fieldClauseStart = $index; + continue; + } + if ($fieldClauseStart !== NULL) { + // this is part of the list of field options. Concatenate it back on. + $parts[$fieldClauseStart] .= ',' . $part; + unset($parts[$index]); + if (!strstr($parts[$fieldClauseStart], ')')) { + // we have not reached the end of the list. + continue; + } + // We have the last piece of the field() clause, time to escape it. + $parts[$fieldClauseStart] = self::mysqlOrderByFieldFunctionCallback($parts[$fieldClauseStart]); + $fieldClauseStart = NULL; + continue; + + } + // Normal clause. + $part = preg_replace_callback('/^(?:(?:((?:`[\w-]{1,64}`|[\w-]{1,64}))(?:\.))?(`[\w-]{1,64}`|[\w-]{1,64})(?: (asc|desc))?)$/i', ['CRM_Utils_Type', 'mysqlOrderByCallback'], trim($part)); } return implode(', ', $parts); } @@ -321,13 +401,47 @@ public static function escape($data, $type, $abort = TRUE) { * The type to validate against. * @param bool $abort * If TRUE, the operation will CRM_Core_Error::fatal() on invalid data. - * @name string $name + * @param string $name * The name of the attribute + * @param bool $isThrowException + * Should an exception be thrown rather than a using a deprecated fatal error. * * @return mixed * The data, escaped if necessary + * + * @throws \CRM_Core_Exception */ - public static function validate($data, $type, $abort = TRUE, $name = 'One of parameters ') { + public static function validate($data, $type, $abort = TRUE, $name = 'One of parameters ', $isThrowException = FALSE) { + + $possibleTypes = [ + 'Integer', + 'Int', + 'Positive', + 'CommaSeparatedIntegers', + 'Boolean', + 'Float', + 'Money', + 'Text', + 'String', + 'Link', + 'Memo', + 'Date', + 'Timestamp', + 'ContactReference', + 'MysqlColumnNameOrAlias', + 'MysqlOrderByDirection', + 'MysqlOrderBy', + 'ExtensionKey', + 'Json', + 'Alphanumeric', + 'Color', + ]; + if (!in_array($type, $possibleTypes)) { + if ($isThrowException) { + throw new CRM_Core_Exception(ts('Invalid type, must be one of : ' . implode($possibleTypes))); + } + CRM_Core_Error::fatal(ts('Invalid type, must be one of : ' . implode($possibleTypes))); + } switch ($type) { case 'Integer': case 'Int': @@ -342,6 +456,12 @@ public static function validate($data, $type, $abort = TRUE, $name = 'One of par } break; + case 'CommaSeparatedIntegers': + if (CRM_Utils_Rule::commaSeparatedIntegers($data)) { + return $data; + } + break; + case 'Boolean': if (CRM_Utils_Rule::boolean($data)) { return $data; @@ -418,19 +538,55 @@ public static function validate($data, $type, $abort = TRUE, $name = 'One of par } break; - default: - CRM_Core_Error::fatal("Cannot recognize $type for $data"); + case 'ExtensionKey': + if (CRM_Utils_Rule::checkExtensionKeyIsValid($data)) { + return $data; + } + break; + + case 'Json': + if (CRM_Utils_Rule::json($data)) { + return $data; + } + break; + + case 'Alphanumeric': + if (CRM_Utils_Rule::alphanumeric($data)) { + return $data; + } + break; + + case 'Color': + if (CRM_Utils_Rule::color($data)) { + return $data; + } break; } if ($abort) { $data = htmlentities($data); + if ($isThrowException) { + throw new CRM_Core_Exception("$name (value: $data) is not of the type $type"); + } CRM_Core_Error::fatal("$name (value: $data) is not of the type $type"); } return NULL; } + /** + * Preg_replace_callback for mysqlOrderByFieldFunction escape. + * + * Add backticks around the field name. + * + * @param string $clause + * + * @return string + */ + public static function mysqlOrderByFieldFunctionCallback($clause) { + return preg_replace('/field\((\w*)/', 'field(`${1}`', $clause); + } + /** * preg_replace_callback for MysqlOrderBy escape. */ @@ -457,12 +613,12 @@ public static function mysqlOrderByCallback($matches) { } /** - * Get list of avaliable Data Tupes for Option Groups + * Get list of avaliable Data Types for Option Groups * * @return array */ public static function dataTypes() { - $types = array( + $types = [ 'Integer', 'String', 'Date', @@ -470,7 +626,7 @@ public static function dataTypes() { 'Timestamp', 'Money', 'Email', - ); + ]; return array_combine($types, $types); } diff --git a/CRM/Utils/Url.php b/CRM/Utils/Url.php new file mode 100644 index 000000000000..4396263aacef --- /dev/null +++ b/CRM/Utils/Url.php @@ -0,0 +1,55 @@ +__toString(); + } + +} diff --git a/CRM/Utils/Verp.php b/CRM/Utils/Verp.php index 411b5869b1da..a9d5140c3cdd 100644 --- a/CRM/Utils/Verp.php +++ b/CRM/Utils/Verp.php @@ -1,9 +1,9 @@ '2B', '@' => '40', ':' => '3A', @@ -44,11 +46,13 @@ class CRM_Utils_Verp { '-' => '2D', '[' => '5B', ']' => '5D', - ); + ]; - /* Mapping of hex codes to reserved characters */ - - static $decodeMap = array( + /** + * Mapping of hex codes to reserved characters + * @var array + */ + public static $decodeMap = [ '40' => '@', '3A' => ':', '25' => '%', @@ -57,7 +61,7 @@ class CRM_Utils_Verp { '5B' => '[', '5D' => ']', '2B' => '+', - ); + ]; /** * Encode the sender's address with the VERPed recipient. @@ -109,7 +113,7 @@ public static function &verpdecode($address) { $rdomain = preg_replace("/+$code/i", $char, $rdomain); } - return array("$slocal@$sdomain", "$rlocal@$rdomain"); + return ["$slocal@$sdomain", "$rlocal@$rdomain"]; } } diff --git a/CRM/Utils/VersionCheck.php b/CRM/Utils/VersionCheck.php index 0f965601fefa..eb72bd94518a 100644 --- a/CRM/Utils/VersionCheck.php +++ b/CRM/Utils/VersionCheck.php @@ -1,9 +1,9 @@ localVersion = CRM_Utils_System::version(); - $this->localMajorVersion = $this->getMajorVersion($this->localVersion); $this->cacheFile = CRM_Core_Config::singleton()->uploadDir . self::CACHEFILE_NAME; } @@ -106,7 +98,7 @@ public function initialize() { // Populate remote $versionInfo from cache file $this->isInfoAvailable = $this->readCacheFile(); - // Poor-man's cron fallback if scheduled job is enabled but has failed to run + // Fallback if scheduled job is enabled but has failed to run. $expiryTime = time() - self::CACHEFILE_EXPIRE; if (!empty($this->cronJob['is_active']) && (!$this->isInfoAvailable || filemtime($this->cacheFile) < $expiryTime) @@ -126,113 +118,19 @@ public function initialize() { * * @param $info */ - public function setVersionInfo($info) { - $this->versionInfo = (array) $info; - // Sort version info in ascending order for easier comparisons - ksort($this->versionInfo, SORT_NUMERIC); - } - - /** - * Finds the release info for a minor version. - * @param string $version - * @return array|null - */ - public function getReleaseInfo($version) { - $majorVersion = $this->getMajorVersion($version); - if (isset($this->versionInfo[$majorVersion])) { - foreach ($this->versionInfo[$majorVersion]['releases'] as $info) { - if ($info['version'] == $version) { - return $info; - } - } - } - return NULL; + protected function setVersionInfo($info) { + $this->versionInfo = $info; } /** - * @param $minorVersion - * @return string + * @return array|NULL + * message: string + * title: string + * severity: string + * Ex: 'info', 'notice', 'warning', 'critical'. */ - public function getMajorVersion($minorVersion) { - if (!$minorVersion) { - return NULL; - } - list($a, $b) = explode('.', $minorVersion); - return "$a.$b"; - } - - - /** - * Get the latest version number if it's newer than the local one - * - * @return array - * Returns version number of the latest release if it is greater than the local version, - * along with the type of upgrade (regular/security) needed and the status of the major - * version - */ - public function isNewerVersionAvailable() { - $return = array( - 'version' => NULL, - 'upgrade' => NULL, - 'status' => NULL, - ); - - if ($this->versionInfo && $this->localVersion) { - if (isset($this->versionInfo[$this->localMajorVersion])) { - switch (CRM_Utils_Array::value('status', $this->versionInfo[$this->localMajorVersion])) { - case 'stable': - case 'lts': - case 'testing': - // look for latest version in this major version - $releases = $this->checkBranchForNewVersion($this->versionInfo[$this->localMajorVersion]); - if ($releases['newest']) { - $return['version'] = $releases['newest']; - - // check for intervening security releases - $return['upgrade'] = ($releases['security']) ? 'security' : 'regular'; - } - break; - - case 'eol': - default: - // look for latest version ever - foreach ($this->versionInfo as $majorVersionNumber => $majorVersion) { - if ($majorVersionNumber < $this->localMajorVersion || $majorVersion['status'] == 'testing') { - continue; - } - $releases = $this->checkBranchForNewVersion($this->versionInfo[$majorVersionNumber]); - - if ($releases['newest']) { - $return['version'] = $releases['newest']; - - // check for intervening security releases - $return['upgrade'] = ($releases['security'] || $return['upgrade'] == 'security') ? 'security' : 'regular'; - } - } - } - $return['status'] = $this->versionInfo[$this->localMajorVersion]['status']; - } - else { - // Figure if the version is really old or really new - $wayOld = TRUE; - - foreach ($this->versionInfo as $majorVersionNumber => $majorVersion) { - $wayOld = ($this->localMajorVersion < $majorVersionNumber); - } - - if ($wayOld) { - $releases = $this->checkBranchForNewVersion($majorVersion); - - $return = array( - 'version' => $releases['newest'], - 'upgrade' => 'security', - 'status' => 'eol', - ); - } - } - } - - return $return; + public function getVersionMessages() { + return $this->isInfoAvailable ? $this->versionInfo : NULL; } /** @@ -243,28 +141,6 @@ public function fetch() { $this->pingBack(); } - /** - * @param $majorVersion - * @return null|string - */ - private function checkBranchForNewVersion($majorVersion) { - $newerVersion = array( - 'newest' => NULL, - 'security' => NULL, - ); - if (!empty($majorVersion['releases'])) { - foreach ($majorVersion['releases'] as $release) { - if (version_compare($this->localVersion, $release['version']) < 0) { - $newerVersion['newest'] = $release['version']; - if (CRM_Utils_Array::value('security', $release)) { - $newerVersion['security'] = $release['version']; - } - } - } - } - return $newerVersion; - } - /** * Collect info about the site to be sent as pingback data. */ @@ -273,11 +149,11 @@ private function getSiteStats() { $siteKey = md5(defined('CIVICRM_SITE_KEY') ? CIVICRM_SITE_KEY : ''); // Calorie-free pingback for alphas - $this->stats = array('version' => $this->localVersion); + $this->stats = ['version' => $this->localVersion]; // Non-alpha versions get the full treatment if ($this->localVersion && !strpos($this->localVersion, 'alpha')) { - $this->stats += array( + $this->stats += [ 'hash' => md5($siteKey . $config->userFrameworkBaseURL), 'uf' => $config->userFramework, 'lang' => $config->lcMessages, @@ -286,7 +162,8 @@ private function getSiteStats() { 'PHP' => phpversion(), 'MySQL' => CRM_CORE_DAO::singleValueQuery('SELECT VERSION()'), 'communityMessagesUrl' => Civi::settings()->get('communityMessagesUrl'), - ); + ]; + $this->getDomainStats(); $this->getPayProcStats(); $this->getEntityStats(); $this->getExtensionStats(); @@ -300,13 +177,12 @@ private function getPayProcStats() { $dao = new CRM_Financial_DAO_PaymentProcessor(); $dao->is_active = 1; $dao->find(); - $ppTypes = array(); - - // Get title and id for all processor types - $ppTypeNames = CRM_Core_PseudoConstant::paymentProcessorType(); + $ppTypes = []; + // Get title for all processor types + // FIXME: This should probably be getName, but it has always returned translated label so we stick with that for now as it would affect stats while ($dao->fetch()) { - $ppTypes[] = $ppTypeNames[$dao->payment_processor_type_id]; + $ppTypes[] = CRM_Core_PseudoConstant::getLabel('CRM_Financial_BAO_PaymentProcessor', 'payment_processor_type_id', $dao->payment_processor_type_id); } // add the .-separated list of the processor types $this->stats['PPTypes'] = implode(',', array_unique($ppTypes)); @@ -317,7 +193,7 @@ private function getPayProcStats() { * Add info to the 'entities' array */ private function getEntityStats() { - $tables = array( + $tables = [ 'CRM_Activity_DAO_Activity' => 'is_test = 0', 'CRM_Case_DAO_Case' => 'is_deleted = 0', 'CRM_Contact_DAO_Contact' => 'is_deleted = 0', @@ -339,17 +215,18 @@ private function getEntityStats() { 'CRM_Member_DAO_MembershipBlock' => 'is_active = 1', 'CRM_Pledge_DAO_Pledge' => 'is_test = 0', 'CRM_Pledge_DAO_PledgeBlock' => NULL, - ); + 'CRM_Mailing_Event_DAO_Delivered' => NULL, + ]; foreach ($tables as $daoName => $where) { $dao = new $daoName(); if ($where) { $dao->whereAdd($where); } $short_name = substr($daoName, strrpos($daoName, '_') + 1); - $this->stats['entities'][] = array( + $this->stats['entities'][] = [ 'name' => $short_name, 'size' => $dao->count(), - ); + ]; } } @@ -361,11 +238,11 @@ private function getExtensionStats() { // Core components $config = CRM_Core_Config::singleton(); foreach ($config->enableComponents as $comp) { - $this->stats['extensions'][] = array( + $this->stats['extensions'][] = [ 'name' => 'org.civicrm.component.' . strtolower($comp), 'enabled' => 1, 'version' => $this->stats['version'], - ); + ]; } // Contrib extensions $mapper = CRM_Extension_System::singleton()->getMapper(); @@ -373,11 +250,41 @@ private function getExtensionStats() { $dao->find(); while ($dao->fetch()) { $info = $mapper->keyToInfo($dao->full_name); - $this->stats['extensions'][] = array( + $this->stats['extensions'][] = [ 'name' => $dao->full_name, 'enabled' => $dao->is_active, 'version' => isset($info->version) ? $info->version : NULL, - ); + ]; + } + } + + /** + * Fetch stats about domain and add to 'stats' array. + */ + private function getDomainStats() { + // Start with default value NULL, then check to see if there's a better + // value to be had. + $this->stats['domain_isoCode'] = NULL; + $params = [ + 'id' => CRM_Core_Config::domainID(), + ]; + $domain_result = civicrm_api3('domain', 'getsingle', $params); + if (!empty($domain_result['contact_id'])) { + $address_params = [ + 'contact_id' => $domain_result['contact_id'], + 'is_primary' => 1, + 'sequential' => 1, + ]; + $address_result = civicrm_api3('address', 'get', $address_params); + if ($address_result['count'] == 1 && !empty($address_result['values'][0]['country_id'])) { + $country_params = [ + 'id' => $address_result['values'][0]['country_id'], + ]; + $country_result = civicrm_api3('country', 'getsingle', $country_params); + if (!empty($country_result['iso_code'])) { + $this->stats['domain_isoCode'] = $country_result['iso_code']; + } + } } } @@ -386,13 +293,13 @@ private function getExtensionStats() { * Store results in the cache file */ private function pingBack() { - $params = array( - 'http' => array( + $params = [ + 'http' => [ 'method' => 'POST', 'header' => 'Content-type: application/x-www-form-urlencoded', 'content' => http_build_query($this->stats), - ), - ); + ], + ]; $ctx = stream_context_create($params); $rawJson = file_get_contents($this->pingbackUrl, FALSE, $ctx); $versionInfo = $rawJson ? json_decode($rawJson, TRUE) : NULL; @@ -428,16 +335,25 @@ private function writeCacheFile($contents) { } } + /** + * Removes cached version info. + */ + public function flushCache() { + if (file_exists($this->cacheFile)) { + unlink($this->cacheFile); + } + } + /** * Lookup version_check scheduled job */ private function getJob() { - $jobs = civicrm_api3('Job', 'get', array( + $jobs = civicrm_api3('Job', 'get', [ 'sequential' => 1, 'api_action' => "version_check", 'api_entity' => "job", - )); - $this->cronJob = CRM_Utils_Array::value(0, $jobs['values'], array()); + ]); + $this->cronJob = CRM_Utils_Array::value(0, $jobs['values'], []); } } diff --git a/CRM/Utils/VisualBundle.php b/CRM/Utils/VisualBundle.php new file mode 100644 index 000000000000..139a7ec19e8a --- /dev/null +++ b/CRM/Utils/VisualBundle.php @@ -0,0 +1,110 @@ +addScriptUrl(Civi::service('asset_manager')->getUrl('visual-bundle.js')); + Civi::resources()->addStyleUrl(Civi::service('asset_manager')->getUrl('visual-bundle.css')); + } + + /** + * Generate asset content (when accessed via AssetBuilder). + * + * @param \Civi\Core\Event\GenericHookEvent $event + * @see CRM_Utils_hook::buildAsset() + * @see \Civi\Core\AssetBuilder + */ + public static function buildAssetJs($event) { + if ($event->asset !== 'visual-bundle.js') { + return; + } + + $files = [ + 'crossfilter' => '[civicrm.bower]/crossfilter-1.3.x/crossfilter.min.js', + 'd3' => '[civicrm.bower]/d3-3.5.x/d3.min.js', + 'dc' => '[civicrm.bower]/dc-2.1.x/dc.min.js', + ]; + + $content = []; + $content[] = "(function(){"; + $content[] = "var backups = {d3: window.d3, crossfilter: window.crossfilter, dc: window.dc}"; + $content[] = 'window.CRM = window.CRM || {};'; + $content[] = 'CRM.visual = CRM.visual || {};'; + foreach ($files as $var => $file) { + $content[] = "// File: $file"; + $content[] = file_get_contents(Civi::paths()->getPath($file)); + } + foreach ($files as $var => $file) { + $content[] = "CRM.visual.$var = $var;"; + } + foreach ($files as $var => $file) { + $content[] = "window.$var = backups.$var;"; + } + $content[] = "})();"; + + $event->mimeType = 'application/javascript'; + $event->content = implode("\n", $content); + } + + /** + * Generate asset content (when accessed via AssetBuilder). + * + * @param \Civi\Core\Event\GenericHookEvent $event + * @see CRM_Utils_hook::buildAsset() + * @see \Civi\Core\AssetBuilder + */ + public static function buildAssetCss($event) { + if ($event->asset !== 'visual-bundle.css') { + return; + } + + $files = [ + '[civicrm.bower]/dc-2.1.x/dc.min.css', + ]; + + $content = []; + foreach ($files as $file) { + $content[] = "// File: $file"; + $content[] = file_get_contents(Civi::paths()->getPath($file)); + } + + $event->mimeType = 'text/css'; + $event->content = implode("\n", $content); + } + +} diff --git a/CRM/Utils/Weight.php b/CRM/Utils/Weight.php index 8da6d84a157e..3e4add68b7cf 100644 --- a/CRM/Utils/Weight.php +++ b/CRM/Utils/Weight.php @@ -1,9 +1,9 @@ fetch()) { if (in_array($field->weight, $weights)) { $sameWeightCount++; @@ -308,11 +309,11 @@ public static function &query( $fields = &$dao->fields(); $fieldlist = array_keys($fields); - $whereConditions = array(); + $whereConditions = []; if ($additionalWhere) { $whereConditions[] = $additionalWhere; } - $params = array(); + $params = []; $fieldNum = 0; if (is_array($fieldValues)) { foreach ($fieldValues as $fieldName => $value) { @@ -323,7 +324,7 @@ public static function &query( $fieldNum++; $whereConditions[] = "$fieldName = %$fieldNum"; $fieldType = $fields[$fieldName]['type']; - $params[$fieldNum] = array($value, CRM_Utils_Type::typeToString($fieldType)); + $params[$fieldNum] = [$value, CRM_Utils_Type::typeToString($fieldType)]; } } $where = implode(' AND ', $whereConditions); @@ -386,13 +387,13 @@ public static function addOrder(&$rows, $daoName, $idName, $returnURL, $filter = $config = CRM_Core_Config::singleton(); $imageURL = $config->userFrameworkResourceURL . 'i/arrow'; - $queryParams = array( + $queryParams = [ 'reset' => 1, 'dao' => $daoName, 'idName' => $idName, 'url' => $returnURL, 'filter' => $filter, - ); + ]; $signer = new CRM_Utils_Signer(CRM_Core_Key::privateKey(), self::$SIGNABLE_FIELDS); $queryParams['_sgn'] = $signer->sign($queryParams); @@ -403,7 +404,7 @@ public static function addOrder(&$rows, $daoName, $idName, $returnURL, $filter = $prevID = $ids[$i - 1]; $nextID = $ids[$i + 1]; - $links = array(); + $links = []; $url = "{$baseURL}&src=$id"; if ($prevID != 0) { @@ -434,7 +435,7 @@ public static function addOrder(&$rows, $daoName, $idName, $returnURL, $filter = } public static function fixOrder() { - $signature = CRM_Utils_Request::retrieve('_sgn', 'String', CRM_Core_DAO::$_nullObject); + $signature = CRM_Utils_Request::retrieve('_sgn', 'String'); $signer = new CRM_Utils_Signer(CRM_Core_Key::privateKey(), self::$SIGNABLE_FIELDS); // Validate $_GET values b/c subsequent code reads $_GET (via CRM_Utils_Request::retrieve) @@ -443,14 +444,14 @@ public static function fixOrder() { } // Note: Ensure this list matches self::$SIGNABLE_FIELDS - $daoName = CRM_Utils_Request::retrieve('dao', 'String', CRM_Core_DAO::$_nullObject); - $id = CRM_Utils_Request::retrieve('id', 'Integer', CRM_Core_DAO::$_nullObject); - $idName = CRM_Utils_Request::retrieve('idName', 'String', CRM_Core_DAO::$_nullObject); - $url = CRM_Utils_Request::retrieve('url', 'String', CRM_Core_DAO::$_nullObject); - $filter = CRM_Utils_Request::retrieve('filter', 'String', CRM_Core_DAO::$_nullObject); - $src = CRM_Utils_Request::retrieve('src', 'Integer', CRM_Core_DAO::$_nullObject); - $dst = CRM_Utils_Request::retrieve('dst', 'Integer', CRM_Core_DAO::$_nullObject); - $dir = CRM_Utils_Request::retrieve('dir', 'String', CRM_Core_DAO::$_nullObject); + $daoName = CRM_Utils_Request::retrieve('dao', 'String'); + $id = CRM_Utils_Request::retrieve('id', 'Integer'); + $idName = CRM_Utils_Request::retrieve('idName', 'String'); + $url = CRM_Utils_Request::retrieve('url', 'String'); + $filter = CRM_Utils_Request::retrieve('filter', 'String'); + $src = CRM_Utils_Request::retrieve('src', 'Integer'); + $dst = CRM_Utils_Request::retrieve('dst', 'Integer'); + $dir = CRM_Utils_Request::retrieve('dir', 'String'); $object = new $daoName(); $srcWeight = CRM_Core_DAO::getFieldValue($daoName, $src, 'weight', $idName); $dstWeight = CRM_Core_DAO::getFieldValue($daoName, $dst, 'weight', $idName); @@ -461,17 +462,17 @@ public static function fixOrder() { $tableName = $object->tableName(); $query = "UPDATE $tableName SET weight = %1 WHERE $idName = %2"; - $params = array( - 1 => array($dstWeight, 'Integer'), - 2 => array($src, 'Integer'), - ); + $params = [ + 1 => [$dstWeight, 'Integer'], + 2 => [$src, 'Integer'], + ]; CRM_Core_DAO::executeQuery($query, $params); if ($dir == 'swap') { - $params = array( - 1 => array($srcWeight, 'Integer'), - 2 => array($dst, 'Integer'), - ); + $params = [ + 1 => [$srcWeight, 'Integer'], + 2 => [$dst, 'Integer'], + ]; CRM_Core_DAO::executeQuery($query, $params); } elseif ($dir == 'first') { @@ -480,10 +481,10 @@ public static function fixOrder() { if ($filter) { $query .= " AND $filter"; } - $params = array( - 1 => array($src, 'Integer'), - 2 => array($srcWeight, 'Integer'), - ); + $params = [ + 1 => [$src, 'Integer'], + 2 => [$srcWeight, 'Integer'], + ]; CRM_Core_DAO::executeQuery($query, $params); } elseif ($dir == 'last') { @@ -492,10 +493,10 @@ public static function fixOrder() { if ($filter) { $query .= " AND $filter"; } - $params = array( - 1 => array($src, 'Integer'), - 2 => array($srcWeight, 'Integer'), - ); + $params = [ + 1 => [$src, 'Integer'], + 2 => [$srcWeight, 'Integer'], + ]; CRM_Core_DAO::executeQuery($query, $params); } @@ -510,9 +511,9 @@ public static function fixOrderOutput($url) { CRM_Utils_System::redirect($url); } - CRM_Core_Page_AJAX::returnJsonResponse(array( + CRM_Core_Page_AJAX::returnJsonResponse([ 'userContext' => $url, - )); + ]); } } diff --git a/CRM/Utils/Wrapper.php b/CRM/Utils/Wrapper.php index ae23d3dc6bb4..df9b8a8f32f8 100644 --- a/CRM/Utils/Wrapper.php +++ b/CRM/Utils/Wrapper.php @@ -1,9 +1,9 @@ SimpleXMLElement|FALSE, 1 => errorMessage|FALSE) */ public static function parseFile($file) { - $xml = FALSE; // SimpleXMLElement - $error = FALSE; // string + // SimpleXMLElement + $xml = FALSE; + // string + $error = FALSE; if (!file_exists($file)) { $error = 'File ' . $file . ' does not exist.'; @@ -60,7 +62,7 @@ public static function parseFile($file) { libxml_use_internal_errors($oldLibXMLErrors); } - return array($xml, $error); + return [$xml, $error]; } /** @@ -72,8 +74,10 @@ public static function parseFile($file) { * (0 => SimpleXMLElement|FALSE, 1 => errorMessage|FALSE) */ public static function parseString($string) { - $xml = FALSE; // SimpleXMLElement - $error = FALSE; // string + // SimpleXMLElement + $xml = FALSE; + // string + $error = FALSE; $oldLibXMLErrors = libxml_use_internal_errors(); libxml_use_internal_errors(TRUE); @@ -87,7 +91,7 @@ public static function parseString($string) { libxml_use_internal_errors($oldLibXMLErrors); - return array($xml, $error); + return [$xml, $error]; } /** @@ -96,14 +100,14 @@ public static function parseString($string) { * @return string */ protected static function formatErrors($errors) { - $messages = array(); + $messages = []; foreach ($errors as $error) { if ($error->level != LIBXML_ERR_ERROR && $error->level != LIBXML_ERR_FATAL) { continue; } - $parts = array(); + $parts = []; if ($error->file) { $parts[] = "File=$error->file"; } @@ -126,7 +130,7 @@ protected static function formatErrors($errors) { * @return array */ public static function xmlObjToArray($obj) { - $arr = array(); + $arr = []; if (is_object($obj)) { $obj = get_object_vars($obj); } diff --git a/CRM/Utils/Zip.php b/CRM/Utils/Zip.php index 522ae19a5157..2ef4c7915d00 100644 --- a/CRM/Utils/Zip.php +++ b/CRM/Utils/Zip.php @@ -1,9 +1,9 @@ numFiles; $base = FALSE; @@ -77,9 +77,9 @@ static public function findBaseDirName(ZipArchive $zip) { * @return array(string) * no trailing / */ - static public function findBaseDirs(ZipArchive $zip) { + public static function findBaseDirs(ZipArchive $zip) { $cnt = $zip->numFiles; - $basedirs = array(); + $basedirs = []; for ($i = 0; $i < $cnt; $i++) { $filename = $zip->getNameIndex($i); @@ -101,7 +101,7 @@ static public function findBaseDirs(ZipArchive $zip) { * @return string|bool * Return string or FALSE */ - static public function guessBasedir(ZipArchive $zip, $expected) { + public static function guessBasedir(ZipArchive $zip, $expected) { $candidate = FALSE; $basedirs = CRM_Utils_Zip::findBaseDirs($zip); if (in_array($expected, $basedirs)) { @@ -118,7 +118,6 @@ static public function guessBasedir(ZipArchive $zip, $expected) { } } - /** * An inefficient helper for creating a ZIP file from data in memory. * This is only intended for building temp files for unit-testing. @@ -131,7 +130,7 @@ static public function guessBasedir(ZipArchive $zip, $expected) { * Array, keys are file names and values are file contents. * @return bool */ - static public function createTestZip($zipName, $dirs, $files) { + public static function createTestZip($zipName, $dirs, $files) { $zip = new ZipArchive(); $res = $zip->open($zipName, ZipArchive::CREATE); if ($res === TRUE) { diff --git a/CRM/Widget/Widget.php b/CRM/Widget/Widget.php index cf22c5933391..dbc4afe80cce 100644 --- a/CRM/Widget/Widget.php +++ b/CRM/Widget/Widget.php @@ -11,29 +11,29 @@ */ class CRM_Widget_Widget { - static $_methodTable; + public static $_methodTable; public function initialize() { if (!self::$_methodTable) { - self::$_methodTable = array( - 'getContributionPageData' => array( + self::$_methodTable = [ + 'getContributionPageData' => [ 'description' => 'Gets all campaign related data and returns it as a std class.', 'access' => 'remote', - 'arguments' => array( + 'arguments' => [ 'contributionPageID', 'widgetID', - ), - ), - 'getEmbedCode' => array( - 'description' => 'Gets embed code. Perhaps overkill, but we can track dropoffs in this case. by # of people reqeusting emebed code / number of unique instances.', + ], + ], + 'getEmbedCode' => [ + 'description' => 'Gets embed code. Perhaps overkill, but we can track dropoffs in this case. by # of people requesting embed code / number of unique instances.', 'access' => 'remote', - 'arguments' => array( + 'arguments' => [ 'contributionPageID', 'widgetID', 'format', - ), - ), - ); + ], + ], + ]; } } @@ -109,7 +109,7 @@ public function getContributionPageData($contributionPageID, $widgetID) { WHERE is_test = 0 AND contribution_status_id = 1 AND contribution_page_id = %1"; - $params = array(1 => array($contributionPageID, 'Integer')); + $params = [1 => [$contributionPageID, 'Integer']]; $dao = CRM_Core_DAO::executeQuery($query, $params); if ($dao->fetch()) { $data->num_donors = $dao->count; @@ -123,7 +123,7 @@ public function getContributionPageData($contributionPageID, $widgetID) { SELECT goal_amount, start_date, end_date, is_active FROM civicrm_contribution_page WHERE id = %1"; - $params = array(1 => array($contributionPageID, 'Integer')); + $params = [1 => [$contributionPageID, 'Integer']]; $dao = CRM_Core_DAO::executeQuery($query, $params); if ($dao->fetch()) { $data->money_target = $dao->goal_amount; @@ -158,7 +158,7 @@ public function getContributionPageData($contributionPageID, $widgetID) { $data->homepage_link = $widget->url_homepage; // movie clip colors, must be in '0xRRGGBB' format - $data->colors = array(); + $data->colors = []; $hexPrefix = '0x'; $data->colors["title"] = str_replace('#', $hexPrefix, $widget->color_title); diff --git a/Civi.php b/Civi.php index fe16326de75d..12b2119b45bc 100644 --- a/Civi.php +++ b/Civi.php @@ -18,7 +18,7 @@ class Civi { /** * A central location for static variable storage. - * + * @var array * @code * `Civi::$statics[__CLASS__]['foo'] = 'bar'; * @endcode @@ -26,20 +26,24 @@ class Civi { public static $statics = array(); /** - * EXPERIMENTAL. Retrieve a named cache instance. - * - * This interface is flagged as experimental due to political - * ambiguity in PHP community -- PHP-FIG has an open but - * somewhat controversial draft standard for caching. Based on - * the current draft, it's expected that this function could - * simultaneously support both CRM_Utils_Cache_Interface and - * PSR-6, but that depends on whether PSR-6 changes any more. + * Retrieve a named cache instance. * * @param string $name * The name of the cache. The 'default' cache is biased toward * high-performance caches (eg memcache/redis/apc) when * available and falls back to single-request (static) caching. + * Ex: 'short' or 'default' is useful for high-speed, short-lived cache data. + * This is appropriate if you believe that latency (millisecond-level + * read time) is the main factor. For example: caching data from + * a couple SQL queries. + * Ex: 'long' can be useful for longer-lived cache data. It's appropriate if + * you believe that longevity (e.g. surviving for several hours or a day) + * is more important than millisecond-level access time. For example: + * caching the result of a simple metadata-query. + * * @return CRM_Utils_Cache_Interface + * NOTE: Beginning in CiviCRM v5.4, the cache instance complies with + * PSR-16 (\Psr\SimpleCache\CacheInterface). */ public static function cache($name = 'default') { return \Civi\Core\Container::singleton()->get('cache.' . $name); @@ -54,6 +58,15 @@ public static function container() { return Civi\Core\Container::singleton(); } + /** + * Get the event dispatcher. + * + * @return \Symfony\Component\EventDispatcher\EventDispatcherInterface + */ + public static function dispatcher() { + return Civi\Core\Container::singleton()->get('dispatcher'); + } + /** * @return \Civi\Core\Lock\LockManager */ @@ -104,6 +117,22 @@ public static function resources() { return CRM_Core_Resources::singleton(); } + /** + * Obtain the contact's personal settings. + * + * @param NULL|int $contactID + * For the default/active user's contact, leave $domainID as NULL. + * @param NULL|int $domainID + * For the default domain, leave $domainID as NULL. + * @return \Civi\Core\SettingsBag + * @throws CRM_Core_Exception + * If there is no contact, then there's no SettingsBag, and we'll throw + * an exception. + */ + public static function contactSettings($contactID = NULL, $domainID = NULL) { + return \Civi\Core\Container::getBootService('settings_manager')->getBagByContact($domainID, $contactID); + } + /** * Obtain the domain settings. * diff --git a/Civi/API/Api3SelectQuery.php b/Civi/API/Api3SelectQuery.php new file mode 100644 index 000000000000..d036484d2a24 --- /dev/null +++ b/Civi/API/Api3SelectQuery.php @@ -0,0 +1,185 @@ +where as $key => $value) { + $table_name = NULL; + $column_name = NULL; + + if (substr($key, 0, 7) == 'filter.') { + // Legacy support for old filter syntax per the test contract. + // (Convert the style to the later one & then deal with them). + $filterArray = explode('.', $key); + $value = [$filterArray[1] => $value]; + $key = 'filters'; + } + + // Legacy support for 'filter's construct. + if ($key == 'filters') { + foreach ($value as $filterKey => $filterValue) { + if (substr($filterKey, -4, 4) == 'high') { + $key = substr($filterKey, 0, -5); + $value = ['<=' => $filterValue]; + } + + if (substr($filterKey, -3, 3) == 'low') { + $key = substr($filterKey, 0, -4); + $value = ['>=' => $filterValue]; + } + + if ($filterKey == 'is_current' || $filterKey == 'isCurrent') { + // Is current is almost worth creating as a 'sql filter' in the DAO function since several entities have the concept. + $todayStart = date('Ymd000000', strtotime('now')); + $todayEnd = date('Ymd235959', strtotime('now')); + $a = self::MAIN_TABLE_ALIAS; + $this->query->where("($a.start_date <= '$todayStart' OR $a.start_date IS NULL) + AND ($a.end_date >= '$todayEnd' OR $a.end_date IS NULL) + AND a.is_active = 1"); + } + } + } + // Ignore the "options" param if it is referring to api options and not a field in this entity + if ( + $key === 'options' && is_array($value) + && !in_array(\CRM_Utils_Array::first(array_keys($value)), \CRM_Core_DAO::acceptedSQLOperators()) + ) { + continue; + } + $field = $this->getField($key); + if ($field) { + $key = $field['name']; + } + if (in_array($key, $this->entityFieldNames)) { + $table_name = self::MAIN_TABLE_ALIAS; + $column_name = $key; + } + elseif (($cf_id = \CRM_Core_BAO_CustomField::getKeyID($key)) != FALSE) { + // If we check a custom field on 'IS NULL', it should also work when there is no + // record in the custom value table, see CRM-20740. + $side = empty($value['IS NULL']) ? 'INNER' : 'LEFT OUTER'; + list($table_name, $column_name) = $this->addCustomField($this->apiFieldSpec['custom_' . $cf_id], $side); + } + elseif (strpos($key, '.')) { + $fkInfo = $this->addFkField($key, 'INNER'); + if ($fkInfo) { + list($table_name, $column_name) = $fkInfo; + $this->validateNestedInput($key, $value); + } + } + // I don't know why I had to specifically exclude 0 as a key - wouldn't the others have caught it? + // We normally silently ignore null values passed in - if people want IS_NULL they can use acceptedSqlOperator syntax. + if ((!$table_name) || empty($key) || is_null($value)) { + // No valid filter field. This might be a chained call or something. + // Just ignore this for the $where_clause. + continue; + } + $operator = is_array($value) ? \CRM_Utils_Array::first(array_keys($value)) : NULL; + if (!in_array($operator, \CRM_Core_DAO::acceptedSQLOperators(), TRUE)) { + $value = ['=' => $value]; + } + $filters[$key] = \CRM_Core_DAO::createSQLFilter("{$table_name}.{$column_name}", $value); + } + // Support OR groups + if (!empty($this->where['options']['or'])) { + $orGroups = $this->where['options']['or']; + if (is_string($orGroups)) { + $orGroups = array_map('trim', explode(',', $orGroups)); + } + if (!is_array(\CRM_Utils_Array::first($orGroups))) { + $orGroups = [$orGroups]; + } + foreach ($orGroups as $orGroup) { + $orClause = []; + foreach ($orGroup as $key) { + if (!isset($filters[$key])) { + throw new \CiviCRM_API3_Exception("'$key' specified in OR group but not added to params"); + } + $orClause[] = $filters[$key]; + unset($filters[$key]); + } + $this->query->where(implode(' OR ', $orClause)); + } + } + // Add the remaining params using AND + foreach ($filters as $filter) { + $this->query->where($filter); + } + } + + /** + * @inheritDoc + */ + protected function getFields() { + require_once 'api/v3/Generic.php'; + // Call this function directly instead of using the api wrapper to force unique field names off + $apiSpec = \civicrm_api3_generic_getfields([ + 'entity' => $this->entity, + 'version' => 3, + 'params' => ['action' => 'get'], + ], FALSE); + return $apiSpec['values']; + } + + /** + * Fetch a field from the getFields list + * + * Searches by name, uniqueName, and api.aliases + * + * @param string $fieldName + * Field name. + * @return NULL|mixed + */ + protected function getField($fieldName) { + if (!$fieldName) { + return NULL; + } + if (isset($this->apiFieldSpec[$fieldName])) { + return $this->apiFieldSpec[$fieldName]; + } + foreach ($this->apiFieldSpec as $field) { + if ( + $fieldName == \CRM_Utils_Array::value('uniqueName', $field) || + array_search($fieldName, \CRM_Utils_Array::value('api.aliases', $field, [])) !== FALSE + ) { + return $field; + } + } + return NULL; + } + +} diff --git a/Civi/API/Event/AuthorizeEvent.php b/Civi/API/Event/AuthorizeEvent.php index 6fd92aa63dcf..4bc99b730493 100644 --- a/Civi/API/Event/AuthorizeEvent.php +++ b/Civi/API/Event/AuthorizeEvent.php @@ -1,9 +1,9 @@ apiRequest = $apiRequest; diff --git a/Civi/API/Event/ResolveEvent.php b/Civi/API/Event/ResolveEvent.php index a16242fff0a0..396418649173 100644 --- a/Civi/API/Event/ResolveEvent.php +++ b/Civi/API/Event/ResolveEvent.php @@ -1,9 +1,9 @@ */ public static function allEvents() { - return array( + return [ self::AUTHORIZE, self::EXCEPTION, self::PREPARE, self::RESOLVE, self::RESPOND, - ); + ]; + } + + /** + * @param \Civi\Core\Event\GenericHookEvent $e + * @see \CRM_Utils_Hook::eventDefs + */ + public static function hookEventDefs($e) { + $e->inspector->addEventClass(self::AUTHORIZE, 'Civi\API\Event\AuthorizeEvent'); + $e->inspector->addEventClass(self::EXCEPTION, 'Civi\API\Event\ExceptionEvent'); + $e->inspector->addEventClass(self::PREPARE, 'Civi\API\Event\PrepareEvent'); + $e->inspector->addEventClass(self::RESOLVE, 'Civi\API\Event\ResolveEvent'); + $e->inspector->addEventClass(self::RESPOND, 'Civi\API\Event\RespondEvent'); } } diff --git a/Civi/API/Exception/NotImplementedException.php b/Civi/API/Exception/NotImplementedException.php index f2df795ca32a..b8f253f13ebd 100644 --- a/Civi/API/Exception/NotImplementedException.php +++ b/Civi/API/Exception/NotImplementedException.php @@ -8,6 +8,7 @@ * @package Civi\API\Exception */ class NotImplementedException extends \API_Exception { + /** * @param string $message * The human friendly error message. @@ -18,7 +19,7 @@ class NotImplementedException extends \API_Exception { * @param \Exception|NULL $previous * A previous exception which caused this new exception. */ - public function __construct($message, $extraParams = array(), \Exception $previous = NULL) { + public function __construct($message, $extraParams = [], \Exception $previous = NULL) { parent::__construct($message, \API_Exception::NOT_IMPLEMENTED, $extraParams, $previous); } diff --git a/Civi/API/Exception/UnauthorizedException.php b/Civi/API/Exception/UnauthorizedException.php index 0235f1f4408e..4e3bdbf7379e 100644 --- a/Civi/API/Exception/UnauthorizedException.php +++ b/Civi/API/Exception/UnauthorizedException.php @@ -8,6 +8,7 @@ * @package Civi\API\Exception */ class UnauthorizedException extends \API_Exception { + /** * @param string $message * The human friendly error message. @@ -18,7 +19,7 @@ class UnauthorizedException extends \API_Exception { * @param \Exception|NULL $previous * A previous exception which caused this new exception. */ - public function __construct($message, $extraParams = array(), \Exception $previous = NULL) { + public function __construct($message, $extraParams = [], \Exception $previous = NULL) { parent::__construct($message, \API_Exception::UNAUTHORIZED, $extraParams, $previous); } diff --git a/Civi/API/ExternalBatch.php b/Civi/API/ExternalBatch.php index 2e6507e66946..f9ea3ae69cf2 100644 --- a/Civi/API/ExternalBatch.php +++ b/Civi/API/ExternalBatch.php @@ -46,12 +46,17 @@ class ExternalBatch { * @param array $defaultParams * Default values to merge into any API calls. */ - public function __construct($defaultParams = array()) { + public function __construct($defaultParams = []) { global $civicrm_root; $this->root = $civicrm_root; $this->settingsPath = defined('CIVICRM_SETTINGS_PATH') ? CIVICRM_SETTINGS_PATH : NULL; $this->defaultParams = $defaultParams; $this->env = $_ENV; + if (empty($_ENV['PATH'])) { + // FIXME: If we upgrade to newer Symfony\Process and use the newer + // inheritEnv feature, then this becomes unnecessary. + throw new \CRM_Core_Exception('ExternalBatch cannot detect environment: $_ENV is missing. (Tip: Set variables_order=EGPCS in php.ini.)'); + } } /** @@ -60,14 +65,14 @@ public function __construct($defaultParams = array()) { * @param array $params * @return ExternalBatch */ - public function addCall($entity, $action, $params = array()) { + public function addCall($entity, $action, $params = []) { $params = array_merge($this->defaultParams, $params); - $this->apiCalls[] = array( + $this->apiCalls[] = [ 'entity' => $entity, 'action' => $action, 'params' => $params, - ); + ]; return $this; } @@ -114,20 +119,20 @@ public function wait() { while (!empty($this->processes)) { usleep(self::POLL_INTERVAL); foreach (array_keys($this->processes) as $idx) { - /** @var Process $process */ + /** @var \Symfony\Component\Process\Process $process */ $process = $this->processes[$idx]; if (!$process->isRunning()) { $parsed = json_decode($process->getOutput(), TRUE); if ($process->getExitCode() || $parsed === NULL) { - $this->apiResults[] = array( + $this->apiResults[] = [ 'is_error' => 1, 'error_message' => 'External API returned malformed response.', - 'trace' => array( + 'trace' => [ 'code' => $process->getExitCode(), 'stdout' => $process->getOutput(), 'stderr' => $process->getErrorOutput(), - ), - ); + ], + ]; } else { $this->apiResults[] = $parsed; @@ -174,11 +179,11 @@ public function isSupported() { /** * @param array $apiCall * Array with keys: entity, action, params. - * @return Process + * @return \Symfony\Component\Process\Process * @throws \CRM_Core_Exception */ public function createProcess($apiCall) { - $parts = array(); + $parts = []; if (defined('CIVICRM_TEST') && CIVICRM_TEST) { // When testing, civicrm.settings.php may rely on $_CV, which is only @@ -209,9 +214,9 @@ public function createProcess($apiCall) { } $command = implode(" ", $parts); - $env = array_merge($this->env, array( + $env = array_merge($this->env, [ 'CIVICRM_SETTINGS' => $this->settingsPath, - )); + ]); return new Process($command, $this->root, $env); } diff --git a/Civi/API/Kernel.php b/Civi/API/Kernel.php index a22c3fcf6311..cc0cc3805f2c 100644 --- a/Civi/API/Kernel.php +++ b/Civi/API/Kernel.php @@ -1,9 +1,9 @@ apiProviders = $apiProviders; $this->dispatcher = $dispatcher; } /** + * @deprecated * @param string $entity * Type of entities to deal with. * @param string $action @@ -68,38 +68,39 @@ public function __construct($dispatcher, $apiProviders = array()) { * @param array $params * Array to be passed to API function. * @param mixed $extra - * Who knows. - * + * Unused/deprecated. * @return array|int + * @see runSafe */ public function run($entity, $action, $params, $extra = NULL) { - /** - * @var $apiProvider \Civi\API\Provider\ProviderInterface|NULL - */ - $apiProvider = NULL; + return $this->runSafe($entity, $action, $params, $extra); + } - // TODO Define alternative calling convention makes it easier to construct $apiRequest - // without the ambiguity of "data" vs "options" + /** + * Parse and execute an API request. Any errors will be converted to + * normal format. + * + * @param string $entity + * Type of entities to deal with. + * @param string $action + * Create, get, delete or some special action name. + * @param array $params + * Array to be passed to API function. + * @param mixed $extra + * Unused/deprecated. + * + * @return array|int + * @throws \API_Exception + */ + public function runSafe($entity, $action, $params, $extra = NULL) { $apiRequest = Request::create($entity, $action, $params, $extra); try { - if (!is_array($params)) { - throw new \API_Exception('Input variable `params` is not an array', 2000); - } - - $this->boot(); - $errorScope = \CRM_Core_TemporaryErrorScope::useException(); - - list($apiProvider, $apiRequest) = $this->resolve($apiRequest); - $this->authorize($apiProvider, $apiRequest); - $apiRequest = $this->prepare($apiProvider, $apiRequest); - $result = $apiProvider->invoke($apiRequest); - - $apiResponse = $this->respond($apiProvider, $apiRequest, $result); + $apiResponse = $this->runRequest($apiRequest); return $this->formatResult($apiRequest, $apiResponse); } catch (\Exception $e) { - $this->dispatcher->dispatch(Events::EXCEPTION, new ExceptionEvent($e, $apiProvider, $apiRequest, $this)); + $this->dispatcher->dispatch(Events::EXCEPTION, new ExceptionEvent($e, NULL, $apiRequest, $this)); if ($e instanceof \PEAR_Exception) { $err = $this->formatPearException($e, $apiRequest); @@ -125,7 +126,8 @@ public function run($entity, $action, $params, $extra = NULL) { * @param array $params * Array to be passed to function. * @param mixed $extra - * Who knows. + * Unused/deprecated. + * * @return bool * TRUE if authorization would succeed. * @throws \Exception @@ -135,7 +137,7 @@ public function runAuthorize($entity, $action, $params, $extra = NULL) { $apiRequest = Request::create($entity, $action, $params, $extra); try { - $this->boot(); + $this->boot($apiRequest); list($apiProvider, $apiRequest) = $this->resolve($apiRequest); $this->authorize($apiProvider, $apiRequest); return TRUE; @@ -146,12 +148,61 @@ public function runAuthorize($entity, $action, $params, $extra = NULL) { } /** - * Bootstrap - Load basic dependencies. + * Execute an API request. + * + * The request must be in canonical format. Exceptions will be propagated out. + * + * @param array $apiRequest + * @return array + * @throws \API_Exception + * @throws \Civi\API\Exception\NotImplementedException + * @throws \Civi\API\Exception\UnauthorizedException + */ + public function runRequest($apiRequest) { + $this->boot($apiRequest); + $errorScope = \CRM_Core_TemporaryErrorScope::useException(); + + list($apiProvider, $apiRequest) = $this->resolve($apiRequest); + $this->authorize($apiProvider, $apiRequest); + $apiRequest = $this->prepare($apiProvider, $apiRequest); + $result = $apiProvider->invoke($apiRequest); + + return $this->respond($apiProvider, $apiRequest, $result); + } + + /** + * Bootstrap - Load basic dependencies and sanity-check inputs. + * + * @param \Civi\API\V4\Action|array $apiRequest + * @throws \API_Exception */ - public function boot() { - require_once 'api/v3/utils.php'; + public function boot($apiRequest) { require_once 'api/Exception.php'; - _civicrm_api3_initialize(); + + if (!is_array($apiRequest['params'])) { + throw new \API_Exception('Input variable `params` is not an array', 2000); + } + switch ($apiRequest['version']) { + case 2: + case 3: + require_once 'api/v3/utils.php'; + _civicrm_api3_initialize(); + break; + + case 4: + // nothing to do + break; + + default: + throw new \API_Exception('Unknown api version', 2000); + } + } + + /** + * @param array $apiRequest + * @throws \API_Exception + */ + protected function validate($apiRequest) { } /** @@ -161,29 +212,30 @@ public function boot() { * The full description of the API request. * @throws Exception\NotImplementedException * @return array - * Array(0 => ProviderInterface, 1 => array). + * A tuple with the provider-object and a revised apiRequest. + * Array(0 => ProviderInterface, 1 => array $apiRequest). */ public function resolve($apiRequest) { - /** @var ResolveEvent $resolveEvent */ + /** @var \Civi\API\Event\ResolveEvent $resolveEvent */ $resolveEvent = $this->dispatcher->dispatch(Events::RESOLVE, new ResolveEvent($apiRequest, $this)); $apiRequest = $resolveEvent->getApiRequest(); if (!$resolveEvent->getApiProvider()) { throw new \Civi\API\Exception\NotImplementedException("API (" . $apiRequest['entity'] . ", " . $apiRequest['action'] . ") does not exist (join the API team and implement it!)"); } - return array($resolveEvent->getApiProvider(), $apiRequest); + return [$resolveEvent->getApiProvider(), $apiRequest]; } /** * Determine if the API request is allowed (under current policy) * - * @param ProviderInterface $apiProvider + * @param \Civi\API\Provider\ProviderInterface $apiProvider * The API provider responsible for executing the request. * @param array $apiRequest * The full description of the API request. * @throws Exception\UnauthorizedException */ public function authorize($apiProvider, $apiRequest) { - /** @var AuthorizeEvent $event */ + /** @var \Civi\API\Event\AuthorizeEvent $event */ $event = $this->dispatcher->dispatch(Events::AUTHORIZE, new AuthorizeEvent($apiProvider, $apiRequest, $this)); if (!$event->isAuthorized()) { throw new \Civi\API\Exception\UnauthorizedException("Authorization failed"); @@ -193,14 +245,15 @@ public function authorize($apiProvider, $apiRequest) { /** * Allow third-party code to manipulate the API request before execution. * - * @param ProviderInterface $apiProvider + * @param \Civi\API\Provider\ProviderInterface $apiProvider * The API provider responsible for executing the request. * @param array $apiRequest * The full description of the API request. - * @return mixed + * @return array + * The revised API request. */ public function prepare($apiProvider, $apiRequest) { - /** @var PrepareEvent $event */ + /** @var \Civi\API\Event\PrepareEvent $event */ $event = $this->dispatcher->dispatch(Events::PREPARE, new PrepareEvent($apiProvider, $apiRequest, $this)); return $event->getApiRequest(); } @@ -208,16 +261,17 @@ public function prepare($apiProvider, $apiRequest) { /** * Allow third-party code to manipulate the API response after execution. * - * @param ProviderInterface $apiProvider + * @param \Civi\API\Provider\ProviderInterface $apiProvider * The API provider responsible for executing the request. * @param array $apiRequest * The full description of the API request. * @param array $result * The response to return to the client. * @return mixed + * The revised $result. */ public function respond($apiProvider, $apiRequest, $result) { - /** @var RespondEvent $event */ + /** @var \Civi\API\Event\RespondEvent $event */ $event = $this->dispatcher->dispatch(Events::RESPOND, new RespondEvent($apiProvider, $apiRequest, $result, $this)); return $event->getResponse(); } @@ -230,9 +284,9 @@ public function respond($apiProvider, $apiRequest, $result) { */ public function getEntityNames($version) { // Question: Would it better to eliminate $this->apiProviders and just use $this->dispatcher? - $entityNames = array(); + $entityNames = []; foreach ($this->getApiProviders() as $provider) { - /** @var ProviderInterface $provider */ + /** @var \Civi\API\Provider\ProviderInterface $provider */ $entityNames = array_merge($entityNames, $provider->getEntityNames($version)); } $entityNames = array_unique($entityNames); @@ -250,9 +304,9 @@ public function getEntityNames($version) { */ public function getActionNames($version, $entity) { // Question: Would it better to eliminate $this->apiProviders and just use $this->dispatcher? - $actionNames = array(); + $actionNames = []; foreach ($this->getApiProviders() as $provider) { - /** @var ProviderInterface $provider */ + /** @var \Civi\API\Provider\ProviderInterface $provider */ $actionNames = array_merge($actionNames, $provider->getActionNames($version, $entity)); } $actionNames = array_unique($actionNames); @@ -269,7 +323,7 @@ public function getActionNames($version, $entity) { * API response. */ public function formatException($e, $apiRequest) { - $data = array(); + $data = []; if (!empty($apiRequest['params']['debug'])) { $data['trace'] = $e->getTraceAsString(); } @@ -290,7 +344,8 @@ public function formatApiException($e, $apiRequest) { $data['action'] = \CRM_Utils_Array::value('action', $apiRequest); if (\CRM_Utils_Array::value('debug', \CRM_Utils_Array::value('params', $apiRequest)) - && empty($data['trace']) // prevent recursion + // prevent recursion + && empty($data['trace']) ) { $data['trace'] = $e->getTraceAsString(); } @@ -307,7 +362,7 @@ public function formatApiException($e, $apiRequest) { * API response. */ public function formatPearException($e, $apiRequest) { - $data = array(); + $data = []; $error = $e->getCause(); if ($error instanceof \DB_Error) { $data["error_code"] = \DB::errorMessage($error->getCode()); @@ -355,7 +410,7 @@ public function createError($msg, $data, $apiRequest, $code = NULL) { } } - $data = civicrm_api3_create_error($msg, $data); + $data = \civicrm_api3_create_error($msg, $data); if (isset($apiRequest['params']) && is_array($apiRequest['params']) && !empty($apiRequest['params']['api.has_parent'])) { $errorCode = empty($data['error_code']) ? 'chained_api_failed' : $data['error_code']; @@ -404,7 +459,7 @@ public function setApiProviders($apiProviders) { } /** - * @param ProviderInterface $apiProvider + * @param \Civi\API\Provider\ProviderInterface $apiProvider * The API provider responsible for executing the request. * @return Kernel */ diff --git a/Civi/API/Provider/AdhocProvider.php b/Civi/API/Provider/AdhocProvider.php index f0644ba5f33d..4a03ed04864c 100644 --- a/Civi/API/Provider/AdhocProvider.php +++ b/Civi/API/Provider/AdhocProvider.php @@ -1,9 +1,9 @@ array( - array('onApiResolve', Events::W_EARLY), - ), - Events::AUTHORIZE => array( - array('onApiAuthorize', Events::W_EARLY), - ), - ); + return [ + Events::RESOLVE => [ + ['onApiResolve', Events::W_EARLY], + ], + Events::AUTHORIZE => [ + ['onApiAuthorize', Events::W_EARLY], + ], + ]; } /** * @var array (string $name => array('perm' => string, 'callback' => callable)) */ - protected $actions = array(); + protected $actions = []; /** * @var string @@ -87,13 +87,13 @@ public function __construct($version, $entity) { * Permissions required for invoking the action. * @param mixed $callback * The function which executes the API. - * @return ReflectionProvider + * @return AdhocProvider */ public function addAction($name, $perm, $callback) { - $this->actions[strtolower($name)] = array( + $this->actions[strtolower($name)] = [ 'perm' => $perm, 'callback' => $callback, - ); + ]; return $this; } @@ -137,7 +137,7 @@ public function invoke($apiRequest) { * @return array */ public function getEntityNames($version) { - return array($this->entity); + return [$this->entity]; } /** @@ -151,7 +151,7 @@ public function getActionNames($version, $entity) { return array_keys($this->actions); } else { - return array(); + return []; } } diff --git a/Civi/API/Provider/MagicFunctionProvider.php b/Civi/API/Provider/MagicFunctionProvider.php index 9035bca91fba..346b47035987 100644 --- a/Civi/API/Provider/MagicFunctionProvider.php +++ b/Civi/API/Provider/MagicFunctionProvider.php @@ -1,9 +1,9 @@ array( - array('onApiResolve', Events::W_MIDDLE), - ), - ); + return [ + Events::RESOLVE => [ + ['onApiResolve', Events::W_MIDDLE], + ], + ]; } /** @@ -54,7 +55,7 @@ public static function getSubscribedEvents() { /** */ public function __construct() { - $this->cache = array(); + $this->cache = []; } /** @@ -83,10 +84,21 @@ public function invoke($apiRequest) { // Unlike normal API implementations, generic implementations require explicit // knowledge of the entity and action (as well as $params). Bundle up these bits // into a convenient data structure. + if ($apiRequest['action'] === 'getsingle') { + // strip any api nested parts here as otherwise chaining may happen twice + // see https://lab.civicrm.org/dev/core/issues/643 + // testCreateBAODefaults fails without this. + foreach ($apiRequest['params'] as $key => $param) { + if ($key !== 'api.has_parent' && substr($key, 0, 4) === 'api.' || substr($key, 0, 4) === 'api_') { + unset($apiRequest['params'][$key]); + } + } + } $result = $function($apiRequest); + } elseif ($apiRequest['function'] && !$apiRequest['is_generic']) { - $result = isset($extra) ? $function($apiRequest['params'], $extra) : $function($apiRequest['params']); + $result = $function($apiRequest['params']); } return $result; } @@ -97,12 +109,12 @@ public function invoke($apiRequest) { * @return array */ public function getEntityNames($version) { - $entities = array(); + $entities = []; $include_dirs = array_unique(explode(PATH_SEPARATOR, get_include_path())); #$include_dirs = array(dirname(__FILE__). '/../../'); foreach ($include_dirs as $include_dir) { $api_dir = implode(DIRECTORY_SEPARATOR, - array($include_dir, 'api', 'v' . $version)); + [$include_dir, 'api', 'v' . $version]); if (!is_dir($api_dir)) { continue; } @@ -126,7 +138,7 @@ public function getEntityNames($version) { } } } - $entities = array_diff($entities, array('Generic')); + $entities = array_diff($entities, ['Generic']); $entities = array_unique($entities); sort($entities); @@ -143,12 +155,12 @@ public function getActionNames($version, $entity) { $entity = _civicrm_api_get_camel_name($entity); $entities = $this->getEntityNames($version); if (!in_array($entity, $entities)) { - return array(); + return []; } $this->loadEntity($entity, $version); $functions = get_defined_functions(); - $actions = array(); + $actions = []; $prefix = 'civicrm_api' . $version . '_' . _civicrm_api_get_entity_name_from_camel($entity) . '_'; $prefixGeneric = 'civicrm_api' . $version . '_generic_'; foreach ($functions['user'] as $fct) { @@ -192,21 +204,21 @@ protected function resolve($apiRequest) { // someone already loaded the appropriate file // FIXME: This has the affect of masking bugs in load order; this is // included to provide bug-compatibility. - $this->cache[$cachekey] = array('function' => $stdFunction, 'is_generic' => FALSE); + $this->cache[$cachekey] = ['function' => $stdFunction, 'is_generic' => FALSE]; return $this->cache[$cachekey]; } - $stdFiles = array( + $stdFiles = [ // By convention, the $camelName.php is more likely to contain the // function, so test it first 'api/v' . $apiRequest['version'] . '/' . $camelName . '.php', 'api/v' . $apiRequest['version'] . '/' . $camelName . '/' . $actionCamelName . '.php', - ); + ]; foreach ($stdFiles as $stdFile) { if (\CRM_Utils_File::isIncludable($stdFile)) { require_once $stdFile; if (function_exists($stdFunction)) { - $this->cache[$cachekey] = array('function' => $stdFunction, 'is_generic' => FALSE); + $this->cache[$cachekey] = ['function' => $stdFunction, 'is_generic' => FALSE]; return $this->cache[$cachekey]; } } @@ -216,23 +228,23 @@ protected function resolve($apiRequest) { require_once 'api/v3/Generic.php'; # $genericFunction = 'civicrm_api3_generic_' . $apiRequest['action']; $genericFunction = $this->getFunctionName('generic', $apiRequest['action'], $apiRequest['version']); - $genericFiles = array( + $genericFiles = [ // By convention, the Generic.php is more likely to contain the // function, so test it first 'api/v' . $apiRequest['version'] . '/Generic.php', 'api/v' . $apiRequest['version'] . '/Generic/' . $actionCamelName . '.php', - ); + ]; foreach ($genericFiles as $genericFile) { if (\CRM_Utils_File::isIncludable($genericFile)) { require_once $genericFile; if (function_exists($genericFunction)) { - $this->cache[$cachekey] = array('function' => $genericFunction, 'is_generic' => TRUE); + $this->cache[$cachekey] = ['function' => $genericFunction, 'is_generic' => TRUE]; return $this->cache[$cachekey]; } } } - $this->cache[$cachekey] = array('function' => FALSE, 'is_generic' => FALSE); + $this->cache[$cachekey] = ['function' => FALSE, 'is_generic' => FALSE]; return $this->cache[$cachekey]; } @@ -274,12 +286,13 @@ protected function loadEntity($entity, $version) { } // Check for standalone action files; to match _civicrm_api_resolve(), only load the first one - $loaded_files = array(); // array($relativeFilePath => TRUE) + // array($relativeFilePath => TRUE) + $loaded_files = []; $include_dirs = array_unique(explode(PATH_SEPARATOR, get_include_path())); foreach ($include_dirs as $include_dir) { - foreach (array($camelName, 'Generic') as $name) { + foreach ([$camelName, 'Generic'] as $name) { $action_dir = implode(DIRECTORY_SEPARATOR, - array($include_dir, 'api', "v${version}", $name)); + [$include_dir, 'api', "v${version}", $name]); if (!is_dir($action_dir)) { continue; } @@ -288,7 +301,8 @@ protected function loadEntity($entity, $version) { foreach ($iterator as $fileinfo) { $file = $fileinfo->getFilename(); if (array_key_exists($file, $loaded_files)) { - continue; // action provided by an earlier item on include_path + // action provided by an earlier item on include_path + continue; } $parts = explode(".", $file); diff --git a/Civi/API/Provider/ProviderInterface.php b/Civi/API/Provider/ProviderInterface.php index 6a25186d9058..4fbc8b7e316a 100644 --- a/Civi/API/Provider/ProviderInterface.php +++ b/Civi/API/Provider/ProviderInterface.php @@ -1,9 +1,9 @@ array( + return [ + Events::RESOLVE => [ // TODO decide if we really want to override others - array('onApiResolve', Events::W_EARLY), - ), - Events::AUTHORIZE => array( + ['onApiResolve', Events::W_EARLY], + ], + Events::AUTHORIZE => [ // TODO decide if we really want to override others - array('onApiAuthorize', Events::W_EARLY), - ), - ); + ['onApiAuthorize', Events::W_EARLY], + ], + ]; } /** @@ -66,10 +67,11 @@ public static function getSubscribedEvents() { */ public function __construct($apiKernel) { $this->apiKernel = $apiKernel; - $this->actions = array( - 'Entity' => array('get', 'getactions'), - '*' => array('getactions'), // 'getfields' - ); + $this->actions = [ + 'Entity' => ['get', 'getactions'], + // 'getfields' + '*' => ['getactions'], + ]; } /** @@ -133,7 +135,7 @@ public function invoke($apiRequest) { * @return array */ public function getEntityNames($version) { - return array('Entity'); + return ['Entity']; } /** diff --git a/Civi/API/Provider/StaticProvider.php b/Civi/API/Provider/StaticProvider.php index d6cd7a4fcb77..0a10aee85a95 100644 --- a/Civi/API/Provider/StaticProvider.php +++ b/Civi/API/Provider/StaticProvider.php @@ -1,9 +1,9 @@ array( - array('onApiResolve', Events::W_MIDDLE), - ), - Events::AUTHORIZE => array( - array('onApiAuthorize', Events::W_MIDDLE), - ), - ); + return [ + Events::RESOLVE => [ + ['onApiResolve', Events::W_MIDDLE], + ], + Events::AUTHORIZE => [ + ['onApiAuthorize', Events::W_MIDDLE], + ], + ]; } /** @@ -66,21 +66,21 @@ public static function getSubscribedEvents() { * @param array $records * List of mock records to be read/updated by API calls. */ - public function __construct($version, $entity, $fields, $perms = array(), $records = array()) { + public function __construct($version, $entity, $fields, $perms = [], $records = []) { parent::__construct($version, $entity); - $perms = array_merge(array( + $perms = array_merge([ 'create' => \CRM_Core_Permission::ALWAYS_ALLOW_PERMISSION, 'get' => \CRM_Core_Permission::ALWAYS_ALLOW_PERMISSION, 'delete' => \CRM_Core_Permission::ALWAYS_ALLOW_PERMISSION, - ), $perms); + ], $perms); - $this->records = \CRM_Utils_Array::index(array('id'), $records); + $this->records = \CRM_Utils_Array::index(['id'], $records); $this->fields = $fields; - $this->addAction('create', $perms['create'], array($this, 'doCreate')); - $this->addAction('get', $perms['get'], array($this, 'doGet')); - $this->addAction('delete', $perms['delete'], array($this, 'doDelete')); + $this->addAction('create', $perms['create'], [$this, 'doCreate']); + $this->addAction('get', $perms['get'], [$this, 'doGet']); + $this->addAction('delete', $perms['delete'], [$this, 'doDelete']); } /** @@ -102,7 +102,7 @@ public function setRecords($records) { * @param array $apiRequest * The full description of the API request. * @return array - * Formatted API result + * Formatted API result * @throws \API_Exception */ public function doCreate($apiRequest) { @@ -111,7 +111,7 @@ public function doCreate($apiRequest) { } else { $id = max(array_keys($this->records)) + 1; - $this->records[$id] = array(); + $this->records[$id] = []; } if (!isset($this->records[$id])) { @@ -131,7 +131,7 @@ public function doCreate($apiRequest) { * @param array $apiRequest * The full description of the API request. * @return array - * Formatted API result + * Formatted API result * @throws \API_Exception */ public function doGet($apiRequest) { @@ -142,7 +142,7 @@ public function doGet($apiRequest) { * @param array $apiRequest * The full description of the API request. * @return array - * Formatted API result + * Formatted API result * @throws \API_Exception */ public function doDelete($apiRequest) { @@ -150,7 +150,7 @@ public function doDelete($apiRequest) { if ($id && isset($this->records[$id])) { unset($this->records[$id]); } - return civicrm_api3_create_success(array()); + return civicrm_api3_create_success([]); } } diff --git a/Civi/API/Request.php b/Civi/API/Request.php index 86451eb1626a..b40ece71e73d 100644 --- a/Civi/API/Request.php +++ b/Civi/API/Request.php @@ -1,9 +1,9 @@ = 4) { - $options = array(); - $data = array(); - $chains = array(); - foreach ($params as $key => $value) { - if ($key == 'options') { - $options = array_merge($options, $value); - } - elseif ($key == 'return') { - if (!isset($options['return'])) { - $options['return'] = array(); - } - $options['return'] = array_merge($options['return'], $value); - } - elseif (preg_match('/^option\.(.*)$/', $key, $matches)) { - $options[$matches[1]] = $value; - } - elseif (preg_match('/^return\.(.*)$/', $key, $matches)) { - if ($value) { - if (!isset($options['return'])) { - $options['return'] = array(); - } - $options['return'][] = $matches[1]; - } - } - elseif (preg_match('/^format\.(.*)$/', $key, $matches)) { - if ($value) { - if (!isset($options['format'])) { - $options['format'] = $matches[1]; - } - else { - throw new \API_Exception("Too many API formats specified"); - } - } - } - elseif (preg_match('/^api\./', $key)) { - // FIXME: represent subrequests as instances of "Request" - $chains[$key] = $value; - } - elseif ($key == 'debug') { - $options['debug'] = $value; + case 4: + $callable = ["Civi\\Api4\\$entity", $action]; + if (!is_callable($callable)) { + throw new Exception\NotImplementedException("API ($entity, $action) does not exist (join the API team and implement it!)"); } - elseif ($key == 'version') { - // ignore + $apiCall = call_user_func($callable); + $apiRequest['id'] = self::$nextId++; + unset($params['version']); + foreach ($params as $name => $param) { + $setter = 'set' . ucfirst($name); + $apiCall->$setter($param); } - else { - $data[$key] = $value; - - } - } - $apiRequest['options'] = new \CRM_Utils_OptionBag($options); - $apiRequest['data'] = new \CRM_Utils_OptionBag($data); - $apiRequest['chains'] = $chains; + return $apiCall; } - return $apiRequest; } /** - * Normalize/validate entity and action names + * Normalize entity to be CamelCase. + * + * APIv1-v3 munges entity/action names, and accepts any mixture of case and underscores. * * @param string $entity * @param int $version * @return string - * @throws \API_Exception */ public static function normalizeEntityName($entity, $version) { - if ($version <= 3) { - // APIv1-v3 munges entity/action names, and accepts any mixture of case and underscores. - // We normalize entity to be CamelCase. - return \CRM_Utils_String::convertStringToCamel(\CRM_Utils_String::munge($entity)); - } - else { - // APIv4 requires exact spelling & capitalization of entity/action name; deviations should cause errors - if (!preg_match('/^[a-zA-Z][a-zA-Z0-9]*$/', $entity)) { - throw new \API_Exception("Malformed entity"); - } - return $entity; - } - } - - public static function normalizeActionName($action, $version) { - if ($version <= 3) { - // APIv1-v3 munges entity/action names, and accepts any mixture of case and underscores. - // We normalize action to be lowercase. - return strtolower(\CRM_Utils_String::munge($action)); - } - else { - // APIv4 requires exact spelling & capitalization of entity/action name; deviations should cause errors - if (!preg_match('/^[a-zA-Z][a-zA-Z0-9]*$/', $action)) { - throw new \API_Exception("Malformed action"); - } - // TODO: Not sure about camelCase actions - in v3 they are all lowercase. - return strtolower($action{0}) . substr($action, 1); - } + return \CRM_Utils_String::convertStringToCamel(\CRM_Utils_String::munge($entity)); } /** - * We must be sure that every request uses only one version of the API. + * Normalize api action name to be lowercase. * - * @param array $params - * API parameters. - * @return int + * APIv1-v3 munges entity/action names, and accepts any mixture of case and underscores. + * + * @param $action + * @param $version + * @return string */ - protected static function parseVersion($params) { - $desired_version = empty($params['version']) ? NULL : (int) $params['version']; - if (isset($desired_version) && is_int($desired_version)) { - return $desired_version; - } - else { - // we will set the default to version 3 as soon as we find that it works. - return 3; - } + public static function normalizeActionName($action, $version) { + return strtolower(\CRM_Utils_String::munge($action)); } } diff --git a/Civi/API/SelectQuery.php b/Civi/API/SelectQuery.php index c8332fe6d54d..f7b61cd8910b 100644 --- a/Civi/API/SelectQuery.php +++ b/Civi/API/SelectQuery.php @@ -1,9 +1,9 @@ entity = $entity; + require_once 'api/v3/utils.php'; + $baoName = _civicrm_api3_get_BAO($entity); $bao = new $baoName(); - $this->entity = _civicrm_api_get_entity_name_from_dao($bao); - $this->params = $params; - $this->isFillUniqueFields = $isFillUniqueFields; - $this->checkPermissions = \CRM_Utils_Array::value('check_permissions', $this->params, FALSE); - $this->options = _civicrm_api3_get_options_from_params($this->params); $this->entityFieldNames = _civicrm_api3_field_names(_civicrm_api3_build_fields_array($bao)); - // Call this function directly instead of using the api wrapper to force unique field names off - require_once 'api/v3/Generic.php'; - $apiSpec = \civicrm_api3_generic_getfields(array('entity' => $this->entity, 'version' => 3, 'params' => array('action' => 'get')), FALSE); - $this->apiFieldSpec = $apiSpec['values']; + $this->apiFieldSpec = $this->getFields(); $this->query = \CRM_Utils_SQL_Select::from($bao->tableName() . ' ' . self::MAIN_TABLE_ALIAS); $bao->free(); // Add ACLs first to avoid redundant subclauses + $this->checkPermissions = $checkPermissions; $this->query->where($this->getAclClause(self::MAIN_TABLE_ALIAS, $baoName)); } /** * Build & execute the query and return results array * - * @return array + * @return array|int * @throws \API_Exception * @throws \CRM_Core_Exception * @throws \Exception */ public function run() { - // $select_fields maps column names to the field names of the result values. - $select_fields = $custom_fields = array(); - - // populate $select_fields - $return_all_fields = (empty($this->options['return']) || !is_array($this->options['return'])); - $return = $return_all_fields ? array_fill_keys($this->entityFieldNames, 1) : $this->options['return']; - - // core return fields - foreach ($return as $field_name => $include) { - if ($include) { - $field = $this->getField($field_name); - if ($field && in_array($field['name'], $this->entityFieldNames)) { - $select_fields[self::MAIN_TABLE_ALIAS . ".{$field['name']}"] = $field['name']; - } - elseif ($include && strpos($field_name, '.')) { - $fkField = $this->addFkField($field_name, 'LEFT'); - if ($fkField) { - $select_fields[implode('.', $fkField)] = $field_name; - } - } - } - } - - // Do custom fields IF the params contain the word "custom" or we are returning * - if ($return_all_fields || strpos(json_encode($this->params), 'custom')) { - $custom_fields = _civicrm_api3_custom_fields_for_entity($this->entity); - foreach ($custom_fields as $cf_id => $custom_field) { - $field_name = "custom_$cf_id"; - if ($return_all_fields || !empty($this->options['return'][$field_name]) - || - // This is a tested format so we support it. - !empty($this->options['return']['custom']) - ) { - list($table_name, $column_name) = $this->addCustomField($custom_field, 'LEFT'); - - if ($custom_field["data_type"] != "ContactReference") { - // 'ordinary' custom field. We will select the value as custom_XX. - $select_fields["$table_name.$column_name"] = $field_name; - } - else { - // contact reference custom field. The ID will be stored in custom_XX_id. - // custom_XX will contain the sort name of the contact. - $this->query->join("c_$cf_id", "LEFT JOIN civicrm_contact c_$cf_id ON c_$cf_id.id = `$table_name`.`$column_name`"); - $select_fields["$table_name.$column_name"] = $field_name . "_id"; - // We will call the contact table for the join c_XX. - $select_fields["c_$cf_id.sort_name"] = $field_name; - } - } - } - } - // Always select the ID. - $select_fields[self::MAIN_TABLE_ALIAS . ".id"] = "id"; - - // populate where_clauses - foreach ($this->params as $key => $value) { - $table_name = NULL; - $column_name = NULL; - - if (substr($key, 0, 7) == 'filter.') { - // Legacy support for old filter syntax per the test contract. - // (Convert the style to the later one & then deal with them). - $filterArray = explode('.', $key); - $value = array($filterArray[1] => $value); - $key = 'filters'; - } - - // Legacy support for 'filter's construct. - if ($key == 'filters') { - foreach ($value as $filterKey => $filterValue) { - if (substr($filterKey, -4, 4) == 'high') { - $key = substr($filterKey, 0, -5); - $value = array('<=' => $filterValue); - } - - if (substr($filterKey, -3, 3) == 'low') { - $key = substr($filterKey, 0, -4); - $value = array('>=' => $filterValue); - } + $this->buildSelectFields(); - if ($filterKey == 'is_current' || $filterKey == 'isCurrent') { - // Is current is almost worth creating as a 'sql filter' in the DAO function since several entities have the concept. - $todayStart = date('Ymd000000', strtotime('now')); - $todayEnd = date('Ymd235959', strtotime('now')); - $a = self::MAIN_TABLE_ALIAS; - $this->query->where("($a.start_date <= '$todayStart' OR $a.start_date IS NULL) - AND ($a.end_date >= '$todayEnd' OR $a.end_date IS NULL) - AND a.is_active = 1"); - } - } - } - // Ignore the "options" param if it is referring to api options and not a field in this entity - if ( - $key === 'options' && is_array($value) - && !in_array(\CRM_Utils_Array::first(array_keys($value)), \CRM_Core_DAO::acceptedSQLOperators()) - ) { - continue; - } - $field = $this->getField($key); - if ($field) { - $key = $field['name']; - } - if (in_array($key, $this->entityFieldNames)) { - $table_name = self::MAIN_TABLE_ALIAS; - $column_name = $key; - } - elseif (($cf_id = \CRM_Core_BAO_CustomField::getKeyID($key)) != FALSE) { - list($table_name, $column_name) = $this->addCustomField($custom_fields[$cf_id], 'INNER'); - } - elseif (strpos($key, '.')) { - $fkInfo = $this->addFkField($key, 'INNER'); - if ($fkInfo) { - list($table_name, $column_name) = $fkInfo; - $this->validateNestedInput($key, $value); - } - } - // I don't know why I had to specifically exclude 0 as a key - wouldn't the others have caught it? - // We normally silently ignore null values passed in - if people want IS_NULL they can use acceptedSqlOperator syntax. - if ((!$table_name) || empty($key) || is_null($value)) { - // No valid filter field. This might be a chained call or something. - // Just ignore this for the $where_clause. - continue; - } - if (!is_array($value)) { - $this->query->where(array("`$table_name`.`$column_name` = @value"), array( - "@value" => $value, - )); - } - else { - // We expect only one element in the array, of the form - // "operator" => "rhs". - $operator = \CRM_Utils_Array::first(array_keys($value)); - if (!in_array($operator, \CRM_Core_DAO::acceptedSQLOperators())) { - $this->query->where(array( - "{$table_name}.{$column_name} = @value"), array("@value" => $value) - ); - } - else { - $this->query->where(\CRM_Core_DAO::createSQLFilter("{$table_name}.{$column_name}", $value)); - } - } + $this->buildWhereClause(); + if (in_array('count_rows', $this->select)) { + $this->query->select("count(*) as c"); } - - if (!$this->options['is_count']) { - foreach ($select_fields as $column => $alias) { + else { + foreach ($this->selectFields as $column => $alias) { $this->query->select("$column as `$alias`"); } - } - else { - $this->query->select("count(*) as c"); - } - - // Order by - if (!empty($this->options['sort'])) { - $this->orderBy($this->options['sort']); + // Order by + $this->buildOrderBy(); } // Limit - if (!empty($this->options['limit']) || !empty($this->options['offset'])) { - $this->query->limit($this->options['limit'], $this->options['offset']); + if (!empty($this->limit) || !empty($this->offset)) { + $this->query->limit($this->limit, $this->offset); } - $result_entities = array(); + $result_entities = []; $result_dao = \CRM_Core_DAO::executeQuery($this->query->toSQL()); while ($result_dao->fetch()) { - if ($this->options['is_count']) { + if (in_array('count_rows', $this->select)) { $result_dao->free(); return (int) $result_dao->c; } - $result_entities[$result_dao->id] = array(); - foreach ($select_fields as $column => $alias) { + $result_entities[$result_dao->id] = []; + foreach ($this->selectFields as $column => $alias) { $returnName = $alias; $alias = str_replace('.', '_', $alias); if (property_exists($result_dao, $alias) && $result_dao->$alias != NULL) { @@ -340,7 +194,7 @@ public function merge($sqlFragment) { * @throws \API_Exception * @throws \Civi\API\Exception\UnauthorizedException */ - private function addFkField($fkFieldName, $side) { + protected function addFkField($fkFieldName, $side) { $stack = explode('.', $fkFieldName); if (count($stack) < 2) { return NULL; @@ -362,11 +216,12 @@ private function addFkField($fkFieldName, $side) { if ($depth > self::MAX_JOINS) { throw new UnauthorizedException("Maximum number of joins exceeded in parameter $fkFieldName"); } + $subStack = array_slice($stack, 0, $depth); + $this->getJoinInfo($fkField, $subStack); if (!isset($fkField['FKApiName']) || !isset($fkField['FKClassName'])) { // Join doesn't exist - might be another param with a dot in it for some reason, we'll just ignore it. return NULL; } - $subStack = array_slice($stack, 0, $depth); // Ensure we have permission to access the other api if (!$this->checkPermissionToJoin($fkField['FKApiName'], $subStack)) { throw new UnauthorizedException("Authorization failed to join onto {$fkField['FKApiName']} api in parameter $fkFieldName"); @@ -376,8 +231,8 @@ private function addFkField($fkFieldName, $side) { } $fieldInfo = \CRM_Utils_Array::value($fieldName, $fkField['FKApiSpec']); - // FIXME: What if the foreign key is not the "id" column? - if (!$fieldInfo || !isset($fkField['FKApiSpec']['id'])) { + $keyColumn = \CRM_Utils_Array::value('FKKeyColumn', $fkField, 'id'); + if (!$fieldInfo || !isset($fkField['FKApiSpec'][$keyColumn])) { // Join doesn't exist - might be another param with a dot in it for some reason, we'll just ignore it. return NULL; } @@ -386,10 +241,14 @@ private function addFkField($fkFieldName, $side) { // Add acl condition $joinCondition = array_merge( - array("$prev.$fk = $tableAlias.id"), + ["$prev.$fk = $tableAlias.$keyColumn"], $this->getAclClause($tableAlias, \_civicrm_api3_get_BAO($fkField['FKApiName']), $subStack) ); + if (!empty($fkField['FKCondition'])) { + $joinCondition[] = str_replace($fkTable, $tableAlias, $fkField['FKCondition']); + } + $this->join($side, $fkTable, $tableAlias, $joinCondition); if (strpos($fieldName, 'custom_') === 0) { @@ -401,7 +260,30 @@ private function addFkField($fkFieldName, $side) { $fkField = &$fkField['FKApiSpec'][$fieldName]; $prev = $tableAlias; } - return array($tableAlias, $fieldName); + return [$tableAlias, $fieldName]; + } + + /** + * Get join info for dynamically-joined fields (e.g. "entity_id", "option_group") + * + * @param $fkField + * @param $stack + */ + protected function getJoinInfo(&$fkField, $stack) { + if ($fkField['name'] == 'entity_id') { + $entityTableParam = substr(implode('.', $stack), 0, -2) . 'table'; + $entityTable = \CRM_Utils_Array::value($entityTableParam, $this->where); + if ($entityTable && is_string($entityTable) && \CRM_Core_DAO_AllCoreTables::getClassForTable($entityTable)) { + $fkField['FKClassName'] = \CRM_Core_DAO_AllCoreTables::getClassForTable($entityTable); + $fkField['FKApiName'] = \CRM_Core_DAO_AllCoreTables::getBriefName($fkField['FKClassName']); + } + } + if (!empty($fkField['pseudoconstant']['optionGroupName'])) { + $fkField['FKClassName'] = 'CRM_Core_DAO_OptionValue'; + $fkField['FKApiName'] = 'OptionValue'; + $fkField['FKKeyColumn'] = 'value'; + $fkField['FKCondition'] = "civicrm_option_value.option_group_id = (SELECT id FROM civicrm_option_group WHERE name = '{$fkField['pseudoconstant']['optionGroupName']}')"; + } } /** @@ -415,39 +297,21 @@ private function addFkField($fkFieldName, $side) { * @return array * Returns the table and field name for adding this field to a SELECT or WHERE clause */ - private function addCustomField($customField, $side, $baseTable = self::MAIN_TABLE_ALIAS) { + protected function addCustomField($customField, $side, $baseTable = self::MAIN_TABLE_ALIAS) { $tableName = $customField["table_name"]; $columnName = $customField["column_name"]; $tableAlias = "{$baseTable}_to_$tableName"; - $this->join($side, $tableName, $tableAlias, array("`$tableAlias`.entity_id = `$baseTable`.id")); - return array($tableAlias, $columnName); + $this->join($side, $tableName, $tableAlias, ["`$tableAlias`.entity_id = `$baseTable`.id"]); + return [$tableAlias, $columnName]; } /** * Fetch a field from the getFields list * - * Searches by name, uniqueName, and api.aliases - * * @param string $fieldName * @return array|null */ - private function getField($fieldName) { - if (!$fieldName) { - return NULL; - } - if (isset($this->apiFieldSpec[$fieldName])) { - return $this->apiFieldSpec[$fieldName]; - } - foreach ($this->apiFieldSpec as $field) { - if ( - $fieldName == \CRM_Utils_Array::value('uniqueName', $field) || - array_search($fieldName, \CRM_Utils_Array::value('api.aliases', $field, array())) !== FALSE - ) { - return $field; - } - } - return NULL; - } + abstract protected function getField($fieldName); /** * Perform input validation on params that use the join syntax @@ -459,7 +323,7 @@ private function getField($fieldName) { * @param $value * @throws \Exception */ - private function validateNestedInput($fieldName, &$value) { + protected function validateNestedInput($fieldName, &$value) { $stack = explode('.', $fieldName); $spec = $this->apiFieldSpec; $fieldName = array_pop($stack); @@ -467,7 +331,7 @@ private function validateNestedInput($fieldName, &$value) { $entity = $spec[$name]['FKApiName']; $spec = $spec[$name]['FKApiSpec']; } - $params = array($fieldName => $value); + $params = [$fieldName => $value]; \_civicrm_api3_validate_fields($entity, 'get', $params, $spec); $value = $params[$fieldName]; } @@ -480,24 +344,24 @@ private function validateNestedInput($fieldName, &$value) { * The stack of fields leading up to this join * @return bool */ - private function checkPermissionToJoin($entity, $fieldStack) { + protected function checkPermissionToJoin($entity, $fieldStack) { if (!$this->checkPermissions) { return TRUE; } // Build an array of params that relate to the joined entity - $params = array( + $params = [ 'version' => 3, - 'return' => array(), + 'return' => [], 'check_permissions' => $this->checkPermissions, - ); + ]; $prefix = implode('.', $fieldStack) . '.'; $len = strlen($prefix); - foreach ($this->options['return'] as $key => $ret) { + foreach ($this->select as $key => $ret) { if (strpos($key, $prefix) === 0) { $params['return'][substr($key, $len)] = $ret; } } - foreach ($this->params as $key => $param) { + foreach ($this->where as $key => $param) { if (strpos($key, $prefix) === 0) { $params[substr($key, $len)] = $param; } @@ -514,15 +378,15 @@ private function checkPermissionToJoin($entity, $fieldStack) { * @param array $stack * @return array */ - private function getAclClause($tableAlias, $baoName, $stack = array()) { + protected function getAclClause($tableAlias, $baoName, $stack = []) { if (!$this->checkPermissions) { - return array(); + return []; } // Prevent (most) redundant acl sub clauses if they have already been applied to the main entity. // FIXME: Currently this only works 1 level deep, but tracking through multiple joins would increase complexity // and just doing it for the first join takes care of most acl clause deduping. if (count($stack) === 1 && in_array($stack[0], $this->aclFields)) { - return array(); + return []; } $clauses = $baoName::getSelectWhereClause($tableAlias); if (!$stack) { @@ -535,30 +399,28 @@ private function getAclClause($tableAlias, $baoName, $stack = array()) { /** * Orders the query by one or more fields * - * e.g. - * @code - * $this->orderBy(array('last_name DESC', 'birth_date')); - * @endcode - * - * @param string|array $sortParams * @throws \API_Exception * @throws \Civi\API\Exception\UnauthorizedException */ - public function orderBy($sortParams) { - $orderBy = array(); - foreach (is_array($sortParams) ? $sortParams : explode(',', $sortParams) as $item) { - $words = preg_split("/[\s]+/", trim($item)); + protected function buildOrderBy() { + $sortParams = is_string($this->orderBy) ? explode(',', $this->orderBy) : (array) $this->orderBy; + foreach ($sortParams as $index => $item) { + $item = trim($item); + if ($item == '(1)') { + continue; + } + $words = preg_split("/[\s]+/", $item); if ($words) { // Direction defaults to ASC unless DESC is specified $direction = strtoupper(\CRM_Utils_Array::value(1, $words, '')) == 'DESC' ? ' DESC' : ''; $field = $this->getField($words[0]); if ($field) { - $orderBy[] = self::MAIN_TABLE_ALIAS . '.' . $field['name'] . $direction; + $this->query->orderBy(self::MAIN_TABLE_ALIAS . '.' . $field['name'] . $direction, NULL, $index); } elseif (strpos($words[0], '.')) { $join = $this->addFkField($words[0], 'LEFT'); if ($join) { - $orderBy[] = "`{$join[0]}`.`{$join[1]}`$direction"; + $this->query->orderBy("`{$join[0]}`.`{$join[1]}`$direction", NULL, $index); } } else { @@ -566,7 +428,6 @@ public function orderBy($sortParams) { } } } - $this->query->orderBy($orderBy); } /** @@ -583,4 +444,70 @@ public function join($side, $tableName, $tableAlias, $conditions) { } } + /** + * Populate where clauses + * + * @throws \Civi\API\Exception\UnauthorizedException + * @throws \Exception + */ + abstract protected function buildWhereClause(); + + /** + * Populate $this->selectFields + * + * @throws \Civi\API\Exception\UnauthorizedException + */ + protected function buildSelectFields() { + $return_all_fields = (empty($this->select) || !is_array($this->select)); + $return = $return_all_fields ? $this->entityFieldNames : $this->select; + if ($return_all_fields || in_array('custom', $this->select)) { + foreach (array_keys($this->apiFieldSpec) as $fieldName) { + if (strpos($fieldName, 'custom_') === 0) { + $return[] = $fieldName; + } + } + } + + // Always select the ID if the table has one. + if (array_key_exists('id', $this->apiFieldSpec)) { + $this->selectFields[self::MAIN_TABLE_ALIAS . ".id"] = "id"; + } + + // core return fields + foreach ($return as $fieldName) { + $field = $this->getField($fieldName); + if ($field && in_array($field['name'], $this->entityFieldNames)) { + $this->selectFields[self::MAIN_TABLE_ALIAS . ".{$field['name']}"] = $field['name']; + } + elseif (strpos($fieldName, '.')) { + $fkField = $this->addFkField($fieldName, 'LEFT'); + if ($fkField) { + $this->selectFields[implode('.', $fkField)] = $fieldName; + } + } + elseif ($field && strpos($fieldName, 'custom_') === 0) { + list($table_name, $column_name) = $this->addCustomField($field, 'LEFT'); + + if ($field['data_type'] != 'ContactReference') { + // 'ordinary' custom field. We will select the value as custom_XX. + $this->selectFields["$table_name.$column_name"] = $fieldName; + } + else { + // contact reference custom field. The ID will be stored in custom_XX_id. + // custom_XX will contain the sort name of the contact. + $this->query->join("c_$fieldName", "LEFT JOIN civicrm_contact c_$fieldName ON c_$fieldName.id = `$table_name`.`$column_name`"); + $this->selectFields["$table_name.$column_name"] = $fieldName . "_id"; + // We will call the contact table for the join c_XX. + $this->selectFields["c_$fieldName.sort_name"] = $fieldName; + } + } + } + } + + /** + * Load entity fields + * @return array + */ + abstract protected function getFields(); + } diff --git a/Civi/API/Subscriber/APIv3SchemaAdapter.php b/Civi/API/Subscriber/APIv3SchemaAdapter.php index 263bb13f0872..e6428711eb4d 100644 --- a/Civi/API/Subscriber/APIv3SchemaAdapter.php +++ b/Civi/API/Subscriber/APIv3SchemaAdapter.php @@ -1,9 +1,9 @@ array( - array('onApiPrepare', Events::W_MIDDLE), - array('onApiPrepare_validate', Events::W_LATE), - ), - ); + return [ + Events::PREPARE => [ + ['onApiPrepare', Events::W_MIDDLE], + ['onApiPrepare_validate', Events::W_LATE], + ], + ]; } /** @@ -96,7 +97,7 @@ public function onApiPrepare_validate(\Civi\API\Event\Event $event) { * @return array */ public function getDefaults($fields) { - $defaults = array(); + $defaults = []; foreach ($fields as $field => $values) { if (isset($values['api.default'])) { @@ -112,7 +113,7 @@ public function getDefaults($fields) { * @return array */ public function getRequired($fields) { - $required = array('version'); + $required = ['version']; foreach ($fields as $field => $values) { if (!empty($values['api.required'])) { diff --git a/Civi/API/Subscriber/ChainSubscriber.php b/Civi/API/Subscriber/ChainSubscriber.php index dfe9176e04e9..84b55753a0e9 100644 --- a/Civi/API/Subscriber/ChainSubscriber.php +++ b/Civi/API/Subscriber/ChainSubscriber.php @@ -1,9 +1,9 @@ array('onApiRespond', Events::W_EARLY), - ); + return [ + Events::RESPOND => ['onApiRespond', Events::W_EARLY], + ]; } /** @@ -67,10 +68,12 @@ public static function getSubscribedEvents() { */ public function onApiRespond(\Civi\API\Event\RespondEvent $event) { $apiRequest = $event->getApiRequest(); - $result = $event->getResponse(); - if (\CRM_Utils_Array::value('is_error', $result, 0) == 0) { - $this->callNestedApi($event->getApiKernel(), $apiRequest['params'], $result, $apiRequest['action'], $apiRequest['entity'], $apiRequest['version']); - $event->setResponse($result); + if ($apiRequest['version'] < 4) { + $result = $event->getResponse(); + if (\CRM_Utils_Array::value('is_error', $result, 0) == 0) { + $this->callNestedApi($event->getApiKernel(), $apiRequest['params'], $result, $apiRequest['action'], $apiRequest['entity'], $apiRequest['version']); + $event->setResponse($result); + } } } @@ -91,7 +94,7 @@ protected function callNestedApi($apiKernel, &$params, &$result, $action, $entit // We don't need to worry about nested api in the getfields/getoptions // actions, so just return immediately. - if (in_array($action, array('getfields', 'getfield', 'getoptions'))) { + if (in_array($action, ['getfields', 'getfield', 'getoptions'])) { return; } @@ -100,7 +103,7 @@ protected function callNestedApi($apiKernel, &$params, &$result, $action, $entit // $result to be a recursive array // $result['values'][0] = $result; $oldResult = $result; - $result = array('values' => array(0 => $oldResult)); + $result = ['values' => [0 => $oldResult]]; } foreach ($params as $field => $newparams) { if ((is_array($newparams) || $newparams === 1) && $field <> 'api.has_parent' && substr($field, 0, 3) == 'api') { @@ -108,7 +111,7 @@ protected function callNestedApi($apiKernel, &$params, &$result, $action, $entit // 'api.participant.delete' => 1 is a valid options - handle 1 // instead of an array if ($newparams === 1) { - $newparams = array('version' => $version); + $newparams = ['version' => $version]; } // can be api_ or api. $separator = $field[3]; @@ -118,11 +121,22 @@ protected function callNestedApi($apiKernel, &$params, &$result, $action, $entit $subAPI = explode($separator, $field); $subaction = empty($subAPI[2]) ? $action : $subAPI[2]; - $subParams = array( + $subParams = [ 'debug' => \CRM_Utils_Array::value('debug', $params), - ); + ]; $subEntity = _civicrm_api_get_entity_name_from_camel($subAPI[1]); + // Hard coded list of entitys that have fields starting api_ and shouldn't be automatically + // deemed to be chained API calls + $skipList = [ + 'SmsProvider' => ['type', 'url', 'params'], + 'Job' => ['prefix', 'entity', 'action'], + 'Contact' => ['key'], + ]; + if (isset($skipList[$entity]) && in_array($subEntity, $skipList[$entity])) { + continue; + } + foreach ($result['values'] as $idIndex => $parentAPIValues) { if ($subEntity != 'contact') { @@ -137,7 +151,7 @@ protected function callNestedApi($apiKernel, &$params, &$result, $action, $entit $subParams['entity_table'] = 'civicrm_' . $lowercase_entity; } - $crm16084 = FALSE; + $addEntityId = TRUE; if ($subEntity == 'relationship' && $lowercase_entity == 'contact') { // if a relationship call is chained to a contact call, we need // to check whether contact_id_a or contact_id_b for the @@ -146,12 +160,12 @@ protected function callNestedApi($apiKernel, &$params, &$result, $action, $entit // See CRM-16084. foreach (array_keys($newparams) as $key) { if (substr($key, 0, 11) == 'contact_id_') { - $crm16084 = TRUE; + $addEntityId = FALSE; break; } } } - if (!$crm16084) { + if ($addEntityId) { $subParams[$lowercase_entity . "_id"] = $parentAPIValues['id']; } } diff --git a/Civi/API/Subscriber/DynamicFKAuthorization.php b/Civi/API/Subscriber/DynamicFKAuthorization.php index 8e7868b17c71..647172ceddfb 100644 --- a/Civi/API/Subscriber/DynamicFKAuthorization.php +++ b/Civi/API/Subscriber/DynamicFKAuthorization.php @@ -1,9 +1,9 @@ array( - array('onApiAuthorize', Events::W_EARLY), - ), - ); + return [ + Events::AUTHORIZE => [ + ['onApiAuthorize', Events::W_EARLY], + ], + ]; } /** @@ -146,7 +146,7 @@ public function onApiAuthorize(\Civi\API\Event\AuthorizeEvent $event) { $apiRequest = $event->getApiRequest(); if ($apiRequest['version'] == 3 && \CRM_Utils_String::convertStringToCamel($apiRequest['entity']) == $this->entityName && in_array(strtolower($apiRequest['action']), $this->actions)) { if (isset($apiRequest['params']['field_name'])) { - $fldIdx = \CRM_Utils_Array::index(array('field_name'), $this->getCustomFields()); + $fldIdx = \CRM_Utils_Array::index(['field_name'], $this->getCustomFields()); if (empty($fldIdx[$apiRequest['params']['field_name']])) { throw new \Exception("Failed to map custom field to entity table"); } @@ -179,6 +179,9 @@ public function onApiAuthorize(\Civi\API\Event\AuthorizeEvent $event) { } if (isset($apiRequest['params']['entity_table'])) { + if (!\CRM_Core_DAO_AllCoreTables::isCoreTable($apiRequest['params']['entity_table'])) { + throw new \API_Exception("Unrecognized target entity table {$apiRequest['params']['entity_table']}"); + } $this->authorizeDelegate( $apiRequest['action'], $apiRequest['params']['entity_table'], @@ -201,10 +204,15 @@ public function onApiAuthorize(\Civi\API\Event\AuthorizeEvent $event) { * The target entity ID. * @param array $apiRequest * The full API request. + * @throws \Exception * @throws \API_Exception * @throws \Civi\API\Exception\UnauthorizedException */ public function authorizeDelegate($action, $entityTable, $entityId, $apiRequest) { + if ($this->isTrusted($apiRequest)) { + return; + } + $entity = $this->getDelegatedEntityName($entityTable); if (!$entity) { throw new \API_Exception("Failed to run permission check: Unrecognized target entity table ($entityTable)"); @@ -213,29 +221,26 @@ public function authorizeDelegate($action, $entityTable, $entityId, $apiRequest) throw new \Civi\API\Exception\UnauthorizedException("Authorization failed on ($entity): Missing entity_id"); } - if ($this->isTrusted($apiRequest)) { - return; - } - /** * @var \Exception $exception */ $exception = NULL; $self = $this; \CRM_Core_Transaction::create(TRUE)->run(function($tx) use ($entity, $action, $entityId, &$exception, $self) { - $tx->rollback(); // Just to be safe. + // Just to be safe. + $tx->rollback(); - $params = array( + $params = [ 'version' => 3, 'check_permissions' => 1, 'id' => $entityId, - ); + ]; $result = $self->kernel->run($entity, $self->getDelegatedAction($action), $params); if ($result['is_error'] || empty($result['values'])) { - $exception = new \Civi\API\Exception\UnauthorizedException("Authorization failed on ($entity,$entityId)", array( + $exception = new \Civi\API\Exception\UnauthorizedException("Authorization failed on ($entity,$entityId)", [ 'cause' => $result, - )); + ]); } }); @@ -321,25 +326,25 @@ public function getDelegatedAction($action) { * @throws \Exception */ public function getDelegate($id) { - $query = \CRM_Core_DAO::executeQuery($this->lookupDelegateSql, array( - 1 => array($id, 'Positive'), - )); + $query = \CRM_Core_DAO::executeQuery($this->lookupDelegateSql, [ + 1 => [$id, 'Positive'], + ]); if ($query->fetch()) { if (!preg_match('/^civicrm_value_/', $query->entity_table)) { // A normal attachment directly on its entity. - return array($query->is_valid, $query->entity_table, $query->entity_id); + return [$query->is_valid, $query->entity_table, $query->entity_id]; } // Ex: Translate custom-field table ("civicrm_value_foo_4") to // entity table ("civicrm_activity"). - $tblIdx = \CRM_Utils_Array::index(array('table_name'), $this->getCustomFields()); + $tblIdx = \CRM_Utils_Array::index(['table_name'], $this->getCustomFields()); if (isset($tblIdx[$query->entity_table])) { - return array($query->is_valid, $tblIdx[$query->entity_table]['entity_table'], $query->entity_id); + return [$query->is_valid, $tblIdx[$query->entity_table]['entity_table'], $query->entity_id]; } throw new \Exception('Failed to lookup entity table for custom field.'); } else { - return array(FALSE, NULL, NULL); + return [FALSE, NULL, NULL]; } } @@ -359,14 +364,14 @@ public function isTrusted($apiRequest) { */ public function getCustomFields() { $query = \CRM_Core_DAO::executeQuery($this->lookupCustomFieldSql); - $rows = array(); + $rows = []; while ($query->fetch()) { - $rows[] = array( + $rows[] = [ 'field_name' => $query->field_name, 'table_name' => $query->table_name, 'extends' => $query->extends, 'entity_table' => \CRM_Core_BAO_CustomGroup::getTableNameByEntityName($query->extends), - ); + ]; } return $rows; } diff --git a/Civi/API/Subscriber/I18nSubscriber.php b/Civi/API/Subscriber/I18nSubscriber.php index 42ed6bf60ae3..c430928202ba 100644 --- a/Civi/API/Subscriber/I18nSubscriber.php +++ b/Civi/API/Subscriber/I18nSubscriber.php @@ -1,9 +1,9 @@ array('onApiPrepare', Events::W_MIDDLE), - ); + return [ + Events::PREPARE => ['onApiPrepare', Events::W_MIDDLE], + ]; } /** @@ -80,7 +81,7 @@ public function setLocale($lcMessagesRequest) { // on multi-lang sites based on request and civicrm_uf_match if ($multiLang) { $config = \CRM_Core_Config::singleton(); - $languageLimit = array(); + $languageLimit = []; if (isset($config->languageLimit) and $config->languageLimit) { $languageLimit = $config->languageLimit; } @@ -89,7 +90,7 @@ public function setLocale($lcMessagesRequest) { $lcMessages = $lcMessagesRequest; } else { - throw new \API_Exception(ts('Language not enabled: %1', array(1 => $lcMessagesRequest))); + throw new \API_Exception(ts('Language not enabled: %1', [1 => $lcMessagesRequest])); } } diff --git a/Civi/API/Subscriber/PermissionCheck.php b/Civi/API/Subscriber/PermissionCheck.php index b2c7900d4e5d..c4fe5c748139 100644 --- a/Civi/API/Subscriber/PermissionCheck.php +++ b/Civi/API/Subscriber/PermissionCheck.php @@ -1,9 +1,9 @@ array( - array('onApiAuthorize', Events::W_LATE), - ), - ); + return [ + Events::AUTHORIZE => [ + ['onApiAuthorize', Events::W_LATE], + ], + ]; } /** @@ -90,6 +91,12 @@ public function onApiAuthorize(\Civi\API\Event\AuthorizeEvent $event) { $event->authorize(); $event->stopPropagation(); } + elseif ($apiRequest['version'] == 4) { + if (!$apiRequest->getCheckPermissions()) { + $event->authorize(); + $event->stopPropagation(); + } + } } /** @@ -116,8 +123,8 @@ public function checkACLPermission($apiRequest) { case 'ActionSchedule': $events = \CRM_Event_BAO_Event::getEvents(); $aclEdit = \CRM_ACL_API::group(\CRM_Core_Permission::EDIT, NULL, 'civicrm_event', $events); - $param = array('id' => $apiRequest['params']['id']); - $eventId = \CRM_Core_BAO_ActionSchedule::retrieve($param, $value = array()); + $param = ['id' => $apiRequest['params']['id']]; + $eventId = \CRM_Core_BAO_ActionSchedule::retrieve($param, $value = []); if (in_array($eventId->entity_value, $aclEdit)) { return TRUE; } diff --git a/Civi/API/Subscriber/TransactionSubscriber.php b/Civi/API/Subscriber/TransactionSubscriber.php index 529146f51417..4f7b0c64d870 100644 --- a/Civi/API/Subscriber/TransactionSubscriber.php +++ b/Civi/API/Subscriber/TransactionSubscriber.php @@ -1,9 +1,9 @@ array('onApiPrepare', Events::W_EARLY), - Events::RESPOND => array('onApiRespond', Events::W_MIDDLE), - Events::EXCEPTION => array('onApiException', Events::W_EARLY), - ); + return [ + Events::PREPARE => ['onApiPrepare', Events::W_EARLY], + Events::RESPOND => ['onApiRespond', Events::W_MIDDLE], + Events::EXCEPTION => ['onApiException', Events::W_EARLY], + ]; } /** * @var array (scalar $apiRequestId => CRM_Core_Transaction $tx) */ - private $transactions = array(); + private $transactions = []; /** * @var array (scalar $apiRequestId => bool) @@ -66,7 +67,7 @@ public static function getSubscribedEvents() { * A list of requests which should be forcibly rolled back to * their save points. */ - private $forceRollback = array(); + private $forceRollback = []; /** * Determine if an API request should be treated as transactional. diff --git a/Civi/API/Subscriber/WhitelistSubscriber.php b/Civi/API/Subscriber/WhitelistSubscriber.php index 128fde9c4f5b..fbb272d3959d 100644 --- a/Civi/API/Subscriber/WhitelistSubscriber.php +++ b/Civi/API/Subscriber/WhitelistSubscriber.php @@ -1,9 +1,9 @@ array('onApiAuthorize', Events::W_EARLY), - Events::RESPOND => array('onApiRespond', Events::W_MIDDLE), - ); + return [ + Events::AUTHORIZE => ['onApiAuthorize', Events::W_EARLY], + Events::RESPOND => ['onApiRespond', Events::W_MIDDLE], + ]; } /** @@ -70,11 +69,12 @@ public static function getSubscribedEvents() { * @param array $rules * Array of WhitelistRule. * @see WhitelistRule + * @throws \CRM_Core_Exception */ public function __construct($rules) { - $this->rules = array(); + $this->rules = []; foreach ($rules as $rule) { - /** @var WhitelistRule $rule */ + /** @var \Civi\API\WhitelistRule $rule */ if ($rule->isValid()) { $this->rules[] = $rule; } @@ -88,7 +88,7 @@ public function __construct($rules) { * Determine which, if any, whitelist rules apply this request. * Reject unauthorized requests. * - * @param AuthorizeEvent $event + * @param \Civi\API\Event\AuthorizeEvent $event * @throws \CRM_Core_Exception */ public function onApiAuthorize(AuthorizeEvent $event) { @@ -107,7 +107,7 @@ public function onApiAuthorize(AuthorizeEvent $event) { /** * Apply any filtering rules based on the chosen whitelist rule. - * @param RespondEvent $event + * @param \Civi\API\Event\RespondEvent $event */ public function onApiRespond(RespondEvent $event) { $apiRequest = $event->getApiRequest(); diff --git a/Civi/API/Subscriber/WrapperAdapter.php b/Civi/API/Subscriber/WrapperAdapter.php index fc939e1b029d..736aa548bd8c 100644 --- a/Civi/API/Subscriber/WrapperAdapter.php +++ b/Civi/API/Subscriber/WrapperAdapter.php @@ -1,9 +1,9 @@ array('onApiPrepare', Events::W_MIDDLE), - Events::RESPOND => array('onApiRespond', Events::W_EARLY * 2), - ); + return [ + Events::PREPARE => ['onApiPrepare', Events::W_MIDDLE], + Events::RESPOND => ['onApiRespond', Events::W_EARLY * 2], + ]; } /** @@ -56,7 +56,7 @@ public static function getSubscribedEvents() { * @param array $defaults * array(\API_Wrapper). */ - public function __construct($defaults = array()) { + public function __construct($defaults = []) { $this->defaults = $defaults; } diff --git a/Civi/API/Subscriber/XDebugSubscriber.php b/Civi/API/Subscriber/XDebugSubscriber.php index a80b37fcb6f1..0648cf50b73b 100644 --- a/Civi/API/Subscriber/XDebugSubscriber.php +++ b/Civi/API/Subscriber/XDebugSubscriber.php @@ -1,9 +1,9 @@ array('onApiRespond', Events::W_LATE), - ); + return [ + Events::RESPOND => ['onApiRespond', Events::W_LATE], + ]; } /** diff --git a/Civi/API/WhitelistRule.php b/Civi/API/WhitelistRule.php index 1004825a9c7b..0cc9ef01292e 100644 --- a/Civi/API/WhitelistRule.php +++ b/Civi/API/WhitelistRule.php @@ -1,9 +1,9 @@ actions = '*'; } else { - $this->actions = array(); + $this->actions = []; foreach ((array) $ruleSpec['actions'] as $action) { $this->actions[] = Request::normalizeActionName($action, $ruleSpec['version']); } @@ -197,7 +197,7 @@ public function matches($apiRequest) { // Kind'a silly we need to (re(re))parse here for each rule; would be more // performant if pre-parsed by Request::create(). $options = _civicrm_api3_get_options_from_params($apiRequest['params'], TRUE, $apiRequest['entity'], 'get'); - $return = \CRM_Utils_Array::value('return', $options, array()); + $return = \CRM_Utils_Array::value('return', $options, []); $activatedFields = array_merge($activatedFields, array_keys($return)); } @@ -267,7 +267,7 @@ public function filter($apiRequest, $apiResult) { * List of acceptable keys. */ protected function filterFields($keys) { - $r = array(); + $r = []; foreach ($keys as $key) { if (in_array($key, $this->fields)) { $r[] = $key; diff --git a/Civi/ActionSchedule/Event/MailingQueryEvent.php b/Civi/ActionSchedule/Event/MailingQueryEvent.php index 472c1c3bcdd4..e8b136e84dc4 100644 --- a/Civi/ActionSchedule/Event/MailingQueryEvent.php +++ b/Civi/ActionSchedule/Event/MailingQueryEvent.php @@ -1,7 +1,6 @@ Mapping $mapping). */ - protected $mappings = array(); + protected $mappings = []; /** * Register a new mapping. * - * @param MappingInterface $mapping + * @param \Civi\ActionSchedule\MappingInterface $mapping * The new mapping. * @return MappingRegisterEvent */ diff --git a/Civi/ActionSchedule/Events.php b/Civi/ActionSchedule/Events.php index 15e49f71db83..3ff1a385e132 100644 --- a/Civi/ActionSchedule/Events.php +++ b/Civi/ActionSchedule/Events.php @@ -8,12 +8,12 @@ class Events { * * @see EntityListEvent */ - const MAPPINGS = 'actionSchedule.getMappings'; + const MAPPINGS = 'civi.actionSchedule.getMappings'; /** * Prepare the pre-mailing query. This query loads details about * the contact/entity so that they're available for mail-merge. */ - const MAILING_QUERY = 'actionSchedule.prepareMailingQuery'; + const MAILING_QUERY = 'civi.actionSchedule.prepareMailingQuery'; } diff --git a/Civi/ActionSchedule/Mapping.php b/Civi/ActionSchedule/Mapping.php index 6fe50fb41c01..9e6dcbf91972 100644 --- a/Civi/ActionSchedule/Mapping.php +++ b/Civi/ActionSchedule/Mapping.php @@ -1,9 +1,9 @@ entity_status); @@ -242,7 +242,7 @@ public function getStatusLabels($value) { * Array(string $fieldName => string $fieldLabel). */ public function getDateFields() { - $dateFieldLabels = array(); + $dateFieldLabels = []; if (!empty($this->entity_date_start)) { $dateFieldLabels[$this->entity_date_start] = ucwords(str_replace('_', ' ', $this->entity_date_start)); } @@ -263,7 +263,7 @@ public function getDateFields() { * Ex: array('assignee' => 'Activity Assignee'). */ public function getRecipientTypes() { - return array(); + return []; } /** @@ -280,13 +280,14 @@ public function getRecipientTypes() { * @see getRecipientTypes */ public function getRecipientListing($recipientType) { - return array(); + return []; } protected static function getValueLabelMap($name) { static $valueLabelMap = NULL; if ($valueLabelMap === NULL) { - $valueLabelMap['activity_type'] = \CRM_Core_PseudoConstant::activityType(TRUE, TRUE); + // CRM-20510: Include CiviCampaign activity types along with CiviCase IF component is enabled + $valueLabelMap['activity_type'] = \CRM_Core_PseudoConstant::activityType(TRUE, TRUE, FALSE, 'label', TRUE); asort($valueLabelMap['activity_type']); $valueLabelMap['activity_status'] = \CRM_Core_PseudoConstant::activityStatus(); @@ -299,11 +300,11 @@ protected static function getValueLabelMap($name) { $valueLabelMap['civicrm_membership_type'] = \CRM_Member_PseudoConstant::membershipType(); $allCustomFields = \CRM_Core_BAO_CustomField::getFields(''); - $dateFields = array( + $dateFields = [ 'birth_date' => ts('Birth Date'), 'created_date' => ts('Created Date'), 'modified_date' => ts('Modified Date'), - ); + ]; foreach ($allCustomFields as $fieldID => $field) { if ($field['data_type'] == 'Date') { $dateFields["custom_$fieldID"] = $field['label']; @@ -325,7 +326,7 @@ protected static function getValueLabelMap($name) { * List of error messages. */ public function validateSchedule($schedule) { - return array(); + return []; } /** @@ -338,6 +339,6 @@ public function validateSchedule($schedule) { * @param array $defaultParams * @return \CRM_Utils_SQL_Select */ - public abstract function createQuery($schedule, $phase, $defaultParams); + abstract public function createQuery($schedule, $phase, $defaultParams); } diff --git a/Civi/ActionSchedule/MappingInterface.php b/Civi/ActionSchedule/MappingInterface.php index 54d28a3051c8..18627968aea2 100644 --- a/Civi/ActionSchedule/MappingInterface.php +++ b/Civi/ActionSchedule/MappingInterface.php @@ -1,9 +1,9 @@ prepareQuery(self::PHASE_ADDITION_FIRST); $insertAdditionalSql = \CRM_Utils_SQL_Select::from("civicrm_contact c") - ->merge($query, array('params')) + ->merge($query, ['params']) ->merge($this->selectIntoActionLog(self::PHASE_ADDITION_FIRST, $query)) ->merge($this->joinReminder('LEFT JOIN', 'addl', $query)) ->where('reminder.id IS NULL') @@ -279,13 +279,13 @@ protected function buildRelRepeatPass() { ->merge($this->selectActionLogFields(self::PHASE_RELATION_REPEAT, $query)) ->select("MAX(reminder.action_date_time) as latest_log_time") ->merge($this->prepareRepetitionEndFilter($query['casDateField'])) - ->where($this->actionSchedule->start_action_date ? $startDateClauses[0] : array()) + ->where($this->actionSchedule->start_action_date ? $startDateClauses[0] : []) ->groupBy("reminder.contact_id, reminder.entity_id, reminder.entity_table") // @todo replace use of timestampdiff with a direct comparison as TIMESTAMPDIFF cannot use an index. ->having("TIMESTAMPDIFF(HOUR, latest_log_time, CAST(!casNow AS datetime)) >= TIMESTAMPDIFF(HOUR, latest_log_time, DATE_ADD(latest_log_time, INTERVAL !casRepetitionInterval))") - ->param(array( + ->param([ 'casRepetitionInterval' => $this->parseRepetitionInterval(), - )) + ]) ->strict() ->toSQL(); @@ -296,7 +296,7 @@ protected function buildRelRepeatPass() { if ($arrValues) { \CRM_Core_DAO::executeQuery( \CRM_Utils_SQL_Insert::into('civicrm_action_log') - ->columns(array('contact_id', 'entity_id', 'entity_table', 'action_schedule_id')) + ->columns(['contact_id', 'entity_id', 'entity_table', 'action_schedule_id']) ->rows($arrValues) ->toSQL() ); @@ -314,7 +314,7 @@ protected function buildAddlRepeatPass() { $addlCheck = \CRM_Utils_SQL_Select::from($query['casAddlCheckFrom']) ->select('*') - ->merge($query, array('wheres'))// why only where? why not the joins? + ->merge($query, ['params', 'wheres', 'joins']) ->merge($this->prepareRepetitionEndFilter($query['casDateField'])) ->limit(1) ->strict() @@ -326,14 +326,14 @@ protected function buildAddlRepeatPass() { ->merge($this->selectActionLogFields(self::PHASE_ADDITION_REPEAT, $query)) ->merge($this->joinReminder('INNER JOIN', 'addl', $query)) ->select("MAX(reminder.action_date_time) as latest_log_time") - ->merge($this->prepareAddlFilter('c.id')) + ->merge($this->prepareAddlFilter('c.id'), ['params']) ->where("c.is_deleted = 0 AND c.is_deceased = 0") ->groupBy("reminder.contact_id") // @todo replace use of timestampdiff with a direct comparison as TIMESTAMPDIFF cannot use an index. - ->having("TIMESTAMPDIFF(HOUR, latest_log_time, CAST(!casNow AS datetime)) >= TIMESTAMPDIFF(HOUR, latest_log_time, DATE_ADD(latest_log_time, INTERVAL !casRepetitionInterval)") - ->param(array( + ->having("TIMESTAMPDIFF(HOUR, latest_log_time, CAST(!casNow AS datetime)) >= TIMESTAMPDIFF(HOUR, latest_log_time, DATE_ADD(latest_log_time, INTERVAL !casRepetitionInterval))") + ->param([ 'casRepetitionInterval' => $this->parseRepetitionInterval(), - )) + ]) ->strict() ->toSQL(); @@ -344,7 +344,7 @@ protected function buildAddlRepeatPass() { if ($addValues) { \CRM_Core_DAO::executeQuery( \CRM_Utils_SQL_Insert::into('civicrm_action_log') - ->columns(array('contact_id', 'entity_id', 'entity_table', 'action_schedule_id')) + ->columns(['contact_id', 'entity_id', 'entity_table', 'action_schedule_id']) ->rows($addValues) ->toSQL() ); @@ -358,12 +358,12 @@ protected function buildAddlRepeatPass() { * @throws \CRM_Core_Exception */ protected function prepareQuery($phase) { - $defaultParams = array( + $defaultParams = [ 'casActionScheduleId' => $this->actionSchedule->id, 'casMappingId' => $this->mapping->getId(), 'casMappingEntity' => $this->mapping->getEntity(), 'casNow' => $this->now, - ); + ]; /** @var \CRM_Utils_SQL_Select $query */ $query = $this->mapping->createQuery($this->actionSchedule, $phase, $defaultParams); @@ -419,18 +419,31 @@ protected function prepareContactFilter($contactIdField) { $actionSchedule = $this->actionSchedule; if ($actionSchedule->group_id) { - if ($this->isSmartGroup($actionSchedule->group_id)) { - // Check that the group is in place in the cache and up to date - \CRM_Contact_BAO_GroupContactCache::check($actionSchedule->group_id); - return \CRM_Utils_SQL_Select::fragment() - ->join('grp', "INNER JOIN civicrm_group_contact_cache grp ON {$contactIdField} = grp.contact_id") - ->where(" grp.group_id IN ({$actionSchedule->group_id})"); + $regularGroupIDs = $smartGroupIDs = $groupWhereCLause = []; + $query = \CRM_Utils_SQL_Select::fragment(); + + // get child group IDs if any + $childGroupIDs = \CRM_Contact_BAO_Group::getChildGroupIds($actionSchedule->group_id); + foreach (array_merge([$actionSchedule->group_id], $childGroupIDs) as $groupID) { + if ($this->isSmartGroup($groupID)) { + // Check that the group is in place in the cache and up to date + \CRM_Contact_BAO_GroupContactCache::check($groupID); + $smartGroupIDs[] = $groupID; + } + else { + $regularGroupIDs[] = $groupID; + } } - else { - return \CRM_Utils_SQL_Select::fragment() - ->join('grp', " INNER JOIN civicrm_group_contact grp ON {$contactIdField} = grp.contact_id AND grp.status = 'Added'") - ->where(" grp.group_id IN ({$actionSchedule->group_id})"); + + if (!empty($smartGroupIDs)) { + $query->join('sg', "LEFT JOIN civicrm_group_contact_cache sg ON {$contactIdField} = sg.contact_id"); + $groupWhereCLause[] = " sg.group_id IN ( " . implode(', ', $smartGroupIDs) . " ) "; + } + if (!empty($regularGroupIDs)) { + $query->join('rg', " LEFT JOIN civicrm_group_contact rg ON {$contactIdField} = rg.contact_id AND rg.status = 'Added'"); + $groupWhereCLause[] = " rg.group_id IN ( " . implode(', ', $regularGroupIDs) . " ) "; } + return $query->where(implode(" OR ", $groupWhereCLause)); } elseif (!empty($actionSchedule->recipient_manual)) { $rList = \CRM_Utils_Type::escape($actionSchedule->recipient_manual, 'String'); @@ -468,7 +481,7 @@ protected function prepareLanguageFilter($contactTableAlias) { */ protected function prepareStartDateClauses() { $actionSchedule = $this->actionSchedule; - $startDateClauses = array(); + $startDateClauses = []; if ($actionSchedule->start_action_date) { $op = ($actionSchedule->start_action_condition == 'before' ? '<=' : '>='); $operator = ($actionSchedule->start_action_condition == 'before' ? 'DATE_SUB' : 'DATE_ADD'); @@ -523,9 +536,9 @@ protected function prepareRepetitionEndFilter($dateField) { return \CRM_Utils_SQL_Select::fragment() ->where("@casNow <= !repetitionEndDate") - ->param(array( + ->param([ '!repetitionEndDate' => $repeatEventDateExpr, - )); + ]); } /** @@ -561,25 +574,30 @@ protected function selectActionLogFields($phase, $query) { $fragment->select($query['casDateField']); } $fragment->select( - array( + [ "!casContactIdField as contact_id", "!casEntityIdField as entity_id", "@casMappingEntity as entity_table", "#casActionScheduleId as action_schedule_id", - ) + ] ); break; case self::PHASE_ADDITION_FIRST: case self::PHASE_ADDITION_REPEAT: - $fragment = \CRM_Utils_SQL_Select::fragment(); + //CRM-19017: Load default params for fragment query object. + $params = [ + 'casActionScheduleId' => $this->actionSchedule->id, + 'casNow' => $this->now, + ]; + $fragment = \CRM_Utils_SQL_Select::fragment()->param($params); $fragment->select( - array( + [ "c.id as contact_id", "c.id as entity_id", "'civicrm_contact' as entity_table", "#casActionScheduleId as action_schedule_id", - ) + ] ); break; @@ -601,12 +619,12 @@ protected function selectActionLogFields($phase, $query) { * @throws \CRM_Core_Exception */ protected function selectIntoActionLog($phase, $query) { - $actionLogColumns = array( + $actionLogColumns = [ "contact_id", "entity_id", "entity_table", "action_schedule_id", - ); + ]; if ($phase === self::PHASE_RELATION_FIRST || $phase === self::PHASE_RELATION_REPEAT) { if (!empty($query['casUseReferenceDate'])) { array_unshift($actionLogColumns, 'reference_date'); diff --git a/Civi/Angular/AngularLoader.php b/Civi/Angular/AngularLoader.php new file mode 100644 index 000000000000..181c8d2c9347 --- /dev/null +++ b/Civi/Angular/AngularLoader.php @@ -0,0 +1,321 @@ +

    ` or `angular.bootstrap(...)`. + * + * @code + * $loader = new AngularLoader(); + * $loader->setPageName('civicrm/case/a'); + * $loader->setModules(array('crmApp')); + * $loader->load(); + * @endCode + * + * @link https://docs.angularjs.org/guide/bootstrap + */ +class AngularLoader { + + /** + * The weight to assign to any Angular JS module files. + */ + const DEFAULT_MODULE_WEIGHT = 200; + + /** + * The resource manager. + * + * Do not use publicly. Inject your own copy! + * + * @var \CRM_Core_Resources + */ + protected $res; + + /** + * The Angular module manager. + * + * Do not use publicly. Inject your own copy! + * + * @var \Civi\Angular\Manager + */ + protected $angular; + + /** + * The region of the page into which JavaScript will be loaded. + * + * @var string + */ + protected $region; + + /** + * @var string + * Ex: 'civicrm/a'. + */ + protected $pageName; + + /** + * @var array + * A list of modules to load. + */ + protected $modules; + + /** + * @var array|NULL + */ + protected $crmApp = NULL; + + /** + * AngularLoader constructor. + */ + public function __construct() { + $this->res = \CRM_Core_Resources::singleton(); + $this->angular = \Civi::service('angular'); + $this->region = \CRM_Utils_Request::retrieve('snippet', 'String') ? 'ajax-snippet' : 'html-header'; + $this->pageName = isset($_GET['q']) ? $_GET['q'] : NULL; + $this->modules = []; + } + + /** + * Register resources required by Angular. + * + * @return AngularLoader + */ + public function load() { + $angular = $this->getAngular(); + $res = $this->getRes(); + + if ($this->crmApp !== NULL) { + $this->addModules($this->crmApp['modules']); + $region = \CRM_Core_Region::instance($this->crmApp['region']); + $region->update('default', ['disabled' => TRUE]); + $region->add(['template' => $this->crmApp['file'], 'weight' => 0]); + $this->res->addSetting([ + 'crmApp' => [ + 'defaultRoute' => $this->crmApp['defaultRoute'], + ], + ]); + + // If trying to load an Angular page via AJAX, the route must be passed as a + // URL parameter, since the server doesn't receive information about + // URL fragments (i.e, what comes after the #). + $this->res->addSetting([ + 'angularRoute' => $this->crmApp['activeRoute'], + ]); + } + + $moduleNames = $this->findActiveModules(); + if (!$this->isAllModules($moduleNames)) { + $assetParams = ['modules' => implode(',', $moduleNames)]; + } + else { + // The module list will be "all modules that the user can see". + $assetParams = ['nonce' => md5(implode(',', $moduleNames))]; + } + + $res->addSettingsFactory(function () use (&$moduleNames, $angular, $res, $assetParams) { + // TODO optimization; client-side caching + $result = array_merge($angular->getResources($moduleNames, 'settings', 'settings'), [ + 'resourceUrls' => \CRM_Extension_System::singleton()->getMapper()->getActiveModuleUrls(), + 'angular' => [ + 'modules' => $moduleNames, + 'requires' => $angular->getResources($moduleNames, 'requires', 'requires'), + 'cacheCode' => $res->getCacheCode(), + 'bundleUrl' => \Civi::service('asset_builder')->getUrl('angular-modules.json', $assetParams), + ], + ]); + return $result; + }); + + $res->addScriptFile('civicrm', 'bower_components/angular/angular.min.js', 100, $this->getRegion(), FALSE); + $res->addScriptFile('civicrm', 'js/crm.angular.js', 101, $this->getRegion(), FALSE); + + $headOffset = 0; + $config = \CRM_Core_Config::singleton(); + if ($config->debug) { + foreach ($moduleNames as $moduleName) { + foreach ($this->angular->getResources($moduleName, 'css', 'cacheUrl') as $url) { + $res->addStyleUrl($url, self::DEFAULT_MODULE_WEIGHT + (++$headOffset), $this->getRegion()); + } + foreach ($this->angular->getResources($moduleName, 'js', 'cacheUrl') as $url) { + $res->addScriptUrl($url, self::DEFAULT_MODULE_WEIGHT + (++$headOffset), $this->getRegion()); + // addScriptUrl() bypasses the normal string-localization of addScriptFile(), + // but that's OK because all Angular strings (JS+HTML) will load via crmResource. + } + } + } + else { + // Note: addScriptUrl() bypasses the normal string-localization of addScriptFile(), + // but that's OK because all Angular strings (JS+HTML) will load via crmResource. + // $aggScriptUrl = \CRM_Utils_System::url('civicrm/ajax/angular-modules', 'format=js&r=' . $res->getCacheCode(), FALSE, NULL, FALSE); + $aggScriptUrl = \Civi::service('asset_builder')->getUrl('angular-modules.js', $assetParams); + $res->addScriptUrl($aggScriptUrl, 120, $this->getRegion()); + + // FIXME: The following CSS aggregator doesn't currently handle path-adjustments - which can break icons. + //$aggStyleUrl = \CRM_Utils_System::url('civicrm/ajax/angular-modules', 'format=css&r=' . $res->getCacheCode(), FALSE, NULL, FALSE); + //$aggStyleUrl = \Civi::service('asset_builder')->getUrl('angular-modules.css', $assetParams); + //$res->addStyleUrl($aggStyleUrl, 120, $this->getRegion()); + + foreach ($this->angular->getResources($moduleNames, 'css', 'cacheUrl') as $url) { + $res->addStyleUrl($url, self::DEFAULT_MODULE_WEIGHT + (++$headOffset), $this->getRegion()); + } + } + + return $this; + } + + /** + * Use Civi's generic "application" module. + * + * This is suitable for use on a basic, standalone Angular page + * like `civicrm/a`. (If you need to integrate Angular with pre-existing, + * non-Angular pages... then this probably won't help.) + * + * The Angular bootstrap process requires an HTML directive like + * `
    `. + * + * Calling useApp() will replace the page's main body with the + * `
    ...
    ` and apply some configuration options + * for the `crmApp` module. + * + * @param array $settings + * A list of settings. Accepted values: + * - activeRoute: string, the route to open up immediately + * Ex: '/case/list' + * - defaultRoute: string, use this to redirect the default route (`/`) to another page + * Ex: '/case/list' + * - region: string, the place on the page where we should insert the angular app + * Ex: 'page-body' + * @return AngularLoader + * @link https://code.angularjs.org/1.5.11/docs/guide/bootstrap + */ + public function useApp($settings = []) { + $defaults = [ + 'modules' => ['crmApp'], + 'activeRoute' => NULL, + 'defaultRoute' => NULL, + 'region' => 'page-body', + 'file' => 'Civi/Angular/Page/Main.tpl', + ]; + $this->crmApp = array_merge($defaults, $settings); + return $this; + } + + /** + * Get a list of all Angular modules which should be activated on this + * page. + * + * @return array + * List of module names. + * Ex: array('angularFileUpload', 'crmUi', 'crmUtil'). + */ + public function findActiveModules() { + return $this->angular->resolveDependencies(array_merge( + $this->getModules(), + $this->angular->resolveDefaultModules($this->getPageName()) + )); + } + + /** + * @param $moduleNames + * @return int + */ + private function isAllModules($moduleNames) { + $allModuleNames = array_keys($this->angular->getModules()); + return count(array_diff($allModuleNames, $moduleNames)) === 0; + } + + /** + * @return \CRM_Core_Resources + */ + public function getRes() { + return $this->res; + } + + /** + * @param \CRM_Core_Resources $res + * @return AngularLoader + */ + public function setRes($res) { + $this->res = $res; + return $this; + } + + /** + * @return \Civi\Angular\Manager + */ + public function getAngular() { + return $this->angular; + } + + /** + * @param \Civi\Angular\Manager $angular + * @return AngularLoader + */ + public function setAngular($angular) { + $this->angular = $angular; + return $this; + } + + /** + * @return string + */ + public function getRegion() { + return $this->region; + } + + /** + * @param string $region + * @return AngularLoader + */ + public function setRegion($region) { + $this->region = $region; + return $this; + } + + /** + * @return string + * Ex: 'civicrm/a'. + */ + public function getPageName() { + return $this->pageName; + } + + /** + * @param string $pageName + * Ex: 'civicrm/a'. + * @return AngularLoader + */ + public function setPageName($pageName) { + $this->pageName = $pageName; + return $this; + } + + /** + * @param array|string $modules + * @return AngularLoader + */ + public function addModules($modules) { + $modules = (array) $modules; + $this->modules = array_unique(array_merge($this->modules, $modules)); + return $this; + } + + /** + * @return array + */ + public function getModules() { + return $this->modules; + } + + /** + * @param array $modules + * @return AngularLoader + */ + public function setModules($modules) { + $this->modules = $modules; + return $this; + } + +} diff --git a/Civi/Angular/ChangeSet.php b/Civi/Angular/ChangeSet.php new file mode 100644 index 000000000000..f0eb38148cbf --- /dev/null +++ b/Civi/Angular/ChangeSet.php @@ -0,0 +1,191 @@ +resFilters as $filter) { + if ($filter['resourceType'] === $resourceType) { + $resources = call_user_func($filter['callback'], $resources); + } + } + } + return $resources; + } + + /** + * Update a set of HTML snippets. + * + * @param array $changeSets + * Array(ChangeSet). + * @param array $strings + * Array(string $path => string $html). + * @return array + * Updated list of $strings. + * @throws \CRM_Core_Exception + */ + private static function applyHtmlFilters($changeSets, $strings) { + $coder = new Coder(); + + foreach ($strings as $path => $html) { + /** @var \phpQueryObject $doc */ + $doc = NULL; + + // Most docs don't need phpQueryObject. Initialize phpQuery on first match. + + foreach ($changeSets as $changeSet) { + /** @var ChangeSet $changeSet */ + foreach ($changeSet->htmlFilters as $filter) { + if (preg_match($filter['regex'], $path)) { + if ($doc === NULL) { + $doc = \phpQuery::newDocument($html, 'text/html'); + if (\CRM_Core_Config::singleton()->debug && !$coder->checkConsistentHtml($html)) { + throw new \CRM_Core_Exception("Cannot process $path: inconsistent markup. Use check-angular.php to investigate."); + } + } + call_user_func($filter['callback'], $doc, $path); + } + } + } + + if ($doc !== NULL) { + $strings[$path] = $coder->encode($doc); + } + } + return $strings; + } + + /** + * @var string + */ + protected $name; + + /** + * @var array + * Each item is an array with keys: + * - resourceType: string + * - callback: function + */ + protected $resFilters = []; + + /** + * @var array + * Each item is an array with keys: + * - regex: string + * - callback: function + */ + protected $htmlFilters = []; + + /** + * @param string $name + * Symbolic name for this changeset. + * @return \Civi\Angular\ChangeSetInterface + */ + public static function create($name) { + $changeSet = new ChangeSet(); + $changeSet->name = $name; + return $changeSet; + } + + /** + * Declare that $module requires additional dependencies. + * + * @param string $module + * @param string|array $dependencies + * @return ChangeSet + */ + public function requires($module, $dependencies) { + $dependencies = (array) $dependencies; + return $this->alterResource('requires', + function ($values) use ($module, $dependencies) { + if (!isset($values[$module])) { + $values[$module] = []; + } + $values[$module] = array_unique(array_merge($values[$module], $dependencies)); + return $values; + }); + } + + /** + * Declare a change to a resource. + * + * @param string $resourceType + * @param callable $callback + * @return ChangeSet + */ + public function alterResource($resourceType, $callback) { + $this->resFilters[] = [ + 'resourceType' => $resourceType, + 'callback' => $callback, + ]; + return $this; + } + + /** + * Declare a change to HTML. + * + * @param string $file + * A file name, wildcard, or regex. + * Ex: '~/crmHello/intro.html' (filename) + * Ex: '~/crmHello/*.html' (wildcard) + * Ex: ';(Edit|List)Ctrl\.html$;' (regex) + * @param callable $callback + * Function which accepts up to two parameters: + * - phpQueryObject $doc + * - string $path + * @return ChangeSet + */ + public function alterHtml($file, $callback) { + $this->htmlFilters[] = [ + 'regex' => ($file{0} === ';') ? $file : $this->createRegex($file), + 'callback' => $callback, + ]; + return $this; + } + + /** + * Convert a string with a wildcard (*) to a regex. + * + * @param string $filterExpr + * Ex: "/foo/*.bar" + * @return string + * Ex: ";^/foo/[^/]*\.bar$;" + */ + protected function createRegex($filterExpr) { + $regex = preg_quote($filterExpr, ';'); + $regex = str_replace('\\*', '[^/]*', $regex); + $regex = ";^$regex$;"; + return $regex; + } + + /** + * @return string + */ + public function getName() { + return $this->name; + } + + /** + * @param string $name + */ + public function setName($name) { + $this->name = $name; + } + +} diff --git a/Civi/Angular/ChangeSetInterface.php b/Civi/Angular/ChangeSetInterface.php new file mode 100644 index 000000000000..9f30447e374f --- /dev/null +++ b/Civi/Angular/ChangeSetInterface.php @@ -0,0 +1,38 @@ +recode($html); + } + catch (\Exception $e) { + return FALSE; + } + + $htmlSig = preg_replace('/[ \t\r\n\/]+/', '', $this->cleanup($html)); + $docSig = preg_replace('/[ \t\r\n\/]+/', '', $recodedHtml); + if ($htmlSig !== $docSig || empty($html) != empty($htmlSig)) { + return FALSE; + } + return TRUE; + } + + /** + * Parse an HTML snippet and re-encode is as HTML. + * + * This is useful for detecting cases where the parser or encoder + * have quirks/bugs. + * + * @param string $html + * @return string + */ + public function recode($html) { + $doc = \phpQuery::newDocument("$html", 'text/html'); + return $this->encode($doc); + } + + /** + * Encode a phpQueryObject as HTML. + * + * @param \phpQueryObject $doc + * @return string + * HTML + */ + public function encode($doc) { + $doc->document->formatOutput = TRUE; + return $this->cleanup($doc->markupOuter()); + } + + protected function cleanup($html) { + $html = preg_replace_callback("/([\\-a-zA-Z0-9]+)=(')([^']*)(')/", [$this, 'cleanupAttribute'], $html); + $html = preg_replace_callback('/([\-a-zA-Z0-9]+)=(")([^"]*)(")/', [$this, 'cleanupAttribute'], $html); + return $html; + } + + protected function cleanupAttribute($matches) { + list ($full, $attr, $lquote, $value, $rquote) = $matches; + + switch ($attr) { + case 'href': + if (strpos($value, '%7B%7B') !== FALSE && strpos($value, '%7D%7D') !== FALSE) { + $value = urldecode($value); + } + break; + + default: + $value = html_entity_decode($value); + break; + } + + return "$attr=$lquote$value$rquote"; + } + +} diff --git a/Civi/Angular/Manager.php b/Civi/Angular/Manager.php index 9982b21c6b83..f822d481d5c6 100644 --- a/Civi/Angular/Manager.php +++ b/Civi/Angular/Manager.php @@ -30,12 +30,25 @@ class Manager { */ protected $modules = NULL; + /** + * @var \CRM_Utils_Cache_Interface + */ + protected $cache; + + /** + * @var array + * Array(string $name => ChangeSet $change). + */ + protected $changeSets = NULL; + /** * @param \CRM_Core_Resources $res * The resource manager. + * @param $cache */ - public function __construct($res) { + public function __construct($res, \CRM_Utils_Cache_Interface $cache = NULL) { $this->res = $res; + $this->cache = $cache ? $cache : new \CRM_Utils_Cache_Arraycache([]); } /** @@ -58,92 +71,40 @@ public function __construct($res) { public function getModules() { if ($this->modules === NULL) { $config = \CRM_Core_Config::singleton(); + global $civicrm_root; + + // Note: It would be nice to just glob("$civicrm_root/ang/*.ang.php"), but at time + // of writing CiviMail and CiviCase have special conditionals. - $angularModules = array(); - $angularModules['angularFileUpload'] = array( - 'ext' => 'civicrm', - 'js' => array('bower_components/angular-file-upload/angular-file-upload.min.js'), - ); - $angularModules['crmApp'] = array( - 'ext' => 'civicrm', - 'js' => array('ang/crmApp.js'), - ); - $angularModules['crmAttachment'] = array( - 'ext' => 'civicrm', - 'js' => array('ang/crmAttachment.js'), - 'css' => array('ang/crmAttachment.css'), - 'partials' => array('ang/crmAttachment'), - 'settings' => array( - 'token' => \CRM_Core_Page_AJAX_Attachment::createToken(), - ), - ); - $angularModules['crmAutosave'] = array( - 'ext' => 'civicrm', - 'js' => array('ang/crmAutosave.js'), - ); - $angularModules['crmCxn'] = array( - 'ext' => 'civicrm', - 'js' => array('ang/crmCxn.js', 'ang/crmCxn/*.js'), - 'css' => array('ang/crmCxn.css'), - 'partials' => array('ang/crmCxn'), - ); - //$angularModules['crmExample'] = array( - // 'ext' => 'civicrm', - // 'js' => array('ang/crmExample.js'), - // 'partials' => array('ang/crmExample'), - //); - $angularModules['crmResource'] = array( - 'ext' => 'civicrm', - // 'js' => array('js/angular-crmResource/byModule.js'), // One HTTP request per module. - 'js' => array('js/angular-crmResource/all.js'), // One HTTP request for all modules. - ); - $angularModules['crmUi'] = array( - 'ext' => 'civicrm', - 'js' => array('ang/crmUi.js'), - 'partials' => array('ang/crmUi'), - ); - $angularModules['crmUtil'] = array( - 'ext' => 'civicrm', - 'js' => array('ang/crmUtil.js'), - ); - // https://github.com/jwstadler/angular-jquery-dialog-service - $angularModules['dialogService'] = array( - 'ext' => 'civicrm', - 'js' => array('bower_components/angular-jquery-dialog-service/dialog-service.js'), - ); - $angularModules['ngRoute'] = array( - 'ext' => 'civicrm', - 'js' => array('bower_components/angular-route/angular-route.min.js'), - ); - $angularModules['ngSanitize'] = array( - 'ext' => 'civicrm', - 'js' => array('bower_components/angular-sanitize/angular-sanitize.min.js'), - ); - $angularModules['ui.utils'] = array( - 'ext' => 'civicrm', - 'js' => array('bower_components/angular-ui-utils/ui-utils.min.js'), - ); - $angularModules['ui.sortable'] = array( - 'ext' => 'civicrm', - 'js' => array('bower_components/angular-ui-sortable/sortable.min.js'), - ); - $angularModules['unsavedChanges'] = array( - 'ext' => 'civicrm', - 'js' => array('bower_components/angular-unsavedChanges/dist/unsavedChanges.min.js'), - ); - - $angularModules['statuspage'] = array( - 'ext' => 'civicrm', - 'js' => array('ang/crmStatusPage.js', 'ang/crmStatusPage/*.js'), - 'css' => array('ang/crmStatusPage.css'), - 'partials' => array('ang/crmStatusPage'), - 'settings' => array(), - ); + $angularModules = []; + $angularModules['angularFileUpload'] = include "$civicrm_root/ang/angularFileUpload.ang.php"; + $angularModules['crmApp'] = include "$civicrm_root/ang/crmApp.ang.php"; + $angularModules['crmAttachment'] = include "$civicrm_root/ang/crmAttachment.ang.php"; + $angularModules['crmAutosave'] = include "$civicrm_root/ang/crmAutosave.ang.php"; + $angularModules['crmCxn'] = include "$civicrm_root/ang/crmCxn.ang.php"; + // $angularModules['crmExample'] = include "$civicrm_root/ang/crmExample.ang.php"; + $angularModules['crmResource'] = include "$civicrm_root/ang/crmResource.ang.php"; + $angularModules['crmRouteBinder'] = include "$civicrm_root/ang/crmRouteBinder.ang.php"; + $angularModules['crmUi'] = include "$civicrm_root/ang/crmUi.ang.php"; + $angularModules['crmUtil'] = include "$civicrm_root/ang/crmUtil.ang.php"; + $angularModules['dialogService'] = include "$civicrm_root/ang/dialogService.ang.php"; + $angularModules['ngRoute'] = include "$civicrm_root/ang/ngRoute.ang.php"; + $angularModules['ngSanitize'] = include "$civicrm_root/ang/ngSanitize.ang.php"; + $angularModules['ui.utils'] = include "$civicrm_root/ang/ui.utils.ang.php"; + $angularModules['ui.bootstrap'] = include "$civicrm_root/ang/ui.bootstrap.ang.php"; + $angularModules['ui.sortable'] = include "$civicrm_root/ang/ui.sortable.ang.php"; + $angularModules['unsavedChanges'] = include "$civicrm_root/ang/unsavedChanges.ang.php"; + $angularModules['statuspage'] = include "$civicrm_root/ang/crmStatusPage.ang.php"; foreach (\CRM_Core_Component::getEnabledComponents() as $component) { $angularModules = array_merge($angularModules, $component->getAngularModules()); } \CRM_Utils_Hook::angularModules($angularModules); + foreach (array_keys($angularModules) as $module) { + if (!isset($angularModules[$module]['basePages'])) { + $angularModules[$module]['basePages'] = ['civicrm/a']; + } + } $this->modules = $this->resolvePatterns($angularModules); } @@ -171,6 +132,59 @@ public function getModule($name) { return $modules[$name]; } + /** + * Resolve a full list of Angular dependencies. + * + * @param array $names + * List of Angular modules. + * Ex: array('crmMailing'). + * @return array + * List of Angular modules, include all dependencies. + * Ex: array('crmMailing', 'crmUi', 'crmUtil', 'ngRoute'). + */ + public function resolveDependencies($names) { + $allModules = $this->getModules(); + $visited = []; + $result = $names; + while (($missingModules = array_diff($result, array_keys($visited))) && !empty($missingModules)) { + foreach ($missingModules as $module) { + $visited[$module] = 1; + if (!isset($allModules[$module])) { + \Civi::log()->warning('Unrecognized Angular module {name}. Please ensure that all Angular modules are declared.', [ + 'name' => $module, + 'civi.tag' => 'deprecated', + ]); + } + elseif (isset($allModules[$module]['requires'])) { + $result = array_unique(array_merge($result, $allModules[$module]['requires'])); + } + } + } + sort($result); + return $result; + } + + /** + * Get a list of Angular modules that should be loaded on the given + * base-page. + * + * @param string $basePage + * The name of the base-page for which we want a list of moudles. + * @return array + * List of Angular modules. + * Ex: array('crmMailing', 'crmUi', 'crmUtil', 'ngRoute'). + */ + public function resolveDefaultModules($basePage) { + $modules = $this->getModules(); + $result = []; + foreach ($modules as $moduleName => $module) { + if (in_array($basePage, $module['basePages']) || in_array('*', $module['basePages'])) { + $result[] = $moduleName; + } + } + return $result; + } + /** * Convert any globs in an Angular module to file names. * @@ -180,10 +194,10 @@ public function getModule($name) { * Updated list of Angular modules */ protected function resolvePatterns($modules) { - $newModules = array(); + $newModules = []; foreach ($modules as $moduleKey => $module) { - foreach (array('js', 'css', 'partials') as $fileset) { + foreach (['js', 'css', 'partials'] as $fileset) { if (!isset($module[$fileset])) { continue; } @@ -196,7 +210,7 @@ protected function resolvePatterns($modules) { } /** - * Get the partial HTML documents for a module. + * Get the partial HTML documents for a module (unfiltered). * * @param string $name * Angular module name. @@ -205,9 +219,9 @@ protected function resolvePatterns($modules) { * @throws \Exception * Invalid partials configuration. */ - public function getPartials($name) { + public function getRawPartials($name) { $module = $this->getModule($name); - $result = array(); + $result = []; if (isset($module['partials'])) { foreach ($module['partials'] as $partialDir) { $partialDir = $this->res->getPath($module['ext']) . '/' . $partialDir; @@ -217,10 +231,31 @@ public function getPartials($name) { $result[$filename] = file_get_contents($partialDir . '/' . $file); } } + return $result; } return $result; } + /** + * Get the partial HTML documents for a module. + * + * @param string $name + * Angular module name. + * @return array + * Array(string $extFilePath => string $html) + * @throws \Exception + * Invalid partials configuration. + */ + public function getPartials($name) { + $cacheKey = "angular-partials_$name"; + $cacheValue = $this->cache->get($cacheKey); + if ($cacheValue === NULL) { + $cacheValue = ChangeSet::applyResourceFilters($this->getChangeSets(), 'partials', $this->getRawPartials($name)); + $this->cache->set($cacheKey, $cacheValue); + } + return $cacheValue; + } + /** * Get list of translated strings for a module. * @@ -231,14 +266,14 @@ public function getPartials($name) { */ public function getTranslatedStrings($name) { $module = $this->getModule($name); - $result = array(); + $result = []; $strings = $this->getStrings($name); foreach ($strings as $string) { // TODO: should we pass translation domain based on $module[ext] or $module[tsDomain]? // It doesn't look like client side really supports the domain right now... - $translated = ts($string, array( - 'domain' => array($module['ext'], NULL), - )); + $translated = ts($string, [ + 'domain' => [$module['ext'], NULL], + ]); if ($translated != $string) { $result[$string] = $translated; } @@ -256,7 +291,7 @@ public function getTranslatedStrings($name) { */ public function getStrings($name) { $module = $this->getModule($name); - $result = array(); + $result = []; if (isset($module['js'])) { foreach ($module['js'] as $file) { $strings = $this->res->getStrings()->get( @@ -267,19 +302,9 @@ public function getStrings($name) { $result = array_unique(array_merge($result, $strings)); } } - if (isset($module['partials'])) { - foreach ($module['partials'] as $partialDir) { - $partialDir = $this->res->getPath($module['ext']) . '/' . $partialDir; - $files = \CRM_Utils_File::findFiles($partialDir, '*.html'); - foreach ($files as $file) { - $strings = $this->res->getStrings()->get( - $module['ext'], - $file, - 'text/html' - ); - $result = array_unique(array_merge($result, $strings)); - } - } + $partials = $this->getPartials($name); + foreach ($partials as $partial) { + $result = array_unique(array_merge($result, \CRM_Utils_JS::parseStrings($partial))); } return $result; } @@ -298,13 +323,18 @@ public function getStrings($name) { * @throws \CRM_Core_Exception */ public function getResources($moduleNames, $resType, $refType) { - $result = array(); + $result = []; $moduleNames = (array) $moduleNames; foreach ($moduleNames as $moduleName) { $module = $this->getModule($moduleName); if (isset($module[$resType])) { foreach ($module[$resType] as $file) { - switch ($refType) { + $refTypeSuffix = ''; + if (is_string($file) && preg_match(';^(assetBuilder|ext)://;', $file)) { + $refTypeSuffix = '-' . parse_url($file, PHP_URL_SCHEME); + } + + switch ($refType . $refTypeSuffix) { case 'path': $result[] = $this->res->getPath($module['ext'], $file); break; @@ -317,7 +347,35 @@ public function getResources($moduleNames, $resType, $refType) { $result[] = $this->res->getUrl($module['ext'], $file, TRUE); break; + case 'path-assetBuilder': + $assetName = parse_url($file, PHP_URL_HOST) . parse_url($file, PHP_URL_PATH); + $assetParams = []; + parse_str('' . parse_url($file, PHP_URL_QUERY), $assetParams); + $result[] = \Civi::service('asset_builder')->getPath($assetName, $assetParams); + break; + + case 'rawUrl-assetBuilder': + case 'cacheUrl-assetBuilder': + $assetName = parse_url($file, PHP_URL_HOST) . parse_url($file, PHP_URL_PATH); + $assetParams = []; + parse_str('' . parse_url($file, PHP_URL_QUERY), $assetParams); + $result[] = \Civi::service('asset_builder')->getUrl($assetName, $assetParams); + break; + + case 'path-ext': + $result[] = $this->res->getPath(parse_url($file, PHP_URL_HOST), ltrim(parse_url($file, PHP_URL_PATH), '/')); + break; + + case 'rawUrl-ext': + $result[] = $this->res->getUrl(parse_url($file, PHP_URL_HOST), ltrim(parse_url($file, PHP_URL_PATH), '/')); + break; + + case 'cacheUrl-ext': + $result[] = $this->res->getUrl(parse_url($file, PHP_URL_HOST), ltrim(parse_url($file, PHP_URL_PATH), '/'), TRUE); + break; + case 'settings': + case 'requires': if (!empty($module[$resType])) { $result[$moduleName] = $module[$resType]; } @@ -329,7 +387,29 @@ public function getResources($moduleNames, $resType, $refType) { } } } - return $result; + + return ChangeSet::applyResourceFilters($this->getChangeSets(), $resType, $result); + } + + /** + * @return array + * Array(string $name => ChangeSet $changeSet). + */ + public function getChangeSets() { + if ($this->changeSets === NULL) { + $this->changeSets = []; + \CRM_Utils_Hook::alterAngular($this); + } + return $this->changeSets; + } + + /** + * @param ChangeSet $changeSet + * @return \Civi\Angular\Manager + */ + public function add($changeSet) { + $this->changeSets[$changeSet->getName()] = $changeSet; + return $this; } } diff --git a/Civi/Angular/Page/Main.php b/Civi/Angular/Page/Main.php index 2f945ebae799..1c2a447a58fe 100644 --- a/Civi/Angular/Page/Main.php +++ b/Civi/Angular/Page/Main.php @@ -8,6 +8,7 @@ * @link https://issues.civicrm.org/jira/browse/CRM-14479 */ class Main extends \CRM_Core_Page { + /** * The weight to assign to any Angular JS module files. */ @@ -19,16 +20,17 @@ class Main extends \CRM_Core_Page { * Do not use publicly. Inject your own copy! * * @var \CRM_Core_Resources + * @deprecated */ public $res; - /** * The Angular module manager. * * Do not use publicly. Inject your own copy! * * @var \Civi\Angular\Manager + * @deprecated */ public $angular; @@ -36,6 +38,7 @@ class Main extends \CRM_Core_Page { * The region of the page into which JavaScript will be loaded. * * @var String + * @deprecated */ public $region; @@ -71,57 +74,14 @@ public function run() { * Register resources required by Angular. */ public function registerResources() { - $modules = $this->angular->getModules(); - $page = $this; // PHP 5.3 does not propagate $this to inner functions. - - $this->res->addSettingsFactory(function () use (&$modules, $page) { - // TODO optimization; client-side caching - return array_merge($page->angular->getResources(array_keys($modules), 'settings', 'settings'), array( - 'resourceUrls' => \CRM_Extension_System::singleton()->getMapper()->getActiveModuleUrls(), - 'angular' => array( - 'modules' => array_merge(array('ngRoute'), array_keys($modules)), - 'cacheCode' => $page->res->getCacheCode(), - ), - )); - }); - - $this->res->addScriptFile('civicrm', 'bower_components/angular/angular.min.js', 100, $this->region, FALSE); - - $headOffset = 0; - $config = \CRM_Core_Config::singleton(); - if ($config->debug) { - foreach ($modules as $moduleName => $module) { - foreach ($this->angular->getResources($moduleName, 'css', 'cacheUrl') as $url) { - $this->res->addStyleUrl($url, self::DEFAULT_MODULE_WEIGHT + (++$headOffset), $this->region); - } - foreach ($this->angular->getResources($moduleName, 'js', 'cacheUrl') as $url) { - $this->res->addScriptUrl($url, self::DEFAULT_MODULE_WEIGHT + (++$headOffset), $this->region); - // addScriptUrl() bypasses the normal string-localization of addScriptFile(), - // but that's OK because all Angular strings (JS+HTML) will load via crmResource. - } - } - } - else { - // Note: addScriptUrl() bypasses the normal string-localization of addScriptFile(), - // but that's OK because all Angular strings (JS+HTML) will load via crmResource. - $aggScriptUrl = \CRM_Utils_System::url('civicrm/ajax/angular-modules', 'format=js&r=' . $page->res->getCacheCode(), FALSE, NULL, FALSE); - $this->res->addScriptUrl($aggScriptUrl, 120, $this->region); - - // FIXME: The following CSS aggregator doesn't currently handle path-adjustments - which can break icons. - //$aggStyleUrl = \CRM_Utils_System::url('civicrm/ajax/angular-modules', 'format=css&r=' . $page->res->getCacheCode(), FALSE, NULL, FALSE); - //$this->res->addStyleUrl($aggStyleUrl, 120, $this->region); - - foreach ($this->angular->getResources(array_keys($modules), 'css', 'cacheUrl') as $url) { - $this->res->addStyleUrl($url, self::DEFAULT_MODULE_WEIGHT + (++$headOffset), $this->region); - } - } + $loader = new \Civi\Angular\AngularLoader(); + $loader->setPageName('civicrm/a'); + $loader->useApp([ + 'activeRoute' => \CRM_Utils_Request::retrieve('route', 'String'), + 'defaultRoute' => NULL, + ]); + $loader->load(); - // If trying to load an Angular page via AJAX, the route must be passed as a - // URL parameter, since the server doesn't receive information about - // URL fragments (i.e, what comes after the #). - \CRM_Core_Resources::singleton()->addSetting(array( - 'angularRoute' => \CRM_Utils_Request::retrieve('route', 'String'), - )); } } diff --git a/Civi/Angular/Page/Modules.php b/Civi/Angular/Page/Modules.php index 2fc3d86368ee..3b6b5a75ba54 100644 --- a/Civi/Angular/Page/Modules.php +++ b/Civi/Angular/Page/Modules.php @@ -20,7 +20,10 @@ class Modules extends \CRM_Core_Page { /** - * See class description. + * Generate asset content (when accessed via older, custom + * "civicrm/ajax/anulgar-modules" route). + * + * @deprecated */ public function run() { /** @@ -59,20 +62,54 @@ public function run() { \CRM_Utils_System::civiExit(); } + /** + * Generate asset content (when accessed via AssetBuilder). + * + * @param \Civi\Core\Event\GenericHookEvent $event + * @see CRM_Utils_hook::buildAsset() + * @see \Civi\Core\AssetBuilder + */ + public static function buildAngularModules($event) { + $page = new Modules(); + $angular = \Civi::service('angular'); + + switch ($event->asset) { + case 'angular-modules.json': + $moduleNames = $page->parseModuleNames(\CRM_Utils_Array::value('modules', $event->params), $angular); + $event->mimeType = 'application/json'; + $event->content = json_encode($page->getMetadata($moduleNames, $angular)); + break; + + case 'angular-modules.js': + $moduleNames = $page->parseModuleNames(\CRM_Utils_Array::value('modules', $event->params), $angular); + $event->mimeType = 'application/javascript'; + $event->content = $page->digestJs($angular->getResources($moduleNames, 'js', 'path')); + break; + + case 'angular-modules.css': + $moduleNames = $page->parseModuleNames(\CRM_Utils_Array::value('modules', $event->params), $angular); + $event->mimeType = 'text/css'; + $event->content = \CRM_Utils_File::concat($angular->getResources($moduleNames, 'css', 'path'), "\n"); + + default: + // Not our problem. + } + } + /** * @param array $files * File paths. * @return string */ public function digestJs($files) { - $scripts = array(); + $scripts = []; foreach ($files as $file) { $scripts[] = file_get_contents($file); } $scripts = \CRM_Utils_JS::dedupeClosures( $scripts, - array('angular', '$', '_'), - array('angular', 'CRM.$', 'CRM._') + ['angular', '$', '_'], + ['angular', 'CRM.$', 'CRM._'] ); // This impl of stripComments currently adds 10-20ms and cuts ~7% return \CRM_Utils_JS::stripComments(implode("\n", $scripts)); @@ -107,10 +144,10 @@ public function parseModuleNames($modulesExpr, $angular) { */ public function getMetadata($moduleNames, $angular) { $modules = $angular->getModules(); - $result = array(); + $result = []; foreach ($moduleNames as $moduleName) { if (isset($modules[$moduleName])) { - $result[$moduleName] = array(); + $result[$moduleName] = []; $result[$moduleName]['domain'] = $modules[$moduleName]['ext']; $result[$moduleName]['js'] = $angular->getResources($moduleName, 'js', 'rawUrl'); $result[$moduleName]['css'] = $angular->getResources($moduleName, 'css', 'rawUrl'); diff --git a/Civi/CCase/Analyzer.php b/Civi/CCase/Analyzer.php index 607afed98158..12de74ab9fc4 100644 --- a/Civi/CCase/Analyzer.php +++ b/Civi/CCase/Analyzer.php @@ -1,9 +1,9 @@ getActivityIndex(array('activity_type_id', 'status_id')); - $activityTypeGroup = civicrm_api3('option_group', 'get', array('name' => 'activity_type')); - $activityType = array( + $idx = $this->getActivityIndex(['activity_type_id', 'status_id']); + $activityTypeGroup = civicrm_api3('option_group', 'get', ['name' => 'activity_type']); + $activityType = [ 'name' => $type, 'option_group_id' => $activityTypeGroup['id'], - ); + ]; $activityTypeID = civicrm_api3('option_value', 'get', $activityType); $activityTypeID = $activityTypeID['values'][$activityTypeID['id']]['value']; if ($status) { - $activityStatusGroup = civicrm_api3('option_group', 'get', array('name' => 'activity_status')); - $activityStatus = array( + $activityStatusGroup = civicrm_api3('option_group', 'get', ['name' => 'activity_status']); + $activityStatus = [ 'name' => $status, 'option_group_id' => $activityStatusGroup['id'], - ); + ]; $activityStatusID = civicrm_api3('option_value', 'get', $activityStatus); $activityStatusID = $activityStatusID['values'][$activityStatusID['id']]['value']; } @@ -115,13 +115,13 @@ public function getActivities() { if ($this->activities === NULL) { // TODO find batch-oriented API for getting all activities in a case $case = $this->getCase(); - $activities = array(); + $activities = []; if (isset($case['activities'])) { foreach ($case['activities'] as $actId) { - $result = civicrm_api3('Activity', 'get', array( + $result = civicrm_api3('Activity', 'get', [ 'id' => $actId, 'is_current_revision' => 1, - )); + ]); $activities = array_merge($activities, $result['values']); } } @@ -132,14 +132,15 @@ public function getActivities() { /** * Get a single activity record by type. + * This function is only used by SequenceListenerTest * * @param string $type * @throws \Civi\CCase\Exception\MultipleActivityException * @return array|NULL, activity record (api/v3) */ public function getSingleActivity($type) { - $idx = $this->getActivityIndex(array('activity_type_id', 'id')); - $actTypes = array_flip(\CRM_Core_PseudoConstant::activityType(TRUE, TRUE, FALSE, 'name')); + $idx = $this->getActivityIndex(['activity_type_id', 'id']); + $actTypes = array_flip(\CRM_Activity_BAO_Activity::buildOptions('activity_type_id', 'validate')); $typeId = $actTypes[$type]; $count = isset($idx[$typeId]) ? count($idx[$typeId]) : 0; @@ -168,13 +169,14 @@ public function getCaseId() { */ public function getCase() { if ($this->case === NULL) { - $this->case = civicrm_api3('case', 'getsingle', array('id' => $this->caseId)); + $this->case = civicrm_api3('case', 'getsingle', ['id' => $this->caseId]); } return $this->case; } /** * @return string + * @throws \CRM_Core_Exception */ public function getCaseType() { if ($this->caseType === NULL) { @@ -205,7 +207,7 @@ public function getActivityIndex($keys) { } /** - * @return SimpleXMLElement|NULL + * @return \SimpleXMLElement|NULL */ public function getXml() { if ($this->xml === NULL) { @@ -221,7 +223,7 @@ public function flush() { $this->case = NULL; $this->caseType = NULL; $this->activities = NULL; - $this->indices = array(); + $this->indices = []; } } diff --git a/Civi/CCase/CaseChangeListener.php b/Civi/CCase/CaseChangeListener.php index 65a4972f3ec2..b473a090e03d 100644 --- a/Civi/CCase/CaseChangeListener.php +++ b/Civi/CCase/CaseChangeListener.php @@ -1,9 +1,9 @@ analyzer = $analyzer; } + /** + * @inheritDoc + */ + public function getHookValues() { + return [$this->analyzer]; + } + } diff --git a/Civi/CCase/Events.php b/Civi/CCase/Events.php index 8323c31d3e39..a3890248f274 100644 --- a/Civi/CCase/Events.php +++ b/Civi/CCase/Events.php @@ -1,9 +1,9 @@ entity) { case 'Activity': if (!empty($event->object->case_id)) { - $caseId = $event->object->case_id; + $caseIds = $event->object->case_id; } break; @@ -58,7 +59,7 @@ public static function fireCaseChange(\Civi\Core\Event\PostEvent $event) { // by the time we get the post-delete event, the record is gone, so // there's nothing to analyze if ($event->action != 'delete') { - $caseId = $event->id; + $caseIds = $event->id; } break; @@ -66,30 +67,36 @@ public static function fireCaseChange(\Civi\Core\Event\PostEvent $event) { throw new \CRM_Core_Exception("CRM_Case_Listener does not support entity {$event->entity}"); } - if ($caseId) { - if (!isset(self::$isActive[$caseId])) { - $tx = new \CRM_Core_Transaction(); - \CRM_Core_Transaction::addCallback( - \CRM_Core_Transaction::PHASE_POST_COMMIT, - array(__CLASS__, 'fireCaseChangeForRealz'), - array($caseId), - "Civi_CCase_Events::fire::{$caseId}" - ); + if ($caseIds) { + foreach ((array) $caseIds as $caseId) { + if (!isset(self::$isActive[$caseId])) { + $tx = new \CRM_Core_Transaction(); + \CRM_Core_Transaction::addCallback( + \CRM_Core_Transaction::PHASE_POST_COMMIT, + [__CLASS__, 'fireCaseChangeForRealz'], + [$caseId], + "Civi_CCase_Events::fire::{$caseId}" + ); + } } } } /** - * @param $caseId + * Fire case change hook + * + * @param int|array $caseIds */ - public static function fireCaseChangeForRealz($caseId) { - if (!isset(self::$isActive[$caseId])) { - $tx = new \CRM_Core_Transaction(); - self::$isActive[$caseId] = 1; - $analyzer = new \Civi\CCase\Analyzer($caseId); - \CRM_Utils_Hook::caseChange($analyzer); - unset(self::$isActive[$caseId]); - unset($tx); + public static function fireCaseChangeForRealz($caseIds) { + foreach ((array) $caseIds as $caseId) { + if (!isset(self::$isActive[$caseId])) { + $tx = new \CRM_Core_Transaction(); + self::$isActive[$caseId] = 1; + $analyzer = new \Civi\CCase\Analyzer($caseId); + \CRM_Utils_Hook::caseChange($analyzer); + unset(self::$isActive[$caseId]); + unset($tx); + } } } diff --git a/Civi/CCase/SequenceListener.php b/Civi/CCase/SequenceListener.php index 340076ca2457..3629914bca9f 100644 --- a/Civi/CCase/SequenceListener.php +++ b/Civi/CCase/SequenceListener.php @@ -16,7 +16,7 @@ class SequenceListener implements CaseChangeListener { /** * @param bool $reset * Whether to forcibly rebuild the entire container. - * @return \Symfony\Component\DependencyInjection\TaggedContainerInterface + * @return SequenceListener */ public static function singleton($reset = FALSE) { if ($reset || self::$singleton === NULL) { @@ -33,9 +33,14 @@ public static function onCaseChange_static(\Civi\CCase\Event\CaseChangeEvent $ev } /** + * Triggers next case activity in sequence if current activity status is updated + * to type=COMPLETED(See CRM-21598). The adjoining activity is created according + * to the sequence configured in case type. + * * @param \Civi\CCase\Event\CaseChangeEvent $event * * @throws \CiviCRM_API3_Exception + * @return void */ public function onCaseChange(\Civi\CCase\Event\CaseChangeEvent $event) { /** @var \Civi\CCase\Analyzer $analyzer */ @@ -46,10 +51,10 @@ public function onCaseChange(\Civi\CCase\Event\CaseChangeEvent $event) { return; } - $actTypes = array_flip(\CRM_Core_PseudoConstant::activityType(TRUE, TRUE, FALSE, 'name')); - $actStatuses = array_flip(\CRM_Core_PseudoConstant::activityStatus('name')); + $actTypes = array_flip(\CRM_Activity_BAO_Activity::buildOptions('activity_type_id', 'validate')); + $actStatuses = array_flip(\CRM_Activity_BAO_Activity::getStatusesByType(\CRM_Activity_BAO_Activity::COMPLETED)); - $actIndex = $analyzer->getActivityIndex(array('activity_type_id', 'status_id')); + $actIndex = $analyzer->getActivityIndex(['activity_type_id', 'status_id']); foreach ($activitySetXML->ActivityTypes->ActivityType as $actTypeXML) { $actTypeId = $actTypes[(string) $actTypeXML->name]; @@ -58,7 +63,7 @@ public function onCaseChange(\Civi\CCase\Event\CaseChangeEvent $event) { $this->createActivity($analyzer, $actTypeXML); return; } - elseif (empty($actIndex[$actTypeId][$actStatuses['Completed']])) { + elseif (!in_array(key($actIndex[$actTypeId]), $actStatuses)) { // Haven't gotten past this step yet! return; } @@ -67,16 +72,16 @@ public function onCaseChange(\Civi\CCase\Event\CaseChangeEvent $event) { //CRM-17452 - Close the case only if all the activities are complete $activities = $analyzer->getActivities(); foreach ($activities as $activity) { - if ($activity['status_id'] != $actStatuses['Completed']) { + if (!in_array($activity['status_id'], $actStatuses)) { return; } } // OK, the all activities have completed - civicrm_api3('Case', 'create', array( + civicrm_api3('Case', 'create', [ 'id' => $analyzer->getCaseId(), 'status_id' => 'Closed', - )); + ]); $analyzer->flush(); } @@ -109,13 +114,18 @@ public function getSequenceXml($xml) { * @param \SimpleXMLElement $actXML the tag which describes the new activity */ public function createActivity(Analyzer $analyzer, \SimpleXMLElement $actXML) { - $params = array( + $params = [ 'activity_type_id' => (string) $actXML->name, 'status_id' => 'Scheduled', 'activity_date_time' => \CRM_Utils_Time::getTime('YmdHis'), 'case_id' => $analyzer->getCaseId(), - ); - $r = civicrm_api3('Activity', 'create', $params); + ]; + $case = $analyzer->getCase(); + if (!empty($case['contact_id'])) { + $params['target_id'] = \CRM_Utils_Array::first($case['contact_id']); + } + + civicrm_api3('Activity', 'create', $params); $analyzer->flush(); } diff --git a/Civi/CiUtil/Arrays.php b/Civi/CiUtil/Arrays.php index f34954c1d339..e3933ee126a2 100644 --- a/Civi/CiUtil/Arrays.php +++ b/Civi/CiUtil/Arrays.php @@ -7,6 +7,7 @@ * @package Civi\CiUtil */ class Arrays { + /** * @param $arr * @param $col @@ -14,7 +15,7 @@ class Arrays { * @return array */ public static function collect($arr, $col) { - $r = array(); + $r = []; foreach ($arr as $k => $item) { $r[$k] = $item[$col]; } diff --git a/Civi/CiUtil/CSVParser.php b/Civi/CiUtil/CSVParser.php index 05893dfda009..f48521fde75b 100644 --- a/Civi/CiUtil/CSVParser.php +++ b/Civi/CiUtil/CSVParser.php @@ -19,7 +19,7 @@ public static function parseResults($csvContent) { fwrite($fh, $csvContent); rewind($fh); - $results = array(); + $results = []; while (($r = fgetcsv($fh)) !== FALSE) { $name = str_replace('.', '::', trim($r[0])); $status = trim($r[1]); diff --git a/Civi/CiUtil/Command/AntagonistCommand.php b/Civi/CiUtil/Command/AntagonistCommand.php index 1f9e9335f669..d8d9761ffe1a 100644 --- a/Civi/CiUtil/Command/AntagonistCommand.php +++ b/Civi/CiUtil/Command/AntagonistCommand.php @@ -7,6 +7,7 @@ * @package Civi\CiUtil\Command */ class AntagonistCommand { + /** * @param $argv */ @@ -17,7 +18,7 @@ public static function main($argv) { } list ($program, $target, $suite) = $argv; - $candidateTests = \Civi\CiUtil\PHPUnitScanner::findTestsByPath(array($suite)); + $candidateTests = \Civi\CiUtil\PHPUnitScanner::findTestsByPath([$suite]); // $candidateTests = array( // array('class' => 'CRM_Core_RegionTest', 'method' => 'testBlank'), // array('class' => 'CRM_Core_RegionTest', 'method' => 'testDefault'), @@ -26,10 +27,10 @@ public static function main($argv) { // ); $antagonist = self::findAntagonist($target, $candidateTests); if ($antagonist) { - print_r(array('found an antagonist' => $antagonist)); + print_r(['found an antagonist' => $antagonist]); } else { - print_r(array('found no antagonists')); + print_r(['found no antagonists']); } } @@ -50,26 +51,26 @@ public static function main($argv) { public static function findAntagonist($target, $candidateTests) { //$phpUnit = new \Civi\CiUtil\EnvTestRunner('./scripts/phpunit', 'EnvTests'); $phpUnit = new \Civi\CiUtil\EnvTestRunner('phpunit', 'tests/phpunit/EnvTests.php'); - $expectedResults = $phpUnit->run(array($target)); - print_r(array('$expectedResults' => $expectedResults)); + $expectedResults = $phpUnit->run([$target]); + print_r(['$expectedResults' => $expectedResults]); foreach ($candidateTests as $candidateTest) { $candidateTestName = $candidateTest['class'] . '::' . $candidateTest['method']; if ($candidateTestName == $target) { continue; } - $actualResults = $phpUnit->run(array( + $actualResults = $phpUnit->run([ $candidateTestName, $target, - )); - print_r(array('$actualResults' => $actualResults)); + ]); + print_r(['$actualResults' => $actualResults]); foreach ($expectedResults as $testName => $expectedResult) { if ($actualResults[$testName] != $expectedResult) { - return array( + return [ 'antagonist' => $candidateTest, 'expectedResults' => $expectedResults, 'actualResults' => $actualResults, - ); + ]; } } } diff --git a/Civi/CiUtil/Command/CompareCommand.php b/Civi/CiUtil/Command/CompareCommand.php index ae8bd6d3f026..beae7ac77976 100644 --- a/Civi/CiUtil/Command/CompareCommand.php +++ b/Civi/CiUtil/Command/CompareCommand.php @@ -7,6 +7,7 @@ * @package Civi\CiUtil\Command */ class CompareCommand { + /** * @param $argv */ @@ -17,21 +18,22 @@ public static function main($argv) { exit(1); } - $parser = array('\Civi\CiUtil\PHPUnitParser', 'parseJsonResults'); + $parser = ['\Civi\CiUtil\PHPUnitParser', 'parseJsonResults']; $printerType = 'txt'; - $suites = array(); // array('file' => string, 'results' => array) + // array('file' => string, 'results' => array) + $suites = []; for ($i = 1; $i < count($argv); $i++) { switch ($argv[$i]) { case '--phpunit-json': - $parser = array('\Civi\CiUtil\PHPUnitParser', 'parseJsonResults'); + $parser = ['\Civi\CiUtil\PHPUnitParser', 'parseJsonResults']; break; case '--jenkins-xml': - $parser = array('\Civi\CiUtil\JenkinsParser', 'parseXmlResults'); + $parser = ['\Civi\CiUtil\JenkinsParser', 'parseXmlResults']; break; case '--csv': - $parser = array('\Civi\CiUtil\CSVParser', 'parseResults'); + $parser = ['\Civi\CiUtil\CSVParser', 'parseResults']; break; case '--out=txt': @@ -43,14 +45,15 @@ public static function main($argv) { break; default: - $suites[] = array( + $suites[] = [ 'file' => $argv[$i], 'results' => call_user_func($parser, file_get_contents($argv[$i])), - ); + ]; } } - $tests = array(); // array(string $name) + // array(string $name) + $tests = []; foreach ($suites as $suite) { $tests = array_unique(array_merge( $tests, @@ -66,7 +69,7 @@ public static function main($argv) { $printer = new \Civi\CiUtil\ComparisonPrinter(\Civi\CiUtil\Arrays::collect($suites, 'file')); } foreach ($tests as $test) { - $values = array(); + $values = []; foreach ($suites as $suite) { $values[] = isset($suite['results'][$test]) ? $suite['results'][$test] : 'MISSING'; } diff --git a/Civi/CiUtil/Command/LsCommand.php b/Civi/CiUtil/Command/LsCommand.php index 88f0d84947e3..f0db547cf54c 100644 --- a/Civi/CiUtil/Command/LsCommand.php +++ b/Civi/CiUtil/Command/LsCommand.php @@ -7,6 +7,7 @@ * @package Civi\CiUtil\Command */ class LsCommand { + /** * @param $argv */ diff --git a/Civi/CiUtil/ComparisonPrinter.php b/Civi/CiUtil/ComparisonPrinter.php index a58a83eb6704..27acd8908c6c 100644 --- a/Civi/CiUtil/ComparisonPrinter.php +++ b/Civi/CiUtil/ComparisonPrinter.php @@ -7,8 +7,8 @@ * @package Civi\CiUtil */ class ComparisonPrinter { - var $headers; - var $hasHeader = FALSE; + public $headers; + public $hasHeader = FALSE; /** * @param $headers diff --git a/Civi/CiUtil/CsvPrinter.php b/Civi/CiUtil/CsvPrinter.php index 4c4ec185a3ef..55c7dbba4393 100644 --- a/Civi/CiUtil/CsvPrinter.php +++ b/Civi/CiUtil/CsvPrinter.php @@ -7,9 +7,9 @@ * @package Civi\CiUtil */ class CsvPrinter { - var $file; - var $headers; - var $hasHeader = FALSE; + public $file; + public $headers; + public $hasHeader = FALSE; /** * @param $file diff --git a/Civi/CiUtil/JenkinsParser.php b/Civi/CiUtil/JenkinsParser.php index 8ca35b0d8488..4a1429876869 100644 --- a/Civi/CiUtil/JenkinsParser.php +++ b/Civi/CiUtil/JenkinsParser.php @@ -5,6 +5,7 @@ * Parse Jenkins result files */ class JenkinsParser { + /** * @param string $content * Xml data. @@ -13,7 +14,7 @@ class JenkinsParser { */ public static function parseXmlResults($content) { $xml = simplexml_load_string($content); - $results = array(); + $results = []; foreach ($xml->suites as $suites) { foreach ($suites->suite as $suite) { foreach ($suite->cases as $cases) { diff --git a/Civi/CiUtil/PHPUnitParser.php b/Civi/CiUtil/PHPUnitParser.php index 06e0ec02aa40..9ff2b7d572c2 100644 --- a/Civi/CiUtil/PHPUnitParser.php +++ b/Civi/CiUtil/PHPUnitParser.php @@ -5,6 +5,7 @@ * Parse phpunit result files */ class PHPUnitParser { + /** * @param string $content * Phpunit streaming JSON. @@ -13,7 +14,7 @@ class PHPUnitParser { */ protected static function parseJsonStream($content) { $content = '[' - . strtr($content, array("}{" => "},{")) + . strtr($content, ["}{" => "},{"]) . ']'; return json_decode($content, TRUE); } @@ -26,7 +27,7 @@ protected static function parseJsonStream($content) { */ public static function parseJsonResults($content) { $records = self::parseJsonStream($content); - $results = array(); + $results = []; foreach ($records as $r) { if ($r['event'] == 'test') { $results[$r['test']] = $r['status']; diff --git a/Civi/CiUtil/PHPUnitScanner.php b/Civi/CiUtil/PHPUnitScanner.php index cd7a1b9a2ecd..a1028f52f5c8 100644 --- a/Civi/CiUtil/PHPUnitScanner.php +++ b/Civi/CiUtil/PHPUnitScanner.php @@ -7,6 +7,7 @@ * Search for PHPUnit test cases */ class PHPUnitScanner { + /** * @param $path * @return array class names @@ -32,7 +33,7 @@ public static function _findTestClasses($path) { * @throws \Exception */ public static function findTestClasses($paths) { - $testClasses = array(); + $testClasses = []; $finder = new Finder(); foreach ($paths as $path) { @@ -77,17 +78,17 @@ public static function findTestClasses($paths) { * - method: string */ public static function findTestsByPath($paths) { - $r = array(); + $r = []; $testClasses = self::findTestClasses($paths); foreach ($testClasses as $testFile => $testClass) { $clazz = new \ReflectionClass($testClass); foreach ($clazz->getMethods() as $method) { if (preg_match('/^test/', $method->name)) { - $r[] = array( + $r[] = [ 'file' => $testFile, 'class' => $testClass, 'method' => $method->name, - ); + ]; } } } diff --git a/Civi/Codeception/CiviAcceptanceTesterTrait.php b/Civi/Codeception/CiviAcceptanceTesterTrait.php new file mode 100644 index 000000000000..b5a4dea57a32 --- /dev/null +++ b/Civi/Codeception/CiviAcceptanceTesterTrait.php @@ -0,0 +1,110 @@ +amOnPage($newPage); + } + + /** + * Dispatcher for login to supported plattforms + * @param $username + * CiviCRM username for the login + * @param $password + * CiviCRM password for the login + */ + public function login($username, $password) { + $config = \CRM_Core_Config::singleton(); + $handler = [$this, 'loginTo' . $config->userFramework]; + if (is_callable($handler)) { + call_user_func($handler, $username, $password); + } + else { + throw new CRM_Core_Exception("Framework {$config->userFramework} is not supported. Implement loginTo{$config->userFramework}."); + } + } + + /** + * Login to Drupal + * @param $username + * CiviCRM username for the login + * @param $password + * CiviCRM password for the login + */ + public function loginToDrupal($username, $password) { + $I = $this; + $I->amOnPage('/user'); + $I->fillField("#edit-name", $username); + $I->fillField("#edit-pass", $password); + $I->click("#edit-submit"); + $I->see("CiviCRM Home"); + } + + /** + * Login to Joomla + * @param $username + * CiviCRM username for the login + * @param $password + * CiviCRM password for the login + */ + public function loginToJoomla($username, $password) { + throw new CRM_Core_Exception("loginToJoomla is not implemented yet. Implement a corresponding login function."); + } + + /** + * Login to Wordpress + * @param $username + * CiviCRM username for the login + * @param $password + * CiviCRM password for the login + */ + public function loginToWordpress($username, $password) { + throw new CRM_Core_Exception("loginToWordpress is not implemented yet. Implement a corresponding login function."); + } + + /** + * Login to Backdrop + * @param $username + * CiviCRM username for the login + * @param $password + * CiviCRM password for the login + */ + public function loginToBackdrop($username, $password) { + throw new CRM_Core_Exception("loginToBackdrop is not implemented yet. Implement a corresponding login function."); + } + + /** + * Login as Admin User + */ + public function loginAsAdmin() { + global $_CV; + $this->login($_CV['ADMIN_USER'], $_CV['ADMIN_PASS']); + } + + /** + * Login as Demo User + */ + public function loginAsDemo() { + global $_CV; + $this->login($_CV['DEMO_USER'], $_CV['DEMO_PASS']); + } + +} diff --git a/Civi/Core/AssetBuilder.php b/Civi/Core/AssetBuilder.php new file mode 100644 index 000000000000..b60f41e20bcc --- /dev/null +++ b/Civi/Core/AssetBuilder.php @@ -0,0 +1,384 @@ +getUrl('api-fields.json'); + * + * // Define the content of `api-fields.json`. + * function hook_civicrm_buildAsset($asset, $params, &$mimeType, &$content) { + * if ($asset !== 'api-fields.json') return; + * + * $entities = civicrm_api3('Entity', 'get', array()); + * $fields = array(); + * foreach ($entities['values'] as $entity) { + * $fields[$entity] = civicrm_api3($entity, 'getfields'); + * } + * + * $mimeType = 'application/json'; + * $content = json_encode($fields); + * } + * @endCode + * + * Assets can be parameterized. Each combination of ($asset,$params) + * will be cached separately. For example, we might want a copy of + * 'api-fields.json' which only includes a handful of chosen entities. + * Simply pass the chosen entities into `getUrl()`, then update + * the definition to use `$params['entities']`, as in: + * + * @code + * // Build a URL to `api-fields.json`. + * $url = \Civi::service('asset_builder')->getUrl('api-fields.json', array( + * 'entities' => array('Contact', 'Phone', 'Email', 'Address'), + * )); + * + * // Define the content of `api-fields.json`. + * function hook_civicrm_buildAsset($asset, $params, &$mimeType, &$content) { + * if ($asset !== 'api-fields.json') return; + * + * $fields = array(); + * foreach ($params['entities'] as $entity) { + * $fields[$entity] = civicrm_api3($entity, 'getfields'); + * } + * + * $mimeType = 'application/json'; + * $content = json_encode($fields); + * } + * @endCode + * + * Note: These assets are designed to hold non-sensitive data, such as + * aggregated JS or common metadata. There probably are ways to + * secure it (e.g. alternative digest() calculations), but the + * current implementation is KISS. + */ +class AssetBuilder { + + /** + * @return array + * Array(string $value => string $label). + */ + public static function getCacheModes() { + return [ + '0' => ts('Disable'), + '1' => ts('Enable'), + 'auto' => ts('Auto'), + ]; + } + + /** + * @var mixed + */ + protected $cacheEnabled; + + /** + * AssetBuilder constructor. + * @param $cacheEnabled + */ + public function __construct($cacheEnabled = NULL) { + if ($cacheEnabled === NULL) { + $cacheEnabled = \Civi::settings()->get('assetCache'); + if ($cacheEnabled === 'auto') { + $cacheEnabled = !\CRM_Core_Config::singleton()->debug; + } + $cacheEnabled = (bool) $cacheEnabled; + } + $this->cacheEnabled = $cacheEnabled; + } + + /** + * Determine if $name is a well-formed asset name. + * + * @param string $name + * @return bool + */ + public function isValidName($name) { + return preg_match(';^[a-zA-Z0-9\.\-_/]+$;', $name) + && strpos($name, '..') === FALSE + && strpos($name, '.') !== FALSE; + } + + /** + * @param string $name + * Ex: 'angular.json'. + * @param array $params + * @return string + * URL. + * Ex: 'http://example.org/files/civicrm/dyn/angular.abcd1234abcd1234.json'. + */ + public function getUrl($name, $params = []) { + if (!$this->isValidName($name)) { + throw new \RuntimeException("Invalid dynamic asset name"); + } + + if ($this->isCacheEnabled()) { + $fileName = $this->build($name, $params); + return $this->getCacheUrl($fileName); + } + else { + return \CRM_Utils_System::url('civicrm/asset/builder', [ + 'an' => $name, + 'ap' => $this->encode($params), + 'ad' => $this->digest($name, $params), + ], TRUE, NULL, FALSE); + } + } + + /** + * @param string $name + * Ex: 'angular.json'. + * @param array $params + * @return string + * URL. + * Ex: '/var/www/files/civicrm/dyn/angular.abcd1234abcd1234.json'. + */ + public function getPath($name, $params = []) { + if (!$this->isValidName($name)) { + throw new \RuntimeException("Invalid dynamic asset name"); + } + + $fileName = $this->build($name, $params); + return $this->getCachePath($fileName); + } + + /** + * Build the cached copy of an $asset. + * + * @param string $name + * Ex: 'angular.json'. + * @param array $params + * @param bool $force + * Build the asset anew, even if it already exists. + * @return string + * File name (relative to cache folder). + * Ex: 'angular.abcd1234abcd1234.json'. + * @throws UnknownAssetException + */ + public function build($name, $params, $force = FALSE) { + if (!$this->isValidName($name)) { + throw new UnknownAssetException("Asset name is malformed"); + } + $nameParts = explode('.', $name); + array_splice($nameParts, -1, 0, [$this->digest($name, $params)]); + $fileName = implode('.', $nameParts); + if ($force || !file_exists($this->getCachePath($fileName))) { + // No file locking, but concurrent writers should produce + // the same data, so we'll just plow ahead. + + if (!file_exists($this->getCachePath())) { + mkdir($this->getCachePath()); + } + + $rendered = $this->render($name, $params); + file_put_contents($this->getCachePath($fileName), $rendered['content']); + return $fileName; + } + return $fileName; + } + + /** + * Generate the content for a dynamic asset. + * + * @param string $name + * @param array $params + * @return array + * Array with keys: + * - statusCode: int, ex: 200. + * - mimeType: string, ex: 'text/html'. + * - content: string, ex: 'Hello world'. + * @throws \CRM_Core_Exception + */ + public function render($name, $params = []) { + if (!$this->isValidName($name)) { + throw new UnknownAssetException("Asset name is malformed"); + } + \CRM_Utils_Hook::buildAsset($name, $params, $mimeType, $content); + if ($mimeType === NULL && $content === NULL) { + throw new UnknownAssetException("Unrecognized asset name: $name"); + } + // Beg your pardon, sir. Please may I have an HTTP response class instead? + return [ + 'statusCode' => 200, + 'mimeType' => $mimeType, + 'content' => $content, + ]; + } + + /** + * Clear out any cache files. + */ + public function clear() { + \CRM_Utils_File::cleanDir($this->getCachePath()); + } + + /** + * Determine the local path of a cache file. + * + * @param string|NULL $fileName + * Ex: 'angular.abcd1234abcd1234.json'. + * @return string + * URL. + * Ex: '/var/www/files/civicrm/dyn/angular.abcd1234abcd1234.json'. + */ + protected function getCachePath($fileName = NULL) { + // imageUploadDir has the correct functional properties but a wonky name. + $suffix = ($fileName === NULL) ? '' : (DIRECTORY_SEPARATOR . $fileName); + return \CRM_Utils_File::addTrailingSlash(\CRM_Core_Config::singleton()->imageUploadDir) + . 'dyn' . $suffix; + } + + /** + * Determine the URL of a cache file. + * + * @param string|NULL $fileName + * Ex: 'angular.abcd1234abcd1234.json'. + * @return string + * URL. + * Ex: 'http://example.org/files/civicrm/dyn/angular.abcd1234abcd1234.json'. + */ + protected function getCacheUrl($fileName = NULL) { + // imageUploadURL has the correct functional properties but a wonky name. + $suffix = ($fileName === NULL) ? '' : ('/' . $fileName); + return \CRM_Utils_File::addTrailingSlash(\CRM_Core_Config::singleton()->imageUploadURL, '/') + . 'dyn' . $suffix; + } + + /** + * Create a unique identifier for the $params. + * + * This identifier is designed to avoid accidental cache collisions. + * + * @param string $name + * @param array $params + * @return string + */ + protected function digest($name, $params) { + // WISHLIST: For secure digest, generate+persist privatekey & call hash_hmac. + ksort($params); + $digest = md5( + $name . + \CRM_Core_Resources::singleton()->getCacheCode() . + \CRM_Core_Config_Runtime::getId() . + json_encode($params) + ); + return $digest; + } + + /** + * Encode $params in a format that's optimized for shorter URLs. + * + * @param array $params + * @return string + */ + protected function encode($params) { + if (empty($params)) { + return ''; + } + + $str = json_encode($params); + if (function_exists('gzdeflate')) { + $str = gzdeflate($str); + } + return base64_encode($str); + } + + /** + * @param string $str + * @return array + */ + protected function decode($str) { + if ($str === NULL || $str === FALSE || $str === '') { + return []; + } + + $str = base64_decode($str); + if (function_exists('gzdeflate')) { + $str = gzinflate($str); + } + return json_decode($str, TRUE); + } + + /** + * @return bool + */ + public function isCacheEnabled() { + return $this->cacheEnabled; + } + + /** + * @param bool|null $cacheEnabled + * @return AssetBuilder + */ + public function setCacheEnabled($cacheEnabled) { + $this->cacheEnabled = $cacheEnabled; + return $this; + } + + /** + * (INTERNAL ONLY) + * + * Execute a page-request for `civicrm/asset/builder`. + */ + public static function pageRun() { + // Beg your pardon, sir. Please may I have an HTTP response class instead? + $asset = self::pageRender($_GET); + if (function_exists('http_response_code')) { + // PHP 5.4+ + http_response_code($asset['statusCode']); + } + else { + header('X-PHP-Response-Code: ' . $asset['statusCode'], TRUE, $asset['statusCode']); + } + + header('Content-Type: ' . $asset['mimeType']); + echo $asset['content']; + \CRM_Utils_System::civiExit(); + } + + /** + * (INTERNAL ONLY) + * + * Execute a page-request for `civicrm/asset/builder`. + * + * @param array $get + * The _GET values. + * @return array + * Array with keys: + * - statusCode: int, ex 200. + * - mimeType: string, ex 'text/html'. + * - content: string, ex 'Hello world'. + */ + public static function pageRender($get) { + // Beg your pardon, sir. Please may I have an HTTP response class instead? + try { + $assets = \Civi::service('asset_builder'); + return $assets->render($get['an'], $assets->decode($get['ap'])); + } + catch (UnknownAssetException $e) { + return [ + 'statusCode' => 404, + 'mimeType' => 'text/plain', + 'content' => $e->getMessage(), + ]; + } + } + +} diff --git a/Civi/Core/CiviEventDispatcher.php b/Civi/Core/CiviEventDispatcher.php new file mode 100644 index 000000000000..2334f1298ca7 --- /dev/null +++ b/Civi/Core/CiviEventDispatcher.php @@ -0,0 +1,135 @@ + trueish). + */ + private $autoListeners = []; + + /** + * Determine whether $eventName should delegate to the CMS hook system. + * + * @param string $eventName + * Ex: 'civi.token.eval', 'hook_civicrm_post`. + * @return bool + */ + protected function isHookEvent($eventName) { + return (substr($eventName, 0, 5) === 'hook_') && (strpos($eventName, '::') === FALSE); + } + + /** + * @inheritDoc + */ + public function dispatch($eventName, Event $event = NULL) { + $this->bindPatterns($eventName); + return parent::dispatch($eventName, $event); + } + + /** + * @inheritDoc + */ + public function getListeners($eventName = NULL) { + $this->bindPatterns($eventName); + return parent::getListeners($eventName); + } + + /** + * @inheritDoc + */ + public function hasListeners($eventName = NULL) { + // All hook_* events have default listeners, so hasListeners(NULL) is a truism. + return ($eventName === NULL || $this->isHookEvent($eventName)) + ? TRUE : parent::hasListeners($eventName); + } + + /** + * Invoke hooks using an event object. + * + * @param \Civi\Core\Event\GenericHookEvent $event + * @param string $eventName + * Ex: 'hook_civicrm_dashboard'. + */ + public static function delegateToUF($event, $eventName) { + $hookName = substr($eventName, 5); + $hooks = \CRM_Utils_Hook::singleton(); + $params = $event->getHookValues(); + $count = count($params); + + switch ($count) { + case 0: + $fResult = $hooks->invokeViaUF($count, \CRM_Utils_Hook::$_nullObject, \CRM_Utils_Hook::$_nullObject, \CRM_Utils_Hook::$_nullObject, \CRM_Utils_Hook::$_nullObject, \CRM_Utils_Hook::$_nullObject, \CRM_Utils_Hook::$_nullObject, $hookName); + break; + + case 1: + $fResult = $hooks->invokeViaUF($count, $params[0], \CRM_Utils_Hook::$_nullObject, \CRM_Utils_Hook::$_nullObject, \CRM_Utils_Hook::$_nullObject, \CRM_Utils_Hook::$_nullObject, \CRM_Utils_Hook::$_nullObject, $hookName); + break; + + case 2: + $fResult = $hooks->invokeViaUF($count, $params[0], $params[1], \CRM_Utils_Hook::$_nullObject, \CRM_Utils_Hook::$_nullObject, \CRM_Utils_Hook::$_nullObject, \CRM_Utils_Hook::$_nullObject, $hookName); + break; + + case 3: + $fResult = $hooks->invokeViaUF($count, $params[0], $params[1], $params[2], \CRM_Utils_Hook::$_nullObject, \CRM_Utils_Hook::$_nullObject, \CRM_Utils_Hook::$_nullObject, $hookName); + break; + + case 4: + $fResult = $hooks->invokeViaUF($count, $params[0], $params[1], $params[2], $params[3], \CRM_Utils_Hook::$_nullObject, \CRM_Utils_Hook::$_nullObject, $hookName); + break; + + case 5: + $fResult = $hooks->invokeViaUF($count, $params[0], $params[1], $params[2], $params[3], $params[4], \CRM_Utils_Hook::$_nullObject, $hookName); + break; + + case 6: + $fResult = $hooks->invokeViaUF($count, $params[0], $params[1], $params[2], $params[3], $params[4], $params[5], $hookName); + break; + + default: + throw new \RuntimeException("hook_{$hookName} cannot support more than 6 parameters"); + } + + $event->addReturnValues($fResult); + } + + /** + * @param string $eventName + * Ex: 'civi.api.resolve' or 'hook_civicrm_dashboard'. + */ + protected function bindPatterns($eventName) { + if ($eventName !== NULL && !isset($this->autoListeners[$eventName])) { + $this->autoListeners[$eventName] = 1; + if ($this->isHookEvent($eventName)) { + // WISHLIST: For native extensions (and possibly D6/D7/D8/BD), enumerate + // the listeners and list them one-by-one. This would make it easier to + // inspect via "cv debug:event-dispatcher". + $this->addListener($eventName, [ + '\Civi\Core\CiviEventDispatcher', + 'delegateToUF', + ], self::DEFAULT_HOOK_PRIORITY); + } + } + } + +} diff --git a/Civi/Core/CiviEventInspector.php b/Civi/Core/CiviEventInspector.php new file mode 100644 index 000000000000..ad260c099c32 --- /dev/null +++ b/Civi/Core/CiviEventInspector.php @@ -0,0 +1,227 @@ +getAll())); + * @endCode + * + * An event definition includes these fields: + * - type: string, required. Ex: 'hook' or 'object' + * - name: string, required. Ex: 'hook_civicrm_post' or 'civi.dao.postInsert' + * - class: string, required. Ex: 'Civi\Core\Event\GenericHookEvent'. + * - signature: string, required FOR HOOKS. Ex: '$first, &$second'. + * - fields: array, required FOR HOOKS. List of hook parameters. + * - stub: ReflectionMethod, optional. An example function with docblocks/inputs. + * + * Note: The inspector is only designed for use in developer workflows, such + * as code-generation and inspection. It should be not called by regular + * runtime logic. + */ +class CiviEventInspector { + + /** + * Register the default hooks defined by 'CRM_Utils_Hook'. + * + * @param \Civi\Core\Event\GenericHookEvent $e + * @see \CRM_Utils_Hook::eventDefs() + */ + public static function findBuiltInEvents(\Civi\Core\Event\GenericHookEvent $e) { + $skipList = ['singleton']; + $e->inspector->addStaticStubs('CRM_Utils_Hook', 'hook_civicrm_', + function ($eventDef, $method) use ($skipList) { + return in_array($method->name, $skipList) ? NULL : $eventDef; + }); + } + + /** + * @var array + * Array(string $name => array $eventDef). + * + * Ex: $eventDefs['hook_civicrm_foo']['description_html'] = 'Hello world'; + */ + protected $eventDefs; + + /** + * Perform a scan to identify/describe all events. + * + * @param bool $force + * @return CiviEventInspector + */ + public function build($force = FALSE) { + if ($force || $this->eventDefs === NULL) { + $this->eventDefs = []; + \CRM_Utils_Hook::eventDefs($this); + ksort($this->eventDefs); + } + return $this; + } + + /** + * Get a list of all events. + * + * @return array + * Array(string $name => array $eventDef). + * Ex: $result['hook_civicrm_foo']['description_html'] = 'Hello world'; + */ + public function getAll() { + $this->build(); + return $this->eventDefs; + } + + /** + * Find any events that match a pattern. + * + * @param string $regex + * @return array + * Array(string $name => array $eventDef). + * Ex: $result['hook_civicrm_foo']['description_html'] = 'Hello world'; + */ + public function find($regex) { + $this->build(); + return array_filter($this->eventDefs, function ($e) use ($regex) { + return preg_match($regex, $e['name']); + }); + } + + /** + * Get the definition of one event. + * + * @param string $name + * Ex: 'hook_civicrm_alterSettingsMetaData'. + * @return array + * Ex: $result['description_html'] = 'Hello world'; + */ + public function get($name) { + $this->build(); + return $this->eventDefs[$name]; + } + + /** + * @param $eventDef + * @return bool + * TRUE if valid. + */ + public function validate($eventDef) { + if (!is_array($eventDef) || empty($eventDef['name']) || !isset($eventDef['type'])) { + return FALSE; + } + + if (!in_array($eventDef['type'], ['hook', 'object'])) { + return FALSE; + } + + if ($eventDef['type'] === 'hook') { + if (!isset($eventDef['signature']) || !is_array($eventDef['fields'])) { + return FALSE; + } + } + + return TRUE; + } + + /** + * Add a new event definition. + * + * @param array $eventDef + * @return CiviEventInspector + */ + public function add($eventDef) { + $name = isset($eventDef['name']) ? $eventDef['name'] : NULL; + + if (!isset($eventDef['type'])) { + $eventDef['type'] = preg_match('/^hook_/', $eventDef['name']) ? 'hook' : 'object'; + } + + if ($eventDef['type'] === 'hook' && empty($eventDef['signature'])) { + $eventDef['signature'] = implode(', ', array_map( + function ($field) { + $sigil = $field['ref'] ? '&$' : '$'; + return $sigil . $field['name']; + }, + $eventDef['fields'] + )); + } + + if (TRUE !== $this->validate($eventDef)) { + throw new \CRM_Core_Exception("Failed to register event ($name). Invalid definition."); + } + + $this->eventDefs[$name] = $eventDef; + return $this; + } + + /** + * Scan a Symfony event class for metadata, and add it. + * + * @param string $event + * Ex: 'civi.api.authorize'. + * @param string $className + * Ex: 'Civi\API\Event\AuthorizeEvent'. + * @return CiviEventInspector + */ + public function addEventClass($event, $className) { + $this->add([ + 'name' => $event, + 'class' => $className, + ]); + return $this; + } + + /** + * Scan a class for hook stubs, and add all of them. + * + * @param string $className + * The name of a class which contains static stub functions. + * Ex: 'CRM_Utils_Hook'. + * @param string $prefix + * A prefix to apply to all hook names. + * Ex: 'hook_civicrm_'. + * @param null|callable $filter + * An optional function to filter/rewrite the metadata for each hook. + * @return CiviEventInspector + */ + public function addStaticStubs($className, $prefix, $filter = NULL) { + $class = new \ReflectionClass($className); + + foreach ($class->getMethods(\ReflectionMethod::IS_STATIC) as $method) { + if (!isset($method->name)) { + continue; + } + + $eventDef = [ + 'name' => $prefix . $method->name, + 'description_html' => $method->getDocComment() ? \CRM_Admin_Page_APIExplorer::formatDocBlock($method->getDocComment()) : '', + 'fields' => [], + 'class' => 'Civi\Core\Event\GenericHookEvent', + 'stub' => $method, + ]; + + foreach ($method->getParameters() as $parameter) { + $eventDef['fields'][$parameter->getName()] = [ + 'name' => $parameter->getName(), + 'ref' => (bool) $parameter->isPassedByReference(), + // WISHLIST: 'type' => 'mixed', + ]; + } + + if ($filter !== NULL) { + $eventDef = $filter($eventDef, $method); + if ($eventDef === NULL) { + continue; + } + } + + $this->add($eventDef); + } + + return $this; + } + +} diff --git a/Civi/Core/Container.php b/Civi/Core/Container.php index cad5ff23edb2..201111773ad0 100644 --- a/Civi/Core/Container.php +++ b/Civi/Core/Container.php @@ -3,20 +3,11 @@ use Civi\Core\Event\SystemInstallEvent; use Civi\Core\Lock\LockManager; -use Doctrine\Common\Annotations\AnnotationReader; -use Doctrine\Common\Annotations\AnnotationRegistry; -use Doctrine\Common\Annotations\FileCacheReader; -use Doctrine\Common\Cache\FilesystemCache; -use Doctrine\ORM\EntityManager; -use Doctrine\ORM\Mapping\Driver\AnnotationDriver; -use Doctrine\ORM\Tools\Setup; use Symfony\Component\Config\ConfigCache; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Dumper\PhpDumper; use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher; use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass; // TODO use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; @@ -57,18 +48,20 @@ public static function singleton($reset = FALSE) { * - CIVICRM_DOMAIN_ID * - CIVICRM_TEMPLATE_COMPILEDIR * - * @return ContainerInterface + * @return \Symfony\Component\DependencyInjection\ContainerInterface */ public function loadContainer() { // Note: The container's raison d'etre is to manage construction of other // services. Consequently, we assume a minimal service available -- the classloader // has been setup, and civicrm.settings.php is loaded, but nothing else works. - $cacheMode = defined('CIVICRM_CONTAINER_CACHE') ? CIVICRM_CONTAINER_CACHE : 'always'; + $cacheMode = defined('CIVICRM_CONTAINER_CACHE') ? CIVICRM_CONTAINER_CACHE : 'auto'; // In pre-installation environments, don't bother with caching. if (!defined('CIVICRM_TEMPLATE_COMPILEDIR') || !defined('CIVICRM_DSN') || $cacheMode === 'never' || \CRM_Utils_System::isInUpgradeMode()) { - return $this->createContainer(); + $containerBuilder = $this->createContainer(); + $containerBuilder->compile(); + return $containerBuilder; } $envId = \CRM_Core_Config_Runtime::getId(); @@ -79,21 +72,20 @@ public function loadContainer() { $containerBuilder->compile(); $dumper = new PhpDumper($containerBuilder); $containerConfigCache->write( - $dumper->dump(array('class' => 'CachedCiviContainer')), + $dumper->dump(['class' => 'CachedCiviContainer']), $containerBuilder->getResources() ); } require_once $file; $c = new \CachedCiviContainer(); - $c->set('service_container', $c); return $c; } /** * Construct a new container. * - * @var ContainerBuilder + * @var \Symfony\Component\DependencyInjection\ContainerBuilder * @return \Symfony\Component\DependencyInjection\ContainerBuilder */ public function createContainer() { @@ -103,9 +95,12 @@ public function createContainer() { $container->addObjectResource($this); $container->setParameter('civicrm_base_path', $civicrm_base_path); //$container->set(self::SELF, $this); + + $container->addResource(new \Symfony\Component\Config\Resource\FileResource(__FILE__)); + $container->setDefinition(self::SELF, new Definition( 'Civi\Core\Container', - array() + [] )); // TODO Move configuration to an external file; define caching structure @@ -126,54 +121,66 @@ public function createContainer() { $container->setDefinition('angular', new Definition( 'Civi\Angular\Manager', - array() + [] )) - ->setFactoryService(self::SELF)->setFactoryMethod('createAngularManager'); + ->setFactory([new Reference(self::SELF), 'createAngularManager']); $container->setDefinition('dispatcher', new Definition( - 'Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher', - array(new Reference('service_container')) + 'Civi\Core\CiviEventDispatcher', + [new Reference('service_container')] )) - ->setFactoryService(self::SELF)->setFactoryMethod('createEventDispatcher'); + ->setFactory([new Reference(self::SELF), 'createEventDispatcher']); $container->setDefinition('magic_function_provider', new Definition( 'Civi\API\Provider\MagicFunctionProvider', - array() + [] )); $container->setDefinition('civi_api_kernel', new Definition( 'Civi\API\Kernel', - array(new Reference('dispatcher'), new Reference('magic_function_provider')) + [new Reference('dispatcher'), new Reference('magic_function_provider')] )) - ->setFactoryService(self::SELF)->setFactoryMethod('createApiKernel'); + ->setFactory([new Reference(self::SELF), 'createApiKernel']); $container->setDefinition('cxn_reg_client', new Definition( 'Civi\Cxn\Rpc\RegistrationClient', - array() + [] )) - ->setFactoryClass('CRM_Cxn_BAO_Cxn')->setFactoryMethod('createRegistrationClient'); - - $container->setDefinition('psr_log', new Definition('CRM_Core_Error_Log', array())); - - foreach (array('js_strings', 'community_messages') as $cacheName) { - $container->setDefinition("cache.{$cacheName}", new Definition( + ->setFactory('CRM_Cxn_BAO_Cxn::createRegistrationClient'); + + $container->setDefinition('psr_log', new Definition('CRM_Core_Error_Log', [])); + + $basicCaches = [ + 'js_strings' => 'js_strings', + 'community_messages' => 'community_messages', + 'checks' => 'checks', + 'session' => 'CiviCRM Session', + 'long' => 'long', + ]; + foreach ($basicCaches as $cacheSvc => $cacheGrp) { + $container->setDefinition("cache.{$cacheSvc}", new Definition( 'CRM_Utils_Cache_Interface', - array( - array( - 'name' => $cacheName, - 'type' => array('*memory*', 'SqlGroup', 'ArrayCache'), - ), - ) - ))->setFactoryClass('CRM_Utils_Cache')->setFactoryMethod('create'); + [ + [ + 'name' => $cacheGrp, + 'type' => ['*memory*', 'SqlGroup', 'ArrayCache'], + ], + ] + ))->setFactory('CRM_Utils_Cache::create'); } $container->setDefinition('sql_triggers', new Definition( 'Civi\Core\SqlTriggers', - array() + [] + )); + + $container->setDefinition('asset_builder', new Definition( + 'Civi\Core\AssetBuilder', + [] )); $container->setDefinition('pear_mail', new Definition('Mail')) - ->setFactoryClass('CRM_Utils_Mail')->setFactoryMethod('createMailer'); + ->setFactory('CRM_Utils_Mail::createMailer'); if (empty(\Civi::$statics[__CLASS__]['boot'])) { throw new \RuntimeException("Cannot initialize container. Boot services are undefined."); @@ -183,33 +190,102 @@ public function createContainer() { } // Expose legacy singletons as services in the container. - $singletons = array( - 'resources' => 'CRM_Core_Resources', + $singletons = [ 'httpClient' => 'CRM_Utils_HttpClient', 'cache.default' => 'CRM_Utils_Cache', 'i18n' => 'CRM_Core_I18n', // Maybe? 'config' => 'CRM_Core_Config', // Maybe? 'smarty' => 'CRM_Core_Smarty', - ); + ]; foreach ($singletons as $name => $class) { $container->setDefinition($name, new Definition( $class )) - ->setFactoryClass($class)->setFactoryMethod('singleton'); + ->setFactory([$class, 'singleton']); } + $container->setAlias('cache.short', 'cache.default'); + + $container->setDefinition('resources', new Definition( + 'CRM_Core_Resources', + [new Reference('service_container')] + ))->setFactory([new Reference(self::SELF), 'createResources']); + + $container->setDefinition('prevnext', new Definition( + 'CRM_Core_PrevNextCache_Interface', + [new Reference('service_container')] + ))->setFactory([new Reference(self::SELF), 'createPrevNextCache']); + + $container->setDefinition('prevnext.driver.sql', new Definition( + 'CRM_Core_PrevNextCache_Sql', + [] + )); + + $container->setDefinition('prevnext.driver.redis', new Definition( + 'CRM_Core_PrevNextCache_Redis', + [new Reference('cache_config')] + )); + + $container->setDefinition('cache_config', new Definition('ArrayObject')) + ->setFactory([new Reference(self::SELF), 'createCacheConfig']); + + $container->setDefinition('civi.mailing.triggers', new Definition( + 'Civi\Core\SqlTrigger\TimestampTriggers', + ['civicrm_mailing', 'Mailing'] + ))->addTag('kernel.event_listener', ['event' => 'hook_civicrm_triggerInfo', 'method' => 'onTriggerInfo']); + + $container->setDefinition('civi.activity.triggers', new Definition( + 'Civi\Core\SqlTrigger\TimestampTriggers', + ['civicrm_activity', 'Activity'] + ))->addTag('kernel.event_listener', ['event' => 'hook_civicrm_triggerInfo', 'method' => 'onTriggerInfo']); + + $container->setDefinition('civi.case.triggers', new Definition( + 'Civi\Core\SqlTrigger\TimestampTriggers', + ['civicrm_case', 'Case'] + ))->addTag('kernel.event_listener', ['event' => 'hook_civicrm_triggerInfo', 'method' => 'onTriggerInfo']); + + $container->setDefinition('civi.case.staticTriggers', new Definition( + 'Civi\Core\SqlTrigger\StaticTriggers', + [ + [ + [ + 'upgrade_check' => ['table' => 'civicrm_case', 'column' => 'modified_date'], + 'table' => 'civicrm_case_activity', + 'when' => 'AFTER', + 'event' => ['INSERT'], + 'sql' => "\nUPDATE civicrm_case SET modified_date = CURRENT_TIMESTAMP WHERE id = NEW.case_id;\n", + ], + [ + 'upgrade_check' => ['table' => 'civicrm_case', 'column' => 'modified_date'], + 'table' => 'civicrm_activity', + 'when' => 'BEFORE', + 'event' => ['UPDATE', 'DELETE'], + 'sql' => "\nUPDATE civicrm_case SET modified_date = CURRENT_TIMESTAMP WHERE id IN (SELECT ca.case_id FROM civicrm_case_activity ca WHERE ca.activity_id = OLD.id);\n", + ], + ], + ] + )) + ->addTag('kernel.event_listener', ['event' => 'hook_civicrm_triggerInfo', 'method' => 'onTriggerInfo']); $container->setDefinition('civi_token_compat', new Definition( 'Civi\Token\TokenCompatSubscriber', - array() + [] + ))->addTag('kernel.event_subscriber'); + $container->setDefinition("crm_mailing_action_tokens", new Definition( + "CRM_Mailing_ActionTokens", + [] ))->addTag('kernel.event_subscriber'); - foreach (array('Activity', 'Contribute', 'Event', 'Member') as $comp) { + foreach (['Activity', 'Contribute', 'Event', 'Mailing', 'Member'] as $comp) { $container->setDefinition("crm_" . strtolower($comp) . "_tokens", new Definition( "CRM_{$comp}_Tokens", - array() + [] ))->addTag('kernel.event_subscriber'); } + if (\CRM_Utils_Constant::value('CIVICRM_FLEXMAILER_HACK_SERVICES')) { + \Civi\Core\Resolver::singleton()->call(CIVICRM_FLEXMAILER_HACK_SERVICES, [$container]); + } + \CRM_Utils_Hook::container($container); return $container; @@ -223,46 +299,61 @@ public function createAngularManager() { } /** - * @param ContainerInterface $container + * @param \Symfony\Component\DependencyInjection\ContainerInterface $container * @return \Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher */ public function createEventDispatcher($container) { - $dispatcher = new ContainerAwareEventDispatcher($container); - $dispatcher->addListener(SystemInstallEvent::EVENT_NAME, array('\Civi\Core\InstallationCanary', 'check')); - $dispatcher->addListener(SystemInstallEvent::EVENT_NAME, array('\Civi\Core\DatabaseInitializer', 'initialize')); - $dispatcher->addListener('hook_civicrm_post::Activity', array('\Civi\CCase\Events', 'fireCaseChange')); - $dispatcher->addListener('hook_civicrm_post::Case', array('\Civi\CCase\Events', 'fireCaseChange')); - $dispatcher->addListener('hook_civicrm_caseChange', array('\Civi\CCase\Events', 'delegateToXmlListeners')); - $dispatcher->addListener('hook_civicrm_caseChange', array('\Civi\CCase\SequenceListener', 'onCaseChange_static')); - $dispatcher->addListener('DAO::post-insert', array('\CRM_Core_BAO_RecurringEntity', 'triggerInsert')); - $dispatcher->addListener('DAO::post-update', array('\CRM_Core_BAO_RecurringEntity', 'triggerUpdate')); - $dispatcher->addListener('DAO::post-delete', array('\CRM_Core_BAO_RecurringEntity', 'triggerDelete')); - $dispatcher->addListener('hook_civicrm_unhandled_exception', array( + $dispatcher = new CiviEventDispatcher($container); + $dispatcher->addListener(SystemInstallEvent::EVENT_NAME, ['\Civi\Core\InstallationCanary', 'check']); + $dispatcher->addListener(SystemInstallEvent::EVENT_NAME, ['\Civi\Core\DatabaseInitializer', 'initialize']); + $dispatcher->addListener(SystemInstallEvent::EVENT_NAME, ['\Civi\Core\LocalizationInitializer', 'initialize']); + $dispatcher->addListener('hook_civicrm_pre', ['\Civi\Core\Event\PreEvent', 'dispatchSubevent'], 100); + $dispatcher->addListener('hook_civicrm_post', ['\Civi\Core\Event\PostEvent', 'dispatchSubevent'], 100); + $dispatcher->addListener('hook_civicrm_post::Activity', ['\Civi\CCase\Events', 'fireCaseChange']); + $dispatcher->addListener('hook_civicrm_post::Case', ['\Civi\CCase\Events', 'fireCaseChange']); + $dispatcher->addListener('hook_civicrm_caseChange', ['\Civi\CCase\Events', 'delegateToXmlListeners']); + $dispatcher->addListener('hook_civicrm_caseChange', ['\Civi\CCase\SequenceListener', 'onCaseChange_static']); + $dispatcher->addListener('hook_civicrm_eventDefs', ['\Civi\Core\CiviEventInspector', 'findBuiltInEvents']); + // TODO We need a better code-convention for metadata about non-hook events. + $dispatcher->addListener('hook_civicrm_eventDefs', ['\Civi\API\Events', 'hookEventDefs']); + $dispatcher->addListener('hook_civicrm_eventDefs', ['\Civi\Core\Event\SystemInstallEvent', 'hookEventDefs']); + $dispatcher->addListener('hook_civicrm_buildAsset', ['\Civi\Angular\Page\Modules', 'buildAngularModules']); + $dispatcher->addListener('hook_civicrm_buildAsset', ['\CRM_Utils_VisualBundle', 'buildAssetJs']); + $dispatcher->addListener('hook_civicrm_buildAsset', ['\CRM_Utils_VisualBundle', 'buildAssetCss']); + $dispatcher->addListener('hook_civicrm_buildAsset', ['\CRM_Core_Resources', 'renderMenubarStylesheet']); + $dispatcher->addListener('civi.dao.postInsert', ['\CRM_Core_BAO_RecurringEntity', 'triggerInsert']); + $dispatcher->addListener('civi.dao.postUpdate', ['\CRM_Core_BAO_RecurringEntity', 'triggerUpdate']); + $dispatcher->addListener('civi.dao.postDelete', ['\CRM_Core_BAO_RecurringEntity', 'triggerDelete']); + $dispatcher->addListener('hook_civicrm_unhandled_exception', [ 'CRM_Core_LegacyErrorHandler', 'handleException', - )); - $dispatcher->addListener(\Civi\ActionSchedule\Events::MAPPINGS, array('CRM_Activity_ActionMapping', 'onRegisterActionMappings')); - $dispatcher->addListener(\Civi\ActionSchedule\Events::MAPPINGS, array('CRM_Contact_ActionMapping', 'onRegisterActionMappings')); - $dispatcher->addListener(\Civi\ActionSchedule\Events::MAPPINGS, array('CRM_Contribute_ActionMapping_ByPage', 'onRegisterActionMappings')); - $dispatcher->addListener(\Civi\ActionSchedule\Events::MAPPINGS, array('CRM_Contribute_ActionMapping_ByType', 'onRegisterActionMappings')); - $dispatcher->addListener(\Civi\ActionSchedule\Events::MAPPINGS, array('CRM_Event_ActionMapping', 'onRegisterActionMappings')); - $dispatcher->addListener(\Civi\ActionSchedule\Events::MAPPINGS, array('CRM_Member_ActionMapping', 'onRegisterActionMappings')); + ], -200); + $dispatcher->addListener(\Civi\ActionSchedule\Events::MAPPINGS, ['CRM_Activity_ActionMapping', 'onRegisterActionMappings']); + $dispatcher->addListener(\Civi\ActionSchedule\Events::MAPPINGS, ['CRM_Contact_ActionMapping', 'onRegisterActionMappings']); + $dispatcher->addListener(\Civi\ActionSchedule\Events::MAPPINGS, ['CRM_Contribute_ActionMapping_ByPage', 'onRegisterActionMappings']); + $dispatcher->addListener(\Civi\ActionSchedule\Events::MAPPINGS, ['CRM_Contribute_ActionMapping_ByType', 'onRegisterActionMappings']); + $dispatcher->addListener(\Civi\ActionSchedule\Events::MAPPINGS, ['CRM_Event_ActionMapping', 'onRegisterActionMappings']); + $dispatcher->addListener(\Civi\ActionSchedule\Events::MAPPINGS, ['CRM_Member_ActionMapping', 'onRegisterActionMappings']); + + if (\CRM_Utils_Constant::value('CIVICRM_FLEXMAILER_HACK_LISTENERS')) { + \Civi\Core\Resolver::singleton()->call(CIVICRM_FLEXMAILER_HACK_LISTENERS, [$dispatcher]); + } return $dispatcher; } /** - * @return LockManager + * @return \Civi\Core\Lock\LockManager */ public static function createLockManager() { // Ideally, downstream implementers could override any definitions in // the container. For now, we'll make-do with some define()s. $lm = new LockManager(); $lm - ->register('/^cache\./', defined('CIVICRM_CACHE_LOCK') ? CIVICRM_CACHE_LOCK : array('CRM_Core_Lock', 'createScopedLock')) - ->register('/^data\./', defined('CIVICRM_DATA_LOCK') ? CIVICRM_DATA_LOCK : array('CRM_Core_Lock', 'createScopedLock')) - ->register('/^worker\.mailing\.send\./', defined('CIVICRM_WORK_LOCK') ? CIVICRM_WORK_LOCK : array('CRM_Core_Lock', 'createCivimailLock')) - ->register('/^worker\./', defined('CIVICRM_WORK_LOCK') ? CIVICRM_WORK_LOCK : array('CRM_Core_Lock', 'createScopedLock')); + ->register('/^cache\./', defined('CIVICRM_CACHE_LOCK') ? CIVICRM_CACHE_LOCK : ['CRM_Core_Lock', 'createScopedLock']) + ->register('/^data\./', defined('CIVICRM_DATA_LOCK') ? CIVICRM_DATA_LOCK : ['CRM_Core_Lock', 'createScopedLock']) + ->register('/^worker\.mailing\.send\./', defined('CIVICRM_WORK_LOCK') ? CIVICRM_WORK_LOCK : ['CRM_Core_Lock', 'createCivimailLock']) + ->register('/^worker\./', defined('CIVICRM_WORK_LOCK') ? CIVICRM_WORK_LOCK : ['CRM_Core_Lock', 'createScopedLock']); // Registrations may use complex resolver expressions, but (as a micro-optimization) // the default factory is specified as an array. @@ -283,12 +374,12 @@ public function createApiKernel($dispatcher, $magicFunctionProvider) { $dispatcher->addSubscriber($magicFunctionProvider); $dispatcher->addSubscriber(new \Civi\API\Subscriber\PermissionCheck()); $dispatcher->addSubscriber(new \Civi\API\Subscriber\APIv3SchemaAdapter()); - $dispatcher->addSubscriber(new \Civi\API\Subscriber\WrapperAdapter(array( + $dispatcher->addSubscriber(new \Civi\API\Subscriber\WrapperAdapter([ \CRM_Utils_API_HTMLInputCoder::singleton(), \CRM_Utils_API_NullOutputCoder::singleton(), \CRM_Utils_API_ReloadOption::singleton(), \CRM_Utils_API_MatchOption::singleton(), - ))); + ])); $dispatcher->addSubscriber(new \Civi\API\Subscriber\XDebugSubscriber()); $kernel = new \Civi\API\Kernel($dispatcher); @@ -298,7 +389,7 @@ public function createApiKernel($dispatcher, $magicFunctionProvider) { $dispatcher->addSubscriber(new \Civi\API\Subscriber\DynamicFKAuthorization( $kernel, 'Attachment', - array('create', 'get', 'delete'), + ['create', 'get', 'delete'], // Given a file ID, determine the entity+table it's attached to. 'SELECT if(cf.id,1,0) as is_valid, cef.entity_table, cef.entity_id FROM civicrm_file cf @@ -312,17 +403,57 @@ public function createApiKernel($dispatcher, $magicFunctionProvider) { INNER JOIN civicrm_custom_group grp ON fld.custom_group_id = grp.id WHERE fld.data_type = "File" ', - array('civicrm_activity', 'civicrm_mailing', 'civicrm_contact', 'civicrm_grant') + ['civicrm_activity', 'civicrm_mailing', 'civicrm_contact', 'civicrm_grant'] )); - $kernel->setApiProviders(array( + $kernel->setApiProviders([ $reflectionProvider, $magicFunctionProvider, - )); + ]); return $kernel; } + /** + * @param \Symfony\Component\DependencyInjection\ContainerInterface $container + * @return \CRM_Core_Resources + */ + public static function createResources($container) { + $sys = \CRM_Extension_System::singleton(); + return new \CRM_Core_Resources( + $sys->getMapper(), + $container->get('cache.js_strings'), + \CRM_Core_Config::isUpgradeMode() ? NULL : 'resCacheCode' + ); + } + + /** + * @param \Symfony\Component\DependencyInjection\ContainerInterface $container + * @return \CRM_Core_PrevNextCache_Interface + */ + public static function createPrevNextCache($container) { + $setting = \Civi::settings()->get('prevNextBackend'); + if ($setting === 'default') { + // For initial release (5.8.x), continue defaulting to SQL. + $isTransitional = version_compare(\CRM_Utils_System::version(), '5.9.alpha1', '<'); + $cacheDriver = \CRM_Utils_Cache::getCacheDriver(); + $service = 'prevnext.driver.' . strtolower($cacheDriver); + return $container->has($service) && !$isTransitional + ? $container->get($service) + : $container->get('prevnext.driver.sql'); + } + else { + return $container->get('prevnext.driver.' . $setting); + } + } + + public static function createCacheConfig() { + $driver = \CRM_Utils_Cache::getCacheDriver(); + $settings = \CRM_Utils_Cache::getCacheSettings($driver); + $settings['driver'] = $driver; + return new \ArrayObject($settings); + } + /** * Get a list of boot services. * @@ -333,7 +464,7 @@ public function createApiKernel($dispatcher, $magicFunctionProvider) { */ public static function boot($loadFromDB) { // Array(string $serviceId => object $serviceInstance). - $bootServices = array(); + $bootServices = []; \Civi::$statics[__CLASS__]['boot'] = &$bootServices; $bootServices['runtime'] = $runtime = new \CRM_Core_Config_Runtime(); @@ -348,10 +479,10 @@ public static function boot($loadFromDB) { $userPermissionClass = 'CRM_Core_Permission_' . $runtime->userFramework; $bootServices['userPermissionClass'] = new $userPermissionClass(); - $bootServices['cache.settings'] = \CRM_Utils_Cache::create(array( + $bootServices['cache.settings'] = \CRM_Utils_Cache::create([ 'name' => 'settings', - 'type' => array('*memory*', 'SqlGroup', 'ArrayCache'), - )); + 'type' => ['*memory*', 'SqlGroup', 'ArrayCache'], + ]); $bootServices['settings_manager'] = new \Civi\Core\SettingsManager($bootServices['cache.settings']); @@ -378,4 +509,13 @@ public static function getBootService($name) { return \Civi::$statics[__CLASS__]['boot'][$name]; } + /** + * Determine whether the container services are available. + * + * @return bool + */ + public static function isContainerBooted() { + return isset(\Civi::$statics[__CLASS__]['container']); + } + } diff --git a/Civi/Core/DAO/Event/PostDelete.php b/Civi/Core/DAO/Event/PostDelete.php index e9a4f3c5c1bc..e90732da7a4b 100644 --- a/Civi/Core/DAO/Event/PostDelete.php +++ b/Civi/Core/DAO/Event/PostDelete.php @@ -1,9 +1,9 @@ object = $object; $this->result = $result; } + } diff --git a/Civi/Core/DAO/Event/PostUpdate.php b/Civi/Core/DAO/Event/PostUpdate.php index 690b2236760b..362f026c5a65 100644 --- a/Civi/Core/DAO/Event/PostUpdate.php +++ b/Civi/Core/DAO/Event/PostUpdate.php @@ -1,9 +1,9 @@ object = $object; } + } diff --git a/Civi/Core/DAO/Event/PreDelete.php b/Civi/Core/DAO/Event/PreDelete.php new file mode 100644 index 000000000000..7182c30e705e --- /dev/null +++ b/Civi/Core/DAO/Event/PreDelete.php @@ -0,0 +1,48 @@ +object = $object; + } + +} diff --git a/Civi/Core/DatabaseInitializer.php b/Civi/Core/DatabaseInitializer.php index ea9b29888ea7..e13c8b7f89a8 100644 --- a/Civi/Core/DatabaseInitializer.php +++ b/Civi/Core/DatabaseInitializer.php @@ -1,9 +1,9 @@ 3, 'triggers' => 1, 'session' => 1, - ); + ]; civicrm_api('System', 'flush', $api_params); } diff --git a/Civi/Core/Event/GenericHookEvent.php b/Civi/Core/Event/GenericHookEvent.php new file mode 100644 index 000000000000..c98053d20e45 --- /dev/null +++ b/Civi/Core/Event/GenericHookEvent.php @@ -0,0 +1,255 @@ + 'abc', + * 'whiz' => &$whiz, + * 'bang' => &$bang, + * ); + * + * // Accessing event properties. + * echo $event->bar; + * $event->whiz['array_field'] = 123; + * $event->bang->objProperty = 'abcd'; + * + * // Dispatching an event. + * Civi::service('dispatcher')->dispatch('hook_civicrm_foo', $event); + * @endCode + * + * Design Discussion: + * + * 1. Implementing new event classes for every hook would produce a + * large amount of boilerplate. Symfony Events have an interesting solution to + * that problem: use `GenericEvent` instead of custom event classes. + * `GenericHookEvent` is conceptually similar to `GenericEvent`, but it adds + * support for (a) altering properties and (b) mapping properties to hook notation + * (an ordered parameter list). + * + * 2. A handful of hooks define a return-value. The return-value is treated + * as an array, and all the returned values are merged into one big array. + * You can add and retrieve return-values using these methods: + * + * @code + * $event->addReturnValues(array(...)); + * foreach ($event->getReturnValues() as $retVal) { ... } + * @endCode + */ +class GenericHookEvent extends \Symfony\Component\EventDispatcher\Event { + + /** + * @var array + * Ex: array(0 => &$contactID, 1 => &$contentPlacement). + */ + protected $hookValues; + + /** + * @var array + * Ex: array(0 => 'contactID', 1 => 'contentPlacement'). + */ + protected $hookFields; + + /** + * @var array + * Ex: array('contactID' => 0, 'contentPlacement' => 1). + */ + protected $hookFieldsFlip; + + /** + * Some legacy hooks expect listener-functions to return a value. + * OOP listeners may set the $returnValue. + * + * This field is not recommended for use in new hooks. The return-value + * convention is not portable across different implementations of the hook + * system. Instead, it's more portable to provide an alterable, named field. + * + * @var mixed + * @deprecated + */ + private $returnValues = []; + + /** + * List of field names that are prohibited due to conflicts + * in the class-hierarchy. + * + * @var array + */ + private static $BLACKLIST = [ + 'name', + 'dispatcher', + 'propagationStopped', + 'hookBlacklist', + 'hookValues', + 'hookFields', + 'hookFieldsFlip', + ]; + + /** + * Create a GenericHookEvent using key-value pairs. + * + * @param array $params + * Ex: array('contactID' => &$contactID, 'contentPlacement' => &$contentPlacement). + * @return \Civi\Core\Event\GenericHookEvent + */ + public static function create($params) { + $e = new static(); + $e->hookValues = array_values($params); + $e->hookFields = array_keys($params); + $e->hookFieldsFlip = array_flip($e->hookFields); + self::assertValidHookFields($e->hookFields); + return $e; + } + + /** + * Create a GenericHookEvent using ordered parameters. + * + * @param array $hookFields + * Ex: array(0 => 'contactID', 1 => 'contentPlacement'). + * @param array $hookValues + * Ex: array(0 => &$contactID, 1 => &$contentPlacement). + * @return \Civi\Core\Event\GenericHookEvent + */ + public static function createOrdered($hookFields, $hookValues) { + $e = new static(); + if (count($hookValues) > count($hookFields)) { + $hookValues = array_slice($hookValues, 0, count($hookFields)); + } + $e->hookValues = $hookValues; + $e->hookFields = $hookFields; + $e->hookFieldsFlip = array_flip($e->hookFields); + self::assertValidHookFields($e->hookFields); + return $e; + } + + /** + * @param array $fields + * List of field names. + */ + private static function assertValidHookFields($fields) { + $bad = array_intersect($fields, self::$BLACKLIST); + if ($bad) { + throw new \RuntimeException("Hook relies on conflicted field names: " + . implode(', ', $bad)); + } + } + + /** + * @return array + * Ex: array(0 => &$contactID, 1 => &$contentPlacement). + */ + public function getHookValues() { + return $this->hookValues; + } + + /** + * @return mixed + * @deprecated + */ + public function getReturnValues() { + return empty($this->returnValues) ? TRUE : $this->returnValues; + } + + /** + * @param mixed $fResult + * @return GenericHookEvent + * @deprecated + */ + public function addReturnValues($fResult) { + if (!empty($fResult) && is_array($fResult)) { + $this->returnValues = array_merge($this->returnValues, $fResult); + } + return $this; + } + + /** + * @inheritDoc + */ + public function &__get($name) { + if (isset($this->hookFieldsFlip[$name])) { + return $this->hookValues[$this->hookFieldsFlip[$name]]; + } + } + + /** + * @inheritDoc + */ + public function __set($name, $value) { + if (isset($this->hookFieldsFlip[$name])) { + $this->hookValues[$this->hookFieldsFlip[$name]] = $value; + } + } + + /** + * @inheritDoc + */ + public function __isset($name) { + return isset($this->hookFieldsFlip[$name]) + && isset($this->hookValues[$this->hookFieldsFlip[$name]]); + } + + /** + * @inheritDoc + */ + public function __unset($name) { + if (isset($this->hookFieldsFlip[$name])) { + // Unset while preserving order. + $this->hookValues[$this->hookFieldsFlip[$name]] = NULL; + } + } + + /** + * Determine whether the hook supports the given field. + * + * The field may or may not be empty. Use isset() or empty() to + * check that. + * + * @param string $name + * @return bool + */ + public function hasField($name) { + return isset($this->hookFieldsFlip[$name]); + } + +} diff --git a/Civi/Core/Event/PostEvent.php b/Civi/Core/Event/PostEvent.php index e7253fe0db2c..fe69d3f98e2d 100644 --- a/Civi/Core/Event/PostEvent.php +++ b/Civi/Core/Event/PostEvent.php @@ -1,9 +1,9 @@ dispatch("hook_civicrm_post::" . $event->entity, $event); + } /** * @var string 'create'|'edit'|'delete' etc @@ -59,11 +70,18 @@ class PostEvent extends \Symfony\Component\EventDispatcher\Event { * @param $id * @param $object */ - public function __construct($action, $entity, $id, $object) { + public function __construct($action, $entity, $id, &$object) { $this->action = $action; $this->entity = $entity; $this->id = $id; - $this->object = $object; + $this->object = &$object; + } + + /** + * @inheritDoc + */ + public function getHookValues() { + return [$this->action, $this->entity, $this->id, &$this->object]; } } diff --git a/Civi/Core/Event/PreEvent.php b/Civi/Core/Event/PreEvent.php index 4c536c68bfb9..6332658a7bc1 100644 --- a/Civi/Core/Event/PreEvent.php +++ b/Civi/Core/Event/PreEvent.php @@ -1,9 +1,9 @@ dispatch("hook_civicrm_pre::" . $event->entity, $event); + } /** * @var string 'create'|'edit'|'delete' etc @@ -59,11 +70,18 @@ class PreEvent extends \Symfony\Component\EventDispatcher\Event { * @param $id * @param $params */ - public function __construct($action, $entity, $id, $params) { + public function __construct($action, $entity, $id, &$params) { $this->action = $action; $this->entity = $entity; $this->id = $id; - $this->params = $params; + $this->params = &$params; + } + + /** + * @inheritDoc + */ + public function getHookValues() { + return [$this->action, $this->entity, $this->id, &$this->params]; } } diff --git a/Civi/Core/Event/SystemInstallEvent.php b/Civi/Core/Event/SystemInstallEvent.php index d1e41efe5886..eeeff18f0785 100644 --- a/Civi/Core/Event/SystemInstallEvent.php +++ b/Civi/Core/Event/SystemInstallEvent.php @@ -1,9 +1,9 @@ inspector->addEventClass(self::EVENT_NAME, __CLASS__); + } + } diff --git a/Civi/Core/Event/UnhandledExceptionEvent.php b/Civi/Core/Event/UnhandledExceptionEvent.php index 2c7fb08c8f08..dbb56c48eb55 100644 --- a/Civi/Core/Event/UnhandledExceptionEvent.php +++ b/Civi/Core/Event/UnhandledExceptionEvent.php @@ -1,9 +1,9 @@ exception = $e; } + /** + * @inheritDoc + */ + public function getHookValues() { + return [$this->exception, $this->request]; + } + } diff --git a/Civi/Core/Exception/UnknownAssetException.php b/Civi/Core/Exception/UnknownAssetException.php new file mode 100644 index 000000000000..1d2359810943 --- /dev/null +++ b/Civi/Core/Exception/UnknownAssetException.php @@ -0,0 +1,6 @@ + $value) { + if (in_array($setting, $validSettings)) { + $settingsParams[$setting] = $value; + } + + } + + // ensure we don't mess with multilingual + unset($settingsParams['languageLimit']); + + // support for enabled languages (option group) + if (isset($settings['languagesOption']) && count($settings['languagesOption']) > 0) { + \CRM_Core_BAO_OptionGroup::setActiveValues('languages', $settings['languagesOption']); + } + + // set default currency in currencies_enabled (option group) + if (isset($settings['defaultCurrency'])) { + \CRM_Admin_Form_Setting_Localization::updateEnabledCurrencies([$settings['defaultCurrency']], $settings['defaultCurrency']); + } + + } + + } + + // in any case, enforce the seedLanguage as the default language + $settingsParams['lcMessages'] = $seedLanguage; + + // apply the config + civicrm_api3('Setting', 'create', $settingsParams); + + } + +} diff --git a/Civi/Core/Lock/LockInterface.php b/Civi/Core/Lock/LockInterface.php index 21def75830ab..c84aa20f256a 100644 --- a/Civi/Core/Lock/LockInterface.php +++ b/Civi/Core/Lock/LockInterface.php @@ -1,9 +1,9 @@ getFactory($name); if ($factory) { /** @var LockInterface $lock */ - $lock = call_user_func_array($factory, array($name)); + $lock = call_user_func_array($factory, [$name]); return $lock; } else { @@ -111,10 +111,10 @@ public function getFactory($name) { * @see Resolver */ public function register($pattern, $factory) { - $this->rules[] = array( + $this->rules[] = [ 'pattern' => $pattern, 'factory' => $factory, - ); + ]; return $this; } diff --git a/Civi/Core/Lock/NullLock.php b/Civi/Core/Lock/NullLock.php index fad1ebfdcee2..bdacbf8937b2 100644 --- a/Civi/Core/Lock/NullLock.php +++ b/Civi/Core/Lock/NullLock.php @@ -1,9 +1,9 @@ array(url => $, path => $)). */ - private $variables = array(); + private $variables = []; - private $variableFactory = array(); + private $variableFactory = []; /** * Class constructor. */ public function __construct() { + $paths = $this; $this ->register('civicrm.root', function () { return \CRM_Core_Config::singleton()->userSystem->getCiviSourceStorage(); }) + ->register('civicrm.packages', function () { + return [ + 'path' => \Civi::paths()->getPath('[civicrm.root]/packages/'), + 'url' => \Civi::paths()->getUrl('[civicrm.root]/packages/'), + ]; + }) + ->register('civicrm.vendor', function () { + return [ + 'path' => \Civi::paths()->getPath('[civicrm.root]/vendor/'), + 'url' => \Civi::paths()->getUrl('[civicrm.root]/vendor/'), + ]; + }) + ->register('civicrm.bower', function () { + return [ + 'path' => \Civi::paths()->getPath('[civicrm.root]/bower_components/'), + 'url' => \Civi::paths()->getUrl('[civicrm.root]/bower_components/'), + ]; + }) ->register('civicrm.files', function () { return \CRM_Core_Config::singleton()->userSystem->getDefaultFileStorage(); }) + ->register('wp.frontend.base', function () { + return ['url' => rtrim(CIVICRM_UF_BASEURL, '/') . '/']; + }) + ->register('wp.frontend', function () use ($paths) { + $config = \CRM_Core_Config::singleton(); + $suffix = defined('CIVICRM_UF_WP_BASEPAGE') ? CIVICRM_UF_WP_BASEPAGE : $config->wpBasePage; + return [ + 'url' => $paths->getVariable('wp.frontend.base', 'url') . $suffix, + ]; + }) + ->register('wp.backend.base', function () { + return ['url' => rtrim(CIVICRM_UF_BASEURL, '/') . '/wp-admin/']; + }) + ->register('wp.backend', function () use ($paths) { + return [ + 'url' => $paths->getVariable('wp.backend.base', 'url') . 'admin.php', + ]; + }) ->register('cms', function () { - return array( + return [ 'path' => \CRM_Core_Config::singleton()->userSystem->cmsRootPath(), 'url' => \CRM_Utils_System::baseCMSURL(), - ); + ]; }) ->register('cms.root', function () { - return array( + return [ 'path' => \CRM_Core_Config::singleton()->userSystem->cmsRootPath(), // Misleading: this *removes* the language part of the URL, producing a pristine base URL. 'url' => \CRM_Utils_System::languageNegotiationURL(\CRM_Utils_System::baseCMSURL(), FALSE, TRUE), - ); + ]; }); } @@ -78,6 +115,9 @@ public function register($name, $factory) { public function getVariable($name, $attr) { if (!isset($this->variables[$name])) { $this->variables[$name] = call_user_func($this->variableFactory[$name]); + if (isset($GLOBALS['civicrm_paths'][$name])) { + $this->variables[$name] = array_merge($this->variables[$name], $GLOBALS['civicrm_paths'][$name]); + } } if (!isset($this->variables[$name][$attr])) { throw new \RuntimeException("Cannot resolve path using \"$name.$attr\""); diff --git a/Civi/Core/Resolver.php b/Civi/Core/Resolver.php index 70654bac29a4..b1e54684a627 100644 --- a/Civi/Core/Resolver.php +++ b/Civi/Core/Resolver.php @@ -53,7 +53,7 @@ public static function singleton() { * @param string|array $id * A callback expression; any of the following. * - * @return array + * @return array|callable * A PHP callback. Do not serialize (b/c it may include an object). * @throws \RuntimeException */ @@ -77,7 +77,7 @@ public function get($id) { case 'call': // Callback: Object/method in container. $obj = \Civi::service($url['host']); - return array($obj, ltrim($url['path'], '/')); + return [$obj, ltrim($url['path'], '/')]; case 'api3': // Callback: API. @@ -91,7 +91,7 @@ public function get($id) { throw new \RuntimeException("Unsupported callback scheme: " . $url['scheme']); } } - elseif (in_array($id, array('0', '1'))) { + elseif (in_array($id, ['0', '1'])) { // Callback: Constant value. return new ResolverConstantCallback((int) $id); } @@ -184,7 +184,7 @@ public function __construct($url) { * Fire an API call. */ public function __invoke() { - $apiParams = array(); + $apiParams = []; if (isset($this->url['query'])) { parse_str($this->url['query'], $apiParams); } @@ -212,7 +212,7 @@ public function __invoke() { * (e.g. "@1" => "firstValue"). */ protected function createPlaceholders($prefix, $args) { - $result = array(); + $result = []; foreach ($args as $offset => $arg) { $result[$prefix . (1 + $offset)] = $arg; } @@ -250,7 +250,8 @@ protected function interpolate(&$array, $replacements) { } class ResolverGlobalCallback { - private $mode, $path; + private $mode; + private $path; /** * Class constructor. @@ -277,6 +278,7 @@ public function __invoke($arg1 = NULL) { } elseif ($this->mode === 'setter') { \CRM_Utils_Array::pathSet($GLOBALS, explode('/', $this->path), $arg1); + return NULL; } else { throw new \RuntimeException("Resolver failed: global:// must specify getter or setter mode."); diff --git a/Civi/Core/SettingsBag.php b/Civi/Core/SettingsBag.php index 7b621e5e7acc..613703305ac0 100644 --- a/Civi/Core/SettingsBag.php +++ b/Civi/Core/SettingsBag.php @@ -1,9 +1,9 @@ domainId = $domainId; $this->contactId = $contactId; - $this->values = array(); + $this->values = []; $this->combined = NULL; } @@ -128,7 +128,7 @@ public function loadValues() { // Note: Don't use DAO child classes. They require fields() which require // translations -- which are keyed off settings! - $this->values = array(); + $this->values = []; $this->combined = NULL; // Ordinarily, we just load values from `civicrm_setting`. But upgrades require care. @@ -139,9 +139,9 @@ public function loadValues() { $isUpgradeMode = \CRM_Core_Config::isUpgradeMode(); - if ($isUpgradeMode && empty($this->contactId) && \CRM_Core_DAO::checkFieldExists('civicrm_domain', 'config_backend', FALSE)) { + if ($isUpgradeMode && empty($this->contactId) && \CRM_Core_BAO_SchemaHandler::checkIfFieldExists('civicrm_domain', 'config_backend', FALSE)) { $config_backend = \CRM_Core_DAO::singleValueQuery('SELECT config_backend FROM civicrm_domain WHERE id = %1', - array(1 => array($this->domainId, 'Positive'))); + [1 => [$this->domainId, 'Positive']]); $oldSettings = \CRM_Upgrade_Incremental_php_FourSeven::convertBackendToSettings($this->domainId, $config_backend); \CRM_Utils_Array::extend($this->values, $oldSettings); } @@ -180,7 +180,7 @@ public function add(array $settings) { public function all() { if ($this->combined === NULL) { $this->combined = $this->combine( - array($this->defaults, $this->values, $this->mandatory) + [$this->defaults, $this->values, $this->mandatory] ); } return $this->combined; @@ -281,16 +281,16 @@ public function set($key, $value) { protected function createQuery() { $select = \CRM_Utils_SQL_Select::from('civicrm_setting') ->select('id, name, value, domain_id, contact_id, is_domain, component_id, created_date, created_id') - ->where('domain_id = #id', array( + ->where('domain_id = #id', [ 'id' => $this->domainId, - )); + ]); if ($this->contactId === NULL) { $select->where('is_domain = 1'); } else { - $select->where('contact_id = #id', array( + $select->where('contact_id = #id', [ 'id' => $this->contactId, - )); + ]); $select->where('is_domain = 0'); } return $select; @@ -306,7 +306,7 @@ protected function createQuery() { * @return array */ protected function combine($arrays) { - $combined = array(); + $combined = []; foreach ($arrays as $array) { foreach ($array as $k => $v) { if ($v !== NULL) { @@ -326,13 +326,8 @@ protected function combine($arrays) { * The new value of the setting. */ protected function setDb($name, $value) { - if (\CRM_Core_BAO_Setting::isUpgradeFromPreFourOneAlpha1()) { - // civicrm_setting table is not going to be present. - return; - } - - $fields = array(); - $fieldsToSet = \CRM_Core_BAO_Setting::validateSettingsInput(array($name => $value), $fields); + $fields = []; + $fieldsToSet = \CRM_Core_BAO_Setting::validateSettingsInput([$name => $value], $fields); //We haven't traditionally validated inputs to setItem, so this breaks things. //foreach ($fieldsToSet as $settingField => &$settingValue) { // self::validateSetting($settingValue, $fields['values'][$settingField]); @@ -352,6 +347,10 @@ protected function setDb($name, $value) { } $dao->find(TRUE); + // Call 'on_change' listeners. It would be nice to only fire when there's + // a genuine change in the data. However, PHP developers have mixed + // expectations about whether 0, '0', '', NULL, and FALSE represent the same + // value, so there's no universal way to determine if a change is genuine. if (isset($metadata['on_change'])) { foreach ($metadata['on_change'] as $callback) { call_user_func( @@ -364,7 +363,7 @@ protected function setDb($name, $value) { } } - if (\CRM_Utils_System::isNull($value)) { + if (!is_array($value) && \CRM_Utils_System::isNull($value)) { $dao->value = 'null'; } else { @@ -374,7 +373,7 @@ protected function setDb($name, $value) { if (!isset(\Civi::$statics[__CLASS__]['upgradeMode'])) { \Civi::$statics[__CLASS__]['upgradeMode'] = \CRM_Core_Config::isUpgradeMode(); } - if (\Civi::$statics[__CLASS__]['upgradeMode'] && \CRM_Core_DAO::checkFieldExists('civicrm_setting', 'group_name')) { + if (\Civi::$statics[__CLASS__]['upgradeMode'] && \CRM_Core_BAO_SchemaHandler::checkIfFieldExists('civicrm_setting', 'group_name')) { $dao->group_name = 'placeholder'; } diff --git a/Civi/Core/SettingsManager.php b/Civi/Core/SettingsManager.php index 0194d2e4953c..1954404ca381 100644 --- a/Civi/Core/SettingsManager.php +++ b/Civi/Core/SettingsManager.php @@ -1,9 +1,9 @@ SettingsBag $bag). */ - protected $bagsByDomain = array(), $bagsByContact = array(); + protected $bagsByDomain = []; + + + /** + * @var array + * Array (int $id => SettingsBag $bag). + */ + protected $bagsByContact = []; /** * @var array|NULL @@ -171,13 +178,24 @@ public function getBagByDomain($domainId) { /** * @param int|NULL $domainId + * For the default domain, leave $domainID as NULL. * @param int|NULL $contactId + * For the default/active user's contact, leave $domainID as NULL. * @return SettingsBag + * @throws \CRM_Core_Exception + * If there is no contact, then there's no SettingsBag, and we'll throw + * an exception. */ public function getBagByContact($domainId, $contactId) { if ($domainId === NULL) { $domainId = \CRM_Core_Config::domainID(); } + if ($contactId === NULL) { + $contactId = \CRM_Core_Session::getLoggedInContactID(); + if (!$contactId) { + throw new \CRM_Core_Exception("Cannot access settings subsystem - user or domain is unavailable"); + } + } $key = "$domainId:$contactId"; if (!isset($this->bagsByContact[$key])) { @@ -205,13 +223,13 @@ protected function getDefaults($entity) { return self::getSystemDefaults($entity); } - $cacheKey = 'defaults:' . $entity; + $cacheKey = 'defaults_' . $entity; $defaults = $this->cache->get($cacheKey); if (!is_array($defaults)) { - $specs = SettingsMetadata::getMetadata(array( + $specs = SettingsMetadata::getMetadata([ 'is_contact' => ($entity === 'contact' ? 1 : 0), - )); - $defaults = array(); + ]); + $defaults = []; foreach ($specs as $key => $spec) { $defaults[$key] = \CRM_Utils_Array::value('default', $spec); } @@ -255,12 +273,12 @@ protected function getMandatory($entity) { * @return array */ public static function parseMandatorySettings($civicrm_setting) { - $result = array( - 'domain' => array(), - 'contact' => array(), - ); + $result = [ + 'domain' => [], + 'contact' => [], + ]; - $rewriteGroups = array( + $rewriteGroups = [ //\CRM_Core_BAO_Setting::ADDRESS_STANDARDIZATION_PREFERENCES_NAME => 'domain', //\CRM_Core_BAO_Setting::CAMPAIGN_PREFERENCES_NAME => 'domain', //\CRM_Core_BAO_Setting::CONTRIBUTE_PREFERENCES_NAME => 'domain', @@ -279,7 +297,7 @@ public static function parseMandatorySettings($civicrm_setting) { //\CRM_Core_BAO_Setting::URL_PREFERENCES_NAME => 'domain', 'domain' => 'domain', 'contact' => 'contact', - ); + ]; if (is_array($civicrm_setting)) { foreach ($civicrm_setting as $oldGroup => $values) { @@ -299,7 +317,8 @@ public function flush() { $this->mandatory = NULL; $this->cache->flush(); - \Civi::cache('settings')->flush(); // SettingsMetadata; not guaranteed to use same cache. + // SettingsMetadata; not guaranteed to use same cache. + \Civi::cache('settings')->flush(); foreach ($this->bagsByDomain as $bag) { /** @var SettingsBag $bag */ @@ -330,12 +349,12 @@ public function flush() { * @return array */ private static function getSystemDefaults($entity) { - $defaults = array(); + $defaults = []; switch ($entity) { case 'domain': - $defaults = array( + $defaults = [ 'installed' => FALSE, - 'enable_components' => array('CiviEvent', 'CiviContribute', 'CiviMember', 'CiviMail', 'CiviReport', 'CiviPledge'), + 'enable_components' => ['CiviEvent', 'CiviContribute', 'CiviMember', 'CiviMail', 'CiviReport', 'CiviPledge'], 'customFileUploadDir' => '[civicrm.files]/custom/', 'imageUploadDir' => '[civicrm.files]/persist/contribute/', 'uploadDir' => '[civicrm.files]/upload/', @@ -344,7 +363,7 @@ private static function getSystemDefaults($entity) { 'extensionsURL' => '[civicrm.files]/ext/', 'resourceBase' => '[civicrm.root]/', 'userFrameworkResourceURL' => '[civicrm.root]/', - ); + ]; break; } diff --git a/Civi/Core/SettingsMetadata.php b/Civi/Core/SettingsMetadata.php index 3afc37d26618..00d04d34215c 100644 --- a/Civi/Core/SettingsMetadata.php +++ b/Civi/Core/SettingsMetadata.php @@ -1,9 +1,9 @@ get(self::ALL); if (empty($settingsMetadata)) { global $civicrm_root; - $metaDataFolders = array($civicrm_root . '/settings'); + $metaDataFolders = [$civicrm_root . '/settings']; \CRM_Utils_Hook::alterSettingsFolders($metaDataFolders); $settingsMetadata = self::loadSettingsMetaDataFolders($metaDataFolders); $cache->set(self::ALL, $settingsMetadata); @@ -106,13 +106,13 @@ public static function getMetadata($filters = array(), $domainID = NULL) { * @return array */ protected static function loadSettingsMetaDataFolders($metaDataFolders) { - $settingsMetadata = array(); - $loadedFolders = array(); + $settingsMetadata = []; + $loadedFolders = []; foreach ($metaDataFolders as $metaDataFolder) { $realFolder = realpath($metaDataFolder); if (is_dir($realFolder) && !isset($loadedFolders[$realFolder])) { $loadedFolders[$realFolder] = TRUE; - $settingsMetadata = $settingsMetadata + self::loadSettingsMetaData($metaDataFolder); + $settingsMetadata = $settingsMetadata + self::loadSettingsMetadata($metaDataFolder); } } return $settingsMetadata; @@ -126,7 +126,7 @@ protected static function loadSettingsMetaDataFolders($metaDataFolders) { * @return array */ protected static function loadSettingsMetadata($metaDataFolder) { - $settingMetaData = array(); + $settingMetaData = []; $settingsFiles = \CRM_Utils_File::findFiles($metaDataFolder, '*.setting.php'); foreach ($settingsFiles as $file) { $settings = include $file; @@ -148,8 +148,8 @@ protected static function _filterSettingsSpecification($filters, &$settingSpec) if (empty($filters)) { return; } - elseif (array_keys($filters) == array('name')) { - $settingSpec = array($filters['name'] => \CRM_Utils_Array::value($filters['name'], $settingSpec, '')); + elseif (array_keys($filters) == ['name']) { + $settingSpec = [$filters['name'] => \CRM_Utils_Array::value($filters['name'], $settingSpec, '')]; return; } else { diff --git a/Civi/Core/SettingsStack.php b/Civi/Core/SettingsStack.php new file mode 100644 index 000000000000..9183857d5cb6 --- /dev/null +++ b/Civi/Core/SettingsStack.php @@ -0,0 +1,57 @@ +stack[] = [$setting, $GLOBALS['civicrm_setting']['domain'][$setting]]; + } + else { + $this->stack[] = [$setting, NULL]; + } + $GLOBALS['civicrm_setting']['domain'][$setting] = $settingValue; + \Civi::service('settings_manager')->useMandatory(); + } + + /** + * Restore original settings. + */ + public function popAll() { + while ($frame = array_pop($this->stack)) { + list($setting, $value) = $frame; + if ($value === NULL) { + unset($GLOBALS['civicrm_setting']['domain'][$setting]); + } + else { + $GLOBALS['civicrm_setting']['domain'][$setting] = $value; + } + } + \Civi::service('settings_manager')->useMandatory(); + } + +} diff --git a/Civi/Core/SqlTrigger/StaticTriggers.php b/Civi/Core/SqlTrigger/StaticTriggers.php new file mode 100644 index 000000000000..c0f6f981b773 --- /dev/null +++ b/Civi/Core/SqlTrigger/StaticTriggers.php @@ -0,0 +1,126 @@ + 'civicrm_case', 'column'=> 'modified_date'); + * + * @see \CRM_Utils_Hook::triggerInfo + */ + private $triggers; + + /** + * StaticTriggers constructor. + * @param $triggers + */ + public function __construct($triggers) { + $this->triggers = $triggers; + } + + /** + * Add our list of triggers to the global list. + * + * @param \Civi\Core\Event\GenericHookEvent $e + * @see \CRM_Utils_Hook::triggerInfo + */ + public function onTriggerInfo($e) { + $this->alterTriggerInfo($e->info, $e->tableName); + } + + /** + * Add our list of triggers to the global list. + * + * @see \CRM_Utils_Hook::triggerInfo + * @see \CRM_Core_DAO::triggerRebuild + * + * @param array $info + * See hook_civicrm_triggerInfo. + * @param string|NULL $tableFilter + * See hook_civicrm_triggerInfo. + */ + public function alterTriggerInfo(&$info, $tableFilter = NULL) { + foreach ($this->getTriggers() as $trigger) { + if ($tableFilter !== NULL) { + // Because sadism. + if (in_array($tableFilter, (array) $trigger['table'])) { + $trigger['table'] = $tableFilter; + } + } + + if (\CRM_Core_Config::isUpgradeMode() && isset($trigger['upgrade_check'])) { + $uc = $trigger['upgrade_check']; + if (!\CRM_Core_BAO_SchemaHandler::checkIfFieldExists($uc['table'], $uc['column']) + ) { + continue; + } + } + unset($trigger['upgrade_check']); + $info[] = $trigger; + } + } + + /** + * @return mixed + */ + public function getTriggers() { + return $this->triggers; + } + + /** + * @param mixed $triggers + * @return StaticTriggers + */ + public function setTriggers($triggers) { + $this->triggers = $triggers; + return $this; + } + + /** + * @param $trigger + * @return StaticTriggers + */ + public function addTrigger($trigger) { + $this->triggers[] = $trigger; + return $this; + } + +} diff --git a/Civi/Core/SqlTrigger/TimestampTriggers.php b/Civi/Core/SqlTrigger/TimestampTriggers.php new file mode 100644 index 000000000000..c2249cf07d45 --- /dev/null +++ b/Civi/Core/SqlTrigger/TimestampTriggers.php @@ -0,0 +1,332 @@ + 'civicrm_bar', 'column' => 'foo_id'); + */ + private $relations; + + /** + * @param string $tableName + * SQL table name. + * Ex: 'civicrm_contact', 'civicrm_activity'. + * @param string $customDataEntity + * An entity name (from civicrm_custom_group.extends). + * Ex: 'Contact', 'Activity'. + * @return TimestampTriggers + */ + public static function create($tableName, $customDataEntity) { + return new static($tableName, $customDataEntity); + } + + /** + * TimestampTriggers constructor. + * + * @param string $tableName + * SQL table name. + * Ex: 'civicrm_contact', 'civicrm_activity'. + * @param string $customDataEntity + * An entity name (from civicrm_custom_group.extends). + * Ex: 'Contact', 'Activity'. + * @param string $createdDate + * SQL column name. + * Ex: 'created_date'. + * @param string $modifiedDate + * SQL column name. + * Ex: 'modified_date'. + * @param array $relations + * Ex: $relations[0] == array('table' => 'civicrm_bar', 'column' => 'foo_id'); + */ + public function __construct( + $tableName, + $customDataEntity, + $createdDate = 'created_date', + $modifiedDate = 'modified_date', + $relations = [] + ) { + $this->tableName = $tableName; + $this->customDataEntity = $customDataEntity; + $this->createdDate = $createdDate; + $this->modifiedDate = $modifiedDate; + $this->relations = $relations; + } + + /** + * Add our list of triggers to the global list. + * + * @param \Civi\Core\Event\GenericHookEvent $e + * @see \CRM_Utils_Hook::triggerInfo + */ + public function onTriggerInfo($e) { + $this->alterTriggerInfo($e->info, $e->tableName); + } + + /** + * Add our list of triggers to the global list. + * + * @see \CRM_Utils_Hook::triggerInfo + * @see \CRM_Core_DAO::triggerRebuild + * + * @param array $info + * See hook_civicrm_triggerInfo. + * @param string|NULL $tableFilter + * See hook_civicrm_triggerInfo. + */ + public function alterTriggerInfo(&$info, $tableFilter = NULL) { + // If we haven't upgraded yet, then the created_date/modified_date may not exist. + // In the past, this was a version-based check, but checkFieldExists() + // seems more robust. + if (\CRM_Core_Config::isUpgradeMode()) { + if (!\CRM_Core_BAO_SchemaHandler::checkIfFieldExists($this->getTableName(), + $this->getCreatedDate()) + ) { + return; + } + } + + if ($tableFilter == NULL || $tableFilter == $this->getTableName()) { + $info[] = [ + 'table' => [$this->getTableName()], + 'when' => 'BEFORE', + 'event' => ['INSERT'], + 'sql' => "\nSET NEW.{$this->getCreatedDate()} = CURRENT_TIMESTAMP;\n", + ]; + } + + // Update timestamp when modifying closely related tables + $relIdx = \CRM_Utils_Array::index( + ['column', 'table'], + $this->getAllRelations() + ); + foreach ($relIdx as $column => $someRelations) { + $this->generateTimestampTriggers($info, $tableFilter, + array_keys($someRelations), $column); + } + } + + /** + * Generate triggers to update the timestamp. + * + * The corresponding civicrm_FOO row is updated on insert/update/delete + * to a table that extends civicrm_FOO. + * Don't regenerate triggers for all such tables if only asked for one table. + * + * @param array $info + * Reference to the array where generated trigger information is being stored + * @param string|null $tableFilter + * Name of the table for which triggers are being generated, or NULL if all tables + * @param array $relatedTableNames + * Array of all core or all custom table names extending civicrm_FOO + * @param string $contactRefColumn + * 'contact_id' if processing core tables, 'entity_id' if processing custom tables + * + * @link https://issues.civicrm.org/jira/browse/CRM-15602 + * @see triggerInfo + */ + public function generateTimestampTriggers( + &$info, + $tableFilter, + $relatedTableNames, + $contactRefColumn + ) { + // Safety + $contactRefColumn = \CRM_Core_DAO::escapeString($contactRefColumn); + + // If specific related table requested, just process that one. + // (Reply: This feels fishy.) + if (in_array($tableFilter, $relatedTableNames)) { + $relatedTableNames = [$tableFilter]; + } + + // If no specific table requested (include all related tables), + // or a specific related table requested (as matched above) + if (empty($tableFilter) || isset($relatedTableNames[$tableFilter])) { + $info[] = [ + 'table' => $relatedTableNames, + 'when' => 'AFTER', + 'event' => ['INSERT', 'UPDATE'], + 'sql' => "\nUPDATE {$this->getTableName()} SET {$this->getModifiedDate()} = CURRENT_TIMESTAMP WHERE id = NEW.$contactRefColumn;\n", + ]; + $info[] = [ + 'table' => $relatedTableNames, + 'when' => 'AFTER', + 'event' => ['DELETE'], + 'sql' => "\nUPDATE {$this->getTableName()} SET {$this->getModifiedDate()} = CURRENT_TIMESTAMP WHERE id = OLD.$contactRefColumn;\n", + ]; + } + } + + /** + * @return string + */ + public function getTableName() { + return $this->tableName; + } + + /** + * @param string $tableName + * @return TimestampTriggers + */ + public function setTableName($tableName) { + $this->tableName = $tableName; + return $this; + } + + /** + * @return string + */ + public function getCustomDataEntity() { + return $this->customDataEntity; + } + + /** + * @param string $customDataEntity + * @return TimestampTriggers + */ + public function setCustomDataEntity($customDataEntity) { + $this->customDataEntity = $customDataEntity; + return $this; + } + + /** + * @return string + */ + public function getCreatedDate() { + return $this->createdDate; + } + + /** + * @param string $createdDate + * @return TimestampTriggers + */ + public function setCreatedDate($createdDate) { + $this->createdDate = $createdDate; + return $this; + } + + /** + * @return string + */ + public function getModifiedDate() { + return $this->modifiedDate; + } + + /** + * @param string $modifiedDate + * @return TimestampTriggers + */ + public function setModifiedDate($modifiedDate) { + $this->modifiedDate = $modifiedDate; + return $this; + } + + /** + * @return array + * Each item is an array('table' => string, 'column' => string) + */ + public function getRelations() { + return $this->relations; + } + + /** + * @param array $relations + * @return TimestampTriggers + */ + public function setRelations($relations) { + $this->relations = $relations; + return $this; + } + + /** + * Get a list of all tracked relations. + * + * This is basically the curated list (`$this->relations`) plus any custom data. + * + * @return array + * Each item is an array('table' => string, 'column' => string) + */ + public function getAllRelations() { + $relations = $this->getRelations(); + + if ($this->getCustomDataEntity()) { + $customGroupDAO = \CRM_Core_BAO_CustomGroup::getAllCustomGroupsByBaseEntity($this->getCustomDataEntity()); + $customGroupDAO->is_multiple = 0; + $customGroupDAO->find(); + while ($customGroupDAO->fetch()) { + $relations[] = [ + 'table' => $customGroupDAO->table_name, + 'column' => 'entity_id', + ]; + } + } + + return $relations; + } + +} diff --git a/Civi/Core/SqlTriggers.php b/Civi/Core/SqlTriggers.php index 64e8ea7cef11..ce54d1844899 100644 --- a/Civi/Core/SqlTriggers.php +++ b/Civi/Core/SqlTriggers.php @@ -1,9 +1,9 @@ triggerInfo($info, $tableName, $force); @@ -82,7 +82,7 @@ public function createTriggers(&$info, $onlyTableName = NULL) { return; } - $triggers = array(); + $triggers = []; // now enumerate the tables and the events and collect the same set in a different format foreach ($info as $value) { @@ -98,14 +98,14 @@ public function createTriggers(&$info, $onlyTableName = NULL) { } if (is_string($value['table']) == TRUE) { - $tables = array($value['table']); + $tables = [$value['table']]; } else { $tables = $value['table']; } if (is_string($value['event']) == TRUE) { - $events = array(strtolower($value['event'])); + $events = [strtolower($value['event'])]; } else { $events = array_map('strtolower', $value['event']); @@ -115,12 +115,12 @@ public function createTriggers(&$info, $onlyTableName = NULL) { foreach ($tables as $tableName) { if (!isset($triggers[$tableName])) { - $triggers[$tableName] = array(); + $triggers[$tableName] = []; } foreach ($events as $eventName) { - $template_params = array('{tableName}', '{eventName}'); - $template_values = array($tableName, $eventName); + $template_params = ['{tableName}', '{eventName}']; + $template_values = [$tableName, $eventName]; $sql = str_replace($template_params, $template_values, @@ -132,17 +132,17 @@ public function createTriggers(&$info, $onlyTableName = NULL) { ); if (!isset($triggers[$tableName][$eventName])) { - $triggers[$tableName][$eventName] = array(); + $triggers[$tableName][$eventName] = []; } if (!isset($triggers[$tableName][$eventName][$whenName])) { // We're leaving out cursors, conditions, and handlers for now // they are kind of dangerous in this context anyway // better off putting them in stored procedures - $triggers[$tableName][$eventName][$whenName] = array( - 'variables' => array(), - 'sql' => array(), - ); + $triggers[$tableName][$eventName][$whenName] = [ + 'variables' => [], + 'sql' => [], + ]; } if ($variables) { @@ -181,7 +181,7 @@ public function createTriggers(&$info, $onlyTableName = NULL) { * the specific table requiring a rebuild; or NULL to rebuild all tables. */ public function dropTriggers($tableName = NULL) { - $info = array(); + $info = []; $logging = new \CRM_Logging_Schema(); $logging->triggerInfo($info, $tableName); @@ -201,17 +201,17 @@ public function dropTriggers($tableName = NULL) { * @param array $params * Optional parameters to interpolate into the string. */ - public function enqueueQuery($triggerSQL, $params = array()) { + public function enqueueQuery($triggerSQL, $params = []) { if (\Civi::settings()->get('logging_no_trigger_permission')) { if (!file_exists($this->getFile())) { // Ugh. Need to let user know somehow. This is the first change. - \CRM_Core_Session::setStatus(ts('The mysql commands you need to run are stored in %1', array( - 1 => $this->getFile(), - )), + \CRM_Core_Session::setStatus(ts('The mysql commands you need to run are stored in %1', [ + 1 => $this->getFile(), + ]), '', 'alert', - array('expires' => 0) + ['expires' => 0] ); } diff --git a/Civi/Core/Transaction/Frame.php b/Civi/Core/Transaction/Frame.php index d12de55fa092..74ee8e83bbdb 100644 --- a/Civi/Core/Transaction/Frame.php +++ b/Civi/Core/Transaction/Frame.php @@ -1,9 +1,9 @@ commitStmt = $commitStmt; $this->rollbackStmt = $rollbackStmt; - $this->callbacks = array( - \CRM_Core_Transaction::PHASE_PRE_COMMIT => array(), - \CRM_Core_Transaction::PHASE_POST_COMMIT => array(), - \CRM_Core_Transaction::PHASE_PRE_ROLLBACK => array(), - \CRM_Core_Transaction::PHASE_POST_ROLLBACK => array(), - ); + $this->callbacks = [ + \CRM_Core_Transaction::PHASE_PRE_COMMIT => [], + \CRM_Core_Transaction::PHASE_POST_COMMIT => [], + \CRM_Core_Transaction::PHASE_PRE_ROLLBACK => [], + \CRM_Core_Transaction::PHASE_POST_ROLLBACK => [], + ]; } public function inc() { @@ -186,16 +186,16 @@ public function forceRollback() { */ public function addCallback($phase, $callback, $params = NULL, $id = NULL) { if ($id) { - $this->callbacks[$phase][$id] = array( + $this->callbacks[$phase][$id] = [ 'callback' => $callback, - 'parameters' => (is_array($params) ? $params : array($params)), - ); + 'parameters' => (is_array($params) ? $params : [$params]), + ]; } else { - $this->callbacks[$phase][] = array( + $this->callbacks[$phase][] = [ 'callback' => $callback, - 'parameters' => (is_array($params) ? $params : array($params)), - ); + 'parameters' => (is_array($params) ? $params : [$params]), + ]; } } diff --git a/Civi/Core/Transaction/Manager.php b/Civi/Core/Transaction/Manager.php index a7b4a90959d6..70f63ab062f1 100644 --- a/Civi/Core/Transaction/Manager.php +++ b/Civi/Core/Transaction/Manager.php @@ -1,9 +1,9 @@ stack of SQL transactions/savepoints */ - private $frames = array(); + private $frames = []; /** * @var int @@ -63,7 +63,7 @@ public static function singleton($fresh = FALSE) { } /** - * @param CRM_Core_DAO $dao + * @param \CRM_Core_DAO $dao * Handle for the DB connection that will execute transaction statements. * (all we really care about is the query() function) */ @@ -131,7 +131,7 @@ public function forceRollback() { // internal state of each frame is consistent with its outcome $oldFrames = $this->frames; - $this->frames = array(); + $this->frames = []; foreach ($oldFrames as $oldFrame) { $oldFrame->forceRollback(); } diff --git a/Civi/Install/Requirements.php b/Civi/Install/Requirements.php index 16fe39bb6b1a..e7c9581106cb 100644 --- a/Civi/Install/Requirements.php +++ b/Civi/Install/Requirements.php @@ -23,14 +23,18 @@ class Requirements { */ const REQUIREMENT_ERROR = 2; - protected $system_checks = array( + /** + * @var array + */ + protected $system_checks = [ 'checkMemory', 'checkServerVariables', 'checkMysqlConnectExists', 'checkJsonEncodeExists', - ); + 'checkMultibyteExists', + ]; - protected $database_checks = array( + protected $database_checks = [ 'checkMysqlConnection', 'checkMysqlVersion', 'checkMysqlInnodb', @@ -39,7 +43,8 @@ class Requirements { 'checkMysqlTrigger', 'checkMysqlThreadStack', 'checkMysqlLockTables', - ); + 'checkMysqlUtf8mb4', + ]; /** * Run all requirements tests. @@ -68,7 +73,7 @@ public function checkAll(array $config) { * @return array */ public function checkSystem(array $file_paths) { - $errors = array(); + $errors = []; $errors[] = $this->checkFilepathIsWritable($file_paths); foreach ($this->system_checks as $check) { @@ -92,7 +97,7 @@ public function checkSystem(array $file_paths) { * @return array */ public function checkDatabase(array $db_config) { - $errors = array(); + $errors = []; foreach ($this->database_checks as $check) { $errors[] = $this->$check($db_config); @@ -101,6 +106,24 @@ public function checkDatabase(array $db_config) { return $errors; } + /** + * Generates a mysql connection + * + * @param $db_config array + * @return object mysqli connection + */ + protected function connect($db_config) { + $host = NULL; + if (!empty($db_config['host'])) { + $host = $db_config['host']; + } + 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); + return $conn; + } + /** * Check configured php Memory. * @return array @@ -112,11 +135,11 @@ public function checkMemory() { $mem = $this->getPHPMemory(); $mem_string = ini_get('memory_limit'); - $results = array( + $results = [ 'title' => 'CiviCRM memory check', 'severity' => $this::REQUIREMENT_OK, 'details' => "You have $mem_string allocated (minimum 32Mb, recommended 64Mb)", - ); + ]; if ($mem < $min && $mem > 0) { $results['severity'] = $this::REQUIREMENT_ERROR; @@ -158,14 +181,14 @@ protected function getPHPMemory() { * @return array */ public function checkServerVariables() { - $results = array( + $results = [ 'title' => 'CiviCRM PHP server variables', 'severity' => $this::REQUIREMENT_OK, 'details' => 'The required $_SERVER variables are set', - ); + ]; - $required_variables = array('SCRIPT_NAME', 'HTTP_HOST', 'SCRIPT_FILENAME'); - $missing = array(); + $required_variables = ['SCRIPT_NAME', 'HTTP_HOST', 'SCRIPT_FILENAME']; + $missing = []; foreach ($required_variables as $required_variable) { if (empty($_SERVER[$required_variable])) { @@ -185,11 +208,11 @@ public function checkServerVariables() { * @return array */ public function checkJsonEncodeExists() { - $results = array( + $results = [ 'title' => 'CiviCRM JSON encoding support', 'severity' => $this::REQUIREMENT_OK, 'details' => 'Function json_encode() found', - ); + ]; if (!function_exists('json_encode')) { $results['severity'] = $this::REQUIREMENT_ERROR; $results['details'] = 'Function json_encode() does not exist'; @@ -198,15 +221,33 @@ public function checkJsonEncodeExists() { return $results; } + /** + * CHeck that PHP Multibyte functions are enabled. + * @return array + */ + public function checkMultibyteExists() { + $results = [ + 'title' => 'CiviCRM MultiByte encoding support', + 'severity' => $this::REQUIREMENT_OK, + 'details' => 'PHP Multibyte etension found', + ]; + if (!function_exists('mb_substr')) { + $results['severity'] = $this::REQUIREMENT_ERROR; + $results['details'] = 'PHP Multibyte extension has not been installed and enabled'; + } + + return $results; + } + /** * @return array */ public function checkMysqlConnectExists() { - $results = array( + $results = [ 'title' => 'CiviCRM MySQL check', 'severity' => $this::REQUIREMENT_OK, 'details' => 'Function mysqli_connect() found', - ); + ]; if (!function_exists('mysqli_connect')) { $results['severity'] = $this::REQUIREMENT_ERROR; $results['details'] = 'Function mysqli_connect() does not exist'; @@ -221,13 +262,13 @@ public function checkMysqlConnectExists() { * @return array */ public function checkMysqlConnection(array $db_config) { - $results = array( + $results = [ 'title' => 'CiviCRM MySQL connection', 'severity' => $this::REQUIREMENT_OK, 'details' => "Connected", - ); + ]; - $conn = @mysqli_connect($db_config['host'], $db_config['username'], $db_config['password']); + $conn = $this->connect($db_config); if (!$conn) { $results['details'] = mysqli_connect_error(); @@ -251,12 +292,12 @@ public function checkMysqlConnection(array $db_config) { */ public function checkMysqlVersion(array $db_config) { $min = '5.1'; - $results = array( + $results = [ 'title' => 'CiviCRM MySQL Version', 'severity' => $this::REQUIREMENT_OK, - ); + ]; - $conn = @mysqli_connect($db_config['host'], $db_config['username'], $db_config['password']); + $conn = $this->connect($db_config); if (!$conn || !($info = mysqli_get_server_info($conn))) { $results['severity'] = $this::REQUIREMENT_WARNING; $results['details'] = "Cannot determine the version of MySQL installed. Please ensure at least version {$min} is installed."; @@ -279,13 +320,13 @@ public function checkMysqlVersion(array $db_config) { * @return array */ public function checkMysqlInnodb(array $db_config) { - $results = array( + $results = [ 'title' => 'CiviCRM InnoDB support', 'severity' => $this::REQUIREMENT_ERROR, 'details' => 'Could not determine if MySQL has InnoDB support. Assuming none.', - ); + ]; - $conn = @mysqli_connect($db_config['host'], $db_config['username'], $db_config['password']); + $conn = $this->connect($db_config); if (!$conn) { return $results; } @@ -314,13 +355,13 @@ public function checkMysqlInnodb(array $db_config) { * @return array */ public function checkMysqlTempTables(array $db_config) { - $results = array( + $results = [ 'title' => 'CiviCRM MySQL Temp Tables', 'severity' => $this::REQUIREMENT_OK, 'details' => 'MySQL server supports temporary tables', - ); + ]; - $conn = @mysqli_connect($db_config['host'], $db_config['username'], $db_config['password']); + $conn = $this->connect($db_config); if (!$conn) { $results['severity'] = $this::REQUIREMENT_ERROR; $results['details'] = "Could not connect to database"; @@ -350,13 +391,13 @@ public function checkMysqlTempTables(array $db_config) { * @return array */ public function checkMysqlTrigger($db_config) { - $results = array( + $results = [ 'title' => 'CiviCRM MySQL Trigger', 'severity' => $this::REQUIREMENT_OK, 'details' => 'Database supports MySQL triggers', - ); + ]; - $conn = @mysqli_connect($db_config['host'], $db_config['username'], $db_config['password']); + $conn = $this->connect($db_config); if (!$conn) { $results['severity'] = $this::REQUIREMENT_ERROR; $results['details'] = 'Could not connect to database'; @@ -395,13 +436,13 @@ public function checkMysqlTrigger($db_config) { * @return array */ public function checkMySQLAutoIncrementIncrementOne(array $db_config) { - $results = array( + $results = [ 'title' => 'CiviCRM MySQL AutoIncrementIncrement', 'severity' => $this::REQUIREMENT_OK, 'details' => 'MySQL server auto_increment_increment is 1', - ); + ]; - $conn = @mysqli_connect($db_config['host'], $db_config['username'], $db_config['password']); + $conn = $this->connect($db_config); if (!$conn) { $results['severity'] = $this::REQUIREMENT_ERROR; $results['details'] = 'Could not connect to database'; @@ -431,13 +472,13 @@ public function checkMySQLAutoIncrementIncrementOne(array $db_config) { public function checkMysqlThreadStack($db_config) { $min_thread_stack = 192; - $results = array( + $results = [ 'title' => 'CiviCRM Mysql thread stack', 'severity' => $this::REQUIREMENT_OK, 'details' => 'MySQL thread_stack is OK', - ); + ]; - $conn = @mysqli_connect($db_config['server'], $db_config['username'], $db_config['password']); + $conn = $this->connect($db_config); if (!$conn) { $results['severity'] = $this::REQUIREMENT_ERROR; $results['details'] = 'Could not connect to database'; @@ -450,7 +491,8 @@ public function checkMysqlThreadStack($db_config) { return $results; } - $r = mysqli_query($conn, "SHOW VARIABLES LIKE 'thread_stack'"); // bytes => kb + // bytes => kb + $r = mysqli_query($conn, "SHOW VARIABLES LIKE 'thread_stack'"); if (!$r) { $results['severity'] = $this::REQUIREMENT_ERROR; $results['details'] = 'Could not query thread_stack value'; @@ -472,13 +514,13 @@ public function checkMysqlThreadStack($db_config) { * @return array */ public function checkMysqlLockTables($db_config) { - $results = array( + $results = [ 'title' => 'CiviCRM MySQL Lock Tables', 'severity' => $this::REQUIREMENT_OK, 'details' => 'Can successfully lock and unlock tables', - ); + ]; - $conn = @mysqli_connect($db_config['server'], $db_config['username'], $db_config['password']); + $conn = $this->connect($db_config); if (!$conn) { $results['severity'] = $this::REQUIREMENT_ERROR; $results['details'] = 'Could not connect to database'; @@ -524,13 +566,13 @@ public function checkMysqlLockTables($db_config) { * @return array */ public function checkFilepathIsWritable($file_paths) { - $results = array( + $results = [ 'title' => 'CiviCRM directories are writable', 'severity' => $this::REQUIREMENT_OK, 'details' => 'All required directories are writable: ' . implode(', ', $file_paths), - ); + ]; - $unwritable_dirs = array(); + $unwritable_dirs = []; foreach ($file_paths as $path) { if (!is_writable($path)) { $unwritable_dirs[] = $path; @@ -545,4 +587,66 @@ public function checkFilepathIsWritable($file_paths) { return $results; } + /** + * @param $db_config + * + * @return array + */ + public function checkMysqlUtf8mb4($db_config) { + $results = [ + 'title' => 'CiviCRM MySQL utf8mb4 Support', + 'severity' => $this::REQUIREMENT_OK, + 'details' => 'Your system supports the MySQL utf8mb4 character set.', + ]; + + $conn = $this->connect($db_config); + if (!$conn) { + $results['severity'] = $this::REQUIREMENT_ERROR; + $results['details'] = 'Could not connect to database'; + return $results; + } + + if (!@mysqli_select_db($conn, $db_config['database'])) { + $results['severity'] = $this::REQUIREMENT_ERROR; + $results['details'] = 'Could not select the database'; + mysqli_close($conn); + return $results; + } + + mysqli_query($conn, 'DROP TABLE IF EXISTS civicrm_utf8mb4_test'); + $r = mysqli_query($conn, 'CREATE TABLE civicrm_utf8mb4_test (id VARCHAR(255), PRIMARY KEY(id(255))) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC ENGINE=INNODB'); + if (!$r) { + $results['severity'] = $this::REQUIREMENT_WARNING; + $results['details'] = 'It is recommended, though not yet required, to configure your MySQL server for utf8mb4 support. You will need the following MySQL server configuration: innodb_large_prefix=true innodb_file_format=barracuda innodb_file_per_table=true'; + mysqli_close($conn); + return $results; + } + mysqli_query($conn, 'DROP TABLE civicrm_utf8mb4_test'); + + // Ensure that the MySQL driver supports utf8mb4 encoding. + $version = mysqli_get_client_info($conn); + if (strpos($version, 'mysqlnd') !== FALSE) { + // The mysqlnd driver supports utf8mb4 starting at version 5.0.9. + $version = preg_replace('/^\D+([\d.]+).*/', '$1', $version); + if (version_compare($version, '5.0.9', '<')) { + $results['severity'] = $this::REQUIREMENT_WARNING; + $results['details'] = 'It is recommended, though not yet required, to upgrade your PHP MySQL driver (mysqlnd) to >= 5.0.9 for utf8mb4 support.'; + mysqli_close($conn); + return $results; + } + } + else { + // The libmysqlclient driver supports utf8mb4 starting at version 5.5.3. + if (version_compare($version, '5.5.3', '<')) { + $results['severity'] = $this::REQUIREMENT_WARNING; + $results['details'] = 'It is recommended, though not yet required, to upgrade your PHP MySQL driver (libmysqlclient) to >= 5.5.3 for utf8mb4 support.'; + mysqli_close($conn); + return $results; + } + } + + mysqli_close($conn); + return $results; + } + } diff --git a/Civi/Payment/System.php b/Civi/Payment/System.php index 9ce8cb196f4a..44047cdc4107 100644 --- a/Civi/Payment/System.php +++ b/Civi/Payment/System.php @@ -16,7 +16,7 @@ class System { /** * @var array cache */ - private $cache = array(); + private $cache = []; /** * @return \Civi\Payment\System @@ -38,7 +38,7 @@ public static function singleton() { * Override the config check. This is required in uninstall as no valid instances exist * but will deliberately not work with any valid processors. * - * @return CRM_Core_Payment|NULL + * @return \CRM_Core_Payment|NULL * * @throws \CRM_Core_Exception */ @@ -53,18 +53,17 @@ public function getByProcessor($processor, $force = FALSE) { } else { $paymentClass = 'CRM_Core_' . $processor['class_name']; - if (empty($paymentClass)) { + if (empty($processor['class_name'])) { throw new \CRM_Core_Exception('no class provided'); } - require_once str_replace('_', DIRECTORY_SEPARATOR, $paymentClass) . '.php'; } - $processorObject = new $paymentClass(!empty($processor['is_test']) ? 'test' : 'live', $processor); - if (!$force && $processorObject->checkConfig()) { - $processorObject = NULL; - } - else { - $processorObject->setPaymentProcessor($processor); + $processorObject = NULL; + if (class_exists($paymentClass)) { + $processorObject = new $paymentClass(!empty($processor['is_test']) ? 'test' : 'live', $processor); + if ($force || !$processorObject->checkConfig()) { + $processorObject->setPaymentProcessor($processor); + } } $this->cache[$id] = $processorObject; } @@ -72,19 +71,47 @@ public function getByProcessor($processor, $force = FALSE) { return $this->cache[$id]; } + /** + * Execute checkConfig() on the payment processor Object. + * This function creates a new instance of the processor object and returns the output of checkConfig + * + * @param array $processor + * + * @return string|NULL + * + * @throws \CRM_Core_Exception + */ + public function checkProcessorConfig($processor) { + $ext = \CRM_Extension_System::singleton()->getMapper(); + if ($ext->isExtensionKey($processor['class_name'])) { + $paymentClass = $ext->keyToClass($processor['class_name'], 'payment'); + require_once $ext->classToPath($paymentClass); + } + else { + $paymentClass = 'CRM_Core_' . $processor['class_name']; + if (empty($paymentClass)) { + throw new \CRM_Core_Exception('no class provided'); + } + require_once str_replace('_', DIRECTORY_SEPARATOR, $paymentClass) . '.php'; + } + + $processorObject = new $paymentClass(!empty($processor['is_test']) ? 'test' : 'live', $processor); + return $processorObject->checkConfig(); + } + /** * Get payment processor by it's ID. * * @param int $id * - * @return \Civi\Payment\CRM_Core_Payment|NULL + * @return \CRM_Core_Payment|NULL * @throws \CiviCRM_API3_Exception */ public function getById($id) { if ($id == 0) { return new \CRM_Core_Payment_Manual(); } - $processor = civicrm_api3('payment_processor', 'getsingle', array('id' => $id, 'is_test' => NULL)); + $processor = civicrm_api3('payment_processor', 'getsingle', ['id' => $id, 'is_test' => NULL]); return self::getByProcessor($processor); } @@ -92,11 +119,11 @@ public function getById($id) { * @param string $name * @param bool $is_test * - * @return \Civi\Payment\CRM_Core_Payment|NULL + * @return \CRM_Core_Payment|NULL * @throws \CiviCRM_API3_Exception */ public function getByName($name, $is_test) { - $processor = civicrm_api3('payment_processor', 'getsingle', array('name' => $name, 'is_test' => $is_test)); + $processor = civicrm_api3('payment_processor', 'getsingle', ['name' => $name, 'is_test' => $is_test]); return self::getByProcessor($processor); } @@ -106,7 +133,12 @@ public function getByName($name, $is_test) { * This is particularly used for tests. */ public function flushProcessors() { - $this->cache = array(); + $this->cache = []; + if (isset(\Civi::$statics['CRM_Contribute_BAO_ContributionRecur'])) { + unset(\Civi::$statics['CRM_Contribute_BAO_ContributionRecur']); + } + \CRM_Core_PseudoConstant::flush('paymentProcessor'); + civicrm_api3('PaymentProcessor', 'getfields', ['cache_clear' => 1]); \CRM_Financial_BAO_PaymentProcessor::getAllPaymentProcessors('all', TRUE); \CRM_Financial_BAO_PaymentProcessor::getAllPaymentProcessors('live', TRUE); \CRM_Financial_BAO_PaymentProcessor::getAllPaymentProcessors('test', TRUE); @@ -124,11 +156,11 @@ public function flushProcessors() { * @throws \CiviCRM_API3_Exception */ public function getByClass($className) { - return $this->getByProcessor(array( + return $this->getByProcessor([ 'class_name' => $className, 'id' => 0, 'is_test' => 0, - ), + ], TRUE); } diff --git a/Civi/Test.php b/Civi/Test.php index e70f0f40262f..51d929054dab 100644 --- a/Civi/Test.php +++ b/Civi/Test.php @@ -14,7 +14,7 @@ class Test { /** * @var array */ - private static $singletons = array(); + private static $singletons = []; /** * Get the data source used for testing. @@ -45,7 +45,7 @@ public static function dsn($part = NULL) { /** * Get a connection to the test database. * - * @return PDO + * @return \PDO */ public static function pdo() { if (!isset(self::$singletons['pdo'])) { @@ -55,7 +55,7 @@ public static function pdo() { try { self::$singletons['pdo'] = new PDO("mysql:host={$host}" . ($port ? ";port=$port" : ""), $dsninfo['username'], $dsninfo['password'], - array(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => TRUE) + [PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => TRUE] ); } catch (PDOException $e) { @@ -127,7 +127,6 @@ public static function schema() { return self::$singletons['schema']; } - /** * @return \Civi\Test\Data */ diff --git a/Civi/Test/Api3DocTrait.php b/Civi/Test/Api3DocTrait.php new file mode 100644 index 000000000000..a4c3ceb565fb --- /dev/null +++ b/Civi/Test/Api3DocTrait.php @@ -0,0 +1,209 @@ +_apiversion; + $result = $this->callAPISuccess($entity, $action, $params); + $this->documentMe($entity, $action, $params, $result, $function, $file, $description, $exampleName); + return $result; + } + + /** + * Create test generated example in api/v3/examples. + * + * To turn this off (e.g. on the server) set + * define(DONT_DOCUMENT_TEST_CONFIG ,1); + * in your settings file + * + * @param string $entity + * @param string $action + * @param array $params + * Array as passed to civicrm_api function. + * @param array $result + * Array as received from the civicrm_api function. + * @param string $testFunction + * Calling function - generally __FUNCTION__. + * @param string $testFile + * Called from file - generally __FILE__. + * @param string $description + * Descriptive text for the example file. + * @param string $exampleName + * Name for this example file (CamelCase) - if omitted the action name will be substituted. + */ + private function documentMe($entity, $action, $params, $result, $testFunction, $testFile, $description = "", $exampleName = NULL) { + if (defined('DONT_DOCUMENT_TEST_CONFIG') && DONT_DOCUMENT_TEST_CONFIG) { + return; + } + $entity = _civicrm_api_get_camel_name($entity); + $action = strtolower($action); + + if (empty($exampleName)) { + // Attempt to convert lowercase action name to CamelCase. + // This is clunky/imperfect due to the convention of all lowercase actions. + $exampleName = \CRM_Utils_String::convertStringToCamel($action); + $knownPrefixes = [ + 'Get', + 'Set', + 'Create', + 'Update', + 'Send', + ]; + foreach ($knownPrefixes as $prefix) { + if (strpos($exampleName, $prefix) === 0 && $prefix != $exampleName) { + $exampleName[strlen($prefix)] = strtoupper($exampleName[strlen($prefix)]); + } + } + } + + $this->tidyExampleResult($result); + if (isset($params['version'])) { + unset($params['version']); + } + // Format multiline description as array + $desc = []; + if (is_string($description) && strlen($description)) { + foreach (explode("\n", $description) as $line) { + $desc[] = trim($line); + } + } + $smarty = \CRM_Core_Smarty::singleton(); + $smarty->assign('testFunction', $testFunction); + $smarty->assign('function', _civicrm_api_get_entity_name_from_camel($entity) . "_$action"); + foreach ($params as $index => $param) { + if (is_string($param)) { + $params[$index] = addslashes($param); + } + } + $smarty->assign('params', $params); + $smarty->assign('entity', $entity); + $smarty->assign('testFile', basename($testFile)); + $smarty->assign('description', $desc); + $smarty->assign('result', $result); + $smarty->assign('action', $action); + + global $civicrm_root; + if (file_exists($civicrm_root . '/tests/templates/documentFunction.tpl')) { + if (!is_dir($civicrm_root . "/api/v3/examples/$entity")) { + mkdir($civicrm_root . "/api/v3/examples/$entity"); + } + $f = fopen($civicrm_root . "/api/v3/examples/$entity/$exampleName.php", "w+b"); + fwrite($f, $smarty->fetch($civicrm_root . '/tests/templates/documentFunction.tpl')); + fclose($f); + } + } + + /** + * Tidy up examples array so that fields that change often ..don't + * and debug related fields are unset + * + * @param array $result + */ + public function tidyExampleResult(&$result) { + if (!is_array($result)) { + return; + } + $fieldsToChange = [ + 'hash' => '67eac7789eaee00', + 'modified_date' => '2012-11-14 16:02:35', + 'created_date' => '2013-07-28 08:49:19', + 'create_date' => '20120130621222105', + 'application_received_date' => '20130728084957', + 'in_date' => '2013-07-28 08:50:19', + 'scheduled_date' => '20130728085413', + 'approval_date' => '20130728085413', + 'pledge_start_date_high' => '20130726090416', + 'start_date' => '2013-07-29 00:00:00', + 'event_start_date' => '2013-07-29 00:00:00', + 'end_date' => '2013-08-04 00:00:00', + 'event_end_date' => '2013-08-04 00:00:00', + 'decision_date' => '20130805000000', + ]; + + $keysToUnset = ['xdebug', 'undefined_fields']; + foreach ($keysToUnset as $unwantedKey) { + if (isset($result[$unwantedKey])) { + unset($result[$unwantedKey]); + } + } + if (isset($result['values'])) { + if (!is_array($result['values'])) { + return; + } + $resultArray = &$result['values']; + } + elseif (is_array($result)) { + $resultArray = &$result; + } + else { + return; + } + + foreach ($resultArray as $index => &$values) { + if (!is_array($values)) { + continue; + } + foreach ($values as $key => &$value) { + if (substr($key, 0, 3) == 'api' && is_array($value)) { + if (isset($value['is_error'])) { + // we have a std nested result format + $this->tidyExampleResult($value); + } + else { + foreach ($value as &$nestedResult) { + // this is an alternative syntax for nested results a keyed array of results + $this->tidyExampleResult($nestedResult); + } + } + } + if (in_array($key, $keysToUnset)) { + unset($values[$key]); + break; + } + if (array_key_exists($key, $fieldsToChange) && !empty($value)) { + $value = $fieldsToChange[$key]; + } + if (is_string($value)) { + $value = addslashes($value); + } + } + } + } + +} diff --git a/Civi/Test/Api3TestTrait.php b/Civi/Test/Api3TestTrait.php new file mode 100644 index 000000000000..5386e93969dc --- /dev/null +++ b/Civi/Test/Api3TestTrait.php @@ -0,0 +1,271 @@ + 1 + * else provide full message + * @param array $result + * @param $expected + * @param array $valuesToExclude + * @param string $prefix + * Extra test to add to message. + */ + public function assertAPIArrayComparison($result, $expected, $valuesToExclude = [], $prefix = '') { + $valuesToExclude = array_merge($valuesToExclude, ['debug', 'xdebug', 'sequential']); + foreach ($valuesToExclude as $value) { + if (isset($result[$value])) { + unset($result[$value]); + } + if (isset($expected[$value])) { + unset($expected[$value]); + } + } + $this->assertEquals($result, $expected, "api result array comparison failed " . $prefix . print_r($result, TRUE) . ' was compared to ' . print_r($expected, TRUE)); + } + + /** + * Check that a deleted item has been deleted. + * + * @param $entity + * @param $id + */ + public function assertAPIDeleted($entity, $id) { + $this->callAPISuccess($entity, 'getcount', ['id' => $id], 0); + } + + /** + * Check that api returned 'is_error' => 1. + * + * @param array $apiResult + * Api result. + * @param string $prefix + * Extra test to add to message. + * @param null $expectedError + */ + public function assertAPIFailure($apiResult, $prefix = '', $expectedError = NULL) { + if (!empty($prefix)) { + $prefix .= ': '; + } + if ($expectedError && !empty($apiResult['is_error'])) { + $this->assertEquals($expectedError, $apiResult['error_message'], 'api error message not as expected' . $prefix); + } + $this->assertEquals(1, $apiResult['is_error'], "api call should have failed but it succeeded " . $prefix . (print_r($apiResult, TRUE))); + $this->assertNotEmpty($apiResult['error_message']); + } + + /** + * Check that api returned 'is_error' => 0. + * + * @param array $apiResult + * Api result. + * @param string $prefix + * Extra test to add to message. + */ + public function assertAPISuccess($apiResult, $prefix = '') { + if (!empty($prefix)) { + $prefix .= ': '; + } + $errorMessage = empty($apiResult['error_message']) ? '' : " " . $apiResult['error_message']; + + if (!empty($apiResult['debug_information'])) { + $errorMessage .= "\n " . print_r($apiResult['debug_information'], TRUE); + } + if (!empty($apiResult['trace'])) { + $errorMessage .= "\n" . print_r($apiResult['trace'], TRUE); + } + $this->assertEquals(0, $apiResult['is_error'], $prefix . $errorMessage); + } + + /** + * This function exists to wrap api functions. + * so we can ensure they fail where expected & throw exceptions without litterering the test with checks + * @param string $entity + * @param string $action + * @param array $params + * @param string $expectedErrorMessage + * Error. + * @param null $extraOutput + * @return array|int + */ + public function callAPIFailure($entity, $action, $params, $expectedErrorMessage = NULL, $extraOutput = NULL) { + if (is_array($params)) { + $params += [ + 'version' => $this->_apiversion, + ]; + } + $result = $this->civicrm_api($entity, $action, $params); + $this->assertAPIFailure($result, "We expected a failure for $entity $action but got a success", $expectedErrorMessage); + return $result; + } + + /** + * wrap api functions. + * so we can ensure they succeed & throw exceptions without litterering the test with checks + * + * @param string $entity + * @param string $action + * @param array $params + * @param mixed $checkAgainst + * Optional value to check result against, implemented for getvalue,. + * getcount, getsingle. Note that for getvalue the type is checked rather than the value + * for getsingle the array is compared against an array passed in - the id is not compared (for + * better or worse ) + * + * @return array|int + */ + public function callAPISuccess($entity, $action, $params, $checkAgainst = NULL) { + $params = array_merge([ + 'version' => $this->_apiversion, + 'debug' => 1, + ], + $params + ); + switch (strtolower($action)) { + case 'getvalue': + return $this->callAPISuccessGetValue($entity, $params, $checkAgainst); + + case 'getsingle': + return $this->callAPISuccessGetSingle($entity, $params, $checkAgainst); + + case 'getcount': + return $this->callAPISuccessGetCount($entity, $params, $checkAgainst); + } + $result = $this->civicrm_api($entity, $action, $params); + $this->assertAPISuccess($result, "Failure in api call for $entity $action"); + return $result; + } + + /** + * This function exists to wrap api getValue function & check the result + * so we can ensure they succeed & throw exceptions without litterering the test with checks + * There is a type check in this + * @param string $entity + * @param array $params + * @param null $count + * @throws \Exception + * @return array|int + */ + public function callAPISuccessGetCount($entity, $params, $count = NULL) { + $params += [ + 'version' => $this->_apiversion, + 'debug' => 1, + ]; + $result = $this->civicrm_api($entity, 'getcount', $params); + if (!is_int($result) || !empty($result['is_error']) || isset($result['values'])) { + throw new \Exception('Invalid getcount result : ' . print_r($result, TRUE) . " type :" . gettype($result)); + } + if (is_int($count)) { + $this->assertEquals($count, $result, "incorrect count returned from $entity getcount"); + } + return $result; + } + + /** + * This function exists to wrap api getsingle function & check the result + * so we can ensure they succeed & throw exceptions without litterering the test with checks + * + * @param string $entity + * @param array $params + * @param array $checkAgainst + * Array to compare result against. + * - boolean + * - integer + * - double + * - string + * - array + * - object + * + * @throws \Exception + * @return array|int + */ + public function callAPISuccessGetSingle($entity, $params, $checkAgainst = NULL) { + $params += [ + 'version' => $this->_apiversion, + ]; + $result = $this->civicrm_api($entity, 'getsingle', $params); + if (!is_array($result) || !empty($result['is_error']) || isset($result['values'])) { + $unfilteredResult = $this->civicrm_api($entity, 'get', $params); + throw new \Exception( + 'Invalid getsingle result' . print_r($result, TRUE) + . "\n entity: $entity . \n params \n " . print_r($params, TRUE) + . "\n entities retrieved with blank params \n" . print_r($unfilteredResult, TRUE) + ); + } + if ($checkAgainst) { + // @todo - have gone with the fn that unsets id? should we check id? + $this->checkArrayEquals($result, $checkAgainst); + } + return $result; + } + + /** + * This function exists to wrap api getValue function & check the result + * so we can ensure they succeed & throw exceptions without litterering the test with checks + * There is a type check in this + * + * @param string $entity + * @param array $params + * @param string $type + * Per http://php.net/manual/en/function.gettype.php possible types. + * - boolean + * - integer + * - double + * - string + * - array + * - object + * + * @return array|int + */ + public function callAPISuccessGetValue($entity, $params, $type = NULL) { + $params += [ + 'version' => $this->_apiversion, + 'debug' => 1, + ]; + $result = $this->civicrm_api($entity, 'getvalue', $params); + if (is_array($result) && (!empty($result['is_error']) || isset($result['values']))) { + throw new \Exception('Invalid getvalue result' . print_r($result, TRUE)); + } + if ($type) { + if ($type == 'integer') { + // api seems to return integers as strings + $this->assertTrue(is_numeric($result), "expected a numeric value but got " . print_r($result, 1)); + } + else { + $this->assertType($type, $result, "returned result should have been of type $type but was "); + } + } + return $result; + } + + /** + * A stub for the API interface. This can be overriden by subclasses to change how the API is called. + * + * @param $entity + * @param $action + * @param array $params + * @return array|int + */ + public function civicrm_api($entity, $action, $params) { + return civicrm_api($entity, $action, $params); + } + +} diff --git a/Civi/Test/CiviEnvBuilder.php b/Civi/Test/CiviEnvBuilder.php index 003e14545dbb..91fc4b767885 100644 --- a/Civi/Test/CiviEnvBuilder.php +++ b/Civi/Test/CiviEnvBuilder.php @@ -19,7 +19,7 @@ class CiviEnvBuilder { protected $name; - private $steps = array(); + private $steps = []; /** * @var string|NULL diff --git a/Civi/Test/CiviEnvBuilder/CallbackStep.php b/Civi/Test/CiviEnvBuilder/CallbackStep.php index 6d0c9f055c3a..8e6110bee38e 100644 --- a/Civi/Test/CiviEnvBuilder/CallbackStep.php +++ b/Civi/Test/CiviEnvBuilder/CallbackStep.php @@ -1,5 +1,6 @@ action, array('install', 'uninstall'))) { + if (!in_array($this->action, ['install', 'uninstall'])) { return FALSE; } foreach ($this->names as $name) { diff --git a/Civi/Test/CiviEnvBuilder/SqlFileStep.php b/Civi/Test/CiviEnvBuilder/SqlFileStep.php index c901931721a2..ffb9ca1b0d46 100644 --- a/Civi/Test/CiviEnvBuilder/SqlFileStep.php +++ b/Civi/Test/CiviEnvBuilder/SqlFileStep.php @@ -12,13 +12,12 @@ public function __construct($file) { $this->file = $file; } - public function getSig() { - return implode(' ', array( + return implode(' ', [ $this->file, filemtime($this->file), filectime($this->file), - )); + ]); } public function isValid() { diff --git a/Civi/Test/CiviEnvBuilder/SqlStep.php b/Civi/Test/CiviEnvBuilder/SqlStep.php index 7a2736b019c8..e892883e03ce 100644 --- a/Civi/Test/CiviEnvBuilder/SqlStep.php +++ b/Civi/Test/CiviEnvBuilder/SqlStep.php @@ -1,5 +1,6 @@ sql = $sql; } - public function getSig() { return md5($this->sql); } diff --git a/Civi/Test/CiviEnvBuilder/StepInterface.php b/Civi/Test/CiviEnvBuilder/StepInterface.php index 3d6dc95cc1e3..8ed2c2ce77df 100644 --- a/Civi/Test/CiviEnvBuilder/StepInterface.php +++ b/Civi/Test/CiviEnvBuilder/StepInterface.php @@ -2,6 +2,7 @@ namespace Civi\Test\CiviEnvBuilder; interface StepInterface { + public function getSig(); public function isValid(); diff --git a/Civi/Test/CiviTestListener.php b/Civi/Test/CiviTestListener.php index 3bd6f0dbc5d2..9c2a10beacff 100644 --- a/Civi/Test/CiviTestListener.php +++ b/Civi/Test/CiviTestListener.php @@ -25,7 +25,7 @@ class CiviTestListener extends \PHPUnit_Framework_BaseTestListener { * Ex: $cache['Some_Test_Class']['civicrm_foobar'] = 'hook_civicrm_foobar'; * Array(string $testClass => Array(string $hookName => string $methodName)). */ - private $cache = array(); + private $cache = []; /** * @var \CRM_Core_Transaction|NULL @@ -39,7 +39,7 @@ public function startTestSuite(\PHPUnit_Framework_TestSuite $suite) { } public function endTestSuite(\PHPUnit_Framework_TestSuite $suite) { - $this->cache = array(); + $this->cache = []; } public function startTest(\PHPUnit_Framework_Test $test) { @@ -95,10 +95,11 @@ protected function bootHeadless($test) { $test->setUpHeadless(); - $config = \CRM_Core_Config::singleton(TRUE, TRUE); // ugh, performance \CRM_Utils_System::flushCache(); \Civi::reset(); \CRM_Core_Session::singleton()->set('userID', NULL); + // ugh, performance + $config = \CRM_Core_Config::singleton(TRUE, TRUE); if (property_exists($config->userPermissionClass, 'permissions')) { $config->userPermissionClass->permissions = NULL; @@ -113,7 +114,7 @@ protected function bootHeadless($test) { protected function findTestHooks(HookInterface $test) { $class = get_class($test); if (!isset($this->cache[$class])) { - $funcs = array(); + $funcs = []; foreach (get_class_methods($class) as $func) { if (preg_match('/^hook_/', $func)) { $funcs[substr($func, 5)] = $func; @@ -147,7 +148,7 @@ protected function registerHooks(HookInterface $test) { /** @var \CRM_Utils_Hook_UnitTests $hooks */ $hooks = \CRM_Utils_Hook::singleton(); foreach ($this->findTestHooks($test) as $hook => $func) { - $hooks->setHook($hook, array($test, $func)); + $hooks->setHook($hook, [$test, $func]); } } @@ -168,16 +169,20 @@ protected function autoboot($byInterface) { } elseif (!empty($byInterface['HeadlessInterface'])) { putenv('CIVICRM_UF=UnitTests'); + // phpcs:disable eval($this->cv('php:boot --level=full', 'phpcode')); + // phpcs:enable } elseif (!empty($byInterface['EndToEndInterface'])) { putenv('CIVICRM_UF='); + // phpcs:disable eval($this->cv('php:boot --level=full', 'phpcode')); + // phpcs:enable } $blurb = "Tip: Run the headless tests and end-to-end tests separately, e.g.\n" - . " $ phpunit4 --group headless\n" - . " $ phpunit4 --group e2e \n"; + . " $ phpunit5 --group headless\n" + . " $ phpunit5 --group e2e \n"; if (!empty($byInterface['HeadlessInterface']) && CIVICRM_UF !== 'UnitTests') { $testNames = implode(', ', array_keys($byInterface['HeadlessInterface'])); @@ -208,7 +213,7 @@ protected function autoboot($byInterface) { */ protected function cv($cmd, $decode = 'json') { $cmd = 'cv ' . $cmd; - $descriptorSpec = array(0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => STDERR); + $descriptorSpec = [0 => ["pipe", "r"], 1 => ["pipe", "w"], 2 => STDERR]; $oldOutput = getenv('CV_OUTPUT'); putenv("CV_OUTPUT=json"); $process = proc_open($cmd, $descriptorSpec, $pipes, __DIR__); @@ -243,7 +248,7 @@ protected function cv($cmd, $decode = 'json') { * @return array */ protected function indexTestsByInterface($tests) { - $byInterface = array('HeadlessInterface' => array(), 'EndToEndInterface' => array()); + $byInterface = ['HeadlessInterface' => [], 'EndToEndInterface' => []]; foreach ($tests as $test) { /** @var \PHPUnit_Framework_Test $test */ if ($test instanceof HeadlessInterface) { diff --git a/Civi/Test/Data.php b/Civi/Test/Data.php index d1e62e5903fb..8b90b5d493a8 100644 --- a/Civi/Test/Data.php +++ b/Civi/Test/Data.php @@ -15,6 +15,9 @@ public function populate() { \Civi\Test::schema()->truncateAll(); \Civi\Test::schema()->setStrict(FALSE); + + // Ensure that when we populate the database it is done in utf8 mode + \Civi\Test::execute('SET NAMES utf8'); $sqlDir = dirname(dirname(__DIR__)) . "/sql"; $query2 = file_get_contents("$sqlDir/civicrm_data.mysql"); @@ -35,16 +38,16 @@ public function populate() { \Civi\Test::schema()->setStrict(TRUE); // Rebuild triggers - civicrm_api('system', 'flush', array('version' => 3, 'triggers' => 1)); + civicrm_api('system', 'flush', ['version' => 3, 'triggers' => 1]); - \CRM_Core_BAO_ConfigSetting::setEnabledComponents(array( + \CRM_Core_BAO_ConfigSetting::setEnabledComponents([ 'CiviEvent', 'CiviContribute', 'CiviMember', 'CiviMail', 'CiviReport', 'CiviPledge', - )); + ]); return TRUE; } diff --git a/Civi/Test/HeadlessInterface.php b/Civi/Test/HeadlessInterface.php index 85de268943c1..95227f76856c 100644 --- a/Civi/Test/HeadlessInterface.php +++ b/Civi/Test/HeadlessInterface.php @@ -7,7 +7,7 @@ * @package Civi\Test * * To run your test against a fake, headless database, flag it with the - * HeadlessInterface. CiviTestListener will automatically boot + * HeadlessInterface. CiviTestListener will automatically boot Civi. * * Alternatively, if you wish to run a test in a live (CMS-enabled) environment, * flag it with EndToEndInterface. @@ -23,7 +23,7 @@ interface HeadlessInterface { /** - * The setupHeadless functions runs at the start of each test case, right before + * The setupHeadless function runs at the start of each test case, right before * the headless environment reboots. * * It should perform any necessary steps required for putting the database diff --git a/Civi/Test/Schema.php b/Civi/Test/Schema.php index 706f8482f295..b5a9aa525cab 100644 --- a/Civi/Test/Schema.php +++ b/Civi/Test/Schema.php @@ -25,7 +25,7 @@ public function getTables($type) { $pdo->quote($type) ); $tables = $pdo->query($query); - $result = array(); + $result = []; foreach ($tables as $table) { $result[] = $table['table_name']; } @@ -35,20 +35,20 @@ public function getTables($type) { public function setStrict($checks) { $dbName = \Civi\Test::dsn('database'); if ($checks) { - $queries = array( + $queries = [ "USE {$dbName};", "SET global innodb_flush_log_at_trx_commit = 1;", "SET SQL_MODE='STRICT_ALL_TABLES';", "SET foreign_key_checks = 1;", - ); + ]; } else { - $queries = array( + $queries = [ "USE {$dbName};", "SET foreign_key_checks = 0", "SET SQL_MODE='STRICT_ALL_TABLES';", "SET global innodb_flush_log_at_trx_commit = 2;", - ); + ]; } foreach ($queries as $query) { if (\Civi\Test::execute($query) === FALSE) { @@ -59,7 +59,7 @@ public function setStrict($checks) { } public function dropAll() { - $queries = array(); + $queries = []; foreach ($this->getTables('VIEW') as $table) { if (preg_match('/^(civicrm_|log_)/', $table)) { $queries[] = "DROP VIEW $table"; @@ -84,13 +84,13 @@ public function dropAll() { } /** - * @return array + * @return Schema */ public function truncateAll() { $tables = \Civi\Test::schema()->getTables('BASE TABLE'); - $truncates = array(); - $drops = array(); + $truncates = []; + $drops = []; foreach ($tables as $table) { // skip log tables if (substr($table, 0, 4) == 'log_') { diff --git a/Civi/Token/AbstractTokenSubscriber.php b/Civi/Token/AbstractTokenSubscriber.php index a7d21f1344bb..012d306930ae 100644 --- a/Civi/Token/AbstractTokenSubscriber.php +++ b/Civi/Token/AbstractTokenSubscriber.php @@ -1,9 +1,9 @@ 'registerTokens', Events::TOKEN_EVALUATE => 'evaluateTokens', \Civi\ActionSchedule\Events::MAILING_QUERY => 'alterActionScheduleQuery', - ); + ]; } /** @@ -70,16 +71,24 @@ public static function getSubscribedEvents() { /** * @var array - * Ex: array('viewUrl', 'editUrl'). + * List of tokens provided by this class + * Array(string $fieldName => string $label). */ public $tokenNames; + /** + * @var array + * List of active tokens - tokens provided by this class and used in the message + * Array(string $tokenName); + */ + public $activeTokens; + /** * @param $entity * @param array $tokenNames - * Array(string $fieldName => string $label). + * Array(string $tokenName => string $label). */ - public function __construct($entity, $tokenNames = array()) { + public function __construct($entity, $tokenNames = []) { $this->entity = $entity; $this->tokenNames = $tokenNames; } @@ -101,7 +110,7 @@ public function checkActive(\Civi\Token\TokenProcessor $processor) { /** * Register the declared tokens. * - * @param TokenRegisterEvent $e + * @param \Civi\Token\Event\TokenRegisterEvent $e * The registration event. Add new tokens using register(). */ public function registerTokens(TokenRegisterEvent $e) { @@ -109,11 +118,11 @@ public function registerTokens(TokenRegisterEvent $e) { return; } foreach ($this->tokenNames as $name => $label) { - $e->register(array( + $e->register([ 'entity' => $this->entity, 'field' => $name, 'label' => $label, - )); + ]); } } @@ -124,7 +133,7 @@ public function registerTokens(TokenRegisterEvent $e) { * This is method is not always appropriate, but if you're specifically * focused on scheduled reminders, it can be convenient. * - * @param MailingQueryEvent $e + * @param \Civi\ActionSchedule\Event\MailingQueryEvent $e * The pending query which may be modified. See discussion on * MailingQueryEvent::$query. */ @@ -134,22 +143,42 @@ public function alterActionScheduleQuery(MailingQueryEvent $e) { /** * Populate the token data. * - * @param TokenValueEvent $e + * @param \Civi\Token\Event\TokenValueEvent $e * The event, which includes a list of rows and tokens. */ public function evaluateTokens(TokenValueEvent $e) { if (!$this->checkActive($e->getTokenProcessor())) { return; } - // TODO: check if any tokens for $entity are actually used; short-circuit. + + $this->activeTokens = $this->getActiveTokens($e); + if (!$this->activeTokens) { + return; + } $prefetch = $this->prefetch($e); + foreach ($e->getRows() as $row) { - foreach ($this->tokenNames as $field => $label) { + foreach ($this->activeTokens as $field) { $this->evaluateToken($row, $this->entity, $field, $prefetch); } } } + /** + * To handle variable tokens, override this function and return the active tokens. + * + * @param \Civi\Token\Event\TokenValueEvent $e + * + * @return mixed + */ + public function getActiveTokens(TokenValueEvent $e) { + $messageTokens = $e->getTokenProcessor()->getMessageTokens(); + if (!isset($messageTokens[$this->entity])) { + return FALSE; + } + return array_intersect($messageTokens[$this->entity], array_keys($this->tokenNames)); + } + /** * To perform a bulk lookup before rendering tokens, override this * function and return the prefetched data. @@ -175,6 +204,6 @@ public function prefetch(TokenValueEvent $e) { * Any data that was returned by the prefetch(). * @return mixed */ - public abstract function evaluateToken(TokenRow $row, $entity, $field, $prefetch = NULL); + abstract public function evaluateToken(TokenRow $row, $entity, $field, $prefetch = NULL); } diff --git a/Civi/Token/Event/TokenRegisterEvent.php b/Civi/Token/Event/TokenRegisterEvent.php index ff9122543c43..c7fdfa01fe7b 100644 --- a/Civi/Token/Event/TokenRegisterEvent.php +++ b/Civi/Token/Event/TokenRegisterEvent.php @@ -57,10 +57,10 @@ public function register($paramsOrField, $label = NULL) { $params = $paramsOrField; } else { - $params = array( + $params = [ 'field' => $paramsOrField, 'label' => $label, - ); + ]; } $params = array_merge($this->defaults, $params); $this->tokenProcessor->addToken($params); diff --git a/Civi/Token/TokenCompatSubscriber.php b/Civi/Token/TokenCompatSubscriber.php index 3eaf57d3b7cc..672080d29ba4 100644 --- a/Civi/Token/TokenCompatSubscriber.php +++ b/Civi/Token/TokenCompatSubscriber.php @@ -24,16 +24,16 @@ class TokenCompatSubscriber implements EventSubscriberInterface { * @inheritDoc */ public static function getSubscribedEvents() { - return array( + return [ Events::TOKEN_EVALUATE => 'onEvaluate', Events::TOKEN_RENDER => 'onRender', - ); + ]; } /** * Load token data. * - * @param TokenValueEvent $e + * @param \Civi\Token\Event\TokenValueEvent $e * @throws TokenException */ public function onEvaluate(TokenValueEvent $e) { @@ -41,7 +41,7 @@ public function onEvaluate(TokenValueEvent $e) { // hook *categories* (aka entities aka namespaces). We'll cache // this in the TokenProcessor's context. - $hookTokens = array(); + $hookTokens = []; \CRM_Utils_Hook::tokens($hookTokens); $categories = array_keys($hookTokens); $e->getTokenProcessor()->context['hookTokenCategories'] = $categories; @@ -49,17 +49,34 @@ public function onEvaluate(TokenValueEvent $e) { $messageTokens = $e->getTokenProcessor()->getMessageTokens(); foreach ($e->getRows() as $row) { + if (empty($row->context['contactId'])) { + continue; + } + /** @var int $contactId */ $contactId = $row->context['contactId']; if (empty($row->context['contact'])) { - $params = array( - array('contact_id', '=', $contactId, 0, 0), - ); + $params = [ + ['contact_id', '=', $contactId, 0, 0], + ]; list($contact, $_) = \CRM_Contact_BAO_Query::apiQuery($params); - $contact = reset($contact); //CRM-4524 + //CRM-4524 + $contact = reset($contact); if (!$contact || is_a($contact, 'CRM_Core_Error')) { // FIXME: Need to differentiate errors which kill the batch vs the individual row. throw new TokenException("Failed to generate token data. Invalid contact ID: " . $row->context['contactId']); } + + //update value of custom field token + if (!empty($messageTokens['contact'])) { + foreach ($messageTokens['contact'] as $token) { + if (\CRM_Core_BAO_CustomField::getKeyID($token)) { + $contact[$token] = civicrm_api3('Contact', 'getvalue', [ + 'return' => $token, + 'id' => $contactId, + ]); + } + } + } } else { $contact = $row->context['contact']; @@ -71,14 +88,13 @@ public function onEvaluate(TokenValueEvent $e) { $contact = array_merge($contact, $row->context['tmpTokenParams']); } - $contactArray = !is_array($contactId) ? array($contactId => $contact) : $contact; + $contactArray = !is_array($contactId) ? [$contactId => $contact] : $contact; // Note: This is a small contract change from the past; data should be missing // less randomly. - //\CRM_Utils_Hook::tokenValues($contact, $row->context['contactId']); \CRM_Utils_Hook::tokenValues($contactArray, (array) $contactId, - empty($row->context['mailingJob']) ? NULL : $row->context['mailingJob']->id, + empty($row->context['mailingJobId']) ? NULL : $row->context['mailingJobId'], $messageTokens, $row->context['controller'] ); @@ -94,21 +110,22 @@ public function onEvaluate(TokenValueEvent $e) { /** * Apply the various CRM_Utils_Token helpers. * - * @param TokenRenderEvent $e + * @param \Civi\Token\Event\TokenRenderEvent $e */ public function onRender(TokenRenderEvent $e) { $isHtml = ($e->message['format'] == 'text/html'); $useSmarty = !empty($e->context['smarty']); - $e->string = \CRM_Utils_Token::replaceDomainTokens($e->string, \CRM_Core_BAO_Domain::getDomain(), $isHtml, $e->message['tokens'], $useSmarty); + $domain = \CRM_Core_BAO_Domain::getDomain(); + $e->string = \CRM_Utils_Token::replaceDomainTokens($e->string, $domain, $isHtml, $e->message['tokens'], $useSmarty); if (!empty($e->context['contact'])) { - $e->string = \CRM_Utils_Token::replaceContactTokens($e->string, $e->context['contact'], $isHtml, $e->message['tokens'], FALSE, $useSmarty); + $e->string = \CRM_Utils_Token::replaceContactTokens($e->string, $e->context['contact'], $isHtml, $e->message['tokens'], TRUE, $useSmarty); // FIXME: This may depend on $contact being merged with hook values. $e->string = \CRM_Utils_Token::replaceHookTokens($e->string, $e->context['contact'], $e->context['hookTokenCategories'], $isHtml, $useSmarty); - \CRM_Utils_Token::replaceGreetingTokens($e->string, NULL, $e->context['contact']['contact_id'], NULL, $useSmarty); + \CRM_Utils_Token::replaceGreetingTokens($e->string, $e->context['contact'], $e->context['contact']['contact_id'], NULL, $useSmarty); } if ($useSmarty) { diff --git a/Civi/Token/TokenProcessor.php b/Civi/Token/TokenProcessor.php index 99a0f0f9c43d..1364ed714faa 100644 --- a/Civi/Token/TokenProcessor.php +++ b/Civi/Token/TokenProcessor.php @@ -4,7 +4,6 @@ use Civi\Token\Event\TokenRegisterEvent; use Civi\Token\Event\TokenRenderEvent; use Civi\Token\Event\TokenValueEvent; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Traversable; class TokenProcessor { @@ -25,11 +24,15 @@ class TokenProcessor { * automatically from contactId.) * - actionSchedule: DAO, the rule which triggered the mailing * [for CRM_Core_BAO_ActionScheduler]. + * - schema: array, a list of fields that will be provided for each row. + * This is automatically populated with any general context + * keys, but you may need to add extra keys for token-row data. + * ex: ['contactId', 'activityId']. */ public $context; /** - * @var EventDispatcherInterface + * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface */ protected $dispatcher; @@ -68,13 +71,23 @@ class TokenProcessor { */ protected $tokens = NULL; + /** + * A list of available tokens formatted for display + * @var array + * Array('{' . $dottedName . '}' => 'labelString') + */ + protected $listTokens = NULL; + protected $next = 0; /** - * @param EventDispatcherInterface $dispatcher + * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher * @param array $context */ public function __construct($dispatcher, $context) { + $context['schema'] = isset($context['schema']) + ? array_unique(array_merge($context['schema'], array_keys($context))) + : array_keys($context); $this->dispatcher = $dispatcher; $this->context = $context; } @@ -91,11 +104,11 @@ public function __construct($dispatcher, $context) { * @return TokenProcessor */ public function addMessage($name, $value, $format) { - $this->messages[$name] = array( + $this->messages[$name] = [ 'string' => $value, 'format' => $format, 'tokens' => \CRM_Utils_Token::getTokens($value), - ); + ]; return $this; } @@ -106,11 +119,11 @@ public function addMessage($name, $value, $format) { */ public function addRow() { $key = $this->next++; - $this->rowContexts[$key] = array(); - $this->rowValues[$key] = array( - 'text/plain' => array(), - 'text/html' => array(), - ); + $this->rowContexts[$key] = []; + $this->rowValues[$key] = [ + 'text/plain' => [], + 'text/html' => [], + ]; return new TokenRow($this, $key); } @@ -146,7 +159,7 @@ public function getMessage($name) { * @return array */ public function getMessageTokens() { - $tokens = array(); + $tokens = []; foreach ($this->messages as $message) { $tokens = \CRM_Utils_Array::crmArrayMerge($tokens, $message['tokens']); } @@ -168,6 +181,44 @@ public function getRows() { return new TokenRowIterator($this, new \ArrayIterator($this->rowContexts)); } + /** + * Get a list of all unique values for a given context field, + * whether defined at the processor or row level. + * + * @param string $field + * Ex: 'contactId'. + * @param $subfield + * @return array + * Ex: [12, 34, 56]. + */ + public function getContextValues($field, $subfield = NULL) { + $values = []; + if (isset($this->context[$field])) { + if ($subfield) { + if (isset($this->context[$field]->$subfield)) { + $values[] = $this->context[$field]->$subfield; + } + } + else { + $values[] = $this->context[$field]; + } + } + foreach ($this->getRows() as $row) { + if (isset($row->context[$field])) { + if ($subfield) { + if (isset($row->context[$field]->$subfield)) { + $values[] = $row->context[$field]->$subfield; + } + } + else { + $values[] = $row->context[$field]; + } + } + } + $values = array_unique($values); + return $values; + } + /** * Get the list of available tokens. * @@ -176,13 +227,29 @@ public function getRows() { */ public function getTokens() { if ($this->tokens === NULL) { - $this->tokens = array(); - $event = new TokenRegisterEvent($this, array('entity' => 'undefined')); + $this->tokens = []; + $event = new TokenRegisterEvent($this, ['entity' => 'undefined']); $this->dispatcher->dispatch(Events::TOKEN_REGISTER, $event); } return $this->tokens; } + /** + * Get the list of available tokens, formatted for display + * + * @return array + * Ex: $tokens[ '{token.name}' ] = "Token label" + */ + public function listTokens() { + if ($this->listTokens === NULL) { + $this->listTokens = []; + foreach ($this->getTokens() as $token => $values) { + $this->listTokens['{' . $token . '}'] = $values['label']; + } + } + return $this->listTokens; + } + /** * Compute and store token values. */ @@ -211,11 +278,13 @@ public function render($name, $row) { $row->fill($message['format']); $useSmarty = !empty($row->context['smarty']); - // FIXME preg_callback. + /** + *@FIXME preg_callback. + */ $tokens = $this->rowValues[$row->tokenRow][$message['format']]; - $flatTokens = array(); + $flatTokens = []; \CRM_Utils_Array::flatten($tokens, $flatTokens, '', '.'); - $filteredTokens = array(); + $filteredTokens = []; foreach ($flatTokens as $k => $v) { $filteredTokens['{' . $k . '}'] = ($useSmarty ? \CRM_Utils_Token::tokenEscapeSmarty($v) : $v); } @@ -237,10 +306,11 @@ class TokenRowIterator extends \IteratorIterator { /** * @param TokenProcessor $tokenProcessor - * @param Traversable $iterator + * @param \Traversable $iterator */ public function __construct(TokenProcessor $tokenProcessor, Traversable $iterator) { - parent::__construct($iterator); // TODO: Change the autogenerated stub + // TODO: Change the autogenerated stub + parent::__construct($iterator); $this->tokenProcessor = $tokenProcessor; } diff --git a/Civi/Token/TokenRow.php b/Civi/Token/TokenRow.php index a18a81c43533..5287219a9401 100644 --- a/Civi/Token/TokenRow.php +++ b/Civi/Token/TokenRow.php @@ -65,7 +65,8 @@ class TokenRow { public function __construct(TokenProcessor $tokenProcessor, $key) { $this->tokenProcessor = $tokenProcessor; $this->tokenRow = $key; - $this->format('text/plain'); // Set a default. + // Set a default. + $this->format('text/plain'); $this->context = new TokenRowContext($tokenProcessor, $key); } @@ -126,6 +127,30 @@ public function tokens($a = NULL, $b = NULL, $c = NULL) { return $this; } + /** + * Update the value of a custom field token. + * + * @param string $entity + * @param int $customFieldID + * @param int $entityID + * @return TokenRow + */ + public function customToken($entity, $customFieldID, $entityID) { + $customFieldName = "custom_" . $customFieldID; + $record = civicrm_api3($entity, "getSingle", [ + 'return' => $customFieldName, + 'id' => $entityID, + ]); + $fieldValue = \CRM_Utils_Array::value($customFieldName, $record, ''); + + // format the raw custom field value into proper display value + if (isset($fieldValue)) { + $fieldValue = \CRM_Core_BAO_CustomField::displayValue($fieldValue, $customFieldID); + } + + return $this->tokens($entity, $customFieldName, $fieldValue); + } + /** * Update the value of a token. Apply formatting based on DB schema. * @@ -134,6 +159,8 @@ public function tokens($a = NULL, $b = NULL, $c = NULL) { * @param string $baoName * @param array $baoField * @param mixed $fieldValue + * @return TokenRow + * @throws \CRM_Core_Exception */ public function dbToken($tokenEntity, $tokenField, $baoName, $baoField, $fieldValue) { if ($fieldValue === NULL || $fieldValue === '') { @@ -179,10 +206,10 @@ public function fill($format = NULL) { } if (!isset($this->tokenProcessor->rowValues[$this->tokenRow]['text/html'])) { - $this->tokenProcessor->rowValues[$this->tokenRow]['text/html'] = array(); + $this->tokenProcessor->rowValues[$this->tokenRow]['text/html'] = []; } if (!isset($this->tokenProcessor->rowValues[$this->tokenRow]['text/plain'])) { - $this->tokenProcessor->rowValues[$this->tokenRow]['text/plain'] = array(); + $this->tokenProcessor->rowValues[$this->tokenRow]['text/plain'] = []; } $htmlTokens = &$this->tokenProcessor->rowValues[$this->tokenRow]['text/html']; @@ -192,6 +219,7 @@ public function fill($format = NULL) { case 'text/html': // Plain => HTML. foreach ($textTokens as $entity => $values) { + $entityFields = civicrm_api3($entity, "getFields", ['api_action' => 'get']); foreach ($values as $field => $value) { if (!isset($htmlTokens[$entity][$field])) { // CRM-18420 - Activity Details Field are enclosed within

    , @@ -200,6 +228,10 @@ public function fill($format = NULL) { if ($entity == 'activity' && $field == 'details') { $htmlTokens[$entity][$field] = $value; } + elseif (\CRM_Utils_Array::value('data_type', \CRM_Utils_Array::value($field, $entityFields['values'])) == 'Memo') { + // Memo fields aka custom fields of type Note are html. + $htmlTokens[$entity][$field] = CRM_Utils_String::purifyHTML($value); + } else { $htmlTokens[$entity][$field] = htmlentities($value); } @@ -274,8 +306,7 @@ public function __construct($tokenProcessor, $tokenRow) { * @return bool */ public function offsetExists($offset) { - return - isset($this->tokenProcessor->rowContexts[$this->tokenRow][$offset]) + return isset($this->tokenProcessor->rowContexts[$this->tokenRow][$offset]) || isset($this->tokenProcessor->context[$offset]); } diff --git a/README.md b/README.md new file mode 100644 index 000000000000..69835a0b75c5 --- /dev/null +++ b/README.md @@ -0,0 +1,50 @@ +![](i/logo_lg.png) Welcome to CiviCRM +===================================== + +About +----- + +CiviCRM is a constituent relationship management system designed to +meet the needs of advocacy, non-profit and non-governmental groups. +It is an open source project, licensed under GNU AGPL 3, and +coordinated by CiviCRM LLC. The project website is https://civicrm.org/ + +CiviCRM is released as a module that runs within the Drupal, Joomla, +and WordPress content management systems. + + +Installation +------------ + +The download URLs and installation instructions are available on our website: +https://civicrm.org/download + +Detailed installation instructions are [on the wiki](https://wiki.civicrm.org/confluence/display/CRMDOC/Installation+and+Upgrades). + + +Documentation +------------- + +Documentation can be found at https://civicrm.org/documentation + + +Support +------- + +Answers for users, administrators & integrators: +http://civicrm.stackexchange.com + +Paid support available from +https://civicrm.org/providers + + +Development and Bugs +-------------------- + +Developers are highly encouraged to join [chat.civicrm.org](https://chat.civicrm.org) and post +questions and ideas in the [Developer Discussion room](https://chat.civicrm.org/civicrm/channels/dev). + +Installing the latest developmental code requires some [special steps](http://wiki.civicrm.org/confluence/display/CRMDOC/Contributing+to+CiviCRM+using+GitHub). + +Report all issues to CiviCRM via GitLab: +https://lab.civicrm.org diff --git a/README.txt b/README.txt deleted file mode 100644 index 147b0df3ae82..000000000000 --- a/README.txt +++ /dev/null @@ -1,67 +0,0 @@ -************************* -README file for CiviCRM -************************* -1. About -2. Installation -3. Documentation -4. Support -5. Development - - - -************************* -1. About -************************* -CiviCRM is a constituent relationship management system designed to -meet the needs of advocacy, non-profit and non-governmental groups. -It is an open source project, licensed under GNU AGPL 3, and -coordinated by CiviCRM LLC. The project website is https://civicrm.org/ - -CiviCRM is released as a module that runs within the Drupal, Joomla, -and WordPress content management systems. - - -************************* -2. Installation -************************* -The download URLs and installation instructions are available on our -website: -https://civicrm.org/download - -Installation instruction can be found on the wiki: -http://wiki.civicrm.org/ - - -************************* -3. Documentation -************************* -Documentation, as well as the project bug tracker and mailing lists, -can be found at https://civicrm.org/ Just click on the appropriate link. - -Our main documentation site is the CiviCRM wiki: -http://wiki.civicrm.org/ - - -************************* -4. Support -************************* -Answers for users, administrators & integrators: -http://civicrm.stackexchange.com - -Paid support available from -https://civicrm.org/providers - - -************************* -5. Development -************************* -Developers are highly encouraged to join the CiviCRM forums and post -questions and ideas on the Developer Discussion board: -http://forum.civicrm.org/index.php/board,20.0.html - -Installing the latest developmental code requires some special steps. Full -instructions are on the wiki: -http://wiki.civicrm.org/confluence/display/CRMDOC/Contributing+to+CiviCRM+using+GitHub - -Report all issues to CiviCRM via JIRA: -https://issues.civicrm.org diff --git a/ang/angularFileUpload.ang.php b/ang/angularFileUpload.ang.php new file mode 100644 index 000000000000..68f4aa99aa1e --- /dev/null +++ b/ang/angularFileUpload.ang.php @@ -0,0 +1,9 @@ + 'civicrm', + 'js' => ['bower_components/angular-file-upload/angular-file-upload.min.js'], +]; diff --git a/ang/crmApp.ang.php b/ang/crmApp.ang.php new file mode 100644 index 000000000000..d4179315bd05 --- /dev/null +++ b/ang/crmApp.ang.php @@ -0,0 +1,9 @@ + 'civicrm', + 'js' => ['ang/crmApp.js'], +]; diff --git a/ang/crmApp.js b/ang/crmApp.js index 0726a045040c..c7bb81e29bc9 100644 --- a/ang/crmApp.js +++ b/ang/crmApp.js @@ -5,6 +5,16 @@ var crmApp = angular.module('crmApp', CRM.angular.modules); crmApp.config(['$routeProvider', function($routeProvider) { + + if (CRM.crmApp.defaultRoute) { + $routeProvider.when('/', { + template: '

    ', + controller: function($location) { + $location.path(CRM.crmApp.defaultRoute); + } + }); + } + $routeProvider.otherwise({ template: ts('Unknown path') }); diff --git a/ang/crmAttachment.ang.php b/ang/crmAttachment.ang.php new file mode 100644 index 000000000000..e278f75e32c7 --- /dev/null +++ b/ang/crmAttachment.ang.php @@ -0,0 +1,15 @@ + 'civicrm', + 'js' => ['ang/crmAttachment.js'], + 'css' => ['ang/crmAttachment.css'], + 'partials' => ['ang/crmAttachment'], + 'settings' => [ + 'token' => \CRM_Core_Page_AJAX_Attachment::createToken(), + ], + 'requires' => ['angularFileUpload', 'crmResource'], +]; diff --git a/ang/crmAttachment.js b/ang/crmAttachment.js index 35dd1c582fe2..b89fe4ca22ef 100644 --- a/ang/crmAttachment.js +++ b/ang/crmAttachment.js @@ -1,7 +1,7 @@ /// crmFile: Manage file attachments (function (angular, $, _) { - angular.module('crmAttachment', ['angularFileUpload']); + angular.module('crmAttachment', CRM.angRequires('crmAttachment')); // crmAttachment manages the list of files which are attached to a given entity angular.module('crmAttachment').factory('CrmAttachments', function (crmApi, crmStatus, FileUploader, $q) { diff --git a/ang/crmAutosave.ang.php b/ang/crmAutosave.ang.php new file mode 100644 index 000000000000..6f998782c709 --- /dev/null +++ b/ang/crmAutosave.ang.php @@ -0,0 +1,10 @@ + 'civicrm', + 'js' => ['ang/crmAutosave.js'], + 'requires' => ['crmUtil'], +]; diff --git a/ang/crmAutosave.js b/ang/crmAutosave.js index a697880b8f11..359ccfebac89 100644 --- a/ang/crmAutosave.js +++ b/ang/crmAutosave.js @@ -1,7 +1,7 @@ /// crmAutosave (function(angular, $, _) { - angular.module('crmAutosave', ['crmUtil']); + angular.module('crmAutosave', CRM.angRequires('crmAutosave')); // usage: // var autosave = new CrmAutosaveCtrl({ diff --git a/ang/crmCaseType.ang.php b/ang/crmCaseType.ang.php new file mode 100644 index 000000000000..21596810d419 --- /dev/null +++ b/ang/crmCaseType.ang.php @@ -0,0 +1,20 @@ +addSetting([ + 'crmCaseType' => [ + 'REL_TYPE_CNAME' => CRM_Case_XMLProcessor::REL_TYPE_CNAME, + ], +]); + +return [ + 'ext' => 'civicrm', + 'js' => ['ang/crmCaseType.js'], + 'css' => ['ang/crmCaseType.css'], + 'partials' => ['ang/crmCaseType'], + 'requires' => ['ngRoute', 'ui.utils', 'crmUi', 'unsavedChanges', 'crmUtil', 'crmResource'], +]; diff --git a/ang/crmCaseType.css b/ang/crmCaseType.css index fa8edc12723b..6352b3d24dc8 100644 --- a/ang/crmCaseType.css +++ b/ang/crmCaseType.css @@ -2,8 +2,14 @@ vertical-align: middle; cursor: move; } -.crmCaseType .crm-i { - margin: 0.4em 0.2em 0 0; + +.crmCaseType .fa-pencil { + margin: 0.2em 0.2em 0 0; + cursor: pointer; +} + +.crmCaseType .fa-trash { + margin: 0.56em 0.2em 0 0; cursor: pointer; } diff --git a/ang/crmCaseType.js b/ang/crmCaseType.js index e5aae2a3516a..ee2344ece81a 100644 --- a/ang/crmCaseType.js +++ b/ang/crmCaseType.js @@ -1,6 +1,6 @@ (function(angular, $, _) { - var crmCaseType = angular.module('crmCaseType', ['ngRoute', 'ui.utils', 'crmUi', 'unsavedChanges', 'crmUtil']); + var crmCaseType = angular.module('crmCaseType', CRM.angRequires('crmCaseType')); // Note: This template will be passed to cloneDeep(), so don't put any funny stuff in here! var newCaseTypeTemplate = { @@ -50,16 +50,33 @@ apiCalls: function($route, crmApi) { var reqs = {}; reqs.actStatuses = ['OptionValue', 'get', { - option_group_id: 'activity_status' + option_group_id: 'activity_status', + sequential: 1, + options: {limit: 0} + }]; + reqs.caseStatuses = ['OptionValue', 'get', { + option_group_id: 'case_status', + sequential: 1, + options: {limit: 0} }]; reqs.actTypes = ['OptionValue', 'get', { option_group_id: 'activity_type', + sequential: 1, options: { sort: 'name', limit: 0 } }]; + reqs.defaultAssigneeTypes = ['OptionValue', 'get', { + option_group_id: 'activity_default_assignee', + sequential: 1, + options: { + limit: 0 + } + }]; reqs.relTypes = ['RelationshipType', 'get', { + sequential: 1, + is_active: 1, options: { sort: CRM.crmCaseType.REL_TYPE_CNAME, limit: 0 @@ -84,14 +101,6 @@ restrict: 'AE', template: '', link: function(scope, element, attrs) { - /// Format list of options for select2's "data" - var getFormattedOptions = function() { - return { - results: _.map(scope[attrs.crmOptions], function(option){ - return {id: option, text: option}; - }) - }; - }; var input = $('input', element); @@ -101,11 +110,14 @@ scope[attrs.crmVar] = ''; }; - $(input).select2({ - data: getFormattedOptions, + $(input).crmSelect2({ + data: function () { + return { results: scope[attrs.crmOptions] }; + }, createSearchChoice: function(term) { - return {id: term, text: term}; + return {id: term, text: term + ' (' + ts('new') + ')'}; }, + createSearchChoicePosition: 'bottom', placeholder: attrs.placeholder }); $(input).on('select2-selecting', function(e) { @@ -114,36 +126,217 @@ scope.$evalAsync('_resetSelection()'); e.preventDefault(); }); - - scope.$watch(attrs.crmOptions, function(value) { - $(input).select2('data', getFormattedOptions); - $(input).select2('val', ''); - }); } }; }); - crmCaseType.controller('CaseTypeCtrl', function($scope, crmApi, apiCalls) { - var ts = $scope.ts = CRM.ts(null); + crmCaseType.directive('crmEditableTabTitle', function($timeout) { + return { + restrict: 'AE', + link: function(scope, element, attrs) { + element.addClass('crm-editable crm-editable-enabled'); + var titleLabel = $(element).find('span'); + var penIcon = $('').prependTo(element); + var saveButton = $('').appendTo(element); + var cancelButton = $('').appendTo(element); + $('button', element).wrapAll(' -
    +
    + +
    {{ts('No available applications')}}
    diff --git a/ang/crmCxn/PermTable.html b/ang/crmCxn/PermTable.html index 08c35ff847b6..d2a1eabfed40 100644 --- a/ang/crmCxn/PermTable.html +++ b/ang/crmCxn/PermTable.html @@ -21,7 +21,7 @@ {{ts('Any')}} {{api.actions}} - + {{action}},
    diff --git a/ang/crmD3.ang.php b/ang/crmD3.ang.php new file mode 100644 index 000000000000..0a737341ae81 --- /dev/null +++ b/ang/crmD3.ang.php @@ -0,0 +1,16 @@ + 'civicrm', + 'js' => [ + 'ang/crmD3.js', + 'bower_components/d3/d3.min.js', + ], + 'requires' => [], +]; diff --git a/ang/crmD3.js b/ang/crmD3.js index eca29a3cfb4b..d06eeac9b40b 100644 --- a/ang/crmD3.js +++ b/ang/crmD3.js @@ -1,4 +1,3 @@ (function (angular, $, _) { - // thin stub for declaring dependencies - angular.module('crmD3', []); + angular.module('crmD3', CRM.angRequires('crmD3')); })(angular, CRM.$, CRM._); diff --git a/ang/crmExample.ang.php b/ang/crmExample.ang.php new file mode 100644 index 000000000000..763f1eaca2ab --- /dev/null +++ b/ang/crmExample.ang.php @@ -0,0 +1,11 @@ + 'civicrm', + 'js' => ['ang/crmExample.js'], + 'partials' => ['ang/crmExample'], + 'requires' => ['crmUtil', 'ngRoute', 'ui.utils', 'crmUi', 'crmResource'], +]; diff --git a/ang/crmExample.js b/ang/crmExample.js index d32d81d29a62..138ce8f12a5b 100644 --- a/ang/crmExample.js +++ b/ang/crmExample.js @@ -1,8 +1,6 @@ (function(angular, $, _) { - angular.module('crmExample', [ - 'crmUtil', 'ngRoute', 'ui.utils', 'crmUi' - ]); + angular.module('crmExample', CRM.angRequires('crmExample')); angular.module('crmExample').config([ '$routeProvider', diff --git a/ang/crmExample/example.html b/ang/crmExample/example.html index c9b6abd03f7d..5393e7c1a521 100644 --- a/ang/crmExample/example.html +++ b/ang/crmExample/example.html @@ -20,17 +20,16 @@
    - +
    - + - +
    -
    {{exampleForm[exName]|json}}
    diff --git a/ang/crmMailing.ang.php b/ang/crmMailing.ang.php new file mode 100644 index 000000000000..a3e140c6058a --- /dev/null +++ b/ang/crmMailing.ang.php @@ -0,0 +1,18 @@ + 'civicrm', + 'js' => [ + 'ang/crmMailing.js', + 'ang/crmMailing/*.js', + ], + 'css' => ['ang/crmMailing.css'], + 'partials' => ['ang/crmMailing'], + 'requires' => ['crmUtil', 'crmAttachment', 'crmAutosave', 'ngRoute', 'ui.utils', 'crmUi', 'dialogService', 'crmResource'], +]; diff --git a/ang/crmMailing.js b/ang/crmMailing.js index 7254b8a06ed9..95148bf27b3f 100644 --- a/ang/crmMailing.js +++ b/ang/crmMailing.js @@ -1,8 +1,6 @@ (function (angular, $, _) { - angular.module('crmMailing', [ - 'crmUtil', 'crmAttachment', 'crmAutosave', 'ngRoute', 'ui.utils', 'crmUi', 'dialogService' - ]); + angular.module('crmMailing', CRM.angRequires('crmMailing')); angular.module('crmMailing').config([ '$routeProvider', @@ -12,41 +10,50 @@ controller: 'ListMailingsCtrl' }); - var editorPaths = { - '': '~/crmMailing/EditMailingCtrl/2step.html', - '/unified': '~/crmMailing/EditMailingCtrl/unified.html', - '/unified2': '~/crmMailing/EditMailingCtrl/unified2.html', - '/wizard': '~/crmMailing/EditMailingCtrl/wizard.html' - }; - angular.forEach(editorPaths, function(editTemplate, pathSuffix) { - if (CRM && CRM.crmMailing && CRM.crmMailing.workflowEnabled) { - editTemplate = '~/crmMailing/EditMailingCtrl/workflow.html'; // override + if (!CRM || !CRM.crmMailing) { + return; + } + + $routeProvider.when('/mailing/new', { + template: '

    ' + ts('Initializing...') + '

    ', + controller: 'CreateMailingCtrl', + resolve: { + selectedMail: function(crmMailingMgr) { + var m = crmMailingMgr.create({ + template_type: CRM.crmMailing.templateTypes[0].name + }); + return crmMailingMgr.save(m); + } } - $routeProvider.when('/mailing/new' + pathSuffix, { - template: '

    ' + ts('Initializing...') + '

    ', - controller: 'CreateMailingCtrl', - resolve: { - selectedMail: function(crmMailingMgr) { - var m = crmMailingMgr.create(); - return crmMailingMgr.save(m); - } + }); + + $routeProvider.when('/mailing/new/:templateType', { + template: '

    ' + ts('Initializing...') + '

    ', + controller: 'CreateMailingCtrl', + resolve: { + selectedMail: function($route, crmMailingMgr) { + var m = crmMailingMgr.create({ + template_type: $route.current.params.templateType + }); + return crmMailingMgr.save(m); } - }); - $routeProvider.when('/mailing/:id' + pathSuffix, { - templateUrl: editTemplate, - controller: 'EditMailingCtrl', - resolve: { - selectedMail: function($route, crmMailingMgr) { - return crmMailingMgr.get($route.current.params.id); - }, - attachments: function($route, CrmAttachments) { - var attachments = new CrmAttachments(function () { - return {entity_table: 'civicrm_mailing', entity_id: $route.current.params.id}; - }); - return attachments.load(); - } + } + }); + + $routeProvider.when('/mailing/:id', { + templateUrl: '~/crmMailing/EditMailingCtrl/base.html', + controller: 'EditMailingCtrl', + resolve: { + selectedMail: function($route, crmMailingMgr) { + return crmMailingMgr.get($route.current.params.id); + }, + attachments: function($route, CrmAttachments) { + var attachments = new CrmAttachments(function () { + return {entity_table: 'civicrm_mailing', entity_id: $route.current.params.id}; + }); + return attachments.load(); } - }); + } }); } ]); diff --git a/ang/crmMailing/BlockMailing.html b/ang/crmMailing/BlockMailing.html index 432387719041..8e09974322ce 100644 --- a/ang/crmMailing/BlockMailing.html +++ b/ang/crmMailing/BlockMailing.html @@ -7,20 +7,7 @@
    -
    - - -
    +
    @@ -49,20 +36,18 @@
    -
    -
    +
    +
    - + />
    @@ -79,5 +64,19 @@ placeholder="Subject" name="subject" />
    +
    +
    + +
    +
    diff --git a/ang/crmMailing/BlockPreview.html b/ang/crmMailing/BlockPreview.html index a9e69eff9f8e..6315466e83cf 100644 --- a/ang/crmMailing/BlockPreview.html +++ b/ang/crmMailing/BlockPreview.html @@ -22,7 +22,7 @@
    --> -
    +
    {{ts('Send test email to:')}} @@ -30,32 +30,28 @@
    + crm-multiple-email + />
    - +
    -
    +
    {{ts('Send test email to group:')}}
    - + />
    - +
    diff --git a/ang/crmMailing/BlockPreview.js b/ang/crmMailing/BlockPreview.js index 561ddf09501c..5e582dc0566a 100644 --- a/ang/crmMailing/BlockPreview.js +++ b/ang/crmMailing/BlockPreview.js @@ -21,6 +21,7 @@ }); }; scope.doSend = function doSend(recipient) { + recipient = JSON.parse(JSON.stringify(recipient).replace(/\,\s/g, ',')); scope.$eval(attr.onSend, { preview: {recipient: recipient} }); @@ -29,16 +30,15 @@ scope.previewTestGroup = function(e) { var $dialog = $(this); $dialog.html('
    ').parent().find('button[data-op=yes]').prop('disabled', true); - $dialog.dialog('option', 'title', ts('Send to %1', {1: _.pluck(_.where(scope.crmMailingConst.groupNames, {id: scope.testGroup.gid}), 'title')[0]})); - CRM.api3('contact', 'get', { - group: scope.testGroup.gid, - options: {limit: 0}, - return: 'display_name,email' + CRM.api3({ + contact: ['contact', 'get', {group: scope.testGroup.gid, options: {limit: 0}, return: 'display_name,email'}], + group: ['group', 'getsingle', {id: scope.testGroup.gid, return: 'title'}] }).done(function(data) { + $dialog.dialog('option', 'title', ts('Send to %1', {1: data.group.title})); var count = 0, // Fixme: should this be in a template? - markup = '
      '; - _.each(data.values, function(row) { + markup = '
        '; + _.each(data.contact.values, function(row) { // Fixme: contact api doesn't seem capable of filtering out contacts with no email, so we're doing it client-side if (row.email) { count++; @@ -49,7 +49,7 @@ markup = '

        ' + ts('A test message will be sent to %1 people:', {1: count}) + '

        ' + markup; if (!count) { markup = '
        ' + - (data.count ? ts('None of the contacts in this group have an email address.') : ts('Group is empty.')) + + (data.contact.count ? ts('None of the contacts in this group have an email address.') : ts('Group is empty.')) + '
        '; } $dialog diff --git a/ang/crmMailing/BlockRecipients.html b/ang/crmMailing/BlockRecipients.html index 5852a8e97c7b..cedfa6dc2441 100644 --- a/ang/crmMailing/BlockRecipients.html +++ b/ang/crmMailing/BlockRecipients.html @@ -1,19 +1,15 @@ -
        - - - - + ng-required="true" /> + +
        + + {{getRecipientCount()}} +
        diff --git a/ang/crmMailing/BlockReview.html b/ang/crmMailing/BlockReview.html index 49b9232a6442..5a0021e40ab6 100644 --- a/ang/crmMailing/BlockReview.html +++ b/ang/crmMailing/BlockReview.html @@ -11,7 +11,7 @@
        - +
        ({{ts('Include:')}} {{getIncludesAsString(mailing)}})
        @@ -34,6 +34,9 @@
        {{ts('None')}}
        +
        + {{crmMailingConst.enabledLanguages[mailing.language]}} +
        diff --git a/ang/crmMailing/BlockTemplates.html b/ang/crmMailing/BlockTemplates.html new file mode 100644 index 000000000000..38a37a91fd77 --- /dev/null +++ b/ang/crmMailing/BlockTemplates.html @@ -0,0 +1,9 @@ +
        + + +
        \ No newline at end of file diff --git a/ang/crmMailing/BlockTemplates.js b/ang/crmMailing/BlockTemplates.js new file mode 100644 index 000000000000..9ee2efbfeea8 --- /dev/null +++ b/ang/crmMailing/BlockTemplates.js @@ -0,0 +1,5 @@ +(function(angular, $, _) { + angular.module('crmMailing').directive('crmMailingBlockTemplates', function(crmMailingSimpleDirective) { + return crmMailingSimpleDirective('crmMailingBlockTemplates', '~/crmMailing/BlockTemplates.html'); + }); +})(angular, CRM.$, CRM._); \ No newline at end of file diff --git a/ang/crmMailing/BodyHtml.html b/ang/crmMailing/BodyHtml.html index 730beecdd866..b38c28c76bfa 100644 --- a/ang/crmMailing/BodyHtml.html +++ b/ang/crmMailing/BodyHtml.html @@ -4,17 +4,18 @@
        - +